feat(ds18b20): термометры DS18B20 поверх программной 1-Wire

Перенесён из репозитория ds18b20, который подключался сабмодулем в
KONOR_ds18b20. История библиотеки осталась там; сюда пришло состояние
на момент переезда.

Ядро на C99 без stm32f10x.h и динамической памяти. Порт — пять функций
(Init, DelayUs, Reset, WriteBit, ReadBit); реализация для STM32F1 идёт
в комплекте в ports/stm32f1. Хостовые тесты на модели шины с виртуальными
датчиками проходят: tests/test_ds18b20.c.
This commit is contained in:
2026-08-23 01:15:34 +03:00
parent 13f2c8fe64
commit 873ac438f3
11 changed files with 1701 additions and 0 deletions

125
c/ds18b20/include/ds18b20.h Normal file
View File

@@ -0,0 +1,125 @@
/**
* @file ds18b20.h
* @brief Драйвер термометров DS18B20 поверх шины 1-Wire.
*
* Модуль хранит перечень найденных датчиков и последние измерения, поэтому
* ответ на запрос GUI формируется без обращения к шине.
*/
#ifndef DS18B20_H
#define DS18B20_H
#include <stdint.h>
#include "onewire.h"
/** Максимум датчиков, помещающихся в один кадр SENSOR_DATA. */
#define DS18B20_MAX_SENSORS 8U
/** Код семейства DS18B20 в первом байте ROM. */
#define DS18B20_FAMILY_CODE 0x28U
/** Размер scratchpad вместе с байтом CRC8. */
#define DS18B20_SCRATCHPAD_SIZE 9U
#define DS18B20_STATUS_VALID 0x01U /**< Измерение достоверно. */
#define DS18B20_STATUS_CRC_ERROR 0x02U /**< Не сошёлся CRC8 scratchpad. */
#define DS18B20_STATUS_NO_PRESENCE 0x04U /**< Датчик не ответил на сброс. */
#define DS18B20_STATUS_PARASITE 0x08U /**< Датчик питается паразитно. */
#define DS18B20_WRITE_OK 0U /**< Запись подтверждена обратным чтением. */
#define DS18B20_WRITE_BUS_ERROR 1U /**< Нет presence или не сошёлся CRC8. */
#define DS18B20_WRITE_REJECTED 2U /**< Датчик ответил, но значение не изменил. */
/**
* @brief Состояние одного датчика на шине.
*/
typedef struct {
uint8_t rom[ONEWIRE_ROM_SIZE]; /**< Уникальный 64-битный идентификатор. */
int16_t raw; /**< Код температуры из scratchpad. */
uint8_t user_byte1; /**< Пользовательский байт 1 (регистр TH). */
uint8_t user_byte2; /**< Пользовательский байт 2 (регистр TL). */
uint8_t config; /**< Байт конфигурации с разрешением. */
uint8_t status; /**< Битовая маска DS18B20_STATUS_*. */
} DS18B20_Sensor;
/**
* @brief Перечень датчиков шины и общее состояние питания.
*/
typedef struct {
DS18B20_Sensor sensors[DS18B20_MAX_SENSORS]; /**< Найденные датчики. */
uint8_t count; /**< Число заполненных элементов массива. */
uint8_t parasite; /**< 1, если хотя бы один датчик питается паразитно. */
} DS18B20_Bus;
/**
* @brief Ищет датчики DS18B20 и запоминает их идентификаторы.
*
* Устройства других семейств пропускаются; ранее накопленные измерения
* сбрасываются.
*
* @param bus Перечень датчиков, принадлежащий приложению.
* @return Количество найденных датчиков.
*/
uint8_t DS18B20_Scan(DS18B20_Bus *bus);
/**
* @brief Запускает преобразование во всех датчиках командой SKIP ROM.
*
* @return 1 при наличии presence-импульса, иначе 0.
*/
uint8_t DS18B20_StartConversion(void);
/**
* @brief Читает scratchpad всех известных датчиков и обновляет измерения.
*
* Ошибка отдельного датчика не прерывает опрос остальных: она отражается в
* поле status соответствующего элемента.
*
* @param bus Перечень датчиков, заполненный DS18B20_Scan.
* @return Количество датчиков с достоверным измерением.
*/
uint8_t DS18B20_ReadAll(DS18B20_Bus *bus);
/**
* @brief Возвращает время преобразования для заданного разрешения.
*
* @param config Байт конфигурации scratchpad.
* @return Время преобразования в миллисекундах.
*/
uint16_t DS18B20_ConversionTimeMs(uint8_t config);
/**
* @brief Собирает байт конфигурации из разрешения в битах.
*
* @param bits Разрешение 9..12 бит.
* @return Байт конфигурации scratchpad.
*/
uint8_t DS18B20_ConfigFromResolution(uint8_t bits);
/**
* @brief Ищет датчик по идентификатору в накопленном перечне.
*
* @param bus Перечень датчиков.
* @param rom Искомый идентификатор.
* @return Указатель на элемент перечня либо 0, если ROM не найден.
*/
DS18B20_Sensor *DS18B20_Find(DS18B20_Bus *bus, const uint8_t *rom);
/**
* @brief Записывает три байта scratchpad выбранного датчика.
*
* @param sensor Датчик из перечня; кэш обновляется при успешной записи.
* @param user_byte1 Новое значение пользовательского байта 1 (TH).
* @param user_byte2 Новое значение пользовательского байта 2 (TL).
* @param config Байт конфигурации с требуемым разрешением.
* @param save_to_eeprom Признак копирования scratchpad в EEPROM датчика.
* @return DS18B20_WRITE_OK при подтверждённой записи, DS18B20_WRITE_BUS_ERROR
* при молчании датчика или сбое CRC8, DS18B20_WRITE_REJECTED если
* датчик ответил, но оставил прежние значения.
*/
uint8_t DS18B20_WriteScratchpad(DS18B20_Sensor *sensor, uint8_t user_byte1,
uint8_t user_byte2, uint8_t config,
uint8_t save_to_eeprom);
#endif /* DS18B20_H */

149
c/ds18b20/include/onewire.h Normal file
View File

@@ -0,0 +1,149 @@
/**
* @file onewire.h
* @brief Программная шина 1-Wire на выводе с открытым стоком.
*
* Драйвер не зависит от типа датчика: он выдаёт тайминги, ищет ROM и считает
* CRC8. Работа с DS18B20 вынесена в ds18b20.c.
*
* Реализация разделена надвое:
* src/onewire.c переносимая часть - побайтовый обмен, CRC8, поиск ROM,
* MATCH ROM и SKIP ROM; ни регистров, ни задержек;
* ports/<платформа> пять функций, зависящих от железа: OneWire_Init,
* OneWire_DelayUs, OneWire_Reset, OneWire_WriteBit
* и OneWire_ReadBit.
*
* Чтобы перенести библиотеку на другой микроконтроллер, достаточно написать
* эти пять функций - остальное собирается как есть.
*/
#ifndef ONEWIRE_H
#define ONEWIRE_H
#include <stdint.h>
/** Размер идентификатора устройства 1-Wire в байтах. */
#define ONEWIRE_ROM_SIZE 8U
/**
* @brief Состояние обхода шины алгоритмом SEARCH ROM.
*
* Структура принадлежит вызывающему коду; параллельные обходы должны
* использовать разные экземпляры.
*/
typedef struct {
uint8_t rom[ONEWIRE_ROM_SIZE]; /**< Текущий найденный идентификатор. */
uint8_t last_discrepancy; /**< Позиция последней развилки, 1..64. */
uint8_t last_family_discrepancy; /**< Развилка внутри кода семейства. */
uint8_t last_device; /**< Признак завершённого обхода. */
} OneWire_Search;
/**
* @brief Настраивает вывод шины и счётчик циклов для микросекундных задержек.
*
* Вызывается один раз после инициализации тактирования системы.
*/
void OneWire_Init(void);
/**
* @brief Формирует импульс сброса и проверяет ответ устройств.
*
* @note На время выборки presence прерывания запрещаются примерно на 75 мкс;
* длительность самого импульса сброса на точность не влияет.
*
* @return 1, если хотя бы одно устройство выдало presence-импульс, иначе 0.
*/
uint8_t OneWire_Reset(void);
/**
* @brief Передаёт один бит в тайм-слоте записи.
*
* @param bit Передаваемое значение, отличное от нуля означает единицу.
*/
void OneWire_WriteBit(uint8_t bit);
/**
* @brief Считывает один бит в тайм-слоте чтения.
*
* @return Принятое значение бита: 0 или 1.
*/
uint8_t OneWire_ReadBit(void);
/**
* @brief Передаёт байт младшим битом вперёд.
*
* @param value Передаваемый байт.
*/
void OneWire_WriteByte(uint8_t value);
/**
* @brief Принимает байт младшим битом вперёд.
*
* @return Принятый байт.
*/
uint8_t OneWire_ReadByte(void);
/**
* @brief Передаёт блок байтов подряд без промежуточных сбросов.
*
* @param data Указатель на передаваемые байты.
* @param size Количество байтов.
*/
void OneWire_WriteBytes(const uint8_t *data, uint32_t size);
/**
* @brief Принимает блок байтов подряд.
*
* @param data Буфер приёмника.
* @param size Количество байтов.
*/
void OneWire_ReadBytes(uint8_t *data, uint32_t size);
/**
* @brief Считает CRC8 Dallas/Maxim с полиномом 0x8C.
*
* @param data Проверяемые байты.
* @param size Количество байтов.
* @return Значение CRC8; для блока вместе с контрольным байтом равно нулю.
*/
uint8_t OneWire_Crc8(const uint8_t *data, uint32_t size);
/**
* @brief Подготавливает состояние к новому полному обходу шины.
*
* @param search Состояние обхода, принадлежащее вызывающему коду.
*/
void OneWire_SearchInit(OneWire_Search *search);
/**
* @brief Находит следующий идентификатор на шине.
*
* @param search Состояние обхода, ранее подготовленное OneWire_SearchInit.
* @return 1, если поле rom содержит новый проверенный ROM, иначе 0.
*/
uint8_t OneWire_SearchNext(OneWire_Search *search);
/**
* @brief Адресует конкретное устройство командой MATCH ROM.
*
* @param rom Восьмибайтовый идентификатор устройства.
* @return 1 при наличии presence-импульса, иначе 0.
*/
uint8_t OneWire_SelectRom(const uint8_t *rom);
/**
* @brief Адресует все устройства шины командой SKIP ROM.
*
* @return 1 при наличии presence-импульса, иначе 0.
*/
uint8_t OneWire_SkipRom(void);
/**
* @brief Задерживает выполнение на заданное число микросекунд.
*
* Используется драйверами шины и модулем DS18B20 при ожидании EEPROM.
*
* @param microseconds Длительность задержки.
*/
void OneWire_DelayUs(uint32_t microseconds);
#endif /* ONEWIRE_H */