Как использовать датчик температуры и влажности воздуха DHT11 с PIC16F628A и LCD

Добавлено 29 октября 2016 в 14:00

В данной статье мы рассмотрим, как, используя PIC-контроллер, можно считать из DHT11 значения температуры и относительной влажности воздуха и показать их на LCD. В этом примере мы будем использовать микроконтроллер PIC16F628A.

Нам потребуется

Чтобы выполнить этот проект вам понадобится следующее:

Введение

DHT11 – это датчик температуры и влажности воздух, который использует один провод для передачи 40 бит данных. Первые 16 бит – это целая и дробная части значения влажности, следующие 16 бит – целая и дробная части значения температуры, и последние 8 бит – это контрольная сумма.

Чтобы DHT11 и микроконтроллер начали общаться друг с другом, их необходимо засинхронизировать. Чтобы засинхронизировать их, микроконтроллер посылает сигнал старта, который представляет собой импульс длительностью 20 мкс на выводе данных. После импульса, микроконтроллер ждет получения данных. В программе мы должны изменить направление вывода данных. Вы можете прочитать подробности о DHT11 в техническом описании. У вас может быть датчик, как в 4-выводном, так и в 3-выводном корпусе; но мы используем 3-выводную версию. Между этими версиями нет никакой разницы, а дополнительный вывод ни к чему не подключен.

Датчик температуры и относительной влажности воздуха DHT11
Датчик температуры и относительной влажности воздуха DHT11

Аппаратная часть

Первая умная вещь, которую стоит сделать при создании гаджета, это нарисовать его структурную схему. Это поможет вам увидеть, что вам необходимо, и как это реализовать. Ниже приведена структурная схема нашего термометра.

Структурная схема термометра на PIC16F628, DHT11 и LCD
Структурная схема термометра на PIC16F628, DHT11 и LCD
(может показаться избыточным, составлять структурную схему для каждой мелочи, но это может быть полезно)

Мы хотим, чтобы DHT11 передавал данные микроконтроллеру.

Мы хотим, чтобы микроконтроллер обрабатывал данные и отображал их на LCD дисплее.

Мы хотим, чтобы у нас была возможность прошивать микроконтроллер через ICSP.

Принципиальная схема

Принципиальная схема разделена на блоки.

Принципиальная схема термометра на PIC16F628 и DHT11
Принципиальная схема термометра на PIC16F628 и DHT11
Блок питания
Приводит входное напряжение к значению 5 вольт. Использует стабилизатор напряжения LM7805.
ICSP
Это 5-пиновый разъем, подключенный к выводам программирования микроконтроллера. Мы используем этот разъем для прошивки микроконтроллера.
DHT11
Это 3-пиновый разъем, к которому подключен датчик. Средний вывод подключен к микроконтроллеру для передачи данных.
MCU
Это PIC16F628, который принимает данные от DHT11 и выводит их на LCD дисплей.
Дисплей
LCD дисплей (2 строки по 16 символов), который показывает влажность и температуру воздуха.

Микроконтроллер у нас будет работать от внутреннего тактового генератора с частотой 4 МГц. Поэтому на схеме нам не нужен кварцевый резонатор.

Eagle сгенерировала нам следующий перечень элементов:

Термометр на PIC16F628 и DHT11. Перечень элементов
Позиционное обозначениеНоминалЭлементКорпусБиблиотекаЛист
C10.1uFC-EU025-050X050C025-050X050rcl1
C2100uFCPOL-EUE2.5-5E2,5-5rcl1
C30.1uFC-EU025-050X050C025-050X050rcl1
C4100uFCPOL-EUE2.5-5E2,5-5rcl1
C50.1uFC-EU025-050X050C025-050X050rcl1
D11N40041N4004DO41-10diode1
IC27805TV7805TVTO220Vlinear1
IC3PIC16F628DIL18DIL18ic-package1
JP1ICSPPINHD-1X51X05pinhead1
JP216x02 LCDPINHD-1X161X16pinhead1
JP3DHT11PINHD-1X31X03pinhead1
R110KR-EU_0204/70204/7rcl1
R25KTRIM_EU-LI10LI10pot1
R34K7R-EU_0204/70204/7rcl1
S1ResetTAC_SWITCHPTHTACTILE-PTHSparkFun1
X17-35vDCW237-10W237-102con-wago-5001

Теперь, когда мы разобрались с аппаратной частью, пришло время заняться программным обеспечением.

Программа

При установке компилятора XC8 вы также добавили и некоторые заголовочные и исходные файлы. В этом руководстве мы используем файлы библиотеки по работе с LCD, которые идут с компилятором XC8: XLCD.H и несколько исходных файлов. Чтобы всё немного упростить, я скопировал содержимое всех исходных файлов в один файл. На моей установке Ubuntu исходные файлы XLCD находятся в каталоге:

/opt/microchip/xc8/v1.34/sources/pic18/plib/XLCD

Там было 10 файлов, bysyxlcd.c, openxlcd.c, putrxlcd.c и т.д.. Я поместил содержимое всех этих файлов в один файл и назвал его my_xlcd.c. Этот файл теперь содержит все функции. Файлы my_xlcd.c и xlcd.h необходимо скопировать в каталог проекта (файл xlcd.h находился в каталоге /opt/microchip/xc8/v1.34/include/plib). Файл xlcd.h – это стандартный файл, который нуждается в небольшом редактировании. Нам необходимо изменить данные о подключении LCD к микроконтроллеру, чтобы они совпадали с нашей схемой:

/* DATA_PORT определяет порт, к которому подключены линии данных LCD */
 #define DATA_PORT      		PORTB
 #define TRIS_DATA_PORT 		TRISB

/* CTRL_PORT определяет порт, к которому подключены линии управления.
 * Это просто для примера, измените их для вашего приложения.
 */
 #define RW_PIN   PORTAbits.RA0   		/* PORT для RW */
 #define TRIS_RW  TRISAbits.TRISA0    	/* TRIS для RW */

 #define RS_PIN   PORTAbits.RA1  		/* PORT для RS */
 #define TRIS_RS  TRISAbits.TRISA1    	/* TRIS для RS */

 #define E_PIN    PORTAbits.RA7 		/* PORT для D  */
 #define TRIS_E   TRISAbits.TRISA7    	/* TRIS для E  */

Теперь связь между LCD дисплеем и микроконтроллером задана. С этими двумя файлами (my_xlcd.h и my_xlcd.c) больше ничего делать не требуется.

Далее основная программа. Она начинается с включения нескольких заголовочных файлов, битов конфигурации, определений, объявления переменных и функций:

// Заголовочные файлы
#include <stdio.h>               // Стандартная библиотека ввода-вывода
#include <stdlib.h>              // Стандартная библиотека функций
#include <xc.h>                  // Библиотека компилятора XC8
#include "my_xlcd.h"             // Наша библиотека LCD 

// Конфигурация
#pragma config FOSC = INTOSCIO  // Биты выбора тактового генератора (внутренний RC генератор: выводы RA6/OSC2/CLKOUT и RA7/OSC1/CLKIN работают, как входы/выходы)
#pragma config WDTE = OFF       // Бит включения сторожевого таймера (WDT выключен)
#pragma config PWRTE = ON       // Бит включения таймера запуска (PWRT включен)
#pragma config MCLRE = ON       // Выбор режима работы вывода RA5/MCLR (вывод RA5/MCLR действует, как MCLR)
#pragma config BOREN = ON       // Бит включения сброса при просадке напряжения питания (сброс BOD включен)
#pragma config LVP = ON         // Бит включения низковольтного программирования (вывод RB4/PGM действует, как PGM, низковольтное программирование включено)
#pragma config CPD = OFF        // Бит защиты данных EEPROM (защита данных выключена)
#pragma config CP = OFF         // Биты защиты кода программы (защита кода программы выключена)

// Определения
#define _XTAL_FREQ 4000000          // Говорим компилятору, что используем 4 МГц
#define data PORTAbits.RA2          // Определение RA0, как вывода данных
#define data_dir TRISAbits.TRISA2   // Определение TRISA0, как порта данных

// Глобальные переменные
char message1[] = "Temp = 00.0 c";
char message2[] = "RH   = 00.0 %";
unsigned short TOUT = 0, CheckSum, i;
unsigned short T_Byte1, T_Byte2, RH_Byte1, RH_Byte2;

// Прототипы функций
void init_XLCD(void);
void DelayFor18TCY(void);
void DelayPORXLCD(void);
void DelayXLCD(void);
void Delay10KTCYx(unsigned char);
void StartSignal(void);
unsigned short ReadByte();
unsigned short CheckResponse();

Чтобы заставить LCD дисплей работать с нашим микроконтроллером, нам необходимо создать несколько функций задержек. В начале вашего файла XLCD.H говорится, что:

Пользователь должен обеспечить три функции задержки:

  • DelayFor18TCY() обеспечивает задержку в 18 циклов;
  • DelayPORXLCD() обеспечивает задержку в 15 мс;
  • DelayXLCD() обеспечивает задержку в 5 мс.

Мы должны добавить еще и четвертую функцию задержки, Delay10KTCYx

Ниже приведены функция инициализации LCD и функции задержек:

// Функции
void init_XLCD(void){
    OpenXLCD(FOUR_BIT & LINES_5X7);     // Установить 4-битный режим и символы 5x7
    while(BusyXLCD());                  // Проверить, занят ли LCD
    WriteCmdXLCD(0x06);                 // Перенести курсов вправо
    WriteCmdXLCD(0x0C);                 // Дисплей включить, курсор выключить
}

void DelayFor18TCY(void){               // как сказано в файле XLCD.H
    NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); 
    NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); 
    return;
}

void DelayPORXLCD(void){               // как сказано в файле XLCD.H
    __delay_ms(15);
}

void DelayXLCD(void){                 // как сказано в файле XLCD.H
    __delay_ms(5);
}

void Delay10KTCYx(unsigned char){     // как сказано в файле XLCD.H
    __delay_ms(10);
}

Далее на очереди функции подачи стартового сигнала, чтения байта и проверки ответа:

void StartSignal(){
    data_dir = 0;       // Установить TRISA2 на выход
    data = 0;           // Установить на RA2 низкий уровень
    __delay_ms(18);     // Ждать 18 мс
    data = 1;           // Установить на RA2 высокий уровень
    __delay_us(20);     // Ждать 20 мс
    data_dir = 1;       // Установить TRISA2 на вход
}

unsigned short ReadByte(){
    unsigned short num = 0, t;
    data_dir = 1;                       // Установить TRISA2 на вход
    for (i=0;i<8;i++){                  // Начать цикл
        while(!data);                   // Когда данные не действительны
        TMR2 = 0;                       // Установить TMR2 в 0
        T2CONbits.TMR2ON = 1;           // Запустить TMR2 от 0, когда обнаруживается фронт
        while(data);                    // импульса, и ждать спада импульса
        T2CONbits.TMR2ON = 0;           // Остановить TMR2, после спада импульса данных
        if(TMR2>40) num |= 1 << (7-i);  // Если время > 40 мкс, то данные равны 1
    }
    return num;                         // Вернуть 8-бит = 1-байт
}

unsigned short CheckResponse(){
    TOUT = 0;
    TMR2 = 0;
    T2CONbits.TMR2ON = 1;       // Включить TMR2
    while(!data && !TOUT);      
    if (TOUT) return 0;         // Вернуть 0 => OK
    else {
        TMR2 = 0;               // Выключить Timer 2
        while(data && !TOUT);   
        if(TOUT) return 0;      // Если Tout = 1, вернуть 0 => OK
        else {
            T2CONbits.TMR2ON = 0;   // Выключить TMR2
            return 1;               // Вернуть 1 => NOT OK   
        }
    }
}

Чтобы узнать, когда микроконтроллер передает сигнал старта, и когда DHT11 закончил передачу своих 40 бит, нам нужна функция прерывания:

void interrupt tc_int(void){
    if(PIR1bits.TMR2IF){            // Если установлен флаг прерывания от совпадения TMR2 и PR2     
        TOUT = 1;
        T2CONbits.TMR2ON = 0;       // Остановить таймер
        PIR1bits.TMR2IF = 0;        // Очистить флаг прерывания TMR2
    }
}

И наконец, основная часть программы:

int main(int argc, char** argv) {
    unsigned short check;
    TRISB = 0b00000000;         // TRISB выход
    PORTB = 0b00000000;         // PORTB низкий уровень
    TRISA = 0b00000001;         // TRISA выход
    PORTA = 0b00000000;         // PORTA низкий уровень
    
    CMCON = 0x07;               // Компараторы выключены
    
    // TIMER
    INTCONbits.GIE = 1;         // Включить глобальное прерывание
    INTCONbits.PEIE = 1;        // Включить прерывание периферии
    PIE1bits.TMR2IE = 1;        // Включить прерывание Timer2
    T2CON = 0;                  // Предделитель 1:1, Timer2 изначально выключен
    PIR1bits.TMR2IF = 0;        // Очистить флаг прерывания TMR INT
    TMR2 = 0;
    
    init_XLCD();                    // Инициализация LCD
    putrsXLCD("  Hello World.");    // Текст приветствия
    SetDDRamAddr(0x40);             // Переместить курсор на строку 2
    putrsXLCD("  I'm alive.");
    __delay_ms(250);
    do {
        __delay_ms(1000);
        StartSignal();              // Передать сигнал старта
        check = CheckResponse();    // Присвоить check 0 = OK, или 1 = NOT OK
        if(!check) {                // OK check = 1 => NOT OK
            WriteCmdXLCD(0x01);     // Очистить экран, установить курсор в позицию 0,0    
            putrsXLCD("No response.");  // Вывести сообщение об ошибке
            SetDDRamAddr(0x40);         
            putrsXLCD("Please check.");
            }
        else {                      // IF check = 0 => OK
    
        RH_Byte1 = ReadByte();      // Прочитать первый байт
        RH_Byte2 = ReadByte();      // Прочитать второй байт
        T_Byte1 = ReadByte();       // Прочитать третий байт
        T_Byte2 = ReadByte();       // Прочитать четвертый байт
        CheckSum = ReadByte();      // Прочитать контрольную сумму
        // Проверить, соответствуют ли все байты контрольной сумме
        if (CheckSum == ((RH_Byte1 + RH_Byte2 + T_Byte1 + T_Byte2) & 0xFF))
        {
            message1[7] = T_Byte1/10 + 48;      // Извлечь десятки
            message1[8] = T_Byte1 + 48;         // Извлечь единицы
            message1[10]= T_Byte2/10 + 48;      // Извлечь десятые доли
            message1[11] = 223;                 // ASCII код для символа градусов          
            message2[7] = RH_Byte1/10 + 48;     // Извлечь десятки
            message2[8] = RH_Byte1 + 48;        // Извлечь единицы
            message2[10] = RH_Byte2/10 + 48;    // Извлечь десятые доли

            WriteCmdXLCD(0x01);
            putrsXLCD(message1);    // Записать значение температуры на LCD
            SetDDRamAddr(0x40);
            putrsXLCD(message2);    // Записать значение влажности на LCD
        }
        else {                      // Контрольная сумма некорректна
            WriteCmdXLCD(0x01);
            putrsXLCD("Checksum error!");
            SetDDRamAddr(0x40);
            putrsXLCD("Please wait.");
        }       
        }
    } while (1);    // Выполнять это постоянно.
}

Это был один из способов использования DHT11 совместно с PIC контроллером и LCD дисплеем.

Заключение

Мы использовали микроконтроллер PIC16F628A совместно с датчиком DHT11 и отобразили значения температуры и относительной влажности воздуха на LCD дисплее. Вы можете, приложив немного усилий, преобразовать/подстроить код для какого-либо другого микроконтроллера. Программа занимает в микроконтроллере 32% памяти данных и 55% памяти программ. Это потому, что микросхема довольно низкого класса.

Разница между PIC16F628 и PIC16F628A в том, что версия с буквой «A» обладает внутренним тактовым генератором. Версия без буквы «A» нуждается во внешнем кварцевом резонаторе или RC цепи. Если вы покупаете PIC16F628, то убедитесь, что он с буквой «A». Другой контроллер является устаревшим.

Фото и видео

Макет термометра на PIC16F628A и DHT11 с LCD
Макет термометра на PIC16F628A и DHT11 с LCD
Макет термометра на PIC16F628A и DHT11 с LCD (микроконтроллер)
Макет термометра на PIC16F628A и DHT11 с LCD (микроконтроллер)
Макет термометра на PIC16F628A и DHT11 с LCD (дисплей)
Макет термометра на PIC16F628A и DHT11 с LCD (дисплей)
Макет термометра на PIC16F628A и DHT11 с LCD (датчик температуры и относительной влажности воздуха)
Макет термометра на PIC16F628A и DHT11 с LCD (датчик температуры и относительной влажности воздуха)

Теги

DHT11LCD дисплейMCUPIC контроллерДатчик температурыИзмерениеИзмерение температурыМикроконтроллерОтносительная влажность воздухаТемпература

На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.

В случае комментирования в качестве гостя (без регистрации на disqus.com) для публикации комментария требуется время на премодерацию.