
经常有在视频中看到网友应用DHT11采集温湿度,其次它与sht11这片温湿度传感就一字之差,臆想着它俩是否是互换代用,上某宝查一查发现这价格很亲民,果断的下单了一片去尝试着搞通它,快递收到后因为时间问题让它躺在元件盒里两个月是没开过包装。近期工作忙完了,拿出来弄一弄,下载芯片资料后发现当时应该买一个DHT11的模块,驱动DHT11还需要加一个5K的上拉电阻,几经翻箱找到一个4.7K的电阻,把它和电阻焊在一块多孔板上做测试。程序测试过程中有数据出不来和数据验证不正确的异常情况,原因都是DHT11对时序要求很严格,对延时函数的精度要求很高,下面就是测试过程,笔者水平有限,错误难免有,请原谅。
一、硬件电路的搭建:

二、编写DHT11的驱动程序:
1、引脚说明:

2、串行接口说明:
DATA用于微处理器与DHT11 之间的通讯和同步,采用单总线数据格式,一次通讯时间4ms左右,数据分小数部分和整数部分,具体格式在下面说明,当前小数部分用于以后扩展,现读出为零。操作流程如下:一次完整的数据传输为 40bit, 高位先出。
数据格式:8bit 湿度整数数据+8bit湿度小数数据+8bit 温度整数数据 +8bit 温度小数数据+8bit 校验和
数据传送正确时校验和数据等于 “8bit 湿度整数数据 +8bit湿度小数数据+8bit 温度整数数据 +8bit 温度小数数据 ” 所得结果的末 8 位。
3、总线的起始信号:
总线空闲状态为高电平 ,主机把总线拉低等待 DHT11 响应 ,主机把总线拉低必须大于 18 毫秒 , 保证 DHT11 能检测到起始信号。 DHT11接收到主机的开始信号后 ,等待主机开始信号结束 ,然后发送80us 低电平响应信号 。主机发送开始信号结束后,延时等待 20-40us 后,读取 DHT11 的响应信号,主机发送开始信号后,可以切换到输入模式,或者输出高电平均可,总线由上拉电阻拉高。

小结起始信号:
(1)MCU拉低18ms,然后再拉高40us,MCU进入等待状态。
(2)MCU等待状态下,等待DHT11响应。
(3)当DHT11接到MCU的起始信号后,拉低总线80us,再释放总线80us。
(4)响应完成后,DHT11开始传输40bit数据,MCU进入数据接收状态。
以下为起始信号函数代码:
unsignedchar Dht11_start()
{
unsigned char i=0;
Dat=1;
Dat=0;
DelayMs(18);//MCU下拉18ms
Dat=1;
while(Dat==1&&i<50)//等待DHT响应,拉低总线
{
i++;
_nop_();//2us
}
if(i>=30)//响应超时或无响应,退出
{
return0;
}
else
{
i=0;
}
while(Dat==0&&i<50)//拉低后,等待DHT释放总线
{
i++;
_nop_();
}
if(i>=50)
{
return 0;
}
else
{
i=0;
}
while(Dat==1&&i<50)// 等待DHT拉低,起始过程结束
{
i++;
_nop_();
}
if(i>=50)
{
return 0;
}
else
{
i=0;
}
return1;
}
4、获取一个数据位:
当起始信号结束后,进入40bit数据传输环节,需要确定传输过来的是0还是1。
每一位bit 数据都以50us低电平时隙开始,高电平的长短定了数据位是0 还是 1。格式见下面图示。如果读取响应信号为高电平 , 则 DHT11 没有响应 , 请检查线路是否连接正常 . 当最后一 bit 数据传送完毕后,DHT11 拉低总线50us, 随后总线由上拉电阻拉高进入空闲状态。


小结数据位的确定:
(1)数字0和数字1,都是以50us的低电平开始。
(2)数字0和数字1,结束高电平时间不相同,数字0是28us就结束了,数字1是70us结束了。
(3)确定是0还是1,结束以延时40us后做为阀值点,如果40us后还是高电平那么这个位就是1,延时40us后是低电平,那么这个数据位就是0。
以下是获取数据位的函数:
unsignedchar Dht11_getBit()
{
unsigned char i=0;
//Dht11_start();
while(Dat==1&&i<25);
{
i++;
_nop_();
}
i=0;
while(Dat==0&&i<25);
{
i++;
_nop_();
}
//以下的40us很重要
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
if(Dat==1)return 1;
else return 0;
}
5、获取40bit的5个字节码:
用户MCU 发送一次开始信号后 ,DHT11从低功耗模式转换到高速模式,等待主机开始信号结束后,DHT11 发送响应信号, 送出40bit 的数据,并触发一次信号采集用户可选择读取部分数据。从模式下,DHT11接收到开始信号触发一次温湿度采集如果没有接收到主机发送开始信号 ,DHT11不会主动进行温湿度采集。采集数据后转换到低速模式。

小结获取字节码:
(1)发送起始信号
(2)以8位为单元,获取5组字节码
(3)对字节码进行校验,前4组加起来等于第5
(4)对字节码分别输出至指定的变量
(6)串口发送到电脑输出调试。
以为字节码的获取函数
voidDht11_getByte(unsigned char *t,unsignedchar *t1,unsigned char *h)
{
unsigned char temp=0,i=0,j=0;
unsigned char dat[]={0,0,0,0,0};
Dht11_start();
for(j=0;j<5;j++)
{
temp=0;
for(i=0;i<8;i++)
{
temp<<=1;
temp|=Dht11_getBit();
}
dat[j]=temp;
P0=j;
}
if(dat[0]+dat[1]+dat[2]+dat[3]==dat[4])
{
*t=dat[2];
*t1=dat[3];
*h=dat[0];
}
uart_sendbyte(dat[0]);
uart_sendbyte(dat[1]);
uart_sendbyte(dat[2]);
uart_sendbyte(dat[3]);
uart_sendbyte(dat[4]);
}
6、输出显示:
charstr[25];
voidnumtostr(unsigned char i)
{
str[0]=i/10%10+'0';
str[1]=i%10+'0';
str[2]='\0';
}
voidmain()
{
unsigned char t=0,t1=0,h=0;
OLED_Init();
uart_init();
while(1)
{
OLED_ShowString(0,0,"Temp:");
OLED_ShowString(0,2,"Humi:");
Dht11_getByte(&t,&t1,&h);
numtostr(t);
OLED_ShowString(40,0,str);
numtostr(t1);
OLED_ShowString(56,0,".");
OLED_ShowString(64,0,str);
OLED_ShowString(80,0," C");
numtostr(h);
OLED_ShowString(40,2,str);
OLED_ShowString(56,2," %");
delay_ms(25);
}
}
三、成品展示:
