本篇博文介绍的是用51单片机的非标准写协议【下】(DHT11温湿度传感器),包含DHT11温湿度传感器初识,发送时序检测DHT11温湿度传感器模块是否存在,读取DHT11数据的时序分析,根据时序写代码获取DHT11的数据,温湿度通过串口传到PC显示。看到这篇博文的朋友,可以先赞再看吗?
一、基本电路标识识别和接线,例如VCC,GND。
二、数电时序图的阅读,高低电平的识别。
三、C变量
四、基本输入输出
五、流程控制
六、函数
七、指针
八,字符串
如果以上知识不清楚,请自行学习后再来浏览。如果我有没例出的,请在评论区写一下。谢谢啦!
DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器,应用领域:暖通空调;汽车;消费品;气象站;湿度调节器;除湿器;家电;医疗;自动控制 。
特点
只有一根数据线DATA
,上官一号发送序列指令给DHT11模块
,模块一次完整的数据传输为40bit
,高位先出
。
8bit湿度整数数据
+8bit湿度小数数据
+8bi温度整数数据
+8bit温度小数数据
+8bit校验和
sbit Data = P3^3; //把DHT11接在单片机的P1.0口
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亮。
}
}
DHT11的供电电压为 3-5.5V。 传感器上电后, 要等待 1s
以越过不稳定状态在此期间无需发送任何指令。 电源引脚(VDD, GND) 之间可增加一个100nF 的电容, 用以去耦滤波。
Delay1000ms(); //等待DHT模块稳定
Delay1000ms();
#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微弱闪烁
}
“8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据+8bit校验和” ==40个字节
根据手册,要读取5轮,每轮8次,共读取40次
注:本程序依据发送时序检测DHT11温湿度传感器模块是否存在
工程建立
char THdata[5]; //存放温湿度数据变量
void startDHT()
{
Data = 1;
Data = 0;
//至少延时18ms,那么延时30ms
Delay30ms();
Data = 1;
//检测d点
while(Data);
//检测e点
while(!Data);
//检测f点
while(Data);
}
在本函数中,需要搞清楚的是要读取五轮
,每轮读取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;
}
}
void main()
{
LED1 = 1; //一上电就让灯灭
Delay1000ms(); //等待DHT模块稳定
Delay1000ms();
while(1) //防止程序退出主函数,导致LED1微弱闪烁
{
Delay1000ms(); //间隔1秒读一次
readDHTData();
}
}
#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();
}
}
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++;
}
}
首先得搞明白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
表示十位
和个位
数。
sendByte(THdata[0]/10 + 0x30); //除10是将十位取出,加上0x30是将数字的数字转换为字符的数字。0标号元素根据手册和读取DHT11数据函数逻辑可以知道是湿度信息
sendByte(THdata[0]%10 + 0x30); //取余十是将个位取出
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");
出现这个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;
}
}
#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");
}
}
很高兴您能看到这里,点个赞再走呗。谢谢您啦!!!