Files
templates/c/ds18b20/instance/PORTING.md

13 KiB
Raw Blame History

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 должен:

  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-проверки выполняются без платы:

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 переносите два поля диагностики лишь в свободные регистры, не сдвигая последующие окна.