Датчик температуры и влажности DHT22

Очень популярный китайский датчик, сообщающий температуру и влажность. Дешёвый, имеет простой протокол передачи данных.

Используется шина с общим коллектором и подтяжкой к плюсу. Протокол общения таков:

DHT22

  1. Прижимаем шину к 0 в течение 80мкс, отпускаем шину. Это служит меткой запроса. Переводим контакт ввода-вывода STM32 в состояние «вход».
  2. Датчик откликается удерживанием линии в 1 течение 90мкс, и начинает передавать данные.
  3. Каждый бит — это 0 в течение 50 мкс и 1 в течение 25 или 125 мкс.
  4. Датчик передаёт 2 байта данных влажности, 2 байта температуры и 1 байт — сумма первых 4. Это CRC.
  5. После завершения передачи (около 4 мс) датчик отпускает линию в состояние 1.

DHT22_2

Осциллограмма создана прибором Red Pitaya. На графике видны паузы между битами — низкий уровень в течение ~50мкс, и биты данных — высокий уровень в течение 25 мкс (передан 0) или в течение 125 мкс (передана 1). Понятно, что раз биты имеют переменную длину, длина всей посылки тоже заранее неизвестна, она будет от 2.7мс (переданы все нули) до 6.5мс (переданы все единицы в байтах данных, crc = 11111100b).

Данные — это просто значение влажности или температуры в десятых долях процента или градуса. Давайте посмотрим на пример:

Пример измерения влажности и температуры на DHT22

DHT22

В начале видим запрос от МК (длинный ноль в момент 0.25мс), ответ от датчика (длинная единица в 0.3мс), и в 0.9мс начинается передача битов: короткие импульсы высокого уровня — это логический ноль, длинные — логическая единица (кстати, похоже на телеграф и на протокол 1-Wire), пауза между битами — низкий уровень. Для наглядности я разделил байты чёрными линиями; приняли 5 байт с такими значениями:

  1. 00000001b = 1, это старший байт (MSB) значения влажности.
  2. 01011111b = 95, это младший байт (LSB) значения влажности.
  3. 00000000b = 0, старший байт (MSB) температуры.
  4. 11100101b = 229, младший байт (LSB) температуры.
  5. 01000101b = 69, это контрольная сумма (CRC), которая должна быть равна сумме всех байт данных. Проверяем: 1+95+0+229 = 325. Понятно, что 325 в байт не влезет, возьмём модуль по 256 (т.е. отрежем старшие биты) — получилось 69. Всё точно!

Последний шаг: влажность = 1 * 256 + 95 = 351, то есть 35.1%; температура = 0 * 256 + 229 = 229, то есть 22.9°. Это я объясняю так долго, а на самом деле надо просто запихнуть принятые байты в uint16_t, и он сразу покажет эти цифры :)

Осталось написать программу для чтения DHT22 на STM32. Для общения с датчиком я использую вывод PA1.

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

GPIO_InitTypeDef  GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_Init(GPIOA, &GPIO_InitStructure);

GPIOA->ODR = GPIO_Pin_1;
for(volatile uint8_t del = 0; del<100; del++);

GPIOA->CRL&=~GPIO_CRL_MODE1;
GPIOA->CRL&=~GPIO_CRL_CNF1;
GPIOA->CRL|= GPIO_CRL_CNF1_0;

int16_t time = 0, old_time = 0;
int8_t pin = GPIOA->IDR & GPIO_Pin_1, old_pin = pin;
uint64_t data = 0;
uint8_t pos = 41;

while(time++, time<1000)
{
if(old_pin != pin)
{
if(pin == 0) data += (uint64_t)(time-old_time > 9 ? 1 : 0) << pos--;
old_time = time;
}
old_pin = pin;
pin = GPIOA->IDR & GPIO_Pin_1;
for(volatile uint8_t del = 0; del<10; del++);
}

uint16_t H = (uint16_t)(data>>24), T = (uint16_t)(data>>8), crc = (uint8_t)data;

Код может показаться не очень понятным, но по сути всё просто: если состояние входа изменилось и стало 0 — значит, только что пришёл новый бит. Измеряем его длительность, если она короткая (меньше 9 циклов опроса — это экспериментальное значение), значит это 0, иначе это 1. Записываем этот бит в переменную типа int64_t на следующую позицию, с каждым новым битом сдвигаемся в этом числе на 1. После окончания приёма ответа в переменной лежит весь ответ, все 5 байт — 40 бит.

Осталось выделить из этого числа два 16-битных числа: влажность (с 24 бита по 39) и температуру (с 8 бита по 23). Я делаю это так: сдвигаю число вправо на 24 бита и привожу результат к 16 битам — к типу uint16_t. Кстати, того же самого обрезания до 16 бит можно было добиться, сделав И с числом 0xFFFF, но это не так наглядно.

На мой взгляд, этот датчик можно использовать:

  1. системе умного дома
  2. в домашней метеостанции
  3. для автоматического управления вентиляцией в теплицах

Правда мне кажется, что это какой-то очень «дополнительный» датчик, не такой важный как термометр или датчик движения. Что делать-то с полученным значением влажности? Может быть, управлять кондиционером?

А как вы используете DHT22?

Ссылка на основную публикацию