#申请原创#
温湿度传感器DHT11模块是常见的传感器,其湿度的测量范围是5~95%,温度的测量范围是-20~60℃,尽管其精度低,误差大,但对普通家用来说是可以满足的。这个传感器在之前也做过测试,本次测试则是按照传感器的通讯方式重新编写代码,读取数据的过程在一个函数内完成,程序简洁明了,执行的效率也提高了。
DHT11传感器是单线通讯协议,平时DHT11模块处于低功耗休眠状态,主机将数据线拉低20ms,然后拉高数据线,DHT11模块便开始响应,首先给出83毫秒的低电平和87毫秒的高电平应答信号,随后便输入40位‘0’或‘1’数据,包含2个字节的湿度数据、2个字节的湿度数据以及1个字节的和校验数据。完整的时序如下:
DHT11的应答信号及数据时序如下:
通讯协议的特征如下:
本次的代码就是按照这些数据编写的,其中判断‘0’和‘1’的方法是通过检查循环次数多少来判断的,而之前的代码则是通过高电平开始后延时40微秒后再读取数据线的电平来判断。
按照上表,信号‘0’的高电平时间为24毫秒左右,信号‘0’的高电平时间为71毫秒左右,而我的读取循环是从100开始每毫秒递减,代码如下:
retry = 100;
while((DHT11_GetValue()>0)&&retry>0){
retry--;
delay_us(1);
}
开始我没有考虑其它代码执行需要的时间,简单地以retry变量数据50为分界线,低于50认为是信号‘0’,高于50则认为是信号‘0’,可实际执行却是读取的全是0。在测试过程中我用一个LED引脚来模拟DHT11信号,即根据DHT11信号线的状态来改变LED引脚的电平,从时序图上看除了稍有滞后外模拟基本正确,但均未读取到有效数据。下面时序图的第一行是DHT11的数据线,第二行则是模拟的LED引脚电平:
最后我只好通过数组将每个信号保存下来,最后发送到串口调试助手来观看,发现上述代码的循环时间远远超过1毫秒,信号‘0’时只循环了2次左右,信号‘1’也仅循环了5次左右。下图为各个信号的retry数值:
最后我将retry值的分界点定在97,才成功地读取出数据。下面是完整的读取函数:
/*********************************************
* 函数名称:DHT11_Read_Val()
* 函数功能:读取温湿度传感器数据
* 入口参数:无
* 出口参数:无
* 备注:数据存放在全局变量DHT中,DHT[0]=湿度整数部分,DHT[1]=湿度小数部分,DHT[2]=温度整数部分,DHT[3]=温度小数部分,
* 湿度值(范围:20% RH ~ 90% RH 分辨率:0.1%RH 精度:±5% RH (25℃))
* 温度值(范围:0℃ ~ 50℃ 分辨率:0.1℃ 精度:±2)
*********************************************/
void DHT11_Read_Val(void)
{
uint8_t i,j,dat,retry;
DHT11_SetDigitalOutput();
DHT11_SetLow(); //拉低DQ,发送启动信号
delay_ms(20); //等待18~30ms
DHT11_SetHigh(); //DQ=1
delay_us(20); //主机拉高20~40us
DHT11_SetDigitalInput(); //总线切换到输入模式
retry = 100;
while((!DHT11_GetValue())&&retry){
retry--;
delay_us(1);
}
if(retry==0){ //低电平响应信号超时
return;
}
retry = 100;
while((DHT11_GetValue())&&retry){
retry--;
delay_us(1);
}
if(retry==0){ //高电平响应信号超时
return;
}
for(j=0; j<5; j++){ //持续读入五个字节数据
dat = 0;
for(i=0; i<8; i++){
retry = 100;
while((0==DHT11_GetValue())&&retry>0){
retry--;
delay_us(1);
}
retry = 100;
while((DHT11_GetValue()>0)&&retry>0){
retry--;
delay_us(1);
}
dat<<=1; //左移一位
if(retry<97)
dat|=0x01;
}
DHT[j] = dat;
}
if(DHT[4]==DHT[0]+DHT[1]+DHT[2]+DHT[3])
DHT[4] = 0;
else
DHT[4] = 1;
}
下图为测试的过程:
这是屏幕中显示读取的数据,前面是湿度值,后面是温度值:
|