DHT11温湿度传感器读写时序及C51单片机程序代码
一:实验原理
DHT11是一个数字温湿度传感器,可以采集环境中的温度和湿度。单线制串行接口,除了电源以外只需要一根信号线就可以和单片机通信,传输距离可达20米,湿度测量范围:20-90%RH,温度测量范围:0-50℃,实乃居家旅行必备之物。原理图如下:
二:读写时序
DHT11温湿度传感器采集,数码管轮流显示温度和湿度,关于代码,就不贴完整的了,直接看DHT11通信部分,按函数块来解释。先来看几个延时函数,使用的是STC12C5A60S2单片机11.0592M晶振。
void Delay25us() //@11.0592MHz
{
unsigned char i;
i = 66;
while (--i);
}
void Delay1us() //@11.0592MHz
{
_nop_();
}
void Delay25ms() //@11.0592MHz
{
unsigned char i, j, k;
i = 2;
j = 13;
k = 237;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
这些延时在单片机与DHT11总线通讯时序中会用到,DHT11通信协议是以总线上的高低电平时间来区分的,比如:主机(单片机)发送起始信号,DHT11的响应信号,DHT11输出0和1的信号。总的时序图如下:
由上图可知,DHT11的通信时序是这样的:首先,单片机向DHT11发送开始信号,然后DHT11读到后会有个响应输出通知单片机,然后单片机就可以读取数据了,这些时序就是由一系列的高低电平时间组成。
1.首先来看,单片机向DHT11发送开始信号的时序,时序图如下:
上图可知,开始信号就是主机(单片机)把总线拉低等待DHT11响应,这个低电平时间要大于18ms,才能确保被DHT11检测到,这样开始信号就发送完了。然后主机再把总线拉高等待20-40us,接下来主机就可以读取DHT11的响应信号了。代码如下:
void MCU_Send_Start()
{
DHT=0;
Delay25ms();//主机至少拉低18MS
// _nop_();_nop_();_nop_();
DHT=1;
Delay25us();//主机拉高20-40us准备等待响应
}
2.再来看,DHT11的响应时序,时序图如下:
上图可知:响应信号就是DHT11把总线拉低,等待主机读取,这个低电平时间为80us,如果读取到的为低电平,响应成功。DHT11发送完响应信号后会把总线再拉高80us,准备输出数据。代码如下:
uchar MCU_Read_DHT11_Respond()
{
uchar retry;
DHT=1;
//如果DHT11有响应会先拉低80US 用while读取判断电平
//如果DHT为高电平即无响应继续等待读取直到超时
while(DHT&&retry<100)
{
retry++;
Delay1us();
}
if(retry>=100) return 1; //超时返回1
else retry=0;
//如果读取到低电平则DHT11有响应,等待80us过去
while(!DHT&&retry<100)
{
retry++;
Delay1us();
}
if(retry>=100) return 1; //超时返回1
return 0;
}
3.再来看,主机发送开始信号以后DHT11如期响应了,那么主机就可以准备接收数据了,由于是单总线,DHT11的数据只能按位输出,数据0和数据1也是按照高低电平长短来决定的。数据0和数据1的时序图如下:
上图可知:DHT11的每一位数据都是以50us低电平间隙开始,然后如果高电平是26-28us就表示0,如果高电平是70us表示1。这样主机只要读取等待50us低电平过去然后再判断高电平长短就能收集数据了,代码如下:
//读取一位 每一位数据都以50US低电平间隙开始
//高电平长短决定0和1 26-28US代表0 70US表示1
uchar DHT11_Read_Bit()
{
uchar retry=0;
//判断上一位的0和1的高电平有没有过去
//确保过去了再读取下一位的低电平间隙
while(DHT&&retry<100)
{
retry++;
Delay1us();
}
//等待50US低电平间隙过去 就可以检测高电平长短了
retry=0;
while(!DHT&&retry<100)
{
retry++;
Delay1us();
}
//由于1是70US长度高电平 0是26-28US长度高电平
//所以延时50US跳过电平0检测当前信号电平是否为1
Delay25us();
Delay25us();
//还是高电平说明是1,返回数字1,否则为数字0
if(DHT) return 1;
else return 0;
}
4.再来看,单片机发送一次开始信号后,DHT11响应信号,然后就一连串送出40位数据,如果DHT11没有接收到单片机的开始信号,是不会主动采集温湿度数据的。这40位数据其中包含了,8位的湿度整数数据+8位湿度小数数据+8位的温度整数数据+8位的温度小数数据+8位的效验和。这时我们就要按照DHT11输出的0和1信号每8位组成一个字节,代码如下:
uchar DHT11_Read_Byte() //主机接收8位数据
{
uchar i,temp=0;
for(i=0;i<8;i++)
{
temp=temp<<1;
temp|=DHT11_Read_Bit();
}
return temp;
}
5.通信部分和数据接收部分已经说完了,下面就可以在主函数中处理接收到的数据显示了,首先,单片机发送一个开始信号,然后判断DHT11有没有响应,有响应就直接读取40位的数据,用一个数组DHT11_Buffer[5]缓存,然后拿出DHT11_Buffer[0]就是湿度,DHT11_Buffer[2]就是温度,这两个数值赋给数码管显示就可以了。代码如下:
void main(void)
{
uchar t,i;
P4SW=0x70; //P4口打开 默认是关闭的
P4M1=0x00;
P4M0=0x00; // 设置为普通IO
P0M1=0x00;
P0M0=0xff; //P0口全部设置为推挽输出 见手册87页 IO口模式配置
while(1)
{
MCU_Send_Start(); //DHT11响应 准备接收数据
if(MCU_Read_DHT11_Respond()==0)
{
for(i=0;i<5;i++)
{ //接收到的数据放入缓冲区中
DHT11_Buffer[i]=DHT11_Read_Byte();
}
if(DHT11_Buffer[0]+DHT11_Buffer[1]+DHT11_Buffer[2]
+DHT11_Buffer[3]==DHT11_Buffer[4]) //校验数据
{
for(t=0;t<200;t++) //显示一会
{
Display(DHT11_Buffer[2]);//显示温度 单位°
}
delay_ms(500); //延时切换成温度
for(t=0;t<200;t++) //显示一会
{
Display(DHT11_Buffer[0]);//显示湿度 单位%
}
delay_ms(500);//延时切换成湿度
}
}
}
}
流程就是这么个流程,按照时序图来写程序,多看几遍就晓得了。