DHT11温湿度模块简介:
DHT11 一款湿温度一体化的数字传感器。通过单片机等微处理器简单的电路连接就能够实时的采集本地湿度和温度。DHT11 与单片机之间能采用简单的单总线进行通信,仅仅需要一个I/O 口。传感器内部湿度和温度数据 40Bit 的数据一次性传给单片机,数据采用校验和方式进行校验,有效的保证数据传输的准确性。DHT11 功耗很低,5V 电源电压下,工作平均最大电流 0.5mA。
性能指标和特性如下:
在讲解代码之前我们先看一下DHT11的数据结构:
DHT11数字温湿度传感器采用的是单总线数据格式,这个和DS18B20是一样的,都是由单个数据引脚端口完成输入输出双向传输。其数据包由5Byte(40Bit)组成。数据分为小数部分和整数部分,具体的格式如下:
一次完整的数据传输为40Bit,高位先出。
数据格式: 8Bit适度整数数据+8Bit湿度小数数据
+8Bit适度整数数据+8Bit湿度小数数据
+8Bit校验和数据
其中校验和数据为前四个字节相加。
数值的计算方法:
humi=byte4.byte3=45.0 (%RH) 湿度
temp=byte2.byte1=28.0 (℃) 温度
校验和即 byte0 =byte1+byte2+byte3+byte4 (如果相等则数据校验成功)
这里还要注意一点就是,DHT11一次通讯时间最大为3ms,主机连续采样间隔建议不小于100ms
DHT11和单片机的连接和程序的编写:
1
先看看ht11.h文件:
#define DHT11 (GPIO_Pin_11) //PG11
#define GPIO_DHT11 GPIOG
#define DHT11_DQ_IN PGin(11) //输入
#define DHT11_DQ_OUT PGout(11) //输出
void DHT11_IO_OUT(void);
void DHT11_IO_IN(void);
u8 DHT11_Init(void);
void DHT11_Rst(void);
u8 DHT11_Check(void);
u8 DHT11_Read_Bit(void);
u8 DHT11_Read_Byte(void);
u8 DHT11_Read_Data(u8 *temp,u8 *humi);
程序的编写思想和DS18B20的同出一辙,首先定义了PG11为数据端口,将PG11通DHT11的输出端口相连就可以进行数据传输了。这种在头文件中利用宏定义来定义一些程序中需要更换的参数,这是有利于程序的移植的,如果需要更换数据输入的端口则只需要在头文件中更改定义的宏即可。
u8 DHT11_Check() void DHT11_Rst()
{ {
u8 retry=0; DHT11_IO_OUT(); //SET OUTPUT
DHT11_IO_IN();//SET INPUT DHT11_DQ_OUT=0; //拉低DQ
while (DHT11_DQ_IN&&retry<100)//DHT11会拉低40~50us delay_ms(20); //拉低至少18ms
{ DHT11_DQ_OUT=1; //DQ=1
retry++; delay_us(30); //主机拉高20~40us
delay_us(1); }
};
if(retry>=100)return 1;
else retry=0;
while (!DHT11_DQ_IN&&retry<100)//DHT11拉低后会再次拉高40~50us
{
retry++;
delay_us(1);
};
if(retry>=100)return 1;
return 0;
}
看这个检测和复位的程序是不是很熟悉,这个和DS18B20的基本相似只是高低电平持续的时间不一样。来看看DHT11的复位和响应号:
(这里不做过多解释,有疑问参考 基于stm32f103zet6的温度检测-X星球电子工程师社区 )
DHT11数据“0”的表示方法:
DHT11数据“1”的表示方法:
这里看到数据“0”和数据“1”的主要区别就是数据线在12-14us的低电平之后高点平的持续时间不一样,数据“0”高点平持续的时间为26-28us而数据“1”高点平持续时间则为116--118us,我们则可以利用这一点来判断DHT11发送的是数据“0”还是数据“1”,即在低电平结束后的30us--118us之间检测数据端口输出的电压值,如果为高电平则为数据“1”反之则为数据“0”。那这时候就有小伙伴会问:我们怎们知道DHT11什么时候发送数据呢?我们再来看看DHT11开始发送数据的流程:
我们可以看到,要想得到DHT11检测到的数据我们首先得向DHT11发送一段开始信号并且再得到DHT11的相应之后才能开始接收数据流,单片机发送开始信号之后,延时等待20-40us后读取DHT11的回应信号,读取总线为低电平,说明DHT11发送相应信号,DHT11发送响应信号之后,再把数据线拉高,准备发送数据,每一bit数据都是以低电平开始,如果读取响应信号为高点平,则DHT11没有响应。具体的程序如下:
u8 DHT11_Read_Bit(void)
{
u8 retry=0;
while(DHT11_DQ_IN&&retry<100)//等待变为低电平 12-14us 开始
{
retry++;
delay_us(1);
}
retry=0;
while(!DHT11_DQ_IN&&retry<100)//等待变高电平 26-28us表示0,116-118us表示1
{
retry++;
delay_us(1);
}
delay_us(40);//等待40us
if(DHT11_DQ_IN)return 1;
else return 0;
}
u8 DHT11_Read_Byte(void)
{
u8 i,dat;
dat=0;
for (i=0;i<8;i++)
{
dat<<=1;
dat|=DHT11_Read_Bit();
}
return dat;
}
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[i]=DHT11_Read_Byte();
}
if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4]) \\因为数据传输从高位开始,则这里的buff[0]其实是byte4
{ \\buff[2]为byte2
*humi=buf[0]; \\因为DHT11的分辨率为1,所以这里只输出整数位
*temp=buf[2];
}
}else return 1;
return 0;
}
主函数就比较简单了:
void data_pros() //数据处理函数
{
u8 temp;
u8 humi;
DHT11_Read_Data(&temp,&humi);
printf("温度=%d°C 湿度=%d%%RH\r\n",temp,humi);
}
int main()
{
u8 i=0;
SysTick_Init(72);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断优先级分组 分2组
LED_Init();
USART1_Init(115200);
while(DHT11_Init()) //检测dht11是否存在
{
printf("DHT11 Check Error!\r\n");
delay_ms(500);
}
printf("DHT11 Check OK!\r\n");
while(1)
{
i++;
if(i%20==0)
{
led1=!led1;
data_pros(); //读取一次DHT11数据最少要大于100ms
}
delay_ms(100);
}
}
在主函数之前定义了一个数据处理函数,用于将得到的DHT11的数据通过串口在上位机串口助手上显示,如果小伙伴们用到其他显示屏幕则可以在这个数据处理函数当中直接修改数据的输出就可以了。这样定义之后在主函数中直接调用这个数据处理函数就可以完成数据的读写了。主函数中是每2秒读取一次DHT11的数值,如果需要修改时间,直接修改delay_ms()函数的延迟或者if(i%20==0)中取余的数值即可,这个也是常用的延迟较长时间的延长方式(因为 delay_ms()函数每次延时的时间是不能超过一定界限的)。
总结:
有了之前DS18B20的基础理解DHT11会更加容易一些,两种传感器的数据传输是比较类似的。