本篇文章对于DHT11进行测试,并且提供程序思路
先看一下DHT11的数据
目前DHT11读出的湿度小数和温度小数都为0(和DHT11的版本有关),它采用单总线协议,但是和DS18B20的不同在于,它没有复杂的控制字节,以及设备编码,还有就是诸如eeprom和温度上下限等特殊存储功能,相比之下操作更简单一些。并且可以一次读出湿度和温度,比较方便。
下面看一下具体时序操作
首先主机要拉低总线至少18ms,并且主机可以马上拉高总线,等待DHT的响应信号,在响应信号后,它会有一段准备时间(DHT拉高总线),然后再拉低总线开始传送数据,每一bit数据都以50us低电平时隙开始,高电平的长短定了数据位是0还是1.格式见下面图示。
这个其实也比较好解决,可以在传送数据的时候等待高电平的到来,到来后等待60us(这个时间不是随意的,不能超过76-78,也就是50us+(26或28)),这个时候如果再去读取,电平为低的话,则为0,电平为高的话,则为1,然后再等待高电平过去。这个思想很好,可以好好想一下。下面提供程序思路
#include "stc15f2k60s2.h"
#include "DHT11.h"
#include "intrins.h"
#define uchar unsigned char
#define uint unsigned int
#define TIMEMAX 2000
sbit DQ = P1^0;
void Delay19ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
_nop_();
_nop_();
i = 1;
j = 205;
k = 97;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void Delay60us() //@11.0592MHz
{
unsigned char i, j;
i = 1;
j = 162;
do
{
while (--j);
} while (--i);
}
//数据传输
uchar Read_Dat()
{
uchar i; uchar Dat;
for( i = 0 ; i < 8 ; i++ )
{
uint timeout = TIMEMAX;
while( !DQ && timeout-- ); //等待低电平信号过去
if(timeout == 0)
return 0;
timeout = TIMEMAX;
//延时60us并且去读取数据,如果是0的话,现在已经是越过了高电平并且50us的低电平已经过了约30us了
//而如果是1,现在还是高电平。
Delay60us();
Dat <<= 1;
if( DQ )
{
Dat |= 1;
}
//如果数据是0的话,就直接跳过了,去执行while( !DQ )来等待剩余的越20us低电平过去,并且进行下一次传送
//如果数据是1的话,就等待高电平过去,然后执行while( !DQ )来等待50us的低电平过去,并且进行下一次传送
while( DQ && timeout--);
if(timeout == 0)
return 0;
}
return( Dat );
}
/*响应信号是DHT11拉低总线 准备信号是DHT11拉高总线*/
uint Read_DHT11()
{
uint Dat;
uchar SD_z , SD_x , WD_z , WD_x , JY; //分别为湿度整数,湿度小数,温度整数,温度小数 , 校验值
uint timeout = TIMEMAX;
DQ = 1;
_nop_();
DQ = 0; //拉低总线至少18ms
Delay19ms();
DQ = 1;
while( DQ && timeout--); // 主机拉高总线,并且等待DHT11响应信号
if(timeout == 0)
return 0;
timeout = TIMEMAX;
while( !DQ && timeout-- ); //响应信号到来后,等待响应信号结束,(也即为等待准备信号 )
if(timeout == 0)
return 0;
timeout = TIMEMAX;
while( DQ && timeout--); //准备信号到来后,等待准备信号结束,并开始数据传输
if(timeout == 0)
return 0;
SD_z = Read_Dat();
SD_x = Read_Dat();
WD_z = Read_Dat();
WD_x = Read_Dat();
JY = Read_Dat();
//校验
if( JY == ( SD_z + SD_x + WD_z + WD_x ) )
Dat = ( ( uint )SD_z << 8 )| WD_z ;
else
Dat = 0;
return( Dat );
}
这个程序其实是不好的,因为没有加超时退出,即如果条件没有达到,会卡死程序,一般调好了,不会出现这个问题,所以,要想更完美点,就加上超时退出就行了。
下面是串口收到的数据
注意,在读取单总线这种对信号严格要求的信号来说,读取的时候,最好将中断关闭!