DHT11简介
特点:相对温湿度测量;全校准;数字输出;长期稳定;20米超长信号传输距离;超低能耗;完全互换直接出结果(比如超声波测距模块就需要代码转化一下结果,这个就不用)
接口:VCC,GND和DATA。
DATA 用于微处理器与 DHT11之间的通讯和同步,采用单总线数据格式,一次
通讯时间4ms左右,数据分小数部分和整数部分,具体格式在下面说明,当前小数
部分用于以后扩展,现读出为零.操作流程如下:
一次完整的数据传输为40bit,高位先出。
数据格式:
8bit湿度整数数据+8bit湿度小数数据+8bit温度整数数据+8bit温度小数数据+8bit校验和
数据传送正确时校验和数据等于“ 8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据” 所得结果的末8位。
DHT11的通讯过程时序
总体过程:
用户MCU发送一次开始信号后,DHT11从低功耗模式转换到高速模式,等待主机开始信号结束后,DHT11发送响应信号,送出40bit的数据,并触发一次信号采集,用户可选择读取部分数据. 从模式下 ,DHT11接收到开始信号触发一次温湿度采集,如果没有接收到主机发送开始号,DHT11不会主动进行温湿度采集.采集数据后转换到低速模式。
激活过程:(使得DHT11由低功耗转到高速模式并开始测量)![](https://i-blog.csdnimg.cn/blog_migrate/0ad2fcc190959721fd8c4a533751342a.png)
- 总线空闲状态为高电平,主机把总线拉低等待DHT11响应,主机把总线拉低必须大于18毫秒,保证DHT11能检测到起始信号。
- DHT11接收到主机的开始信号后, 等待主机开始信号结束,然后发送80us低电平响应信号.
- 主机发送开始信号结束后,延时等待20-40us后, 读取DHT11的响应信号,
- 主机发送开始信号后,可以切换到输入模式,或者输出高电平均可, 总线由上拉电阻拉高。
void Start_DHT() { dht = 1; dht = 0; Delay30ms(); dht = 1; while(dht == 1);//不断读取直到DHT再次变低,跳出循环,说明模块响应 while(dht == 0);//不断读取直到DHT再次拉高,80us之后,再经过50us后DHT变成低电平,就代表要开始变高并开始传输数据了 while(dht == 1);//等待数据传输,不断读取直到DHT再次变低,如退出循环说明数据传输前的50us低电平开始了 }
总线为低电平,说明DHT11发送响应信号,DHT11发送响应信号后,再把总线拉高80us,准备发送数据,每一bit数据都以50us低电平时隙开始,高电平的长短定了数据位是0还是1.格式见下面图示.如果读取响应信号为高电平,则DHT11没有响应,请检查线路是否连接正常.当最后一bit数据传送完毕后,DHT11拉低总线50us,随后总线由上拉电阻拉高进入空闲状态。
数字‘0’信号表示:![](https://i-blog.csdnimg.cn/blog_migrate/d46082577b3d5fed4116ec38ac56b2e7.png)
数字 ‘1’信号表示:
数字1/0的区别就是高电平的时间不同,所以设一个延时40us,如果还是高电平就是‘1’,低电平就是‘0’:
数字01检测实现:
void check_01() { while(dht == 0); //不断读取上拉的一瞬间,也可以写成while(!dht); Delay40us();//高电平持续26-28us是‘0’,高电平持续70us是'1',所以delay40us之后观察还是否是高电平 if(dht == 1){//'1' // while(dht == 1); // 不断读取下拉的一瞬间,因为在开始传输数据之前要延迟80us,如果提前结束,下一次开始传输数据就会出错,因为“开始传输数据”是从判断上拉开始的 }else(dht == 0){ //'0' // } }
完整的代码实现
实验一:读取温湿度数据
#include "reg52.h" #include <intrins.h> sbit led1 = P3^7;//根据原理图(电路图),设备变量led1指向P3组IO口的第7口 sbit dht = P3^3; char datas[]; void Delay1000ms() //@11.0592MHz { unsigned char i, j, k; _nop_(); i = 8; j = 1; k = 243; do { do { while (--k); } while (--j); } while (--i); } void Delay30ms() //@11.0592MHz { unsigned char i, j; i = 54; j = 199; do { while (--j); } while (--i); } void Delay60us() //@11.0592MHz { unsigned char i; i = 25; while (--i); } void Delay40us() //@11.0592MHz { unsigned char i; _nop_(); i = 15; while (--i); } void Start_DHT() { dht = 1; dht = 0; Delay30ms(); dht = 1; while(dht == 1);//不断读取直到DHT再次变低,跳出循环,说明模块响应 while(dht == 0);//不断读取直到DHT再次拉高,80us之后,再经过50us后DHT变成低电平,就代表要开始变高并开始传输数据了 while(dht == 1);//等待数据传输,不断读取直到DHT再次变低,如退出循环说明数据传输前的50us低电平开始了 } void Read_data_From_DHT() { int i;//轮 int j;//每一轮读多少次 char temp; char flag; Start_DHT(); for(i = 0;i < 5; i++) {//5组数据 for(j =0;j<8;j++) {//每组数据8位 while(!dht);//等待上拉 dht = 1 退出循环 Delay40us();//高电平持续26-28us是‘0’,高电平持续70us是'1',所以delay40us之后观察还是否是高电平 if(dht == 1) { flag = 1; while(dht);//不断读取下拉的一瞬间,因为在开始传输数据之前要延迟80us,如果提前结束,下一次开始传输数据就会出错,因为“开始传输数据”是从判断上拉开始的 } else { flag = 0; } temp = temp << 1;//temp左移一位,例如temp= 10101011 左移之后变成-》01010110(第一个数移出去,最后一位补零) temp |= flag;//假如flag=1,temp=01010111 } datas[i] = temp; } } void main() { Delay1000ms();//传感器上电后,要等待 1s 以越过不稳定状态在此期间无需发送任何指令 led1 = 1; Start_DHT(); while(1) { Delay1000ms(); Read_data_From_DHT(); } }
实现二:将温度和湿度的数据在串口助手中显示
#include "reg52.h" #include <intrins.h> #include <string.h> sbit led1 = P3^7;//根据原理图(电路图),设备变量led1指向P3组IO口的第7口 sbit dht = P3^3; char datas[5]; sfr AUXR = 0x8E; void UartInit() //9600bps@11.0592MHz { AUXR = 0x01; SCON = 0x40; //配置串口工作方式1,REN不使能接收 //配置定时器1,工作方式为8位自动重载 TMOD &= 0x0f;// TMOD |= 0x20; TH1 = 0xFD; TL1 = 0xFD; //9600波特率的初值 TR1 = 1;//启动定时器 } void Send_byte(char data_msg) { SBUF = data_msg; while(!TI);//在请求中断时,TI= 1,既!TI=0,等待数据的发送完成;响应中断结束后TI = 0,既!TI = 1,将TI清零 TI = 0; } void Send_string(char* str) { while(*str != '\0') { Send_byte(*str); str++; } } void Delay1000ms() //@11.0592MHz { unsigned char i, j, k; _nop_(); i = 8; j = 1; k = 243; do { do { while (--k); } while (--j); } while (--i); } void Delay30ms() //@11.0592MHz { unsigned char i, j; i = 54; j = 199; do { while (--j); } while (--i); } void Delay40us() //@11.0592MHz { unsigned char i; _nop_(); i = 15; while (--i); } void Start_DHT() { dht = 1; dht = 0; Delay30ms(); dht = 1; while(dht == 1);//不断读取直到DHT再次变低,跳出循环,说明模块响应 while(dht == 0);//不断读取直到DHT再次拉高,80us之后,再经过50us后DHT变成低电平,就代表要开始变高并开始传输数据了 while(dht == 1);//等待数据传输,不断读取直到DHT再次变低,如退出循环说明数据传输前的50us低电平开始了 } void Read_data_From_DHT() { int i;//轮 int j;//每一轮读多少次 char temp; char flag; Start_DHT(); for(i = 0;i < 5; i++) {//5组数据 for(j =0;j<8;j++) {//每组数据8位 while(!dht);//等待上拉 dht = 1 退出循环 Delay40us();//高电平持续26-28us是‘0’,高电平持续70us是'1',所以delay40us之后观察还是否是高电平 if(dht == 1) { flag = 1; while(dht);//不断读取下拉的一瞬间,因为在开始传输数据之前要延迟80us,如果提前结束,下一次开始传输数据就会出错,因为“开始传输数据”是从判断上拉开始的 } else { flag = 0; } temp = temp << 1;//temp左移一位,例如temp= 10101011 左移之后变成-》01010110(第一个数移出去,最后一位补零) temp |= flag;//假如flag=1,temp=01010111 } datas[i] = temp; } } void main() { Delay1000ms();//传感器上电后,要等待 1s 以越过不稳定状态在此期间无需发送任何指令 led1 = 1; UartInit(); Start_DHT(); while(1) { Delay1000ms(); Read_data_From_DHT(); Send_string("H;"); Send_byte(datas[0]/10 + 0x30); Send_byte(datas[0]%10 + 0x30); Send_string("."); Send_byte(datas[1]/10 + 0x30); Send_byte(datas[1]%10 + 0x30); Send_string("\r\n"); Send_string("T;"); Send_byte(datas[2]/10 + 0x30); Send_byte(datas[2]%10 + 0x30); Send_string("."); Send_byte(datas[3]/10 + 0x30); Send_byte(datas[3]%10 + 0x30); Send_string("\r\n"); } }
其中温度和湿度进行数据的转化变成更易读的方式
void Build_datas() { humidity[0] = 'H'; humidity[1] = datas[0]/10 + 0x30; humidity[2] = datas[0]%10 + 0x30; humidity[3] = '.'; humidity[4] = datas[1]/10 + 0x30; humidity[5] = datas[1]%10 + 0x30; humidity[6] = '%'; humidity[7] = '\0'; temperature[0] = 'T'; temperature[1] = datas[2]/10 + 0x30; temperature[2] = datas[2]%10 + 0x30; temperature[3] = '.'; temperature[4] = datas[3]/10 + 0x30; temperature[5] = datas[3]%10 + 0x30; temperature[6] = 'C'; temperature[7] = '\0'; }
实验结果:
实验三:LCD显示温湿度的同时,通过usb串口接收温湿度信息
#include "reg52.h" #include <intrins.h> #include <string.h> #define databuffer P0 //定义8位数据线,Po端口组 sbit led1 = P3^7;//根据原理图(电路图),设备变量led1指向P3组IO口的第7口 sbit dht = P3^3; sbit RS = P1^0; sbit RW = P1^1; sbit EN = P1^4; sfr AUXR = 0x8E; char datas[5]; char temperature[8]; char humidity[8]; void Check_Busy()//(4)以后每次写指令,读/写数据操作均需要检测忙信号 { char temp = 0x80; databuffer = 0x80;//先将databuffer置为忙 while(temp &= 0x80 ){//1000 0000//BF(D7)位为高电平,代表忙 //然后如果temp不变化的话,就一直检查是否busy RS = 0; RW = 1; EN = 0; _nop_(); EN = 1; temp = databuffer;//根据时序图,E为1时开始读 _nop_(); _nop_(); EN = 0; _nop_(); } } void Delay15ms() //@11.0592MHz { unsigned char i, j; i = 27; j = 226; do { while (--j); } while (--i); } void Delay5ms() //@11.0592MHz { unsigned char i, j; i = 9; j = 244; do { while (--j); } while (--i); } void Write_cmd_Func(char cmd)//写入指令的函数 { Check_Busy();//(4)以后每次写指令,读/写数据操作均需要检测忙信号 RS = 0;//写指令 RW = 0; EN = 0; _nop_(); databuffer = cmd; EN = 1; _nop_(); _nop_(); EN = 0; } void Write_data_Func(char dataShow)//写入数据的函数 { Check_Busy();//(4)以后每次写指令,读/写数据操作均需要检测忙信号 RS = 1;//写内容 RW = 0; EN = 0; _nop_(); databuffer = dataShow; _nop_(); EN = 1; _nop_(); _nop_(); EN = 0; } void UartInit() //9600bps@11.0592MHz { AUXR = 0x01; SCON = 0x40; //配置串口工作方式1,REN不使能接收 //配置定时器1,工作方式为8位自动重载 TMOD &= 0x0f;// TMOD |= 0x20; TH1 = 0xFD; TL1 = 0xFD; //9600波特率的初值 TR1 = 1;//启动定时器 } void Send_byte(char data_msg) { SBUF = data_msg; while(!TI);//在请求中断时,TI= 1,既!TI=0,等待数据的发送完成;响应中断结束后TI = 0,既!TI = 1,将TI清零 TI = 0; } void Send_string(char* str) { while(*str != '\0') { Send_byte(*str); str++; } } void Delay1000ms() //@11.0592MHz { unsigned char i, j, k; _nop_(); i = 8; j = 1; k = 243; do { do { while (--k); } while (--j); } while (--i); } void Delay30ms() //@11.0592MHz { unsigned char i, j; i = 54; j = 199; do { while (--j); } while (--i); } void Delay40us() //@11.0592MHz { unsigned char i; _nop_(); i = 15; while (--i); } void Start_DHT() { dht = 1; dht = 0; Delay30ms(); dht = 1; while(dht == 1);//不断读取直到DHT再次变低,跳出循环,说明模块响应 while(dht == 0);//不断读取直到DHT再次拉高,80us之后,再经过50us后DHT变成低电平,就代表要开始变高并开始传输数据了 while(dht == 1);//等待数据传输,不断读取直到DHT再次变低,如退出循环说明数据传输前的50us低电平开始了 } void Read_data_From_DHT() { int i;//轮 int j;//每一轮读多少次 char temp; char flag; Start_DHT(); for(i = 0;i < 5; i++) {//5组数据 for(j =0;j<8;j++) {//每组数据8位 while(!dht);//等待上拉 dht = 1 退出循环 Delay40us();//高电平持续26-28us是‘0’,高电平持续70us是'1',所以delay40us之后观察还是否是高电平 if(dht == 1) { flag = 1; while(dht);//不断读取下拉的一瞬间,因为在开始传输数据之前要延迟80us,如果提前结束,下一次开始传输数据就会出错,因为“开始传输数据”是从判断上拉开始的 } else { flag = 0; } temp = temp << 1;//temp左移一位,例如temp= 10101011 左移之后变成-》01010110(第一个数移出去,最后一位补零) temp |= flag;//假如flag=1,temp=01010111 } datas[i] = temp; } } void LCD1602_Init() { //(1)延时 15ms Delay15ms(); //(2)写指令 38H(不检测忙信号) Write_cmd_Func(0x38); //(3)延时 5ms Delay5ms(); //(4)以后每次写指令,读/写数据操作均需要检测忙信号 //(5)写指令 38H:显示模式设置 Write_cmd_Func(0x38); //(6)写指令 08H:显示关闭 Write_cmd_Func(0x08); //(7)写指令 01H:显示清屏 Write_cmd_Func(0x01); //(8)写指令 06H:显示光标移动设置 Write_cmd_Func(0x06); //(9)写指令 0CH:显示开及光标设置 Write_cmd_Func(0x0C); } void LCD1602_ShowLine(char row,char col,char *string) { switch(row){ case 1: Write_cmd_Func(0x80+col);//只要定下开始的位置,之后光标会自行移动 while(*string){ Write_data_Func(*string); string++; } break; case 2: Write_cmd_Func(0x80+0x40+col);//只要定下开始的位置,之后光标会自行移动 while(*string){ Write_data_Func(*string); string++; } break; } /* char pos; if(hang == 0){//如果第一行 pos = 0x80 + 0x00 + lie; }else if(hang == 1){//如果第二行 pos = 0x80 + 0x40 + lie; } Check_Busy(); Write_data_Func(*string); while(*string != '\0'){ Check_Busy(); Write_data_Func(*string); string++; } */ } void Build_datas() { humidity[0] = 'H'; humidity[1] = datas[0]/10 + 0x30; humidity[2] = datas[0]%10 + 0x30; humidity[3] = '.'; humidity[4] = datas[1]/10 + 0x30; humidity[5] = datas[1]%10 + 0x30; humidity[6] = '%'; humidity[7] = '\0'; temperature[0] = 'T'; temperature[1] = datas[2]/10 + 0x30; temperature[2] = datas[2]%10 + 0x30; temperature[3] = '.'; temperature[4] = datas[3]/10 + 0x30; temperature[5] = datas[3]%10 + 0x30; temperature[6] = 'C'; temperature[7] = '\0'; } void main() { Delay1000ms();//传感器上电后,要等待 1s 以越过不稳定状态在此期间无需发送任何指令 UartInit(); LCD1602_Init(); Start_DHT(); led1 = 1; while(1) { Delay1000ms(); Read_data_From_DHT(); Build_datas(); Send_string(humidity); Send_string("\r\n"); Send_string(temperature); Send_string("\r\n"); LCD1602_ShowLine(1,2,humidity); LCD1602_ShowLine(2,2,temperature); } }
实验结果:
以上就完成了DHT11的基本功能,又结合了 LCD1602和串口。所以代码显得比较冗长。