# DS18B20: перенос на другую платформу ## Что переносится без изменений `Inc/ds18b20.h`, `Inc/ds18b20_config.h` и `Src/ds18b20.c` — переносимое C-ядро. Оно использует `stdint.h`, `stddef.h`, `string.h` и аппаратные callback. Не добавляйте в core HAL, RTOS, Modbus или глобальные дескрипторы конкретного проекта. ## Шаг 1. Добавить файлы в сборку Добавьте `Libraries/DS18B20/Inc` в include path и `Libraries/DS18B20/Src/ds18b20.c` в список исходников. Создайте отдельные каталоги `Port//Inc` и `Port//Src`. Application adapter держите вне библиотеки либо в отдельном `Adapter`: он планирует операции, переводит ошибки и связывает результат с остальным проектом. ## Шаг 2. Создать platform context Context содержит только ресурсы одного физического 1-Wire master: ```c typedef struct { gpio_handle_t gpio; timer_handle_t timer; uint32_t timer_ticks_per_us; irq_state_t saved_irq_state; } ds18b20_my_mcu_t; ``` Не используйте скрытый изменяемый singleton. Для двух шин создаются два context, два `ds18b20_t` и два массива ROM. ## Шаг 3. Реализовать callbacks ```c static void drive_low(void *context); static void release_line(void *context); static uint8_t read_line(void *context); static void delay_us(void *context, uint32_t us); static uint32_t tick_ms(void *context); static void critical_enter(void *context); static void critical_exit(void *context); const ds18b20_onewire_ops_t ds18b20_my_mcu_ops = { drive_low, release_line, read_line, delay_us, tick_ms, critical_enter, critical_exit }; ``` ### GPIO - Линия 1-Wire работает только как open-drain: порт либо тянет её к 0, либо переходит в высокоимпедансное состояние. - Запрещён push-pull высокий уровень. - `release_line` не должна ждать; `read_line` читает реальный pin level. - Номинал внешнего pull-up и допустимая длина/ёмкость шины выбираются по электрическим условиям конкретной платы. STM32F4 port оставляет pin в `GPIO_MODE_OUTPUT_OD`, отпускает линию записью единицы в BSRR и читает IDR без переключения MODER. ### Микросекундная задержка `delay_us` обязана быть монотонной и достаточно точной для 1-Wire standard speed. Не используйте scheduler sleep с миллисекундной гранулярностью. Если задержка основана на hardware timer: - таймер запускается до инициализации библиотеки; - он считает непрерывно во всех вызывающих контекстах; - учитывается переполнение счётчика; - произведение `us * timer_ticks_per_us` не должно переполняться в диапазоне используемых библиотекой задержек; - частота таймера и `timer_ticks_per_us` должны совпадать. ### Tick и критическая секция `tick_ms` нужен только для `ds18b20_wait()`; state-machine adapter может не использовать блокирующий wait. Вычитание tick выполняется как `uint32_t` и допускает wrap. Critical callbacks должны сохранять и восстанавливать предыдущее состояние прерываний, а не безусловно включать их. Если платформа гарантирует timing иначе, оба callback можно оставить `NULL`. ## Шаг 4. Инициализировать экземпляр ```c #define DS_CAPACITY 8U static ds18b20_my_mcu_t port_context; static ds18b20_t bus; static uint8_t rom_storage[DS_CAPACITY][DS18B20_ROM_SIZE]; platform_gpio_timer_init(&port_context); ds18b20_status_t status = ds18b20_init(&bus, &ds18b20_my_mcu_ops, &port_context, rom_storage, DS_CAPACITY); ``` Все три объекта должны жить столько же, сколько используется `bus`. Стековый context или ROM storage нельзя передавать экземпляру, переживающему функцию. ## Шаг 5. Создать application adapter Adapter должен: 1. выполнить явный поиск или восстановить/проверить известные ROM; 2. сериализовать Search ROM, Convert T, scratchpad и EEPROM-команды; 3. запустить Convert T и вернуть управление; 4. опрашивать ready с общим deadline; 5. читать каждый ROM и публиковать значение только после CRC; 6. восстановить state machine после disconnect, CRC error и timeout; 7. не запускать поиск/EEPROM-запись из обычного temperature polling; 8. синхронизировать доступ задач RTOS mutex-ом на уровне экземпляра. Для циклического приложения вызывайте `ds18b20_search_step()` только когда шина не занята преобразованием температуры или User Byte. Храните timeout, отмену и sequence в адаптере приложения: portable core не зависит от HAL, Modbus, GUI и глобального `hdallas`. Не используйте critical callbacks библиотеки как mutex: они защищают короткий 1-Wire slot и могут запрещать прерывания. ## Память и выравнивание - Core не использует heap. - На каждую шину требуется `sizeof(ds18b20_t)` плюс `rom_capacity * DS18B20_ROM_SIZE` байт ROM storage. - Scratchpad — 9 байт у вызывающей стороны. - Специального DMA-выравнивания core не требует; соблюдайте обычное выравнивание C-типов для `ds18b20_t` и context. - `rom_capacity` измеряется в элементах `uint8_t[8]`. ## Timing и питание DS18B20 с внешним питанием может сигнализировать готовность через read slot. Для parasite power требуется strong pull-up на всё время Convert T и Copy Scratchpad. Контракт предоставляет optional callback `strong_pullup(context, enable)`. Для поддержки режима порт обязан: 1. включить активный high не позднее 10 мкс после команды Copy Scratchpad; 2. удерживать его не менее 10 ms без другой активности 1-Wire; 3. безопасно отключать strong pull-up при success, timeout и error; 4. возвращать линию в open-drain idle перед Recall/readback; 5. проверить обычное и parasite-powered подключение на реальной плате. ## Перенос STM32F4 HAL port Перед `ds18b20_stm32f4_hal_init()` заполните: | Поле | Требование | | --- | --- | | `port` | Валидный `GPIO_TypeDef *` с включённым clock | | `pin` | Одна ненулевая GPIO mask | | `timer` | Запущенный свободно работающий `TIM_TypeDef *` | | `timer_ticks_per_us` | Ненулевое число timer ticks за 1 мкс | Порт использует `HAL_GetTick()` и CMSIS PRIMASK. При переносе на другую STM32 семью проверьте HAL-заголовок, разрядность/частоту timer, GPIO BSRR/IDR и способ сохранения interrupt state. ## Проверки Host/mock-проверки выполняются без платы: ```powershell python -m unittest Libraries.PortableTests.test_portable_models git diff --check ``` Для нового порта добавьте тесты reset/presence, write/read slots, timing bounds, CRC error, нескольких экземпляров, timeout и восстановления после ошибки. Затем выполните целевую сборку без ошибок/предупреждений. ## Перенос User Byte mailbox adapter Core-модуль не зависит от Modbus. При переносе STM32 adapter сохраните `selector + contractVersion=2` как одну транзакцию и APPLY как отдельную последнюю запись. Input обязан возвращать selector/version echo. Legacy version `0` можно принимать только для TH; TL без version `2` должен завершаться `invalid`, а не значением TH. Host-проверки adapter: ```powershell powershell -ExecutionPolicy Bypass -File Modules/UserByte/Adapter/STM32_Modbus/Tests/run_host_tests.ps1 ``` ## Checklist - [ ] Core собирается без HAL/RTOS/Modbus include. - [ ] У каждой шины отдельные instance, context и ROM storage. - [ ] GPIO физически open-drain и никогда не выдаёт push-pull high. - [ ] Есть корректный внешний pull-up и общий GND. - [ ] Таймер запущен, частота и overflow проверены. - [ ] Critical section восстанавливает предыдущее состояние. - [ ] Поиск, conversion, scratchpad и EEPROM сериализованы. - [ ] У state machine есть deadline и восстановление после ошибок. - [ ] CRC ROM и scratchpad проверяется до публикации данных. - [ ] Host/mock-тесты и `git diff --check` проходят. - [ ] Целевая сборка даёт 0 ошибок и 0 предупреждений. - [ ] Работа проверена на реальной шине с 0, 1 и несколькими датчиками. - [ ] Parasite power отмечен неподдерживаемым либо проверен со strong pull-up. # Strong pull-up Если порт поддерживает parasite power, добавьте callback `strong_pullup(context, enable)`: включение должно немедленно активно держать high после Copy Scratchpad, выключение — вернуть open-drain idle. Без безопасной аппаратной реализации не разрешайте parasite mode вызывающему приложению. ### Импорт известных адресов После ds18b20_init передайте каждый сохранённый ROM в ds18b20_add_known_rom. Проверяйте возвращаемый статус и лимит ёмкости. Хранилище ROM принадлежит вызывающему коду и живёт всё время работы шины. Импорт не требует GPIO-транзакций и не заменяет проверку CRC температуры. Сохраняйте список перед search_begin (он очищает результаты); по завершении или отмене добавляйте прежние ID обратно вне активной конверсии. Для нескольких шин храните снимки и происхождение ID отдельно для каждой. ### Перенос поиска с повторами Пересоберите всех потребителей: ds18b20_t расширен состоянием поиска и диагностикой. Рабочие буферы остаются caller-owned, HAL и Flash в ядре не нужны. Только BUSY означает продолжение; остальные результаты терминальные. Держите одного владельца шины до завершения/отмены поиска. Адаптер при отмене сбрасывает search_active перед адресным опросом. Search begin нельзя запускать во время strong pull-up. Пауза recovery действует только при search_active. Новые callbacks не требуются; delay_us должен поддерживать добавочные 20 мкс. Проверьте пределы config и внешний deadline. Для Modbus переносите два поля диагностики лишь в свободные регистры, не сдвигая последующие окна.