【ALIENTEK 战舰STM32开发板例程系列连载+教学】第三十六章 DHT11数字温湿度传感器实验

2019-08-16 23:08发布

 

第三十六章  DHT11数字温湿度传感器实验、

上一章,我们介绍了数字温度传感器DS18B20的使用,本章我们将介绍数字温湿度传感器DHT11的使用,该传感器不但能测温度,还能测湿度。本章我们将向大家介绍如何使用STM32来读取DHT11数字温湿度传感器,从而得到环境温度和湿度等信息,并把从温湿度值显示在TFTLCD模块上。本章分为如下几个部分: 36.1 DHT11简介 36.2 硬件设计 36.3 软件设计 36.4 下载验证

36.1 DHT11简介

DHT11是一款湿温度一体化的数字传感器。该传感器包括一个电阻式测湿元件和一个NTC测温元件,并与一个高性能8位单片机相连接。通过单片机等微处理器简单的电路连接就能够实时的采集本地湿度和温度。DHT11与单片机之间能采用简单的单总线进行通信,仅仅需要一个I/O口。传感器内部湿度和温度数据40Bit的数据一次性传给单片机,数据采用校验和方式进行校验,有效的保证数据传输的准确性。DHT11功耗很低,5V电源电压下,工作平均最大电流0.5mA DHT11的技术参数如下: l  工作电压范围:3.3V-5.5V l  工作电流 :平均0.5mA l  输出:单总线数字信号 l  测量范围:湿度20~90RH,温度0~50 l  精度 :湿度±5%,温度±2 l  分辨率 :湿度1%,温度1 DHT11的管脚排列如图36.1.1所示:

36.1.1 DHT11管脚排列图
虽然DHT11DS18B20类似,都是单总线访问,但是DHT11的访问,相对DS18B20来说要简单很多。下面我们先来看看DHT11的数据结构。 DHT11数字湿温度传感器采用单总线数据格式。即,单个数据引脚端口完成输入输出双向传输。其数据包由5Byte40Bit)组成。数据分小数部分和整数部分,一次完整的数据传输为40bit,高位先出。DHT11的数据格式为:8bit湿度整数数据+8bit湿度小数数据+8bit温度整数数据+8bit温度小数数据+8bit校验和。其中校验和数据为前四个字节相加。 传感器数据输出的是未编码的二进制数据。数据(湿度、温度、整数、小数)之间应该分开处理。例如,某次从DHT11读到的数据如图36.1.2所示:

36.1.2 某次读取到DHT11的数据
由以上数据就可得到湿度和温度的值,计算方法: 湿度= byte4 . byte3=45.0 (RH) 温度= byte2 . byte1=28.0 ( ) 校验= byte4+ byte3+ byte2+ byte1=73(=湿度+温度)(校验正确) 可以看出,DHT11的数据格式是十分简单的,DHT11MCU的一次通信最大为3ms左右,建议主机连续读取时间间隔不要小于100ms 下面,我们介绍一下DHT11的传输时序。DHT11的数据发送流程如图36.1.3所示:

36.1.3 DHT11数据发送流程
首先主机发送开始信号,即:拉低数据线,保持t1(至少18ms)时间,然后拉高数据线t220~40us)时间,然后读取DHT11的相应,正常的话,DHT11会拉低数据线,保持t340~50us)时间,作为响应信号,然后DHT11拉高数据线,保持t440~50us)时间后,开始输出数据。  DHT11输出数字‘0’的时序如图36.1.4所示:

36.1.4 DHT11数字‘0’时序
DHT11输出数字‘1’的时序如图36.1.5所示:

36.1.5 DHT11数字‘1’时序
       通过以上了解,我们就可以通过STM32来实现对DHT11的读取了。DHT11的介绍就到这里,更详细的介绍,请参考DHT11的数据手册。

36.2 硬件设计

由于开发板上标准配置是没有DHT11这个传感器的,只有接口,所以要做本章的实验,大家必须找一个DHT11插在预留的DHT11接口上。 本章实验功能简介:开机的时候先检测是否有DHT11存在,如果没有,则提示错误。只有在检测到DHT11之后才开始读取温湿度值,并显示在LCD上,如果发现了DHT11,则程序每隔100ms左右读取一次数据,并把温湿度显示在LCD上。同样我们也是用DS0来指示程序正在运行。 所要用到的硬件资源如下: 1)  指示灯DS0  2) TFTLCD模块 3)  DHT11温湿度传感器 这些我们都已经介绍过了,DHT11DS18B20的接口是共用一个的,不过DHT114条腿,需要把U134个接口都用上,将DHT11传感器插入到这个上面就可以通过STM32来读取温湿度值了。连接示意图如图36.2.1所示:

36.2.1 DHT11连接示意图
       这里要注意,将DHT11贴有字的一面朝内,而有很多孔的一面朝外,然后然后插入如图所示的四个孔内就可以了。

36.3 软件设计

打开上一章的工程,首先在HARDWARE文件夹下新建一个DHT11的文件夹。然后新建一个dht11.cdht11.h的文件保存在DHT11文件夹下,并将这个文件夹加入头文件包含路径。 打开dht11.c在该文件下输入如下代码: #include "dht11.h" #include "delay.h" //复位DHT11 void DHT11_Rst(void)      {                        DHT11_IO_OUT();      //SET OUTPUT     DHT11_DQ_OUT=0;    //拉低DQ     delay_ms(20);    //拉低至少18ms     DHT11_DQ_OUT=1;    //DQ=1        delay_us(30);            //主机拉高20~40us } //等待DHT11的回应 //返回1:未检测到DHT11的存在 //返回0:存在 u8 DHT11_Check(void)           {          u8 retry=0;        DHT11_IO_IN();//SET INPUT         while (DHT11_DQ_IN&&retry<100)//DHT11会拉低40~80us        {               retry++;               delay_us(1);        };           if(retry>=100)return 1;        else retry=0;     while (!DHT11_DQ_IN&&retry<100)//DHT11拉低后会再次拉高40~80us        {               retry++;               delay_us(1);        };        if(retry>=100)return 1;             return 0; } //DHT11读取一个位 //返回值:1/0 u8 DHT11_Read_Bit(void)                   {       u8 retry=0;        while(DHT11_DQ_IN&&retry<100)//等待变为低电平        {               retry++;               delay_us(1);        }        retry=0;        while(!DHT11_DQ_IN&&retry<100)//等待变高电平        {               retry++;               delay_us(1);        }        delay_us(40);//等待40us        if(DHT11_DQ_IN)return 1;        else return 0;             } //DHT11读取一个字节 //返回值:读到的数据 u8 DHT11_Read_Byte(void)     {            u8 i,dat;     dat=0;        for (i=0;i<8;i++)        {             dat<<=1;            dat|=DHT11_Read_Bit();     }                                                return dat; } //DHT11读取一次数据 //temp:温度值(范围:0~50°) //humi:湿度值(范围:20%~90%) //返回值:0,正常;1,读取失败 u8 DHT11_Read_Data(u8 *temp,u8 *humi)    {              u8 buf[5];        u8 i;        DHT11_Rst();        if(DHT11_Check()==0)        {               for(i=0;i<5;i++)//读取40位数据               {                      buf=DHT11_Read_Byte();               }               if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4])               {                      *humi=buf[0];                      *temp=buf[2];               }        }else return 1;        return 0;      } //初始化DHT11IO DQ 同时检测DHT11的存在 //返回1:不存在 //返回0:存在        u8 DHT11_Init(void) {        RCC->APB2ENR|=1<<8;    //使能PORTG口时钟        GPIOG->CRH&=0XFFFF0FFF;//PORTG.11 推挽输出        GPIOG->CRH|=0X00003000;        GPIOG->ODR|=1<<11;      //输出1                                       DHT11_Rst();        return DHT11_Check(); } 该部分代码就是根据我们前面介绍的单总线操作时序来读取DHT11的温湿度值的,DHT11的温湿度值通过DHT11_Read_Data函数读取,如果返回0,则说明读取成功,返回1,则说明读取失败。保存dht11.c,并把该文件加入到HARDWARE组下,然后我们打开dht11.h,在该文件下输入如下内容: #ifndef __DHT11_H #define __DHT11_H #include "sys.h"   //IO方向设置 #define DHT11_IO_IN()  {GPIOG->CRH&=0XFFFF0FFF;GPIOG->CRH|=8<<12;} #define DHT11_IO_OUT() {GPIOG->CRH&=0XFFFF0FFF;GPIOG->CRH|=3<<12;} ////IO操作函数                                                                               #define    DHT11_DQ_OUT PGout(11) //数据端口     PG11 #define    DHT11_DQ_IN  PGin(11)  //数据端口     PG11 u8 DHT11_Init(void);           //初始化DHT11 u8 DHT11_Read_Data(u8 *temp,u8 *humi);//读取温湿度 u8 DHT11_Read_Byte(void); //读出一个字节 u8 DHT11_Read_Bit(void);   //读出一个位 u8 DHT11_Check(void);       //检测是否存在DHT11 void DHT11_Rst(void);         //复位DHT11    #endif 此部分代码比较简单,接下来,我们先保存这段代码,然后打开test.c,在该文件下修改main函数如下: int main(void) {                            u8 t=0;                             u8 temperature;                u8 humidity;                 Stm32_Clock_Init(9);           //系统时钟设置        uart_init(72,9600);             //串口初始化为9600        delay_init(72);                         //延时初始化        LED_Init();                        //初始化与LED连接的硬件接口        LCD_Init();                       //初始化LCD        usmart_dev.init(72);             //初始化USMART              KEY_Init();                         //按键初始化       POINT_COLOR=RED;//设置字体为红 {MOD}        LCD_ShowString(60,50,200,16,16,"WarShip STM32");           LCD_ShowString(60,70,200,16,16,"DHT11 TEST");         LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");        LCD_ShowString(60,110,200,16,16,"2012/9/12");                    while(DHT11_Init())     //DHT11初始化            {               LCD_ShowString(60,130,200,16,16,"DHT11 Error");               delay_ms(200);               LCD_Fill(60,130,239,130+16,WHITE);              delay_ms(200);        }                                                                LCD_ShowString(60,130,200,16,16,"DHT11 OK");        POINT_COLOR=BLUE;//设置字体为蓝 {MOD}       LCD_ShowString(60,150,200,16,16,"Temp:  C");           LCD_ShowString(60,170,200,16,16,"Humi:  %");           while(1)        {                                  if(t%10==0)//100ms读取一次               {                                                                                    DHT11_Read_Data(&temperature,&humidity);          //读取温湿度值                                                          LCD_ShowNum(60+40,150,temperature,2,16);          //显示温度                                          LCD_ShowNum(60+40,170,humidity,2,16);                     //显示湿度                            }                                          delay_ms(10);               t++;               if(t==20)               {                      t=0;                      LED0=!LED0;               }        } } 至此,我们本章的软件设计就结束了。

36.4 下载验证

在代码编译成功之后,我们通过下载代码到ALIENTEK战舰STM32开发板上,可以看到LCD显示开始显示当前的温度值(假定DHT11已经接上去了),如图36.4.1所示:

36.4.1 DHT11实验效果图
至此,本章实验结束。大家可以将本章通过DHT11读取到的温度值,和前一章的通过DS18B20读取到的温度值对比一下,看看哪个更准确?
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
23条回答
victory
1楼-- · 2019-08-19 05:07
[mw_shl_code=c,true]dht11.c部分 #include "dht11.h" #include "delay.h" void DHT11_Rst(void) { DHT11_IO_OUT(); //SET OUTPUT DHT11_DQ_OUT=0; //??DQ delay_ms(20); //????18ms DHT11_DQ_OUT=1; //DQ=1 delay_us(30); //????20~40us } u8 DHT11_Check(void) { u8 retry=0; DHT11_IO_IN();//SET INPUT while (DHT11_DQ_IN&&retry<100)//DHT11???40~80us { retry++; delay_us(1); }; if(retry>=100)return 1; else retry=0; while (!DHT11_DQ_IN&&retry<100)//DHT11????????40~80us { retry++; delay_us(1); }; if(retry>=100)return 1; return 0; } //?DHT11????? //???:1/0 u8 DHT11_Read_Bit(void) { u8 retry=0; while(DHT11_DQ_IN&&retry<100)//??????? { retry++; delay_us(1); } retry=0; while(!DHT11_DQ_IN&&retry<100)//?????? { retry++; delay_us(1); } delay_us(40);//??40us if(DHT11_DQ_IN)return 1; else return 0; } //?DHT11?????? //???:????? u8 DHT11_Read_Byte(void) { u8 i,dat; dat=0; for (i=0;i<8;i++) { dat<<=1; dat|=DHT11_Read_Bit(); } return dat; } //?DHT11?????? //temp:???(??:0~50? //humi:???(??:20%~90%) //???:0,??;1,???? u8 DHT11_Read_Data(u8 *temp,u8 *humi) { u8 buf[5]; u8 i; DHT11_Rst(); if(DHT11_Check()==0) { for(i=0;i<5;i++)//??40??? { buf=DHT11_Read_Byte(); } if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4]) { *humi=buf[0]; *temp=buf[2]; } }else return 1; return 0; } u8 DHT11_Init(void) { RCC->APB2ENR|=1<<2; GPIOA->CRL&=0XFFFFFFF0; GPIOA->CRL|=0X00000003; DHT11_Rst(); return DHT11_Check(); } dht.h部分 #ifndef __DHT11_H #define __DHT11_H #include "sys.h" #define DHT11_IO_IN(){GPIOA->CRL&=0XFFFFFFF0;GPIOA->CRL|=8<<0;} #define DHT11_IO_OUT(){GPIOA->CRL&=0XFFFFFFF0;GPIOA->CRL|=3<<0;} #define DHT11_DQ_OUT PGout(0) #define DHT11_DQ_IN PGin(0) u8 DHT11_Init(void); u8 DHT11_Read_Data(u8 *temp,u8 *humi); u8 DHT11_Read_Byte(void); u8 DHT11_Read_Bit(void); u8 DHT11_Check(void); void DHT11_Rst(void); #endif [/mw_shl_code]    这么快就回复了,太感谢了!能帮我看看代码嘛 ,做毕设要用的。仅修改了端口部分,其他没变,板子是新买的,多谢原子大哥了!!!
victory
2楼-- · 2019-08-19 06:25
[mw_shl_code=c,true]#include "sys.h" #include "usart.h" #include "delay.h" #include "led.h" #include "lcd.h" #include "adc.h" //ALIENTEK Mini STM32开发板范例代码16 #include "dht11.h" int main(void) { u8 t=0; u8 temperature;//温度 u8 humidity;//湿度 Stm32_Clock_Init(9);//系统时钟 uart_init(72,460800);//初始化uart delay_init(72); LED_Init(); LCD_Init(); POINT_COLOR=RED; LCD_ShowString(30,50,200,16,16,"Mini STM32"); LCD_ShowString(30,70,200,16,16,"DHT11 TEST"); LCD_ShowString(30,90,200,16,16,"ALIENTEK"); LCD_ShowString(30,110,200,16,16,"2015/10/12"); while(DHT11_Init()) { LCD_ShowString(30,130,200,16,16,"DHT11 Error"); delay_ms(200); LCD_Fill(30,130,239,130+16,WHITE); delay_ms(200); LCD_ShowString(30,130,200,16,16,"DHT11 OK"); POINT_COLOR=BLUE; LCD_ShowString(30,150,200,16,16,"Temp: C"); LCD_ShowString(30,170,200,16,16,"Humi: %"); while(1) { if(t%10==0) { DHT11_Read_Data(&temperature,&humidity); LCD_ShowNum(30+40,150,temperature,2,16); LCD_ShowNum(30+40,170,humidity,2,16); } delay_ms(10); t++; if(t==20){t=0;LED0=!LED0;} } }} [/mw_shl_code] 还有主函数部分的代码
空翼0
3楼-- · 2019-08-19 09:12
原子哥,我将DHT11_Read_Bit函数中的
      delay_us(40);//等待40us

       if(DHT11_DQ_IN)return 1;

       else return 0;
这三行改为了
retry=0;
while(DHT11_DQ_IN&&retry<150)
{
retry++;
delay_us(1);
}
if(retry<40)
{
return 0;
}
else
return 1;
想着等到变成低电平后判断计数器的值,如果在26-28之间则返回零,在116-118之间则返回1,但下载代码后发现温度和湿度都显示为0,能否请原子哥帮忙看看?谢谢
空翼0
4楼-- · 2019-08-19 14:51
 精彩回答 2  元偷偷看……
异度世界
5楼-- · 2019-08-19 16:34
原子哥,我自己弄了个板子,按照开发板的图画的,DQ使用的是PB9,但是采集不到数据,buf[0]~buf[4]是9A ff ff ff ff ;
端口设置
//IO方向设置
#define DHT11_IO_IN()  {GPIOB->CRH&=0XFFFFFF0F;GPIOB->CRH|=8<<4;}
#define DHT11_IO_OUT() {GPIOB->CRH&=0XFFFFFF0F;GPIOB->CRH|=3<<4;}
////IO操作函数                                                                                          
#define        DHT11_DQ_OUT PBout(9) //数据端口        PA0
#define        DHT11_DQ_IN  PBin(9)  //数据端口        PA0
原子哥能帮忙找找原因吗

一周热门 更多>