385 lines
21 KiB
Markdown
385 lines
21 KiB
Markdown
# DS18B20: справочник API
|
||
|
||
## Назначение
|
||
|
||
Публичный API ядра объявлен только в `Inc/ds18b20.h`. Приложение компилирует
|
||
`Src/ds18b20.c`, но не включает этот файл. Библиотека выполняет Search ROM,
|
||
проверяет Dallas CRC8, запускает Convert T, читает scratchpad, декодирует
|
||
температуру, меняет разрешение и TH/TL.
|
||
|
||
## Константы и состояние
|
||
|
||
- `DS18B20_ROM_SIZE` — 8 байт полного 64-битного ROM.
|
||
- `DS18B20_SCRATCHPAD_SIZE` — 9 байт scratchpad вместе с CRC.
|
||
- `DS18B20_DEFAULT_TIMEOUT_MS` — 750 мс, значение конфигурации по умолчанию.
|
||
- `DS18B20_FAMILY_CODE` — `0x28`.
|
||
- `ds18b20_t` — состояние одной шины. После инициализации его поля напрямую не
|
||
изменяют; список датчиков читают через `ds18b20_count()` и `ds18b20_rom()`.
|
||
|
||
## Platform callbacks
|
||
|
||
`ds18b20_onewire_ops_t` связывает переносимое ядро с аппаратурой:
|
||
|
||
| Callback | Обязателен | Контракт |
|
||
| --- | --- | --- |
|
||
| `drive_low(context)` | да | Активно притянуть open-drain линию к 0 |
|
||
| `release(context)` | да | Отпустить линию; внешний/внутренний pull-up поднимает её |
|
||
| `read(context)` | да | Вернуть текущий логический уровень `0` или `1` |
|
||
| `delay_us(context, us)` | да | Синхронная задержка с микросекундной точностью |
|
||
| `tick_ms(context)` | для `ds18b20_wait` | Монотонный, допускающий uint32 wrap tick |
|
||
| `critical_enter(context)` | нет | Начать защиту одного временного слота |
|
||
| critical_exit(context) | нет | Восстановить состояние после защиты слота |
|
||
| strong_pullup(context, enable) | для parasite Copy | Активно удерживать high и безопасно вернуть open-drain |
|
||
|
||
Обе функции critical section задаются парой либо обе оставляются `NULL`.
|
||
|
||
## Коды возврата
|
||
|
||
| Код | Значение | Значение для приложения |
|
||
| --- | ---: | --- |
|
||
| `DS18B20_OK` | 0 | Успех или преобразование готово |
|
||
| `DS18B20_E_ARGUMENT` | -1 | Неверный указатель, callback, размер или параметр |
|
||
| `DS18B20_E_IO` | -2 | Некорректная конфигурация scratchpad |
|
||
| `DS18B20_E_NO_DEVICE` | -3 | Нет presence pulse / подходящих устройств |
|
||
| `DS18B20_E_CRC` | -4 | CRC ROM или scratchpad не совпал |
|
||
| `DS18B20_E_TIMEOUT` | -5 | Истёк timeout блокирующего ожидания |
|
||
| `DS18B20_E_BUSY` | -6 | Преобразование ещё не готово |
|
||
| `DS18B20_E_CAPACITY` | -7 | Найдено больше ROM, чем помещается в storage |
|
||
| DS18B20_E_ROM | -8 | ROM имеет неверный family code либо отклонён адресной операцией |
|
||
| DS18B20_E_POWER | -9 | Parasite Copy запрошен без strong-pull-up callback |
|
||
|
||
## Инициализация и поиск
|
||
|
||
### `ds18b20_init`
|
||
|
||
```c
|
||
ds18b20_status_t ds18b20_init(
|
||
ds18b20_t *instance,
|
||
const ds18b20_onewire_ops_t *ops,
|
||
void *platform_context,
|
||
uint8_t (*rom_storage)[DS18B20_ROM_SIZE],
|
||
size_t rom_capacity);
|
||
```
|
||
|
||
Обнуляет состояние, сохраняет callback/context/storage и отпускает линию.
|
||
`rom_storage` — массив приложения, `rom_capacity` — число ROM, не число байт.
|
||
Core не выделяет и не освобождает память.
|
||
|
||
### `ds18b20_search`
|
||
|
||
```c
|
||
ds18b20_status_t ds18b20_search(ds18b20_t *instance);
|
||
```
|
||
|
||
Заново выполняет Search ROM и заменяет прежний список. Сохраняются только ROM с
|
||
family `0x28` и корректным CRC. Результаты поиска:
|
||
|
||
- `DS18B20_OK` — найден минимум один корректный DS18B20;
|
||
- `DS18B20_E_NO_DEVICE` — корректные DS18B20 не найдены;
|
||
- `DS18B20_E_CAPACITY` — storage заполнен; уже записанные ROM остаются доступны.
|
||
|
||
### `ds18b20_count` и `ds18b20_rom`
|
||
|
||
```c
|
||
size_t ds18b20_count(const ds18b20_t *instance);
|
||
const uint8_t *ds18b20_rom(const ds18b20_t *instance, size_t index);
|
||
```
|
||
|
||
`count` возвращает число сохранённых ROM либо 0 для неверного экземпляра.
|
||
`rom` возвращает указатель на 8 байт либо `NULL` для неверного индекса.
|
||
Указатель становится логически устаревшим после следующего поиска.
|
||
|
||
## Преобразование и чтение
|
||
|
||
### `ds18b20_start_all`
|
||
|
||
```c
|
||
ds18b20_status_t ds18b20_start_all(ds18b20_t *instance);
|
||
```
|
||
|
||
Посылает `Skip ROM + Convert T` всем устройствам шины. Возвращает
|
||
`DS18B20_E_NO_DEVICE`, если нет presence pulse.
|
||
|
||
### `ds18b20_start`
|
||
|
||
```c
|
||
ds18b20_status_t ds18b20_start(
|
||
ds18b20_t *instance,
|
||
const uint8_t rom[DS18B20_ROM_SIZE]);
|
||
```
|
||
|
||
Проверяет ROM и посылает `Match ROM + Convert T` одному датчику.
|
||
|
||
### `ds18b20_conversion_ready`
|
||
|
||
```c
|
||
ds18b20_status_t ds18b20_conversion_ready(ds18b20_t *instance);
|
||
```
|
||
|
||
Один раз читает 1-Wire ready bit: `DS18B20_OK` означает готовность,
|
||
`DS18B20_E_BUSY` — преобразование продолжается. Это предпочтительная
|
||
неблокирующая проверка для main loop/RTOS.
|
||
|
||
### `ds18b20_wait`
|
||
|
||
```c
|
||
ds18b20_status_t ds18b20_wait(
|
||
ds18b20_t *instance, uint32_t timeout_ms);
|
||
```
|
||
|
||
Блокирующе опрашивает ready bit до готовности или `DS18B20_E_TIMEOUT`. Требует
|
||
`tick_ms`. Функция не делает sleep/yield и не рекомендуется в основном цикле.
|
||
|
||
### `ds18b20_read_scratchpad`
|
||
|
||
```c
|
||
ds18b20_status_t ds18b20_read_scratchpad(
|
||
ds18b20_t *instance,
|
||
const uint8_t rom[DS18B20_ROM_SIZE],
|
||
uint8_t scratchpad[DS18B20_SCRATCHPAD_SIZE]);
|
||
```
|
||
|
||
Проверяет ROM, посылает `Match ROM + Read Scratchpad`, читает 9 байт и
|
||
проверяет CRC. Функция не запускает Convert T и не проверяет, что преобразование
|
||
ранее завершилось.
|
||
|
||
### `ds18b20_decode_temperature`
|
||
|
||
```c
|
||
ds18b20_status_t ds18b20_decode_temperature(
|
||
const uint8_t scratchpad[DS18B20_SCRATCHPAD_SIZE],
|
||
float *temperature_c);
|
||
```
|
||
|
||
Проверяет CRC, маскирует неопределённые младшие биты согласно разрешению 9–12
|
||
бит и возвращает градусы Цельсия. Неизвестная комбинация configuration bits
|
||
даёт `DS18B20_E_IO`.
|
||
|
||
## Конфигурация и User Bytes
|
||
|
||
### `ds18b20_set_resolution`
|
||
|
||
```c
|
||
ds18b20_status_t ds18b20_set_resolution(
|
||
ds18b20_t *instance,
|
||
const uint8_t rom[DS18B20_ROM_SIZE],
|
||
uint8_t bits);
|
||
```
|
||
|
||
`bits` принимает только `9`, `10`, `11` или `12`. Функция читает
|
||
scratchpad, сохраняет TH/TL, записывает новый configuration byte и посылает
|
||
`Copy Scratchpad`.
|
||
|
||
### `ds18b20_write_user_bytes`
|
||
|
||
```c
|
||
ds18b20_status_t ds18b20_write_user_bytes(
|
||
ds18b20_t *instance,
|
||
const uint8_t rom[DS18B20_ROM_SIZE],
|
||
int16_t bytes12,
|
||
int16_t bytes34,
|
||
uint8_t mask);
|
||
```
|
||
|
||
Текущий контракт отражает физические writable bytes DS18B20:
|
||
|
||
- `mask & 0x01` записывает младшие 8 бит `bytes12` в TH, scratchpad[2];
|
||
- `mask & 0x02` записывает старшие 8 бит `bytes12` в TL, scratchpad[3];
|
||
- `bytes34` зарезервирован и не используется: scratchpad[6]/[7] read-only;
|
||
- остальные биты `mask` игнорируются.
|
||
|
||
Перед записью функция читает scratchpad, поэтому невыбранный TH/TL и
|
||
configuration byte сохраняются. Затем выполняются `Write Scratchpad` и
|
||
`Copy Scratchpad`. Функция не выполняет no-op detection, Recall E2 и readback.
|
||
Application adapter обязан добавить эти шаги, если операция используется как
|
||
подтверждённая пользовательская запись.
|
||
|
||
## CRC и ROM
|
||
|
||
```c
|
||
uint8_t ds18b20_crc8(const void *data, size_t length);
|
||
ds18b20_status_t ds18b20_validate_rom(
|
||
const uint8_t rom[DS18B20_ROM_SIZE]);
|
||
```
|
||
|
||
`crc8` вычисляет Dallas/Maxim CRC-8. `validate_rom` отдельно проверяет family
|
||
`0x28` и CRC первых семи байтов против восьмого.
|
||
|
||
## Неблокирующий пример опроса
|
||
|
||
```c
|
||
enum poll_state { POLL_START, POLL_WAIT };
|
||
static enum poll_state state = POLL_START;
|
||
static uint8_t scratchpad[DS18B20_SCRATCHPAD_SIZE];
|
||
|
||
void poll_ds18b20(void)
|
||
{
|
||
if (state == POLL_START) {
|
||
if (ds18b20_start_all(&bus) == DS18B20_OK) {
|
||
state = POLL_WAIT;
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (ds18b20_conversion_ready(&bus) != DS18B20_OK) {
|
||
return;
|
||
}
|
||
|
||
for (size_t i = 0; i < ds18b20_count(&bus); ++i) {
|
||
const uint8_t *rom = ds18b20_rom(&bus, i);
|
||
float temperature_c;
|
||
if (ds18b20_read_scratchpad(&bus, rom, scratchpad) == DS18B20_OK &&
|
||
ds18b20_decode_temperature(scratchpad, &temperature_c) ==
|
||
DS18B20_OK) {
|
||
publish_temperature(rom, temperature_c);
|
||
}
|
||
}
|
||
state = POLL_START;
|
||
}
|
||
```
|
||
|
||
Production adapter должен дополнительно иметь deadline: постоянный
|
||
`DS18B20_E_BUSY` не должен навсегда удерживать state machine.
|
||
|
||
## Диагностика
|
||
|
||
| Симптом | Проверка |
|
||
| --- | --- |
|
||
| `DS18B20_E_NO_DEVICE` | Питание, общий GND, pull-up, pin, presence pulse |
|
||
| Постоянный `DS18B20_E_BUSY` | Deadline, питание датчика, конфликт операций |
|
||
| `DS18B20_E_CRC` | Длина/топология шины, помехи, timing, pull-up |
|
||
| `DS18B20_E_CAPACITY` | Увеличить ROM storage или ограничить число устройств |
|
||
| Неверная температура | Не читать до ready; проверить CRC и resolution bits |
|
||
| Зависание в delay | Таймер должен быть запущен и считать непрерывно |
|
||
|
||
## Ограничения
|
||
|
||
- Вызовы одной шины не reentrant и не ISR-safe.
|
||
- Микросекундные 1-Wire-слоты синхронные; длительное преобразование должно быть
|
||
вынесено в state machine.
|
||
- Callback `strong_pullup` используется модулем UserByte для parasite-powered
|
||
`Copy Scratchpad`; аппаратная схема и timing должны быть проверены на плате.
|
||
- Low-level `Copy Scratchpad` намеренно разделён на `start/finish`; выдержку
|
||
10 ms, Recall E2, CRC/readback и освобождение шины обеспечивает state machine
|
||
`Modules/UserByte`, а не одиночный low-level вызов.
|
||
- Поиск синхронный и не имеет cancel callback; для большой шины или жёстких
|
||
realtime-требований нужен пошаговый автомат поиска.
|
||
|
||
## Тесты
|
||
|
||
Из корня репозитория:
|
||
|
||
```powershell
|
||
python -m unittest Libraries.PortableTests.test_portable_models
|
||
```
|
||
|
||
Модель проверяет CRC, независимость экземпляров и неблокирующую интеграцию.
|
||
`Libraries/PortableTests/test_portable_libraries.c` дополнительно проверяет C
|
||
API CRC, декодирование температуры и независимость двух шин. Для полной задачи
|
||
также обязательны `git diff --check` и целевая Keil-сборка `0/0`.
|
||
# User Byte и EEPROM
|
||
|
||
Для новой логики приложения используйте внутренний модуль `Modules/UserByte`
|
||
с явным selector `TH/TL`, а не legacy `ds18b20_write_user_bytes`. Low-level API `write_scratchpad`, `copy_*`,
|
||
`recall_e2`, `recall_ready` и `recover_bus` предназначен для его state machine.
|
||
`strong_pullup` обязателен только для parasite-powered Copy Scratchpad.
|
||
|
||
`ds18b20_search_retry_due` помогает приложению повторять поиск при `count=0`,
|
||
не вмешиваясь в активную температурную конверсию; период и tick задаёт adapter.
|
||
## Версия Modbus-контракта User Byte
|
||
|
||
### Явный неблокирующий поиск
|
||
|
||
```c
|
||
ds18b20_status_t status = ds18b20_search_begin(&bus);
|
||
while (status == DS18B20_E_BUSY ||
|
||
status == DS18B20_E_CRC ||
|
||
status == DS18B20_E_ROM) {
|
||
status = ds18b20_search_step(&bus);
|
||
/* Между шагами основной цикл продолжает обслуживать Modbus/RTC/SD. */
|
||
}
|
||
```
|
||
|
||
Не вызывайте `ds18b20_search_retry_due()` для GUI-команды: reconnect и polling
|
||
не должны автоматически менять таблицу ROM. В проектном адаптере команда
|
||
захватывается только по `apply=1`, а `sequence` защищает от старого ответа.
|
||
|
||
Legacy API `ds18b20_user_byte_submit()` остаётся TH-only. Для явного TL
|
||
используется selector `DS18B20_USER_BYTE_TL`; STM32 adapter принимает его только
|
||
с `contractVersion=2`, записанным атомарно с selector до APPLY. Это защищает GUI
|
||
от старого bridge/firmware, которое всегда маршрутизировало запрос как TH.
|
||
|
||
### Известные ROM и ошибка поиска detail 2
|
||
|
||
В F407 сохранённые SensorBindings восстанавливаются в список опроса после
|
||
AppStorage_Init. Если список непустой, стартовый SEARCH ROM не запускается.
|
||
Температура читается адресно через MATCH ROM; запуск преобразования общий.
|
||
Даже при отсутствии датчика во время старта его адрес остаётся доступен для
|
||
повторного чтения. Подключение подтверждается успешным чтением температуры.
|
||
|
||
Явный поиск добавляет новые ID. При успехе, ошибке, тайм-ауте и отмене
|
||
Dallas_FinishSearch объединяет результат с прежними ID, включая ещё не
|
||
сохранённые во Flash. Ёмкость списка — 32 ID; найденные ID занимают места
|
||
первыми. Сохранение использует существующий SensorBindings_SyncFound и
|
||
повтор при BUSY. Отключать питание следует после завершения сохранения.
|
||
|
||
MCU detail 2 = DS18B20_E_IO: поиск ROM получил недопустимую комбинацию
|
||
битов либо неполный ROM. Это не доказательство конкретной причины на кабеле.
|
||
Ошибка поиска остаётся видна, но известные ID продолжают опрашиваться.
|
||
Число в каталоге включает известные адреса; наличие определяется connected.
|
||
|
||
API ds18b20_add_known_rom проверяет family/CRC, возвращает E_ARGUMENT,
|
||
E_ROM, E_CRC или E_CAPACITY; дубликат возвращает OK. Функция не делает
|
||
I/O и не подтверждает наличие датчика. Вызывать вне поиска и конверсии.
|
||
|
||
Проверки: `Libraries/DS18B20/Tests/run_host_tests.ps1` и
|
||
`python -m unittest discover -s tests -p test_known_rom_host.py`.
|
||
Для аппаратной проверки: найти датчики по одному, дождаться сохранения,
|
||
подключить все на 20 м, перезапустить МК без команды поиска и проверить
|
||
обновление температур; затем повторить при ошибке/отмене поиска.
|
||
|
||
### Поиск с восстановлением после ошибок
|
||
|
||
Настройки в `Inc/ds18b20_config.h`: три прохода дерева, четыре дополнительных
|
||
повтора каждой неудачной ветки (пять попыток суммарно). Один вызов step делает
|
||
не более одной попытки ROM: Modbus и остальные сервисы работают между ними.
|
||
Перед попыткой сохраняются ROM-путь и discrepancy; после IO, отсутствия presence
|
||
или CRC они восстанавливаются. После пяти ошибок начинается следующий проход.
|
||
Результаты всех проходов объединяются; дубликаты не занимают ёмкость. CRC
|
||
проверяется до принятия пути. Чужое семейство с корректным CRC пропускается.
|
||
|
||
Успех означает, что хотя бы один проход полностью обошёл дерево и найден хотя
|
||
бы один DS18B20. Ранее восстановленные ошибки сохраняются в диагностике, но
|
||
не превращают успешный поиск в CRC_ERROR. Это не гарантия обнаружения каждого
|
||
физического датчика на нестабильной линии. Если ни один проход не завершён,
|
||
возвращается последняя ошибка; проверенные частичные результаты сохраняются.
|
||
Пустая линия ограничена 15 попытками reset. Общий предел 512 попыток защищает
|
||
и блокирующий API; приложение дополнительно ограничивает поиск 12000 мс.
|
||
|
||
Во время поиска после каждого слота добавляется 20 мкс высокого уровня:
|
||
после записи нуля получается минимум 30 мкс вместо 10 мкс. Импульсы 6/60 мкс,
|
||
read-init 3 мкс и выборка через дополнительные 10 мкс остаются прежними.
|
||
Добавочная пауза выполняется с разрешёнными прерываниями. Обычное чтение,
|
||
конверсия и Copy Scratchpad не получают эту добавку. Подбор паузы для кабеля
|
||
требует измерений; программная модель не подтверждает аналоговый фронт.
|
||
|
||
`bus.search_diagnostics` содержит attempts, retries, crc_errors, io_errors,
|
||
passes_finished, complete_passes и последнее место ошибки. Номер бита 1..64;
|
||
0 означает reset/общий лимит, pair=0xFF — пары нет. Диагностика сохраняется
|
||
после последующего успеха и обнуляется новым search_begin.
|
||
|
||
FC04: 1210–1217 совместимы, 1218 и 1219 используют прежний резерв:
|
||
- 1218: биты 0..6 — номер ROM-бита, бит 8 — complement, бит 9 — id,
|
||
биты 12..15 — положительный код последней ошибки; 0 — ошибок не было.
|
||
- 1219: биты 0..7 — число повторов с насыщением 255, биты 8..15 — число
|
||
законченных проходов, включая прерванные после исчерпания повторов.
|
||
|
||
Bridge читает 10 регистров и возвращает `diagnostics` в ответе поиска.
|
||
Для ошибки шины сообщение дополнено битом, id/complement, повторами и проходами.
|
||
Старые восемь полей сохранены. При bit=0 пара не интерпретируется.
|
||
|
||
Проверки: `run_host_tests.ps1`, `test_known_rom_host.py`,
|
||
`test_sensor_search_diagnostics_host.py`, `test_remote_ds18b20_host.py`.
|
||
На плате проверить 20 последовательных поисков трёх датчиков на 20 м,
|
||
отключение/подключение датчика при поиске, отмену и восстановление опроса.
|