209 lines
6.7 KiB
C
209 lines
6.7 KiB
C
/*
|
|
* Работа с термометрами DS18B20: поиск по ROM, запуск преобразования, чтение температуры и
|
|
* настройка scratchpad. ROM является адресом устройства на общей шине; результат чтения нельзя
|
|
* считать действительным до проверки статуса обмена и CRC.
|
|
*/
|
|
|
|
/** 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;
|
|
}
|