Последний из термодатчиков, про который я хочу написать — известнейший цифровой термометр DS18B20, который подключается по шине 1-Wire.
Он довольно удобен: подключается всего тремя проводами (или двумя в режиме паразитного питания), достаточно точен, а шина 1-Wire позволяет подключать множество таких датчиков и других устройств всего лишь через общие два провода: земля и данные/питание.
Содержание:
Команды
Каждое устройство 1-Wire откликается на общие команды (которые поддерживаются всеми устройствами) и на некоторый набор своих специфических команд. К общим командам относятся:
- 0×F0 — перечисление ID устройств
- 0×33 — чтение ID единственного подключенного устройства
- 0×55 — поиск устройства по ID
- 0×CC — обращение ко всем устройствам (пропуск ID)
- 0×EC — поиск устройств, установивших флаг «тревога»
К специальным командам термодатчика DS18B20 относятся:
- 0×44 — запуск измерения температуры
- 0×4E — запись в регистры
- 0×BE — чтение регистров
- 0×48 — запись содержимого регистров в backup — внутренний EEPROM (операция push,)
- 0×B8 — восстановление содержимого регистров из backup (операция pull)
- 0×B4 — определить состояние питания: настоящее или паразитное.
Чтение температуры
Для простого чтения температуры с одного подключенного DS18B20 нам необходимо сделать три вещи:
Настройка регистров — чувствительность термометра (разрядность АЦП)
- 0×CC (обращаемся к единственному устройству на линии)
- 0×4E (запись в регистры)
- 0×4B (верхний порог тревоги)
- 0×46 (нижний порог тревоги)
- 0×5F (разрядность 11 бит)
Запуск измерения температуры
- 0xCC (обращаемся к единственному устройству на линии)
- 0x44 (запускаем измерение)
Нужно дать датчику около полсекунды на измерения, время зависит от точности измерения. Для разрядности 11 бит это 375 миллисекунд.
Чтение результата
- 0×CC (обращаемся к единственному устройству на линии)
- 0×BE (читаем регистры)
- читаем 16 бит
Результат будет в шестнадцатых долях градуса, то есть необходимо умножить его на 16 — или просто обрезать последние 4 бита, если доли градуса не нужны.
Протокол общения
Старт общения (сигнал presence): притяните DQ к земле на 500 мкс и отпустите. Через 20 мкс датчик ответит, притянув DQ к земле на 150 мкс.
Передача битов:
0 — притяните DQ к земле на 60 мкс, отпустите и подождите 30 мкс.
1 — притяните DQ к земле на 10 мкс, отпустите и подождите 80 мкс.
Я использую контакт PA3 в качестве DQ.
void delay(uint32_t del)
{
for(volatile uint32_t i = 0; i<del; i++); } void send_presence() { GPIOA->ODR = GPIO_Pin_3;
delay(100);
GPIOA->ODR = 0;
delay(3500); //420us
GPIOA->ODR = GPIO_Pin_3;
}
void one_wire_write_bit(uint8_t bit)
{
GPIOA->ODR = 0;
delay(bit ? 150 : 500);
GPIOA->ODR = GPIO_Pin_3;
delay(bit ? 650 : 200);
}
uint8_t one_wire_read_bit()
{
uint8_t bit = 0;
GPIOA->ODR = 0;
delay(80);
GPIOA->ODR = GPIO_Pin_3;
delay(50);
GPIOA->CRL &= ~GPIO_CRL_MODE3;
GPIOA->CRL &= ~GPIO_CRL_CNF3;
GPIOA->CRL |= GPIO_CRL_CNF3_0;
bit = (GPIOA->IDR&GPIO_Pin_3?1:0);
GPIOA->CRL |= GPIO_CRL_MODE3;
GPIOA->CRL |= GPIO_CRL_CNF3_0;
delay(600);
return bit;
}
void one_wire_write_byte(uint8_t data)
{
for(uint8_t i = 0; i<8; i++) one_wire_write_bit(data>>i & 1);
}
int main()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_Init(GPIOA, &GPIO_InitStructure);
send_presence();
delay(5500);
one_wire_write_byte(0xCC);
one_wire_write_byte(0x4E);
one_wire_write_byte(0x4B);
one_wire_write_byte(0x46);
one_wire_write_byte(0x5F);
send_presence();
delay(5500);
one_wire_write_byte(0xCC);
one_wire_write_byte(0x44);
delay(6000000);
send_presence();
delay(5500);
one_wire_write_byte(0xCC);
one_wire_write_byte(0xBE);
delay(4000);
uint16_t data = 0;
for(uint8_t i = 0; i<16; i++) data += (uint16_t)one_wire_read_bit()<<i;
float temp = data/16.0;
}
Обязательно прочитайте памятку по подводным камням в 1-Wire и DS18B20.