“掌握温度,感知湿度,一触即知!”DHT11温湿度传感器,为您的生活增添一份关怀与精准。#非标协议【下】
前言
本篇博文介绍的是用51单片机的非标准写协议【下】(DHT11温湿度传感器),包含DHT11温湿度传感器初识,发送时序检测DHT11温湿度传感器模块是否存在,读取DHT11数据的时序分析,根据时序写代码获取DHT11的数据,温湿度通过串口传到PC显示。看到这篇博文的朋友,可以先赞再看吗?
预备知识
一、基本电路标识识别和接线,例如VCC,GND。
二、数电时序图的阅读,高低电平的识别。
三、C变量
四、基本输入输出
五、流程控制
六、函数
七、指针
八,字符串
如果以上知识不清楚,请自行学习后再来浏览。如果我有没例出的,请在评论区写一下。谢谢啦!
1.DHT11温湿度传感器初识
1.1产品概述
DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器,应用领域:暖通空调;汽车;消费品;气象站;湿度调节器;除湿器;家电;医疗;自动控制 。
特点
- 相对湿度和温度测量
- 全部校准,数字输出
- 长期稳定性
- 超长的信号传输距离:20米
- 超低能耗:休眠
- 4 引脚安装:可以买封装好的
- 完全互换 : 直接出结果,不用转化
1.2与51单片机接线
1.3数据传送逻辑和数据格式
- 数据传送逻辑
只有一根数据线DATA
,上官一号发送序列指令给DHT11模块
,模块一次完整的数据传输为40bit
,高位先出
。
- 数据格式
8bit湿度整数数据
+8bit湿度小数数据
+8bi温度整数数据
+8bit温度小数数据
+8bit校验和
- 通讯过程时序图
2.发送时序检测DHT11温湿度传感器模块是否存在
2.1发送时序检测模块是否存在核心思路
- 定义DHT11温湿度传感器数据接线
- 根据手册检测DHT11温湿度传感器模块时序图配置检测函数
- 根据手册让单片机上电启动等待DHT11温湿度传感器稳定。
2.2定义DHT11温湿度传感器数据接线
- 在这里我将DHT11温湿度传感器数据接51单片机P3.3口,需要注意DHT11配套的杜邦线质量不行,会有接触不良。
sbit Data = P3^3; //把DHT11接在单片机的P1.0口
2.3根据手册检测DHT11温湿度传感器模块时序图配置检测函数
- 时序图详解
- 函数代码
void checkDHT()
{
//a: Data = 1
Data = 1;
//b: Data = 0
Data = 0;
//至少延时18ms,那么延时30ms
Delay30ms();
//c: Data = 1
Data = 1;
//20到100,40到120,读取中间交集40到100。单位(us)所以延时60us
Delay60us();
//根据时序图,如果有DHT11模块接入,Data会被拉低
if(Data == 0)
{
LED1 = 0; //检测到DHT11模块LED1亮。
}
}
2.4根据手册让单片机上电启动等待DHT11温湿度传感器稳定。
- 手册内容
DHT11的供电电压为 3-5.5V。 传感器上电后, 要等待 1s
以越过不稳定状态在此期间无需发送任何指令。 电源引脚(VDD, GND) 之间可增加一个100nF 的电容, 用以去耦滤波。
- 代码
Delay1000ms(); //等待DHT模块稳定
Delay1000ms();
2.5完整程序代码
#include "reg52.h"
#include "intrins.h"
sbit LED1 = P3^7; //用尾定义声明LED1
sbit Data = P3^3; //把DHT11接在单片机的P1.0口
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 Delay1000ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 8;
j = 1;
k = 243;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void checkDHT()
{
//a: Data = 1
Data = 1;
//b: Data = 0
Data = 0;
//至少延时18ms,那么延时30ms
Delay30ms();
//c: Data = 1
Data = 1;
//20到100,40到120,读取中间交集40到100。单位(us)所以延时60us
Delay60us();
//根据时序图,如果有DHT11模块接入,Data会被拉低
if(Data == 0)
{
LED1 = 0; //检测到DHT11模块LED1亮。
}
}
void main()
{
LED1 = 1; //一上电就让灯灭
Delay1000ms(); //等待DHT模块稳定
Delay1000ms();
checkDHT(); //检测模块是否存在
while(1); //防止程序退出主函数,导致LED1微弱闪烁
}
3.读取DHT11数据的时序分析
3.1时序图详解
3.2读取DHT11数据的次数
- 手册内容介绍
“8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据+8bit校验和” ==40个字节
- 读取次数计算
根据手册,要读取5轮,每轮8次,共读取40次
4.根据时序写代码获取DHT11的数据
4.1根据时序写代码获取DHT11的数据的核心思路
- 建立存放温湿度数据变量
- 依据3.1构造打开DHT11温湿度传感器高速模式函数
- 依据3.1构造读取DHT11温湿度传感器数据函数
- 主函数中每秒读取一次DHT11温湿度传感器数据。
注:本程序依据发送时序检测DHT11温湿度传感器模块是否存在
工程建立
4.2建立存放温湿度数据变量
char THdata[5]; //存放温湿度数据变量
4.3依据3.1构造打开DHT11温湿度传感器高速模式函数
- 代码看不懂请仔细看3.1的时序图
- 函数代码
void startDHT()
{
Data = 1;
Data = 0;
//至少延时18ms,那么延时30ms
Delay30ms();
Data = 1;
//检测d点
while(Data);
//检测e点
while(!Data);
//检测f点
while(Data);
}
4.4依据3.1构造读取DHT11温湿度传感器数据函数
- 函数核心思路
在本函数中,需要搞清楚的是要读取五轮
,每轮读取8次
,就可以使用两层for循环来读取,外层
为轮次,内层
为每轮的次数。在每次读取的时候,需要检测时序图中的g
点,才能知道是否传送数据。
根据时序图中传送1和0的时间不同,0是26us
,1是70us
。等待60us
后,如果Data = 1
,就传1
,Data = 0
;就传0
。
建立临时变量tmp用于存放传送的数据。然而传送的数据位0 1 0 1 的数据,这是需要使用移位和或等于运算的方法来存放。下图为移位和存数据示意图。
- 函数代码
void readDHTData()
{
char i; //轮次
char j; //次数
char flag;
char tmp;
//打开DHT11高速模式
startDHT();
for(i=0; i<5; i++)
{
for(j=0; j<8; j++)
{
//检测G点
while(!Data);
//根据传送1和0的时间不同,0是26us,1是70us。等待60us后,如果Data = 1,就传1,Data = 0;就传0
Delay60us();
if(Data == 1)
{
flag = 1;
while(Data); //传1的时间比较久,所以要等传1结束
}
else
{
flag = 0;
}
tmp = tmp << 1;
tmp |= flag;
}
THdata[i] = tmp;
}
}
4.5主函数中每秒读取一次DHT11温湿度传感器数据。
void main()
{
LED1 = 1; //一上电就让灯灭
Delay1000ms(); //等待DHT模块稳定
Delay1000ms();
while(1) //防止程序退出主函数,导致LED1微弱闪烁
{
Delay1000ms(); //间隔1秒读一次
readDHTData();
}
}
4.6完整程序代码
#include "reg52.h"
#include "intrins.h"
sbit LED1 = P3^7; //用尾定义声明LED1
sbit Data = P3^3; //把DHT11接在单片机的P1.0口
char THdata[5]; //存放温湿度数据变量
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 Delay1000ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 8;
j = 1;
k = 243;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void startDHT()
{
Data = 1;
Data = 0;
//至少延时18ms,那么延时30ms
Delay30ms();
Data = 1;
//检测d点
while(Data);
//检测e点
while(!Data);
//检测f点
while(Data);
}
void readDHTData()
{
char i; //轮次
char j; //次数
char flag;
char tmp;
//打开DHT11高速模式
startDHT();
for(i=0; i<5; i++)
{
for(j=0; j<8; j++)
{
//检测G点
while(!Data);
//根据传送1和0的时间不同,0是26us,1是70us。等待60us后,如果Data = 1,就传1,Data = 0;就传0
Delay60us();
if(Data == 1)
{
flag = 1;
while(Data); //传1的时间比较久,所以要等传1结束
}
else
{
flag = 0;
}
tmp = tmp << 1;
tmp |= flag;
}
THdata[i] = tmp;
}
}
void main()
{
LED1 = 1; //一上电就让灯灭
Delay1000ms(); //等待DHT模块稳定
Delay1000ms();
while(1) //防止程序退出主函数,导致LED1微弱闪烁
{
Delay1000ms(); //间隔1秒读一次
readDHTData();
}
}
5.温湿度通过串口传到PC显示
5.1温湿度通过串口传到PC显示核心思虑
- 将串口编制03_PC发送指令控制LED中的串口初始化和发送字节和字符串函数拷贝到程序中合理位置,测试是否正常发送字符串。
- 使用发送字节函数发送湿度的整数部分并进行测试。
- 测速完毕,使用发送字节函数和发送字符串函数发送小数点和换行符,还要有中文提示。
- 下载程序后串口无反应BUG调试
5.2将串口编制03_PC发送指令控制LED中的串口初始化和发送字节和字符串函数拷贝到程序中合理位置,测试是否正常发送字符串。
- 串口初始化函数
void UartInit(void) //自己配
{
//配置串口工作方式为方式1,从只收不发改为能收能发
SCON = 0x50;
//配置辅助寄存器,减少电磁辐射,稳定晶振频率
AUXR = 0x01;
//设置定时器工作方式为定时器1的8位自动重装
TMOD &= 0x0F;
TMOD |= 0x20;
//设置串口波特率为9600,0误差
TH1 = 0xFD;
TL1 = 0xFD;
//打开定时器1
TR1 = 1;
}
- 发送字节函数
void sendByte(char data_mas)
{
SBUF = data_mas;
while(!TI);
TI = 0; //一定要软件置零,不然会出现乱序
}
- 发送字符串函数
void sendString(char *str)
{
while(*str != '\0')
{
sendByte(*str);
str++;
}
}
- 测试结果
5.3使用发送字节函数发送湿度的整数部分并进行测试。
- 使用发送字节函数发送湿度的整数部分核心思路
首先得搞明白DHT11传回的数据是8位2进制数字信息
,而串口发送并显示的是文本信息
。那么就要将这数字信息
转化为文本信息
。根据ASCII码表,可以知道0字符的ASCII码为 0011 0000
。除了0以外,1到9的ASCII码高位都是 0011
,低位为相应数字的二进制数
。那么在配置发送字节函数形参时 THdata 中元素的值最终要加上 0x30
。然而DHT11可以检测的温度范围是0°C至50°C
,湿度范围是20%RH至90%RH
,所以我们得将DHT11串回的8位二进制数转为10进制数并且取出十位
和个位上的数值
。在C语言中,8为二进制和10进制数之间的转化不用特殊算法,只需要在变量进行运算时进行10进制的运算
。
此时配置发送字节函数形参可以用 THdata[0] /10 +0x30
和THdata[0] %10 +0x30
表示十位
和个位
数。
- ASCII码表
- 函数配置参数代码
sendByte(THdata[0]/10 + 0x30); //除10是将十位取出,加上0x30是将数字的数字转换为字符的数字。0标号元素根据手册和读取DHT11数据函数逻辑可以知道是湿度信息
sendByte(THdata[0]%10 + 0x30); //取余十是将个位取出
- 测试结果
5.4测速完毕,使用发送字节函数和发送字符串函数发送小数点和换行符,还要有中文提示
- 代码体现
Delay1000ms(); //间隔1秒读一次
readDHTData(); //读取DHT11温湿度数据
sendString("湿度:");
sendByte(THdata[0]/10 + 0x30); //除10是将十位取出,加上0x30是将数字的数字转换为字符的数字。
sendByte(THdata[0]%10 + 0x30); //取余十是将个位取出
sendByte('.');
sendByte(THdata[1]/10 + 0x30); //除10是将十位取出,加上0x30是将数字的数字转换为字符的数字。
sendByte(THdata[1]%10 + 0x30); //取余十是将个位取出
sendString("\r\n");
sendString("温度:");
sendByte(THdata[2]/10 + 0x30); //除10是将十位取出,加上0x30是将数字的数字转换为字符的数字。
sendByte(THdata[2]%10 + 0x30); //取余十是将个位取出
sendByte('.');
sendByte(THdata[3]/10 + 0x30); //除10是将十位取出,加上0x30是将数字的数字转换为字符的数字。
sendByte(THdata[3]%10 + 0x30); //取余十是将个位取出
sendString("\r\n");
5.5下载程序后串口无反应BUG调试
-
出现这个BUG的原因是在读取DHT11传输的数据时,延时过长导致读取数据0出错读取到下一个传送时序,解决办法就是减小延时,减小为40微秒。
-
代码体现
void readDHTData()
{
char i; //轮次
char j; //次数
char flag;
char tmp;
//打开DHT11高速模式
startDHT();
for(i=0; i<5; i++)
{
for(j=0; j<8; j++)
{
//检测G点
while(!Data);
//根据传送1和0的时间不同,0是26us,1是70us。等待60us后,如果Data = 1,就传1,Data = 0;就传0
//Delay60us(); 延时60微妙太长了,可能在读0时读到下一个发送序列了,延时40微妙
Delay40us();
if(Data == 1)
{
flag = 1;
while(Data); //传1的时间比较久,所以要等传1结束
}
else
{
flag = 0;
}
tmp = tmp << 1;
tmp |= flag;
}
THdata[i] = tmp;
}
}
5.6完整程序代码
#include "reg52.h"
#include "intrins.h"
sbit LED1 = P3^7; //用尾定义声明LED1
sbit Data = P3^3; //把DHT11接在单片机的P1.0口
sfr AUXR = 0x8e; //声明AUXR寄存器地址
char THdata[5]; //存放温湿度数据变量
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 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) //自己配
{
//配置串口工作方式为方式1,从只收不发改为能收能发
SCON = 0x50;
//配置辅助寄存器,减少电磁辐射,稳定晶振频率
AUXR = 0x01;
//设置定时器工作方式为定时器1的8位自动重装
TMOD &= 0x0F;
TMOD |= 0x20;
//设置串口波特率为9600,0误差
TH1 = 0xFD;
TL1 = 0xFD;
//打开定时器1
TR1 = 1;
}
void sendByte(char data_mas)
{
SBUF = data_mas;
while(!TI);
TI = 0; //一定要软件置零,不然会出现乱序
}
void sendString(char *str)
{
while(*str != '\0')
{
sendByte(*str);
str++;
}
}
void startDHT()
{
Data = 1;
Data = 0;
//至少延时18ms,那么延时30ms
Delay30ms();
Data = 1;
//检测d点
while(Data);
//检测e点
while(!Data);
//检测f点
while(Data);
}
void readDHTData()
{
char i; //轮次
char j; //次数
char flag;
char tmp;
//打开DHT11高速模式
startDHT();
for(i=0; i<5; i++)
{
for(j=0; j<8; j++)
{
//检测G点
while(!Data);
//根据传送1和0的时间不同,0是26us,1是70us。等待60us后,如果Data = 1,就传1,Data = 0;就传0
//Delay60us(); 延时60微妙太长了,可能在读0时读到下一个发送序列了,延时40微妙
Delay40us();
if(Data == 1)
{
flag = 1;
while(Data); //传1的时间比较久,所以要等传1结束
}
else
{
flag = 0;
}
tmp = tmp << 1;
tmp |= flag;
}
THdata[i] = tmp;
}
}
void main()
{
LED1 = 1; //一上电就让灯灭
UartInit(); //初始化串口
Delay1000ms(); //等待DHT模块稳定
Delay1000ms();
while(1) //防止程序退出主函数,导致LED1微弱闪烁
{
Delay1000ms(); //间隔1秒读一次
readDHTData(); //读取DHT11温湿度数据
sendString("湿度:");
sendByte(THdata[0]/10 + 0x30); //除10是将十位取出,加上0x30是将数字的数字转换为字符的数字。
sendByte(THdata[0]%10 + 0x30); //取余十是将个位取出
sendByte('.');
sendByte(THdata[1]/10 + 0x30); //除10是将十位取出,加上0x30是将数字的数字转换为字符的数字。
sendByte(THdata[1]%10 + 0x30); //取余十是将个位取出
sendString("\r\n");
sendString("温度:");
sendByte(THdata[2]/10 + 0x30); //除10是将十位取出,加上0x30是将数字的数字转换为字符的数字。
sendByte(THdata[2]%10 + 0x30); //取余十是将个位取出
sendByte('.');
sendByte(THdata[3]/10 + 0x30); //除10是将十位取出,加上0x30是将数字的数字转换为字符的数字。
sendByte(THdata[3]%10 + 0x30); //取余十是将个位取出
sendString("\r\n");
}
}
5.7程序运行结果
结束语
很高兴您能看到这里,点个赞再走呗。谢谢您啦!!!