# ds18b20 Драйвер термометров **DS18B20** поверх программной шины **1-Wire**. Вынесен из проекта `KONOR_ds18b20` и подключается в него сабмодулем. Ядро на C99: ни динамической памяти, ни ОС, ни `stm32f10x.h`. Всё аппаратно-зависимое собрано в порте — на новую платформу переносятся пять функций, остальное собирается как есть. ``` приложение │ ds18b20.c поиск датчиков, чтение и запись scratchpad │ onewire.c побайтовый обмен, CRC8, SEARCH ROM, MATCH/SKIP ROM │ ports/<платформа> Init, DelayUs, Reset, WriteBit, ReadBit ``` ## Состав | Файл | Что делает | Зависимости | |---|---|---| | `include/ds18b20.h`, `src/ds18b20.c` | перечень датчиков, измерения, запись TH/TL/разрешения | `onewire.h`, `stdint.h` | | `include/onewire.h`, `src/onewire.c` | байты и блоки, CRC8 Dallas/Maxim, обход SEARCH ROM | `stdint.h` | | `ports/stm32f1/` | тайминги Maxim, вывод с открытым стоком, задержки по DWT | CMSIS `stm32f10x.h` | | `ports/stm32g4/` | то же для STM32G431 и STM32G474 | CMSIS `stm32g4xx.h` | | `ports/stm32f4/` | то же для STM32F407VET6 и родственных F4 | CMSIS `stm32f4xx.h` | | `tests/` | симулятор шины с виртуальными датчиками и host-тесты | — | ## Порт: что нужно от платформы ```c void OneWire_Init(void); /* настроить вывод и источник задержек */ void OneWire_DelayUs(uint32_t us); uint8_t OneWire_Reset(void); /* сброс и проверка presence */ void OneWire_WriteBit(uint8_t bit); uint8_t OneWire_ReadBit(void); ``` Всё остальное — `OneWire_WriteByte`, `OneWire_ReadByte`, `OneWire_Crc8`, `OneWire_SearchNext`, `OneWire_SelectRom`, `OneWire_SkipRom` и весь `ds18b20.c` — уже написано и от платформы не зависит. ### Готовый порт STM32F1 Вывод настроен как выход с открытым стоком; чтение идёт через `IDR`, поэтому режим не переключается. Прерывания запрещаются только на длительность одного тайм-слота, чтобы не терять байты USART. Источник микросекундных задержек — счётчик тактов ядра `DWT->CYCCNT`. Привязку вывода порт берёт из `onewire_config.h`, который предоставляет приложение — шаблон лежит рядом: ```c #include "stm32f10x.h" #define ONEWIRE_GPIO GPIOA #define ONEWIRE_PIN 1U #define ONEWIRE_GPIO_CLOCK RCC_APB2ENR_IOPAEN ``` Требуется внешняя подтяжка 4.7 кОм к питанию. ### Готовые порты STM32G4 и STM32F4 Логика та же, что и в порте STM32F1: открытый сток, чтение через `IDR`, задержки по `DWT->CYCCNT`, запрет прерываний только на один тайм-слот. Отличается лишь настройка вывода — у G4 и F4 это `MODER`/`OTYPER`/ `OSPEEDR`/`PUPDR` вместо `CRL`/`CRH`, а низкий уровень выставляется через верхнюю половину `BSRR`. STM32G431 и STM32G474 обслуживает один порт `ports/stm32g4/`: блок GPIO и шина тактирования у них совпадают, различается только CMSIS-заголовок в `onewire_config.h`. Шаблоны лежат рядом — по одному на каждый камень: ```c /* ports/stm32g4/onewire_config.g474.template.h */ #include "stm32g4xx.h" #define ONEWIRE_GPIO GPIOA #define ONEWIRE_PIN 1U #define ONEWIRE_GPIO_CLOCK RCC_AHB2ENR_GPIOAEN /* у F4 — RCC_AHB1ENR_GPIOAEN */ ``` Порт `ports/stm32f4/` написан под STM32F407VET6 и подходит любому F4 с тем же блоком GPIO; тактирование включается в `RCC->AHB1ENR`. Подтяжка 4.7 кОм — внешняя и здесь: внутренняя `PUPDR` намеренно оставлена выключенной, её сопротивления для шины не хватает. ## Использование ```c DS18B20_Bus bus; OneWire_Init(); DS18B20_Scan(&bus); /* найти датчики на шине */ DS18B20_StartConversion(); /* SKIP ROM + CONVERT T */ /* выдержать DS18B20_ConversionTimeMs(config) */ DS18B20_ReadAll(&bus); /* обновить измерения */ for (uint8_t i = 0; i < bus.count; i++) { if (bus.sensors[i].status & DS18B20_STATUS_VALID) { float celsius = bus.sensors[i].raw / 16.0f; } } ``` Ошибка одного датчика не прерывает опрос остальных — она видна в поле `status` соответствующей записи. ## Тесты Симулятор шины реализует те же пять функций, что и настоящий порт, но работает не с выводом, а с моделью: виртуальные датчики отвечают побитно, включая полный алгоритм `SEARCH ROM`. Так на хосте проверяется именно то, что иначе проверялось бы только осциллографом. ```bash cmake -B build && cmake --build build && ctest --test-dir build ``` ```bash clang -std=c99 -Wall -Wextra -Iinclude -Itests \ tests/test_ds18b20.c tests/fake_onewire.c src/onewire.c src/ds18b20.c -o test && ./test ``` Покрыто: CRC8, обход шины с несколькими датчиками и пустой шины, отсев чужих семейств по коду ROM, паразитное питание, битый scratchpad, пропажа датчика, подтверждение и отказ записи, сохранение в EEPROM датчика, пределы разрешения 9..12 бит. ## Особенности, которые стоит знать * **Порядок датчиков задаёт шина, а не код.** `SEARCH ROM` обходит их в порядке битов идентификатора, поэтому индекс в `bus.sensors[]` не совпадает с порядком подключения. Искать датчик нужно по ROM через `DS18B20_Find()`. * **Пропажу датчика на живой шине не видно по presence.** Импульс подтверждают соседи, и `MATCH ROM` к снятому датчику проходит без признаков ошибки — она проявится как `DS18B20_STATUS_CRC_ERROR`. `DS18B20_STATUS_NO_PRESENCE` означает, что молчит вся шина. * **`DS18B20_MAX_SENSORS` равен 8** — столько датчиков помещается в один кадр `SENSOR_DATA` протокола SETGUI. Для другого применения значение можно поднять. * **Обратное чтение после записи обязательно.** Датчик может ответить и оставить регистры прежними; такой случай отличается от ошибки шины кодом `DS18B20_WRITE_REJECTED`. ## Лицензия и происхождение Код вынесен из `KONOR_ds18b20` (модуль термометрии на STM32F103C8T6) без изменения поведения: разделение на ядро и порт сделано переносом функций, тела не правились.