13 KiB
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/<PLATFORM>/Inc и Port/<PLATFORM>/Src.
Application adapter держите вне библиотеки либо в отдельном Adapter:
он планирует операции, переводит ошибки и связывает результат с остальным
проектом.
Шаг 2. Создать platform context
Context содержит только ресурсы одного физического 1-Wire master:
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
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. Инициализировать экземпляр
#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 должен:
- выполнить явный поиск или восстановить/проверить известные ROM;
- сериализовать Search ROM, Convert T, scratchpad и EEPROM-команды;
- запустить Convert T и вернуть управление;
- опрашивать ready с общим deadline;
- читать каждый ROM и публиковать значение только после CRC;
- восстановить state machine после disconnect, CRC error и timeout;
- не запускать поиск/EEPROM-запись из обычного temperature polling;
- синхронизировать доступ задач 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). Для поддержки режима порт обязан:
- включить активный high не позднее 10 мкс после команды Copy Scratchpad;
- удерживать его не менее 10 ms без другой активности 1-Wire;
- безопасно отключать strong pull-up при success, timeout и error;
- возвращать линию в open-drain idle перед Recall/readback;
- проверить обычное и 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-проверки выполняются без платы:
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 -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 переносите два поля диагностики лишь в свободные регистры, не сдвигая последующие окна.