ATmega. Работа с датчиком DHT11

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

    }

}

Остается подключить железо, прошить МК и проверить результат. Для  фотографии светящегося дисплея на паяльник — не плохо!

ATmega. Работа с датчиком DHT11

Понравилась статья? Поделиться с друзьями:
Комментарии: 2
  1. Влад

    Из статьи в статью ходит этот код со словами типа:»библиотеку для дисплея писал сам» или «библиотеку для LCD мы с вами написали в предыдущем уроке»… Но никто ничего не писал, и никакого предыдущего урока не было :!:

    1. Guesto (автор)

      Влад, конкретно этот код писал самостоятельно, глядя в документацию. Если код ходит из статьи в статью, то можно обратиться к web.archive.org и сравнить даты появления статей.

      Про предыдущий урок упоминаний нет, т.к. код библиотеки еще не выкладывал. При необходимости в коде можно заменить вызов функций библиотеки работы с LCD на любую доступную библиотеку.

Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: