STC89C51基础及项目第11天:温湿度传感器(非标协议外设)

1. DHT11温湿度传感器初识(239.85)

  • DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。
  • 应用领域:暖通空调;汽车;消费品;气象站;湿度调节器;除湿器;家电;医疗;自动控制…

STC89C51基础及项目第11天:温湿度传感器(非标协议外设)_第1张图片
STC89C51基础及项目第11天:温湿度传感器(非标协议外设)_第2张图片

特点

  • 相对湿度和温度测量
  • 全部校准,数字输出
  • 长期稳定性
  • 超长的信号传输距离:20米
  • 超低能耗:休眠
  • 4 引脚安装:可以买封装好的
  • 完全互换 : 直接出结果,不用转化

接线

STC89C51基础及项目第11天:温湿度传感器(非标协议外设)_第3张图片

数据传送逻辑

  • 只有一根数据线DATA,上官一号发送序列指令给DHT11模块,模块一次完整的数据传输为40bit,高位先

数据格式

8bit 湿度整数数据 + 8bit 湿度小数数据 + 8bit 温度整数数据 + 8bit 温度小数数据+ 8bit 校验和

通讯过程时序图

STC89C51基础及项目第11天:温湿度传感器(非标协议外设)_第4张图片

2. 发送时序检测模块是否存在(240.86)

根据如下时序图,做通信初始化,并检测模块是否存在,功能是否正常

STC89C51基础及项目第11天:温湿度传感器(非标协议外设)_第5张图片

STC89C51基础及项目第11天:温湿度传感器(非标协议外设)_第6张图片

时序逻辑分析

  • a:dht = 1
  • b:dht = 0
  • 延时 30 ms
  • c:dht = 1
  • 在 60us 后读 d 点,如果 d 点是低电平(被模块拉低),说明模块存在!

代码(18./DHT11_01检测模块是否存在)

#include "reg52.h"
#include "intrins.h"

sbit led1 = P3^7;
sbit dht  = P3^3;//模块的data数据线插在P3.3口

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 check_DHT(){
//a点 : dht = 1
	dht = 1;
//b点 :dht = 0
	dht = 0;
//延时30ms//主机至少拉低18ms
	Delay30ms();
//c: dht = 1
	dht = 1;
//在60us后读d点,如果d点是低电平(被模块拉低),说明模块存在!
//主机拉高20-40us、DHT响应信号80us,即20-100或40-120取中值60
	Delay60us();
	if(dht == 0){
		led1 = 0;//灯亮,说明模块存在
	}
}

void main(){
	led1 = 1;
	Delay1000ms();
	Delay1000ms();
	
	check_DHT();
	while(1);
}

3. 读取DHT11数据的时序分析(241.87)

根据如下时序图,做通信初始化,并做数据传送

STC89C51基础及项目第11天:温湿度传感器(非标协议外设)_第7张图片
STC89C51基础及项目第11天:温湿度传感器(非标协议外设)_第8张图片

DHT11传输0的时序分析
STC89C51基础及项目第11天:温湿度传感器(非标协议外设)_第9张图片
DHT11传输1的时序分析
STC89C51基础及项目第11天:温湿度传感器(非标协议外设)_第10张图片

  • a:dht = 1
  • b:dht = 0
  • 延时 30 ms
  • c:dht = 1
  • 卡d点:while(dht)(c-d)
  • 卡e点:while(!dht) (d-e)
  • 卡f点:while(dht) (e-f)
  • 卡g点:while(!dht) (f-g)(或者:dht = 0 延时50us)
  • g点之后:有效数据都是高电平,只是持续时间不一样:60us时读,低电平则0、高电平则1

4. 根据时序写代码获取DHT11的数据(242.88)

  • 代码(18./DHT11_02读取温湿度数据)
#include "reg52.h"
#include "intrins.h"

sbit led1 = P3^7;
sbit dht  = P3^3;//模块的data数据线插在P3.3口

char datas[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 DHT11_Start(){
	dht = 1;
	dht = 0;
	//延时30ms
	Delay30ms();
	dht = 1;
	//卡d点:while(dht1)(c-d)
	while(dht);
	//卡e点:while(!dht) (d-e)
	while(!dht);
  //卡f点:while(dht) (e-f)	
	while(dht);
}

void ReadDataFromDHT(){
	int i,j;//多少轮,每一轮读多少次
	char tmp,flag;
	
	DHT11_Start();
	for(i=0;i<5;i++){
		for(j=0;j<8;j++){ //每次循环后输出tmp给datas[5]中的一个元素
			while(!dht)//卡g点:while(!dht)(f-g)
			Delay60us();
			if(dht){
				flag = 1;
				while(dht);
			}else{
				flag = 0;
			}
			tmp  = tmp << 1;//tmp <<= 1//每次把char的数据10左移一位(共8位)
			tmp |= flag;		//|上flag:每次把flag给到tmp的末端一位(共(j<)8次)
		}
		datas[i] = tmp;//依此输出5组温湿度数据即5个字节
	}
}

void main(){
	led1 = 1;
	Delay1000ms();
	Delay1000ms();
	while(1){
		Delay1000ms();
		ReadDataFromDHT();
	}
}

5. 温湿度通过串口传到PC显示(243.89)

  • 代码(18./DHT11_03读取温湿度数据并通过串口上传)
#include "reg52.h"
#include "intrins.h"

sbit led1 = P3^7;
sbit dht  = P3^3;//模块的data数据线插在P3.3口

char datas[5];

sfr AUXR=0x8E;

void UartInit(){
	AUXR =  0x01;//降低单片机时钟对外界的电磁辐射(EMD
	
	SCON =  0x40;//选择串口工作方式1,REN不使能接收//PCON默认的复位就是0,所以可以不用写
	
	TMOD &= 0x0F;//高八位清0,第八位不变
	TMOD |= 0x20;//高八位变为0010,第八位不变//定时器1工作在8位自动重装载
	
	TH1   = 0xFD;
	TL1   = 0xFD;//9600波特率根据公式 算出的定时器1的初始值
	
	TR1   = 1;//定时器1开始工作
}
void sendByte(char data_msg)
{
	SBUF = data_msg;
	while(!TI);
	TI = 0;
}
void sendString(char* str)
{
	while( *str != '\0'){
		sendByte(*str);
		str++;
	}
}

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 DHT11_Start(){
	dht = 1;
	dht = 0;
	//延时30ms
	Delay30ms();
	dht = 1;
	//卡d点:while(dht1)(c-d)
	while(dht);
	//卡e点:while(!dht) (d-e)
	while(!dht);
  //卡f点:while(dht) (e-f)	
	while(dht);
}

void ReadDataFromDHT(){
	int i,j;//多少轮,每一轮读多少次
	char tmp,flag;
	
	DHT11_Start();
	for(i = 0;i < 5;i++){
		for(j = 0;j < 8;j++){ //每次循环后输出tmp给datas[5]中的一个元素
			while(!dht);//卡g点:while(!dht)(f-g)
			Delay40us();
			if(dht){
				flag = 1;
				while(dht);
			}else{
				flag = 0;
			}
			tmp  = tmp << 1;//tmp <<= 1//每次把char的数据10左移一位(共8位)
			tmp |= flag;		//|上flag:每次把flag给到tmp的末端一位(共(j<)8次)
		}
		datas[i] = tmp;//依此输出5组温湿度数据即5个字节
	}
}

void main(){
	
	UartInit();
	Delay1000ms();
	Delay1000ms();
	
	while(1){
		Delay1000ms();
		Delay1000ms();
		ReadDataFromDHT();
		
		sendString("H:");						 //湿度数据
		sendByte(datas[0]/10 + 0x30);//43>a 
		sendByte(datas[0]%10 + 0x30);//将数字转换为 ASCII 字符后的值
		sendByte('.');
		sendByte(datas[1]/10 + 0x30);
		sendByte(datas[1]%10 + 0x30);	
		sendString("\r\n");	
		
		sendString("T:");						 //温度数据
		sendByte(datas[2]/10 + 0x30);
		sendByte(datas[2]%10 + 0x30);	
		sendByte('.');
		sendByte(datas[3]/10 + 0x30); 
		sendByte(datas[3]%10 + 0x30);			
		sendString("\r\n");		
	}
}

6. 温度检测小系统—又臭又长的代码(244.90)

  • 代码(18./DHT11_04在LCD显示并通过蓝牙发送温湿度)
#include "reg52.h"
#include "intrins.h"

sbit led1 = P3^7;
sbit dht  = P3^3;//模块的data数据线插在P3.3口
sbit fan  = P1^6;//风扇 用继电器接
char datas[5];

sfr AUXR=0x8E;
#define dataBuffer P0 //定义8位数据线,P0端口组
sbit RS = P1^0;
sbit RW = P1^1;
sbit EN = P1^4;

char humi[9];
char temp[9];

void check_busy(){
	char tmp = 0x80;
	dataBuffer =0x80;
	
	while(tmp & 0x80){//1000 0000
		RS = 0; 
		RW = 1; //RS为低电平、R/W为高电平时可读忙信号
	
		EN = 0;
		_nop_();
		EN = 1;
		_nop_();
		_nop_();
		tmp = dataBuffer;//数据建立时间(读操作)tD时读数据
		EN = 0;
		_nop_();	
	}
}

void wirte_cmd(char cmd){
	check_busy();
	RS = 0; //低电平时为指令寄存器
	RW = 0; //全程低电平
	
	EN = 0;
	_nop_();//延迟(低电平+tR)的时间,so>=25ns(tR)即可,1us就足够
	dataBuffer = cmd;//把指令传给P0 //数据建立时间(写操作)tSP2时写数据
	_nop_();//最好在拉高之前延迟一会,因为还有一段tR的时间
	EN = 1;
	_nop_();//延迟一个机器周期(@11.0592MHz):1.085us即1085ns
	_nop_();//延迟(tPW+TF)的时间
	EN = 0;
	_nop_();//给低电平一点延迟
}
void wirte_data(char dataShow){
	check_busy();
	RS = 1; //高电平时为数据寄存器
	RW = 0; 
	
	EN = 0;
	_nop_();
	dataBuffer = dataShow;//把数据传给P0
	_nop_();
	EN = 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 UartInit(){
	AUXR =  0x01;//降低单片机时钟对外界的电磁辐射(EMD
	
	SCON =  0x40;//选择串口工作方式1,REN不使能接收//PCON默认的复位就是0,所以可以不用写
	
	TMOD &= 0x0F;//高八位清0,第八位不变
	TMOD |= 0x20;//高八位变为0010,第八位不变//定时器1工作在8位自动重装载
	
	TH1   = 0xFD;
	TL1   = 0xFD;//9600波特率根据公式 算出的定时器1的初始值
	
	TR1   = 1;//定时器1开始工作
}
void sendByte(char data_msg)
{
	SBUF = data_msg;
	while(!TI);
	TI = 0;
}
void sendString(char* str)
{
	while( *str != '\0'){
		sendByte(*str);
		str++;
	}
}

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 DHT11_Start(){
	dht = 1;
	dht = 0;
	//延时30ms
	Delay30ms();
	dht = 1;
	//卡d点:while(dht1)(c-d)
	while(dht);
	//卡e点:while(!dht) (d-e)
	while(!dht);
  //卡f点:while(dht) (e-f)	
	while(dht);
}

void ReadDataFromDHT(){
	int i,j;//多少轮,每一轮读多少次
	char tmp,flag;
	
	DHT11_Start();
	for(i = 0;i < 5;i++){
		for(j = 0;j < 8;j++){ //每次循环后输出tmp给datas[5]中的一个元素
			while(!dht);//卡g点:while(!dht)(f-g)
			Delay40us();
			if(dht){
				flag = 1;
				while(dht);
			}else{
				flag = 0;
			}
			tmp  = tmp << 1;//tmp <<= 1//每次把char的数据10左移一位(共8位)
			tmp |= flag;		//|上flag:每次把flag给到tmp的末端一位(共(j<)8次)
		}
		datas[i] = tmp;//依此输出5组温湿度数据即5个字节
	}
}

void LCD1602_INIT(){
//(1)延时 15ms
	Delay15ms();
//(2)写指令 38H(不检测忙信号)
	wirte_cmd(0x38);
//(3)延时 5ms
	Delay5ms();
//(4)以后每次写指令,读/写数据操作均需要检测忙信号
//(5)写指令 38H:显示模式设置
	wirte_cmd(0x38);
//(6)写指令 08H:显示关闭
	wirte_cmd(0x08);
//(7)写指令 01H:显示清屏
	wirte_cmd(0x01);
//(8)写指令 06H:显示光标移动设置
	wirte_cmd(0x06);
//(9)写指令 0CH:显示开及光标设置
	wirte_cmd(0x0C);
}

void showLine(char line,char list,char* string){
	switch(line){
		case 1:
			wirte_cmd(0x80+list);
			while(*string){
				wirte_data(*string);
				string++;
			}
			break;
		
		case 2:
			wirte_cmd(0x80+0x40+list);
			while(*string){
				wirte_data(*string);
				string++;
			}
			break;
	}
}

void BuildDatas(){
	humi[0] = 'H';
	humi[1] = ':';
	humi[2] = datas[0]/10 + 0x30;
	humi[3] = datas[0]%10 + 0x30;
	humi[4] = '.';
	humi[5] = datas[1]/10 + 0x30;
	humi[6] = datas[2]%10 + 0x30;
	humi[7] = '%';
	humi[8] = '\0';
	
	temp[0] = 'T';
	temp[1] = ':';
	temp[2] = datas[2]/10 + 0x30;
	temp[3] = datas[2]%10 + 0x30;
	temp[4] = '.';
	temp[5] = datas[3]/10 + 0x30;
	temp[6] = datas[3]%10 + 0x30;
	temp[7] = 'C';
	temp[8] = '\0';
	
}
void main(){
	
	Delay1000ms();
	UartInit();
	LCD1602_INIT();
	Delay1000ms();
	Delay1000ms();
	led1 = 0;
	
	while(1){
		Delay1000ms();
		ReadDataFromDHT();
		
		if(datas[2] > 25){
			fan = 0;
		}
		
		BuildDatas();
		
		sendString(humi);
		sendString("\r\n");
		sendString(temp);
		sendString("\r\n");
		
		showLine(1,2,humi);
		showLine(2,2,temp);
	}
}

7. 分文件实现优化代码(245.91)

  • 代码(18./DHT11_05分文件编程)
  • extern 关键字声明变量,可调用其他文件里的全局变量
  • 或者这么用:
    • 头文件中声明
    • 一个源文件中定义
    • 其他源文件只需包含此头文件,不用再次定义
      STC89C51基础及项目第11天:温湿度传感器(非标协议外设)_第11张图片
  • sbit 是特定于单片机的关键字,用于定义特殊功能寄存器位
    STC89C51基础及项目第11天:温湿度传感器(非标协议外设)_第12张图片

你可能感兴趣的:(单片机)