diff --git a/src/dht22.c b/src/dht22.c index 149f05d..dfaf1a4 100644 --- a/src/dht22.c +++ b/src/dht22.c @@ -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; // всё прочитано корректно } diff --git a/src/dht22.h b/src/dht22.h index 3089b29..8474908 100644 --- a/src/dht22.h +++ b/src/dht22.h @@ -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 diff --git a/src/dht22.pio b/src/dht22.pio index 73b1662..b69fa68 100644 --- a/src/dht22.pio +++ b/src/dht22.pio @@ -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 // конец зацикленного участка