Files
Thermometr_pico/src/dht22.c
T
2026-05-02 20:02:11 +03:00

117 lines
7.0 KiB
C

#include "dht22.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 // таймаут на один байт
/*
* Ждёт появления одного слова в 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)) { // ждём появления слова в FIFO
if (time_us_64() > deadline) { // проверяем превышение таймаута
return false; // данных не дождались
}
}
*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; // сохраняем выбранный блок PIO
dev->sm = sm; // сохраняем номер state machine
dev->pin = pin; // сохраняем номер GPIO
dev->offset = pio_add_program(pio, &dht22_program); // загружаем программу в PIO
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); // делаем шаг PIO равным 1 мкс
sm_config_set_in_shift(&c, false, true, 8); // сдвигаем вход и автопушим по 8 бит
pio_sm_init(pio, sm, dev->offset, &c); // записываем конфигурацию в SM
pio_sm_set_enabled(pio, sm, false); // пока не запускаем автомат
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; // иначе шина залипла
}
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); // даём датчику начать ответ
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}; // буфер пяти байт 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; // сохраняем младший байт
}
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; // данные повреждены
}
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; // DHT22 уже даёт влажность в десятых долях
if (raw_t & 0x8000u) { // старший бит хранит знак температуры
*temperature_x10 = -(int16_t)(raw_t & 0x7fffu); // отрицательная температура
} else {
*temperature_x10 = (int16_t)raw_t; // положительная температура
}
return DHT22_OK; // всё прочитано корректно
}