В рамках домашней автоматизации необходимо измерение температуры и влажности. Для измерений в условиях квартиры будет достаточно датчика 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 на любую доступную библиотеку.