В рамках домашней автоматизации необходимо измерение температуры и влажности. Для измерений в условиях квартиры будет достаточно датчика DHT11. В интернете есть готовые библиотеки и готовые примеры кода. Перечитав некоторое количество примеров, для практики пишу свой
Решение
Чтение данных с датчика DHT11 буду производить МК ATmega 328P, сразу заложим возможность подключения нескольких датчиков. Как обычно начинаем с чтения документации, какой из экземпляров оригинальный я не понял, некоторые варианты немного разнятся. Примера документации не приведу, ибо выбирал среднеарифметическое с проверкой на своих датчиках.
Судя из названия датчика, датчик возвращает относительную влажность и температуру окружающей среды. Датчик одновременно возвращает оба значения, поэтому сразу определим специальный тип на основе структуры. Тип назовем temphumi_t, который будет содержать в себе поля temp и humi.
typedef struct temphumi {
uint8_t temp;
uint8_t humi;
} temphumi_t; Далее переходим к теории работы с датчиком, для инициализации датчика необходимо на 18ms подать низкий уровень, затем подать высокий уровень на 40us, и переключить ножку в режим входа. От датчика должен последовать ответ — 54us низкий 80us высокий уровень. Из выше описанного следует, если после паузы не сменится логический уровень, то датчик не отвечает и можно не продолжать.
Если все идет по плану, то сразу последует передача 5-ти байт от датчика:
0 — Влажность;
1 — Вспомогательные данные;
2 — Температура;
3 — Вспомогательные данные;
4 — Контрольная сумма.
Теперь необходимо проверить корректность данных. Если сумма первых четырех байт == 0 или сумма первых четырех байт != пятому(контрольной сумме), то считанные данные можно считать не верными.
Остается описать функцию этому текстовому описанию. Параметрами будем передавать номер ножки, на которую подключен датчик и переменная созданного типа, в которую будет помещен результат опроса датчика.
// Необходимо выполнить инициализацию идентификаторов DHT_PORT, DHT_DDR и DHT_PIN.
//
//Например:
//
//#define DHT_PORT PORTC
//#define DHT_DDR DDRC
//#define DHT_PIN PINC
// Для удобства работы определим свой тип-струкутру с полями temp и humi
typedef struct temphumi {
uint8_t temp;
uint8_t humi;
} temphumi_t;
int DHT_read(uint8_t _PIN, temphumi_t* _DATA) {
// Датчик вернет 5 байт, подготовим перменную
uint8_t DHT_RESPONSE[5] = {0, 0, 0, 0, 0};
// Для инициализации датчика необходимо на 18ms подать низкий уровень.
// Затем подать высокий уровень на 40us, и переключить ножку в режим входа.
DHT_DDR |= (1 << _PIN);
DHT_PORT &= ~(1 << _PIN);
_delay_ms(18);
DHT_PORT |= (1 << _PIN);
DHT_DDR &= ~(1 << _PIN);
_delay_us(40);
// От датчика должен последовать ответ
// 54us низкийи 80us высокий уровень
if(DHT_PIN & (1 << _PIN)) {
return 0;
}
_delay_us(54);
if(!DHT_PIN & (1 << _PIN)) {
return 0;
}
_delay_us(80);
// После ответа последует передача данных, получаем 5 байт
uint8_t _bit, _byte;
while (DHT_PIN & (1 << _PIN));
for (_byte = 0; _byte < 5; _byte++) {
DHT_RESPONSE[_byte] = 0;
for (_bit = 0; _bit < 8; _bit++) {
while (!(DHT_PIN & (1 << _PIN))); // Тут лучше предохраняться таймером
_delay_us(30);
if (DHT_PIN & (1 << _PIN)) {
DHT_RESPONSE[_byte] |= 1 << (7 - _bit);
}
while (DHT_PIN & (1 << _PIN)); // Тут лучше предохраняться таймером
}
}
// Сумма полученных данных не должна быть равна 0
if (DHT_RESPONSE[0] + DHT_RESPONSE[1] + DHT_RESPONSE[2] + DHT_RESPONSE[3] == 0) { return 0; }
// Сумма первых четырех байт должна быть равна пятому(контрольной сумме)
if (DHT_RESPONSE[0] + DHT_RESPONSE[1] + DHT_RESPONSE[2] + DHT_RESPONSE[3] != DHT_RESPONSE[4]) { return 0; }
// Готовим полученные данные для возврата
_DATA -> humi = DHT_RESPONSE[0];
_DATA -> temp = DHT_RESPONSE[2];
return 1;
} В функции используются переменные HT_PORT, DHT_DDR и DHT_PIN, необходимы они для взаимодействия с датчиком, зависят они от порта, на котором будет подключен датчик и будут заданы через define. В своем примере я буду использовать порт C и его третью ножку.
Остается описать вариант применения. Для проверки полученных данных я буду их выводить на дисплей LCD1602, библиотеку к нему писал сам, еще требует доработок, позже выложу.
#define F_CPU 16000000UL
#define DHT_PORT PORTC
#define DHT_DDR DDRC
#define DHT_PIN PINC
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdio.h>
#include <string.h>
#include "DHT11.h"
#include "I2C.h"
#include "LCDTOI2C.h"
void setup() {
// Начальная настройка портов
DDRB = 0x00;
PORTB = 0x00;
DDRC = 0x00;
PORTC = 0x00;
DDRD = 0x00;
PORTD = 0x00;
// Настройка дисплея
I2C_init();
LCD_init(0b01111110); // 0x3F in BIN + 0
LCD_lightON();
}
int main(void) {
char buff[3]; // Буфер для дисплея
temphumi_t DHT_DATA = {0 ,0}; // Данные от датчика
setup();
LCD_setPos(0, 0);
LCD_print("TEMP");
LCD_setPos(1, 0);
LCD_print("HUMI");
while (1) {
if (DHT_read(3, &DHT_DATA) == 1) {
sprintf(buff, "%d", DHT_DATA.temp);
LCD_setPos(0, 5);
LCD_print(buff);
sprintf(buff, "%d", DHT_DATA.humi);
LCD_setPos(1, 5);
LCD_print(buff);
} else {
LCD_setPos(0, 5);
LCD_print("--");
LCD_setPos(1, 5);
LCD_print("--");
}
_delay_ms(5000);
}
} Остается подключить железо, прошить МК и проверить результат. Для фотографии светящегося дисплея на паяльник — не плохо!


Из статьи в статью ходит этот код со словами типа:»библиотеку для дисплея писал сам» или «библиотеку для LCD мы с вами написали в предыдущем уроке»… Но никто ничего не писал, и никакого предыдущего урока не было
Влад, конкретно этот код писал самостоятельно, глядя в документацию. Если код ходит из статьи в статью, то можно обратиться к web.archive.org и сравнить даты появления статей.
Про предыдущий урок упоминаний нет, т.к. код библиотеки еще не выкладывал. При необходимости в коде можно заменить вызов функций библиотеки работы с LCD на любую доступную библиотеку.