mirror of
https://github.com/stasenso/Thermometr_pico.git
synced 2026-10-25 17:13:33 +03:00
117 lines
7.0 KiB
C
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; // всё прочитано корректно
|
|
}
|