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 "hardware/clocks.h"
#include "hardware/gpio.h"
#include "pico/stdlib.h"
#include "dht22.pio.h" // сгенерированная PIO-программа
#include "hardware/clocks.h" // частота системного такта
#include "hardware/gpio.h" // управление GPIO
#include "pico/stdlib.h" // время и задержки
#define DHT22_START_LOW_US 1200u
#define DHT22_PREPARE_US 30u
#define DHT22_WORD_TIMEOUT_US 2000u
#define DHT22_START_LOW_US 1200u // стартовый импульс хоста
#define DHT22_PREPARE_US 30u // пауза перед захватом
#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) {
uint64_t deadline = time_us_64() + timeout_us;
while (pio_sm_is_rx_fifo_empty(pio, sm)) {
if (time_us_64() > deadline) {
return false;
uint64_t deadline = time_us_64() + timeout_us; // вычисляем момент истечения таймаута
while (pio_sm_is_rx_fifo_empty(pio, sm)) { // ждём появления слова в FIFO
if (time_us_64() > deadline) { // проверяем превышение таймаута
return false; // данных не дождались
}
}
*out = pio_sm_get(pio, sm);
return true;
*out = pio_sm_get(pio, sm); // забираем слово из FIFO
return true; // чтение прошло успешно
}
/*
* Инициализирует драйвер DHT22.
* На вход принимает:
* dev - структуру, куда сохраняется состояние драйвера;
* pio - блок PIO, который будет выполнять программу чтения;
* sm - номер state machine внутри выбранного PIO;
* pin - GPIO, к которому подключена линия данных датчика.
* Ничего не возвращает.
*/
void dht22_init(dht22_t *dev, PIO pio, uint sm, uint pin) {
dev->pio = pio;
dev->sm = sm;
dev->pin = pin;
dev->offset = pio_add_program(pio, &dht22_program);
dev->pio = pio; // сохраняем выбранный блок PIO
dev->sm = sm; // сохраняем номер state machine
dev->pin = pin; // сохраняем номер GPIO
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);
sm_config_set_in_pins(&c, pin);
sm_config_set_clkdiv(&c, (float)clock_get_hz(clk_sys) / 1000000.0f);
sm_config_set_in_shift(&c, false, true, 8);
pio_sm_config c = dht22_program_get_default_config(dev->offset); // берём базовую конфигурацию
sm_config_set_in_pins(&c, pin); // читаем биты с нужного пина
sm_config_set_clkdiv(&c, (float)clock_get_hz(clk_sys) / 1000000.0f); // делаем шаг PIO равным 1 мкс
sm_config_set_in_shift(&c, false, true, 8); // сдвигаем вход и автопушим по 8 бит
pio_sm_init(pio, sm, dev->offset, &c);
pio_sm_set_enabled(pio, sm, false);
pio_sm_init(pio, sm, dev->offset, &c); // записываем конфигурацию в SM
pio_sm_set_enabled(pio, sm, false); // пока не запускаем автомат
gpio_init(pin);
gpio_pull_up(pin);
gpio_set_dir(pin, GPIO_IN);
gpio_init(pin); // инициализируем GPIO со стороны CPU
gpio_pull_up(pin); // включаем подтяжку вверх
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) {
if (!gpio_get(dev->pin)) {
return DHT22_BUS_STUCK;
if (!gpio_get(dev->pin)) { // линия должна быть в единице в покое
return DHT22_BUS_STUCK; // иначе шина залипла
}
gpio_set_dir(dev->pin, GPIO_OUT);
gpio_put(dev->pin, 0);
sleep_us(DHT22_START_LOW_US);
gpio_set_dir(dev->pin, GPIO_OUT); // временно берём линию под управление CPU
gpio_put(dev->pin, 0); // тянем линию вниз для старта обмена
sleep_us(DHT22_START_LOW_US); // держим стартовый импульс
gpio_set_dir(dev->pin, GPIO_IN);
gpio_pull_up(dev->pin);
sleep_us(DHT22_PREPARE_US);
gpio_set_dir(dev->pin, GPIO_IN); // отпускаем линию датчику
gpio_pull_up(dev->pin); // оставляем подтяжку вверх
sleep_us(DHT22_PREPARE_US); // даём датчику начать ответ
pio_sm_set_enabled(dev->pio, dev->sm, false);
pio_sm_clear_fifos(dev->pio, dev->sm);
pio_sm_restart(dev->pio, dev->sm);
pio_sm_set_enabled(dev->pio, dev->sm, true);
pio_sm_set_enabled(dev->pio, dev->sm, false); // останавливаем SM перед новым чтением
pio_sm_clear_fifos(dev->pio, dev->sm); // чистим FIFO от старых данных
pio_sm_restart(dev->pio, dev->sm); // сбрасываем состояние SM
pio_sm_set_enabled(dev->pio, dev->sm, true); // запускаем захват битов
uint8_t data[5] = {0};
for (uint i = 0; i < 5; ++i) {
uint32_t word = 0;
if (!wait_for_rx_word(dev->pio, dev->sm, &word, DHT22_WORD_TIMEOUT_US)) {
pio_sm_set_enabled(dev->pio, dev->sm, false);
return DHT22_TIMEOUT;
uint8_t data[5] = {0}; // буфер пяти байт DHT22
for (uint i = 0; i < 5; ++i) { // читаем все 40 бит по байтам
uint32_t word = 0; // временное слово из FIFO
if (!wait_for_rx_word(dev->pio, dev->sm, &word, DHT22_WORD_TIMEOUT_US)) { // ждём очередной байт
pio_sm_set_enabled(dev->pio, dev->sm, false); // останавливаем SM при ошибке
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]);
if (checksum != data[4]) {
return DHT22_CHECKSUM_ERROR;
uint8_t checksum = (uint8_t)(data[0] + data[1] + data[2] + data[3]); // считаем контрольную сумму
if (checksum != data[4]) { // сравниваем с байтом датчика
return DHT22_CHECKSUM_ERROR; // данные повреждены
}
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_h = (uint16_t)((data[0] << 8) | data[1]); // собираем влажность из двух байт
uint16_t raw_t = (uint16_t)((data[2] << 8) | data[3]); // собираем температуру из двух байт
*humidity_x10 = raw_h;
if (raw_t & 0x8000u) {
*temperature_x10 = -(int16_t)(raw_t & 0x7fffu);
*humidity_x10 = raw_h; // DHT22 уже даёт влажность в десятых долях
if (raw_t & 0x8000u) { // старший бит хранит знак температуры
*temperature_x10 = -(int16_t)(raw_t & 0x7fffu); // отрицательная температура
} 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
#define DHT22_H
#ifndef DHT22_H // защита от двойного включения
#define DHT22_H // открываем тело заголовка
#include "hardware/pio.h"
#include "pico/types.h"
#include "hardware/pio.h" // типы и API PIO
#include "pico/types.h" // базовые типы Pico SDK
typedef enum {
DHT22_OK = 0,
DHT22_TIMEOUT,
DHT22_CHECKSUM_ERROR,
DHT22_BUS_STUCK
} dht22_status_t;
typedef enum { // коды результата чтения
DHT22_OK = 0, // чтение успешно
DHT22_TIMEOUT, // датчик не ответил вовремя
DHT22_CHECKSUM_ERROR, // контрольная сумма не сошлась
DHT22_BUS_STUCK // линия данных зажата в нуле
} dht22_status_t; // тип статуса DHT22
typedef struct {
PIO pio;
uint sm;
uint pin;
uint offset;
} dht22_t;
typedef struct { // состояние драйвера датчика
PIO pio; // используемый блок PIO
uint sm; // номер state machine
uint pin; // GPIO линии данных
uint offset; // смещение программы в PIO
} 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.
.wrap_target
wait 0 pin 0
wait 1 pin 0
; Захватываем 40 бит, измеряя уровень примерно через 35 мкс после фронта.
.wrap_target // начало зацикленного участка
wait 0 pin 0 // ждём спад импульса
wait 1 pin 0 // ждём новый подъём импульса
set y, 4
byte_loop:
set x, 7
bit_loop:
wait 0 pin 0
wait 1 pin 0
nop [31]
nop [2]
in pins, 1
jmp x-- bit_loop
jmp y-- byte_loop
.wrap
set y, 4 // нужно принять 5 байт: 4..0
byte_loop: // внешний цикл по байтам
set x, 7 // в каждом байте 8 бит: 7..0
bit_loop: // внутренний цикл по битам
wait 0 pin 0 // ждём низкий уровень перед битом
wait 1 pin 0 // ждём начало высокого импульса
nop [31] // выдерживаем основную часть задержки
nop [2] // добираем задержку до точки выборки
in pins, 1 // считываем один бит с входа
jmp x-- bit_loop // повторяем, пока байт не собран
jmp y-- byte_loop // повторяем, пока не собраны 5 байт
.wrap // конец зацикленного участка