1、概述:
2、工作特点:
3、使用的工作环境:超出建议的工作范围可能导致高达3%RH的临时性漂移信号。返回正常工作条后,传感器会缓慢地向校准状态恢复,加速恢复进程的方法是:置于极限工作条件下或化学蒸汽中的传感器,通过如下处理程序,可使其恢复到校准时的状态。在50-60℃和< 10%RH的湿度条件下保持2 小时(烘干);随后在20-30℃和>70%RH的湿度条件下保持5小时以上。在非正常工作条件下长时间使用会加速产品的老化过程。
4、引脚说明:
编号 | 符号 | 说明 |
1 | VDD | 供电,3~5,5V,DC |
2 | DATA | 串行数据,单总线 |
3 | NC | 空脚,请悬空 |
4 | GND | 接地,电源负极 |
5、DHT11的单总线数据格式:DHT11模块的串行接口是单线双向的,一次通讯时间在4ms左右。机器与机器之间只能用二进制对话,51单片机和DHT11之间只有一根数据线DATA,它的作用是:
模块一次能够完整的数据传输为40bit,高位先出,数据格式(从高到低)为:
阅读时序图,需要关心以下三点:开始、结束、转折。
0、和单片机的接线方式:注意一开始把单片机关机,防止接线的时候出错烧毁DHT11传感器。
1、有效数据读取前的通讯时序:明确起见,在时序图中给每个转折点标注一下字母。
习题1(检测模块是否存在):用51单片机检测DHT11模块插在单片机引脚上,点亮LED【项目工程文件夹】
全局变量:
1. sbit指令找到P3这个I/O口组的第7位P3^7,也就是D5这个LED: sbit ledD5 = P3^7;
2. sbit指令找到P3这个I/O口组的第3位P3^3,它与DHT11模块的DATA数据线相连,用来输出主机指令和接收数据: sbit dht11 = P3^3;
1. 上电后关闭D5: ledD5 = 1;
2. 调用API4. DHT11传感器上电后等待2s,以越过不稳定状态:
Delay1000ms();
Delay1000ms();
3. 调用API1. 检测DHT11模块是否存在: check_DHT11_exist();
4. while(1)死循环,防止程序退出
/* 一级函数:f1、f4 */
f1. 封装检测DHT11模块是否存在的API: void check_DHT11_exist();
f1.1 根据DHT11模块的时序逻辑分析,总结出如下过程:
dht11 = 1;
dht11 = 0;
调用API2. 软件延时30ms: Delay30ms();
dht11 = 1;
调用API3. 软件延时70us: Delay70us();
f1.2 紧接着读取DATA数据线上的信息,判据是 dht11 == 0 //如果为低电平,DHT11模块就存在
如果是,就点亮D5: ledD5 = 0;
否则,啥也不干
f4. 封装软件延时1s的API,用于DHT11模块上电后的稳定: void Delay1000ms();
/* 二级函数:f2、f3 */
f4. 封装软件延时30ms的API,用于API1的时序分析: void Delay30ms();
f5. 封装软件延时5ms的API,用于API1的时序分析: void Delay5ms();
#include "reg52.h"
#include "intrins.h"
sbit ledD5 = P3^7;
sbit dht11 = P3^3; //DHT11模块的DATA数据线
/* API1. 检测DHT11模块是否存在 */
void check_DHT11_exist();
/* API2. 软件延时30ms,用于API1的时序分析 */
void Delay30ms();
/* API3. 软件延时70微秒,用于API1的时序分析 */
void Delay70us();
/* API4. 软件延时1s,用于DHT11模块上电后的稳定 */
void Delay1000ms();
void main(void)
{
ledD5 = 1;
//传感器上电后,要等待1s以上 以越过不稳定状态
Delay1000ms();
Delay1000ms();
check_DHT11_exist();
while(1){} //防止程序退出
}
void check_DHT11_exist()
{
dht11 = 1;
dht11 = 0;
Delay30ms();
dht11 = 1;
Delay70us();
if(dht11 == 0){
ledD5 = 0;
}
}
void Delay30ms() //@11.0592MHz
{
unsigned char i, j;
i = 54;
j = 199;
do
{
while (--j);
} while (--i);
}
void Delay70us() //@11.0592MHz
{
unsigned char i;
_nop_();
i = 29;
while (--i);
}
void Delay1000ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 8;
j = 1;
k = 243;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
阅读时序图,需要关心以下三点:开始、结束、转折。
0、和单片机的接线方式:同上。
1、启动(从低功耗模式到高速模式):
void DHT11_Start()
{
dht11 = 1;
dht11 = 0;
Delay30ms();
dht11 = 1;
while(!(dht11==0)); //卡d点
while(!dht11); //卡e点
while(!(dht11==0)); //卡f点
}
2、有效数据读取时的通讯时序:完整的时序图如下,明确起见,在时序图中给每个转折点标注一下字母。
3、总体时序逻辑:
4、编程逻辑与读取1bit数据的方法:我们用字符型变量temp来保存8bit的采样数据,用字符数组dataDHT11[5]来保存5个这样的采样数据,编程方式就是循环嵌套。
习题2(读取温湿度数据):封装单片机通过DHT11模块读取温湿度数据的函数【项目工程文件夹】
全局变量:
1. sbit指令找到P3这个I/O口组的第3位P3^3,它与DHT11模块的DATA数据线相连,用来输出主机指令和接收数据: sbit dht11 = P3^3;
2. 用于保存DHT11和单片机一次通讯所传输的有效数据,一共40bit: char dataDHT[5];
//dataDHT[5]的传输路线为:dht11(P3.3) ——> API2: Read_Data_From_DHT11() ——> bitFlag ——> temp ——> dataDHT[i]
1. 调用API5. DHT11传感器上电后等待2s,以越过不稳定状态:
Delay1000ms();
Delay1000ms();
2. while(1)死循环,每隔1s读取数据
2.1 调用API5. 软件延时1s: Delay1000ms();
2.2 调用API2. 与DHT11完成一次通讯,读取DHT11模块数据(40bit)放在数组dataDHT[5]中:
Read_Data_From_DHT11();
/* 一级函数:f2、f5 */
f2. 封装读取DHT11模块数据(40bit)放在数组dataDHT[5]中的API: void Read_Data_From_DHT11();
f2.1 定义局部变量temp,用于移位存放1bit数据: char temp;
f2.2 定义1bit数据的标志位,1代表读到的是1,0代表读到的是0: char bitFlag;
f2.3 调用API1. 每次数据采集(40bit)前都要发送开始信号: DHT11_Start();
f2.4 for循环嵌套,实现40bit数据的采集,代表数组下标的外层循环变量i从0开始,<5时进入循环
f2.4.1 for循环,循环变量j从0开始,<8时进入循环
f2.4.1.1 根据DHT11模块的时序逻辑分析,总结出如下过程:
空循环体,卡g点,直到DATA变成高电平:while(!dht11);
调用API4. 软件延时50微秒: Delay50us();
f2.4.1.2 紧接着判断数据线DATA上的电平是否是高电平,判据是dht11 == 1
f2.4.1.2.1 如果是,说明该bit是高电平,那么:
将该位数据以标志位形式保存在标志位变量bitFlag中: bitFlag = 1;
空循环体,卡f’点,直到DATA变成低电平:while(!(dht11==0));
f2.4.1.2.2 否则,说明该bit是低电平,那么:
将该位数据以标志位形式保存在标志位变量bitFlag中: bitFlag = 0;
f2.4.1.3 通过移位和位运算,将1bit数据保存在变量temp中:
temp = tem << 1;
temp |= bitFlag;
f2.4.2 经过内层循环,将已经获取到的8bit数据依次保存到字符数组dataDHT中:
dataDHT[i] = temp;
f5. 封装软件延时1s的API,用于DHT11模块上电后的稳定,以及每隔1s读取DHT11数据: void Delay1000ms();
/* 二级函数:f1、f3、f4 */
f1. 封装启动DHT11模块的API: void DHT11_Start();
f1.1 根据DHT11模块的时序逻辑分析,总结出如下过程:
dht11 = 1;
dht11 = 0;
调用API2. 软件延时30ms: Delay30ms();
dht11 = 1;
空循环体,卡d点,直到DATA变成低电平:while(!(dht11==0));
空循环体,卡e点,直到DATA变成高电平:while(!dht11);
空循环体,卡f点,直到DATA变成低电平:while(!(dht11==0));
f3. 封装软件延时30ms的API,用于API1的时序分析: void Delay30ms();
f4. 封装软件延时50微秒的API,用于DHT11传输数据中每1bit的时序分析: void Delay50us();
#include "reg52.h"
#include "intrins.h"
sbit dht11 = P3^3; //DHT11模块的DATA数据线
char dataDHT[5];
/* API1. 启动DHT11模块 */
void DHT11_Start();
/* API2. 读取DHT11模块数据(40bit)放在数组dataDHT[5]中 */
void Read_Data_From_DHT11();
/* API3. 软件延时30ms,用于API1的时序分析 */
void Delay30ms();
/* API4. 软件延时50微秒,用于DHT11传输数据中每1bit的时序分析 */
void Delay50us();
/* API5. 软件延时1s,用于DHT11模块上电后的稳定 */
void Delay1000ms();
void main(void)
{
//传感器上电后,要等待1s以上 以越过不稳定状态
Delay1000ms();
Delay1000ms();
while(1){ //每隔一秒进行数据读取
Delay1000ms();
Read_Data_From_DHT11();
}
}
void DHT11_Start()
{
dht11 = 1;
dht11 = 0;
Delay30ms();
dht11 = 1;
while(!(dht11==0)); //卡d点
while(!dht11); //卡e点
while(!(dht11==0)); //卡f点
}
void Read_Data_From_DHT11()
{
char i; //轮
char j; //每轮读多少次
char temp; //移位
char bitFlag; //该bit的标志位,1代表读到的是1,0代表读到的是0
DHT11_Start(); //每次数据采集(40bit)前都要发送开始信号
for(i=0; i<5; i++){
for(j=0; j<8; j++){
while(!dht11); //卡g点
Delay50us();
if(dht11 == 1){ //说明该bit是高电平
bitFlag = 1;
while(!(dht11==0));
}else{ //说明该bit是低电平
bitFlag = 0;
}
temp = temp << 1; //temp <<= 1;
temp |= bitFlag;
}
dataDHT[i] = temp;
}
}
void Delay30ms() //@11.0592MHz
{
unsigned char i, j;
i = 54;
j = 199;
do
{
while (--j);
} while (--i);
}
void Delay50us() //@11.0592MHz
{
unsigned char i;
_nop_();
i = 20;
while (--i);
}
void Delay1000ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 8;
j = 1;
k = 243;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
1、数字到字符的转化:
2、代码心得:
习题3(串口显示温湿度数据):【项目工程文件夹】
全局变量:
1. sbit指令找到P3这个I/O口组的第3位P3^3,它与DHT11模块的DATA数据线相连,用来输出主机指令和接收数据: sbit dht11 = P3^3;
2. 用于保存DHT11和单片机一次通讯所传输的有效数据,一共40bit: char dataDHT[5];
//dataDHT[5]的传输路线为:
//路线1:dht11(P3.3) ——> API2: Read_Data_From_DHT11() ——> bitFlag ——> temp ——> dataDHT[i]
//路线2:dataDHT[i] ——> API9:Send_Data_From_DHT11() ——> SBUF ——> PC的串口助手
3. sfr指令直接找到AUXR寄存器: sfr AUXR = 0X8E; //因为AUXR没有在reg52.h中声明
1. 调用API6. 初始化串口: UartInit();
2. 调用API5. DHT11传感器上电后等待2s,以越过不稳定状态:
Delay1000ms();
Delay1000ms();
3. while(1)死循环,每隔1s读取数据,并发送给串口
3.1 调用API5. 软件延时1s: Delay1000ms();
3.2 调用API2. 与DHT11完成一次通讯,读取DHT11模块数据(40bit)放在数组dataDHT[5]中:
Read_Data_From_DHT11();
3.3 调用API9. 将DHT11模块测得的温湿度数据发送给串口: Send_Data_From_DHT11();
/* 一级函数:f2、f5、f6、f9 */
f2. 封装读取DHT11模块数据(40bit)放在数组dataDHT[5]中的API: void Read_Data_From_DHT11();
f2.1 定义局部变量temp,用于移位存放1bit数据: char temp;
f2.2 定义1bit数据的标志位,1代表读到的是1,0代表读到的是0: char bitFlag;
f2.3 调用API1. 每次数据采集(40bit)前都要发送开始信号: DHT11_Start();
f2.4 for循环嵌套,实现40bit数据的采集,代表数组下标的外层循环变量i从0开始,<5时进入循环
f2.4.1 for循环,循环变量j从0开始,<8时进入循环
f2.4.1.1 根据DHT11模块的时序逻辑分析,总结出如下过程:
空循环体,卡g点,直到DATA变成高电平:while(!dht11);
看时序图, 调用API4. 软件延时50微秒: Delay50us();
f2.4.1.2 紧接着判断数据线DATA上的电平是否是高电平,判据是dht11 == 1
f2.4.1.2.1 如果是,说明该bit是高电平,那么:
将该位数据以标志位形式保存在标志位变量bitFlag中: bitFlag = 1;
空循环体,卡f’点,直到DATA变成低电平:while(!(dht11==0));
f2.4.1.2.2 否则,说明该bit是低电平,那么:
将该位数据以标志位形式保存在标志位变量bitFlag中: bitFlag = 0;
f2.4.1.3 通过移位和位运算,将1bit数据保存在变量temp中:
temp = tem << 1;
temp |= bitFlag;
f2.4.2 经过内层循环,将已经获取到的8bit数据依次保存到字符数组dataDHT中:
dataDHT[i] = temp;
f5. 封装软件延时1s的API,用于DHT11模块上电后的稳定,以及每隔1s读取DHT11数据: void Delay1000ms();
f6. 封装初始化串口的API: void UartInit(void);
f6.1 禁用ALE信号: AUXR = 0X01;
f6.2 让串口以方式1工作(8位UART,可变波特率),并允许串口接收: SCON = 0x50;
f6.3 让定时器1以8位重载工作模式工作:
TMOD &= 0xDF;
TMOD |= 0x20;
f6.4 根据波特率为9600,波特率不翻倍,设置定时器1的初值:
TH1 = 0xFD;
TL1 = 0xFD;
f6.5 定时器开始数数: TR1 = 1;
f9. 封装将DHT11模块测得的温湿度数据发送给串口的API: void Send_Data_From_DHT11();
f9.1 调用API8. 给串口发送字符串"RH(%):",来表示相对湿度: sendString("RH(%):");
f9.2 给串口发送湿度(RH)数据:
f9.2.1 调用API7. 根据数字到字符的转化方式,给串口发送代表湿度整数位的两个字符,
湿度整数位数字保存在字符数组dataDHT[]的第0个元素中:
sendByte(dataDHT[0]/10 + 0x30);
sendByte(dataDHT[0]%10 + 0x30);
f9.2.2 调用API7. 给串口发送字符'.',代表小数点: sendByte('.');
f9.2.3 调用API7. 根据数字到字符的转化方式,给串口发送代表湿度小数位的两个字符,
湿度小数位数字保存在字符数组dataDHT[]的第1个元素中:
sendByte(dataDHT[1]/10 + 0x30);
sendByte(dataDHT[1]%10 + 0x30);
f9.2.4 调用API8. 给串口发送字符串"\r\n",表示换行: sendString("\r\n");
f9.3 给串口发送温度(T)数据:
f9.3.1 调用API7. 根据数字到字符的转化方式,给串口发送代表温度整数位的两个字符,
温度整数位数字保存在字符数组dataDHT[]的第2个元素中:
sendByte(dataDHT[2]/10 + 0x30);
sendByte(dataDHT[2]%10 + 0x30);
f9.3.2 调用API7. 给串口发送字符'.',代表小数点: sendByte('.');
f9.3.3 调用API7. 根据数字到字符的转化方式,给串口发送代表温度小数位的两个字符,
温度小数位数字保存在字符数组dataDHT[]的第3个元素中:
sendByte(dataDHT[3]/10 + 0x30);
sendByte(dataDHT[3]%10 + 0x30);
f9.3.4 调用API8. 给串口发送字符串"\r\n\r\n",表示显示空行: sendString("\r\n\r\n");
/* 二级函数:f1、f3、f4、f7、f8 */
f1. 封装启动DHT11模块的API: void DHT11_Start();
f1.1 根据DHT11模块的时序逻辑分析,总结出如下过程:
dht11 = 1;
dht11 = 0;
调用API2. 软件延时30ms: Delay30ms();
dht11 = 1;
空循环体,卡d点,直到DATA变成低电平:while(!(dht11==0));
空循环体,卡e点,直到DATA变成高电平:while(!dht11);
空循环体,卡f点,直到DATA变成低电平:while(!(dht11==0));
f3. 封装软件延时30ms的API,用于API1的时序分析: void Delay30ms();
f4. 封装软件延时50微秒的API,用于DHT11传输数据中每1bit的时序分析: void Delay50us();
f7. 封装定时给PC发送一个字符的API: void sendByte(char data_msg); //形参是字符值
f7.1 往SBUF寄存器中写入字符data_msg: SBUF = data_msg;
f7.2 根据串口发送中断触发位TI,利用空循环体暂停程序: while(TI == 0);
f7.3 程序复位TI: TI = 0;
f8. 封装给PC发送字符串的API: void sendString(char *str); //形参是字符串的地址
f8.1 定义一个字符指针变量p用来保存字符串首地址: char *p = str;
f8.2 while循环,控制循环的变量是*p,当*p != '\0' 时,进入循环,进行单个字符的发送
f8.2.1 通过指针间接访问字符串字符,再调用API3. 发送单个字符: sendByte(*p);
f8.2.2 修改循环变量p的值,让指针p偏移: p++;
/* 三级函数:f7 */
f7. 同上,f7也做f8的三级函数
#include "reg52.h"
#include "intrins.h"
sbit dht11 = P3^3; //DHT11模块的DATA数据线
char dataDHT[5];
sfr AUXR = 0x8E;
/* API1. 启动DHT11模块 */
void DHT11_Start();
/* API2. 读取DHT11模块数据(40bit)放在数组dataDHT[5]中 */
void Read_Data_From_DHT11();
/* API3. 软件延时30ms,用于API1的时序分析 */
void Delay30ms();
/* API4. 软件延时50微秒,用于DHT11传输数据中每1bit的时序分析 */
void Delay50us();
/* API5. 软件延时1s,用于DHT11模块上电后的稳定、每隔1s读取DHT11数据并发送给串口缓冲寄存器 */
void Delay1000ms();
/* API6. 初始化串口 */
void UartInit(void);
/* API7. 通过串口给PC发送一个字符 */
void sendByte(char data_msg);
/* API8. 通过串口给PC发送一个字符串 */
void sendString(char *str);
/* API9. 将DHT11模块测得的温湿度数据发送给串口 */
void Send_Data_From_DHT11();
void main(void)
{
UartInit(); //初始化串口
//传感器上电后,要等待1s以上 以越过不稳定状态
Delay1000ms();
Delay1000ms();
while(1){ //每隔一秒进行数据读取,并串口显示
Delay1000ms();
Read_Data_From_DHT11();
Send_Data_From_DHT11();
}
}
void DHT11_Start()
{
dht11 = 1;
dht11 = 0;
Delay30ms();
dht11 = 1;
while(!(dht11==0)); //卡d点
while(!dht11); //卡e点
while(!(dht11==0)); //卡f点
}
void Read_Data_From_DHT11()
{
char i; //轮
char j; //每轮读多少次
char temp; //移位
char bitFlag; //该bit的标志位,1代表读到的是1,0代表读到的是0
DHT11_Start(); //每次数据采集(40bit)前都要发送开始信号
for(i=0; i<5; i++){
for(j=0; j<8; j++){
while(!dht11); //卡g点
Delay50us();
if(dht11 == 1){ //说明该bit是高电平
bitFlag = 1;
while(!(dht11==0));
}else{ //说明该bit是低电平
bitFlag = 0;
}
temp = temp << 1; //temp <<= 1;
temp |= bitFlag;
}
dataDHT[i] = temp;
}
}
void Delay30ms() //@11.0592MHz
{
unsigned char i, j;
i = 54;
j = 199;
do
{
while (--j);
} while (--i);
}
void Delay50us() //@11.0592MHz
{
unsigned char i;
_nop_();
i = 20;
while (--i);
}
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 UartInit(void) //[email protected]
{
AUXR = 0x01;
SCON = 0x50; //8位UART,允许串口接收
TMOD &= 0xDF;
TMOD |= 0x20; //定时器8位重载工作模式
TH1 = 0xFD;
TL1 = 0xFD; //9600波特率初值
TR1 = 1;
}
void sendByte(char data_msg)
{
SBUF = data_msg;
// Delay10ms();
while(TI == 0);
TI = 0;
}
void sendString(char *str)
{
char *p = str;
while(*p != '\0'){
sendByte(*p);
p++;
}
}
void Send_Data_From_DHT11()
{
sendString("RH(%):");
sendByte(dataDHT[0]/10 + 0x30); //湿度整数位
sendByte(dataDHT[0]%10 + 0x30);
sendByte('.');
sendByte(dataDHT[1]/10 + 0x30); //湿度小数位
sendByte(dataDHT[1]%10 + 0x30);
sendString("\r\n");
sendString("T(celcius):");
sendByte(dataDHT[2]/10 + 0x30); //温度整数位
sendByte(dataDHT[2]%10 + 0x30);
sendByte('.');
sendByte(dataDHT[3]/10 + 0x30); //温度小数位
sendByte(dataDHT[3]%10 + 0x30);
sendString("\r\n\r\n");
}