#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; // всё прочитано корректно }