Add Russian educational comments for DHT22 module

This commit is contained in:
Stanislav N Mikhailov
2026-05-02 20:02:11 +03:00
parent 63d7742e62
commit 23d9ab9401
3 changed files with 137 additions and 94 deletions
+84 -58
View File
@@ -1,90 +1,116 @@
#include "dht22.h" #include "dht22.h" // публичный интерфейс драйвера
#include "dht22.pio.h" #include "dht22.pio.h" // сгенерированная PIO-программа
#include "hardware/clocks.h" #include "hardware/clocks.h" // частота системного такта
#include "hardware/gpio.h" #include "hardware/gpio.h" // управление GPIO
#include "pico/stdlib.h" #include "pico/stdlib.h" // время и задержки
#define DHT22_START_LOW_US 1200u #define DHT22_START_LOW_US 1200u // стартовый импульс хоста
#define DHT22_PREPARE_US 30u #define DHT22_PREPARE_US 30u // пауза перед захватом
#define DHT22_WORD_TIMEOUT_US 2000u #define DHT22_WORD_TIMEOUT_US 2000u // таймаут на один байт
/*
* Ждёт появления одного слова в RX FIFO state machine.
* На вход принимает:
* pio - блок PIO, из которого читается FIFO;
* sm - номер state machine;
* out - указатель, куда будет записано прочитанное слово;
* timeout_us - максимальное время ожидания в микросекундах.
* Возвращает true, если слово получено, иначе false по таймауту.
*/
static bool wait_for_rx_word(PIO pio, uint sm, uint32_t *out, uint32_t timeout_us) { static bool wait_for_rx_word(PIO pio, uint sm, uint32_t *out, uint32_t timeout_us) {
uint64_t deadline = time_us_64() + timeout_us; uint64_t deadline = time_us_64() + timeout_us; // вычисляем момент истечения таймаута
while (pio_sm_is_rx_fifo_empty(pio, sm)) { while (pio_sm_is_rx_fifo_empty(pio, sm)) { // ждём появления слова в FIFO
if (time_us_64() > deadline) { if (time_us_64() > deadline) { // проверяем превышение таймаута
return false; return false; // данных не дождались
} }
} }
*out = pio_sm_get(pio, sm); *out = pio_sm_get(pio, sm); // забираем слово из FIFO
return true; return true; // чтение прошло успешно
} }
/*
* Инициализирует драйвер DHT22.
* На вход принимает:
* dev - структуру, куда сохраняется состояние драйвера;
* pio - блок PIO, который будет выполнять программу чтения;
* sm - номер state machine внутри выбранного PIO;
* pin - GPIO, к которому подключена линия данных датчика.
* Ничего не возвращает.
*/
void dht22_init(dht22_t *dev, PIO pio, uint sm, uint pin) { void dht22_init(dht22_t *dev, PIO pio, uint sm, uint pin) {
dev->pio = pio; dev->pio = pio; // сохраняем выбранный блок PIO
dev->sm = sm; dev->sm = sm; // сохраняем номер state machine
dev->pin = pin; dev->pin = pin; // сохраняем номер GPIO
dev->offset = pio_add_program(pio, &dht22_program); dev->offset = pio_add_program(pio, &dht22_program); // загружаем программу в PIO
pio_gpio_init(pio, pin); pio_gpio_init(pio, pin); // подключаем GPIO к PIO
pio_sm_config c = dht22_program_get_default_config(dev->offset); pio_sm_config c = dht22_program_get_default_config(dev->offset); // берём базовую конфигурацию
sm_config_set_in_pins(&c, pin); sm_config_set_in_pins(&c, pin); // читаем биты с нужного пина
sm_config_set_clkdiv(&c, (float)clock_get_hz(clk_sys) / 1000000.0f); sm_config_set_clkdiv(&c, (float)clock_get_hz(clk_sys) / 1000000.0f); // делаем шаг PIO равным 1 мкс
sm_config_set_in_shift(&c, false, true, 8); sm_config_set_in_shift(&c, false, true, 8); // сдвигаем вход и автопушим по 8 бит
pio_sm_init(pio, sm, dev->offset, &c); pio_sm_init(pio, sm, dev->offset, &c); // записываем конфигурацию в SM
pio_sm_set_enabled(pio, sm, false); pio_sm_set_enabled(pio, sm, false); // пока не запускаем автомат
gpio_init(pin); gpio_init(pin); // инициализируем GPIO со стороны CPU
gpio_pull_up(pin); gpio_pull_up(pin); // включаем подтяжку вверх
gpio_set_dir(pin, GPIO_IN); gpio_set_dir(pin, GPIO_IN); // отпускаем линию в режим входа
} }
/*
* Выполняет одно чтение DHT22.
* На вход принимает:
* dev - ранее инициализированный дескриптор датчика;
* temperature_x10 - указатель, куда будет записана температура в десятых долях градуса;
* humidity_x10 - указатель, куда будет записана влажность в десятых долях процента.
* Возвращает код результата: успех, таймаут, ошибка контрольной суммы или залипшая шина.
*/
dht22_status_t dht22_read(dht22_t *dev, int16_t *temperature_x10, uint16_t *humidity_x10) { dht22_status_t dht22_read(dht22_t *dev, int16_t *temperature_x10, uint16_t *humidity_x10) {
if (!gpio_get(dev->pin)) { if (!gpio_get(dev->pin)) { // линия должна быть в единице в покое
return DHT22_BUS_STUCK; return DHT22_BUS_STUCK; // иначе шина залипла
} }
gpio_set_dir(dev->pin, GPIO_OUT); gpio_set_dir(dev->pin, GPIO_OUT); // временно берём линию под управление CPU
gpio_put(dev->pin, 0); gpio_put(dev->pin, 0); // тянем линию вниз для старта обмена
sleep_us(DHT22_START_LOW_US); sleep_us(DHT22_START_LOW_US); // держим стартовый импульс
gpio_set_dir(dev->pin, GPIO_IN); gpio_set_dir(dev->pin, GPIO_IN); // отпускаем линию датчику
gpio_pull_up(dev->pin); gpio_pull_up(dev->pin); // оставляем подтяжку вверх
sleep_us(DHT22_PREPARE_US); sleep_us(DHT22_PREPARE_US); // даём датчику начать ответ
pio_sm_set_enabled(dev->pio, dev->sm, false); pio_sm_set_enabled(dev->pio, dev->sm, false); // останавливаем SM перед новым чтением
pio_sm_clear_fifos(dev->pio, dev->sm); pio_sm_clear_fifos(dev->pio, dev->sm); // чистим FIFO от старых данных
pio_sm_restart(dev->pio, dev->sm); pio_sm_restart(dev->pio, dev->sm); // сбрасываем состояние SM
pio_sm_set_enabled(dev->pio, dev->sm, true); pio_sm_set_enabled(dev->pio, dev->sm, true); // запускаем захват битов
uint8_t data[5] = {0}; uint8_t data[5] = {0}; // буфер пяти байт DHT22
for (uint i = 0; i < 5; ++i) { for (uint i = 0; i < 5; ++i) { // читаем все 40 бит по байтам
uint32_t word = 0; uint32_t word = 0; // временное слово из FIFO
if (!wait_for_rx_word(dev->pio, dev->sm, &word, DHT22_WORD_TIMEOUT_US)) { if (!wait_for_rx_word(dev->pio, dev->sm, &word, DHT22_WORD_TIMEOUT_US)) { // ждём очередной байт
pio_sm_set_enabled(dev->pio, dev->sm, false); pio_sm_set_enabled(dev->pio, dev->sm, false); // останавливаем SM при ошибке
return DHT22_TIMEOUT; return DHT22_TIMEOUT; // выходим по таймауту
} }
data[i] = (uint8_t)word; data[i] = (uint8_t)word; // сохраняем младший байт
} }
pio_sm_set_enabled(dev->pio, dev->sm, false); pio_sm_set_enabled(dev->pio, dev->sm, false); // останавливаем SM после чтения
uint8_t checksum = (uint8_t)(data[0] + data[1] + data[2] + data[3]); uint8_t checksum = (uint8_t)(data[0] + data[1] + data[2] + data[3]); // считаем контрольную сумму
if (checksum != data[4]) { if (checksum != data[4]) { // сравниваем с байтом датчика
return DHT22_CHECKSUM_ERROR; return DHT22_CHECKSUM_ERROR; // данные повреждены
} }
uint16_t raw_h = (uint16_t)((data[0] << 8) | data[1]); uint16_t raw_h = (uint16_t)((data[0] << 8) | data[1]); // собираем влажность из двух байт
uint16_t raw_t = (uint16_t)((data[2] << 8) | data[3]); uint16_t raw_t = (uint16_t)((data[2] << 8) | data[3]); // собираем температуру из двух байт
*humidity_x10 = raw_h; *humidity_x10 = raw_h; // DHT22 уже даёт влажность в десятых долях
if (raw_t & 0x8000u) { if (raw_t & 0x8000u) { // старший бит хранит знак температуры
*temperature_x10 = -(int16_t)(raw_t & 0x7fffu); *temperature_x10 = -(int16_t)(raw_t & 0x7fffu); // отрицательная температура
} else { } else {
*temperature_x10 = (int16_t)raw_t; *temperature_x10 = (int16_t)raw_t; // положительная температура
} }
return DHT22_OK; return DHT22_OK; // всё прочитано корректно
} }
+36 -19
View File
@@ -1,24 +1,41 @@
#ifndef DHT22_H #ifndef DHT22_H // защита от двойного включения
#define DHT22_H #define DHT22_H // открываем тело заголовка
#include "hardware/pio.h" #include "hardware/pio.h" // типы и API PIO
#include "pico/types.h" #include "pico/types.h" // базовые типы Pico SDK
typedef enum { typedef enum { // коды результата чтения
DHT22_OK = 0, DHT22_OK = 0, // чтение успешно
DHT22_TIMEOUT, DHT22_TIMEOUT, // датчик не ответил вовремя
DHT22_CHECKSUM_ERROR, DHT22_CHECKSUM_ERROR, // контрольная сумма не сошлась
DHT22_BUS_STUCK DHT22_BUS_STUCK // линия данных зажата в нуле
} dht22_status_t; } dht22_status_t; // тип статуса DHT22
typedef struct { typedef struct { // состояние драйвера датчика
PIO pio; PIO pio; // используемый блок PIO
uint sm; uint sm; // номер state machine
uint pin; uint pin; // GPIO линии данных
uint offset; uint offset; // смещение программы в PIO
} dht22_t; } dht22_t; // дескриптор DHT22
dht22_status_t dht22_read(dht22_t *dev, int16_t *temperature_x10, uint16_t *humidity_x10); /*
void dht22_init(dht22_t *dev, PIO pio, uint sm, uint pin); * Инициализирует драйвер DHT22.
* На вход принимает:
* dev - структуру, куда сохраняется состояние драйвера;
* pio - блок PIO, который будет выполнять программу чтения;
* sm - номер state machine внутри выбранного PIO;
* pin - GPIO, к которому подключена линия данных датчика.
* Ничего не возвращает.
*/
void dht22_init(dht22_t *dev, PIO pio, uint sm, uint pin); // инициализирует PIO и GPIO
/*
* Выполняет одно чтение DHT22.
* На вход принимает:
* dev - ранее инициализированный дескриптор датчика;
* temperature_x10 - указатель, куда будет записана температура в десятых долях градуса;
* humidity_x10 - указатель, куда будет записана влажность в десятых долях процента.
* Возвращает код результата: успех, таймаут, ошибка контрольной суммы или залипшая шина.
*/
dht22_status_t dht22_read(dht22_t *dev, int16_t *temperature_x10, uint16_t *humidity_x10); // читает температуру и влажность x10
#endif #endif // DHT22_H
+17 -17
View File
@@ -1,19 +1,19 @@
.program dht22 .program dht22 // имя PIO-программы
; Capture 40 bits from DHT22 by sampling each high pulse around 35us after rising edge. ; Захватываем 40 бит, измеряя уровень примерно через 35 мкс после фронта.
.wrap_target .wrap_target // начало зацикленного участка
wait 0 pin 0 wait 0 pin 0 // ждём спад импульса
wait 1 pin 0 wait 1 pin 0 // ждём новый подъём импульса
set y, 4 set y, 4 // нужно принять 5 байт: 4..0
byte_loop: byte_loop: // внешний цикл по байтам
set x, 7 set x, 7 // в каждом байте 8 бит: 7..0
bit_loop: bit_loop: // внутренний цикл по битам
wait 0 pin 0 wait 0 pin 0 // ждём низкий уровень перед битом
wait 1 pin 0 wait 1 pin 0 // ждём начало высокого импульса
nop [31] nop [31] // выдерживаем основную часть задержки
nop [2] nop [2] // добираем задержку до точки выборки
in pins, 1 in pins, 1 // считываем один бит с входа
jmp x-- bit_loop jmp x-- bit_loop // повторяем, пока байт не собран
jmp y-- byte_loop jmp y-- byte_loop // повторяем, пока не собраны 5 байт
.wrap .wrap // конец зацикленного участка