Files
templates/c/ds18b20/src/ds18b20.c
Andrey Kruchinkin 873ac438f3 feat(ds18b20): термометры DS18B20 поверх программной 1-Wire
Перенесён из репозитория ds18b20, который подключался сабмодулем в
KONOR_ds18b20. История библиотеки осталась там; сюда пришло состояние
на момент переезда.

Ядро на C99 без stm32f10x.h и динамической памяти. Порт — пять функций
(Init, DelayUs, Reset, WriteBit, ReadBit); реализация для STM32F1 идёт
в комплекте в ports/stm32f1. Хостовые тесты на модели шины с виртуальными
датчиками проходят: tests/test_ds18b20.c.
2026-08-23 01:15:34 +03:00

203 lines
6.2 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/** test
* @file ds18b20.c
* @brief Реализация драйвера DS18B20: поиск, чтение и запись scratchpad.
*/
#include "ds18b20.h"
#define DS_CMD_CONVERT_T 0x44U
#define DS_CMD_WRITE_SCRATCHPAD 0x4EU
#define DS_CMD_READ_SCRATCHPAD 0xBEU
#define DS_CMD_COPY_SCRATCHPAD 0x48U
#define DS_CMD_READ_POWER 0xB4U
/** Задержка записи EEPROM датчика по datasheet, мкс. */
#define DS_EEPROM_WRITE_US 12000U
/**
* @brief Считывает scratchpad адресованного датчика с проверкой CRC8.
*
* @param rom Идентификатор датчика.
* @param scratchpad Буфер на DS18B20_SCRATCHPAD_SIZE байт.
* @return Битовая маска ошибок DS18B20_STATUS_* без бита VALID.
*/
static uint8_t ds_read_scratchpad(const uint8_t *rom, uint8_t *scratchpad)
{
if (OneWire_SelectRom(rom) == 0U) {
return DS18B20_STATUS_NO_PRESENCE;
}
OneWire_WriteByte(DS_CMD_READ_SCRATCHPAD);
OneWire_ReadBytes(scratchpad, DS18B20_SCRATCHPAD_SIZE);
if (OneWire_Crc8(scratchpad, DS18B20_SCRATCHPAD_SIZE) != 0U) {
return DS18B20_STATUS_CRC_ERROR;
}
return 0U;
}
/**
* @brief Опрашивает тип питания датчика командой READ POWER SUPPLY.
*
* @param rom Идентификатор датчика.
* @return 1 для паразитного питания, иначе 0.
*/
static uint8_t ds_read_parasite(const uint8_t *rom)
{
if (OneWire_SelectRom(rom) == 0U) {
return 0U;
}
OneWire_WriteByte(DS_CMD_READ_POWER);
/* Датчик с паразитным питанием прижимает шину к нулю. */
return (uint8_t)(OneWire_ReadBit() == 0U);
}
uint8_t DS18B20_ConfigFromResolution(uint8_t bits)
{
if (bits < 9U) {
bits = 9U;
} else if (bits > 12U) {
bits = 12U;
}
return (uint8_t)(0x1FU | ((bits - 9U) << 5U));
}
uint16_t DS18B20_ConversionTimeMs(uint8_t config)
{
switch ((config >> 5U) & 0x03U) {
case 0U:
return 94U;
case 1U:
return 188U;
case 2U:
return 375U;
default:
return 750U;
}
}
uint8_t DS18B20_Scan(DS18B20_Bus *bus)
{
OneWire_Search search;
uint8_t index;
bus->count = 0U;
bus->parasite = 0U;
OneWire_SearchInit(&search);
while ((bus->count < DS18B20_MAX_SENSORS) && (OneWire_SearchNext(&search) != 0U)) {
if (search.rom[0] != DS18B20_FAMILY_CODE) {
continue;
}
DS18B20_Sensor *sensor = &bus->sensors[bus->count];
for (index = 0U; index < ONEWIRE_ROM_SIZE; index++) {
sensor->rom[index] = search.rom[index];
}
sensor->raw = 0;
sensor->user_byte1 = 0U;
sensor->user_byte2 = 0U;
sensor->config = DS18B20_ConfigFromResolution(12U);
sensor->status = 0U;
if (ds_read_parasite(sensor->rom) != 0U) {
sensor->status |= DS18B20_STATUS_PARASITE;
bus->parasite = 1U;
}
bus->count++;
}
return bus->count;
}
uint8_t DS18B20_StartConversion(void)
{
if (OneWire_SkipRom() == 0U) {
return 0U;
}
OneWire_WriteByte(DS_CMD_CONVERT_T);
return 1U;
}
uint8_t DS18B20_ReadAll(DS18B20_Bus *bus)
{
uint8_t scratchpad[DS18B20_SCRATCHPAD_SIZE];
uint8_t valid = 0U;
uint8_t index;
for (index = 0U; index < bus->count; index++) {
DS18B20_Sensor *sensor = &bus->sensors[index];
const uint8_t parasite = (uint8_t)(sensor->status & DS18B20_STATUS_PARASITE);
const uint8_t error = ds_read_scratchpad(sensor->rom, scratchpad);
if (error != 0U) {
sensor->status = (uint8_t)(parasite | error);
continue;
}
sensor->raw = (int16_t)((uint16_t)scratchpad[0] | ((uint16_t)scratchpad[1] << 8U));
sensor->user_byte1 = scratchpad[2];
sensor->user_byte2 = scratchpad[3];
sensor->config = scratchpad[4];
sensor->status = (uint8_t)(parasite | DS18B20_STATUS_VALID);
valid++;
}
return valid;
}
DS18B20_Sensor *DS18B20_Find(DS18B20_Bus *bus, const uint8_t *rom)
{
uint8_t index;
uint8_t byte;
for (index = 0U; index < bus->count; index++) {
for (byte = 0U; byte < ONEWIRE_ROM_SIZE; byte++) {
if (bus->sensors[index].rom[byte] != rom[byte]) {
break;
}
}
if (byte == ONEWIRE_ROM_SIZE) {
return &bus->sensors[index];
}
}
return 0;
}
uint8_t DS18B20_WriteScratchpad(DS18B20_Sensor *sensor, uint8_t user_byte1,
uint8_t user_byte2, uint8_t config,
uint8_t save_to_eeprom)
{
uint8_t scratchpad[DS18B20_SCRATCHPAD_SIZE];
if (OneWire_SelectRom(sensor->rom) == 0U) {
sensor->status = (uint8_t)((sensor->status & DS18B20_STATUS_PARASITE)
| DS18B20_STATUS_NO_PRESENCE);
return DS18B20_WRITE_BUS_ERROR;
}
OneWire_WriteByte(DS_CMD_WRITE_SCRATCHPAD);
OneWire_WriteByte(user_byte1);
OneWire_WriteByte(user_byte2);
OneWire_WriteByte(config);
/* Обратное чтение подтверждает, что датчик принял все три байта. */
if (ds_read_scratchpad(sensor->rom, scratchpad) != 0U) {
return DS18B20_WRITE_BUS_ERROR;
}
if ((scratchpad[2] != user_byte1) || (scratchpad[3] != user_byte2)
|| (scratchpad[4] != config)) {
/* Обмен состоялся, но регистры остались прежними: значение отвергнуто. */
sensor->user_byte1 = scratchpad[2];
sensor->user_byte2 = scratchpad[3];
sensor->config = scratchpad[4];
return DS18B20_WRITE_REJECTED;
}
sensor->user_byte1 = scratchpad[2];
sensor->user_byte2 = scratchpad[3];
sensor->config = scratchpad[4];
if (save_to_eeprom != 0U) {
if (OneWire_SelectRom(sensor->rom) == 0U) {
return DS18B20_WRITE_BUS_ERROR;
}
OneWire_WriteByte(DS_CMD_COPY_SCRATCHPAD);
/* Датчик с внешним питанием тоже допускает выдержку по времени. */
OneWire_DelayUs(DS_EEPROM_WRITE_US);
}
return DS18B20_WRITE_OK;
}