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:
202
c/ds18b20/src/ds18b20.c
Normal file
202
c/ds18b20/src/ds18b20.c
Normal file
@@ -0,0 +1,202 @@
|
||||
/** 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;
|
||||
}
|
||||
177
c/ds18b20/src/onewire.c
Normal file
177
c/ds18b20/src/onewire.c
Normal file
@@ -0,0 +1,177 @@
|
||||
/**
|
||||
* @file onewire.c
|
||||
* @brief Переносимая часть шины 1-Wire: побайтовый обмен, CRC8 и поиск ROM.
|
||||
*
|
||||
* Здесь нет ни регистров, ни задержек, ни запрета прерываний: всё это живёт
|
||||
* в порте (ports/), а этот файл собирается любым C99-компилятором и
|
||||
* проверяется host-тестами на симуляторе шины.
|
||||
*
|
||||
* От порта требуются пять функций: OneWire_Init, OneWire_DelayUs,
|
||||
* OneWire_Reset, OneWire_WriteBit и OneWire_ReadBit.
|
||||
*/
|
||||
|
||||
#include "onewire.h"
|
||||
|
||||
#define OW_CMD_SEARCH_ROM 0xF0U
|
||||
#define OW_CMD_MATCH_ROM 0x55U
|
||||
#define OW_CMD_SKIP_ROM 0xCCU
|
||||
|
||||
void OneWire_WriteByte(uint8_t value)
|
||||
{
|
||||
uint8_t index;
|
||||
|
||||
for (index = 0U; index < 8U; index++) {
|
||||
OneWire_WriteBit((uint8_t)(value & 1U));
|
||||
value = (uint8_t)(value >> 1U);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t OneWire_ReadByte(void)
|
||||
{
|
||||
uint8_t value = 0U;
|
||||
uint8_t index;
|
||||
|
||||
for (index = 0U; index < 8U; index++) {
|
||||
value = (uint8_t)(value >> 1U);
|
||||
if (OneWire_ReadBit() != 0U) {
|
||||
value |= 0x80U;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void OneWire_WriteBytes(const uint8_t *data, uint32_t size)
|
||||
{
|
||||
uint32_t index;
|
||||
|
||||
for (index = 0U; index < size; index++) {
|
||||
OneWire_WriteByte(data[index]);
|
||||
}
|
||||
}
|
||||
|
||||
void OneWire_ReadBytes(uint8_t *data, uint32_t size)
|
||||
{
|
||||
uint32_t index;
|
||||
|
||||
for (index = 0U; index < size; index++) {
|
||||
data[index] = OneWire_ReadByte();
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t OneWire_Crc8(const uint8_t *data, uint32_t size)
|
||||
{
|
||||
uint8_t crc = 0U;
|
||||
uint32_t index;
|
||||
uint8_t bit;
|
||||
|
||||
for (index = 0U; index < size; index++) {
|
||||
crc ^= data[index];
|
||||
for (bit = 0U; bit < 8U; bit++) {
|
||||
crc = (uint8_t)((crc & 1U) != 0U ? ((crc >> 1U) ^ 0x8CU) : (crc >> 1U));
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
void OneWire_SearchInit(OneWire_Search *search)
|
||||
{
|
||||
uint32_t index;
|
||||
|
||||
for (index = 0U; index < ONEWIRE_ROM_SIZE; index++) {
|
||||
search->rom[index] = 0U;
|
||||
}
|
||||
search->last_discrepancy = 0U;
|
||||
search->last_family_discrepancy = 0U;
|
||||
search->last_device = 0U;
|
||||
}
|
||||
|
||||
uint8_t OneWire_SearchNext(OneWire_Search *search)
|
||||
{
|
||||
uint8_t id_bit_number = 1U;
|
||||
uint8_t last_zero = 0U;
|
||||
uint8_t rom_byte_number = 0U;
|
||||
uint8_t rom_byte_mask = 1U;
|
||||
uint8_t search_direction;
|
||||
uint8_t id_bit;
|
||||
uint8_t cmp_id_bit;
|
||||
|
||||
if (search->last_device != 0U) {
|
||||
OneWire_SearchInit(search);
|
||||
return 0U;
|
||||
}
|
||||
if (OneWire_Reset() == 0U) {
|
||||
OneWire_SearchInit(search);
|
||||
return 0U;
|
||||
}
|
||||
OneWire_WriteByte(OW_CMD_SEARCH_ROM);
|
||||
|
||||
do {
|
||||
id_bit = OneWire_ReadBit();
|
||||
cmp_id_bit = OneWire_ReadBit();
|
||||
if ((id_bit != 0U) && (cmp_id_bit != 0U)) {
|
||||
/* Ни одно устройство не ответило: обход прерван. */
|
||||
OneWire_SearchInit(search);
|
||||
return 0U;
|
||||
}
|
||||
|
||||
if (id_bit != cmp_id_bit) {
|
||||
search_direction = id_bit;
|
||||
} else if (id_bit_number < search->last_discrepancy) {
|
||||
search_direction =
|
||||
(uint8_t)((search->rom[rom_byte_number] & rom_byte_mask) != 0U);
|
||||
} else {
|
||||
search_direction = (uint8_t)(id_bit_number == search->last_discrepancy);
|
||||
}
|
||||
|
||||
if ((id_bit == cmp_id_bit) && (search_direction == 0U)) {
|
||||
last_zero = id_bit_number;
|
||||
if (last_zero < 9U) {
|
||||
search->last_family_discrepancy = last_zero;
|
||||
}
|
||||
}
|
||||
|
||||
if (search_direction != 0U) {
|
||||
search->rom[rom_byte_number] |= rom_byte_mask;
|
||||
} else {
|
||||
search->rom[rom_byte_number] &= (uint8_t)~rom_byte_mask;
|
||||
}
|
||||
OneWire_WriteBit(search_direction);
|
||||
|
||||
id_bit_number++;
|
||||
rom_byte_mask = (uint8_t)(rom_byte_mask << 1U);
|
||||
if (rom_byte_mask == 0U) {
|
||||
rom_byte_number++;
|
||||
rom_byte_mask = 1U;
|
||||
}
|
||||
} while (rom_byte_number < ONEWIRE_ROM_SIZE);
|
||||
|
||||
if (OneWire_Crc8(search->rom, ONEWIRE_ROM_SIZE) != 0U) {
|
||||
OneWire_SearchInit(search);
|
||||
return 0U;
|
||||
}
|
||||
|
||||
search->last_discrepancy = last_zero;
|
||||
if (search->last_discrepancy == 0U) {
|
||||
search->last_device = 1U;
|
||||
}
|
||||
return 1U;
|
||||
}
|
||||
|
||||
uint8_t OneWire_SelectRom(const uint8_t *rom)
|
||||
{
|
||||
if (OneWire_Reset() == 0U) {
|
||||
return 0U;
|
||||
}
|
||||
OneWire_WriteByte(OW_CMD_MATCH_ROM);
|
||||
OneWire_WriteBytes(rom, ONEWIRE_ROM_SIZE);
|
||||
return 1U;
|
||||
}
|
||||
|
||||
uint8_t OneWire_SkipRom(void)
|
||||
{
|
||||
if (OneWire_Reset() == 0U) {
|
||||
return 0U;
|
||||
}
|
||||
OneWire_WriteByte(OW_CMD_SKIP_ROM);
|
||||
return 1U;
|
||||
}
|
||||
Reference in New Issue
Block a user