项目三 基于DHT11和蓝牙的温湿度检测系统

DHT11温湿度传感器

工作流程

  • 通过51单片机给DHT11发送指令。DHT11开始测温湿度。DHT11一次完整的数据传输为40bit,数据的高位先出
  • 数据格式:8bit湿度整数数据+8bit湿度小数数据 +8bi温度整数数据+8bit温度小数数据+8bit校验位

发送时序检测DHT11是否存在

image-20230110112354222

检测的时序:

  • 首先总线拉高
  • 总线拉低
  • 等待至少18ms
  • 总线拉高
  • 等待20或者40us后,如果有设备,设备会主动将总线拉低,无设备则继续保持高电平
  • 注意:DHT11的供电电压为 3-5.5V。传感器上电后,要等待 1s 以越过不稳定状态在此 期间无需发送任何指令。

代码实现:

//检测dht11是否存在
void check_dht()
{
	//- 首先总线拉高
	dht = 1;
	//- 总线拉低
	dht = 0;
	//- 等待至少18ms
	//此处为了保险 延时30ms
	Delay30ms();
	//- 总线拉高
	dht = 1;
	//- 等待20或者40us后,如果有设备,设备会主动将总线拉低80us,无设备则继续保持高电平
	//此处等待60us以后在检测dht11总线上的电平
	Delay60us();
	//如果有dht11设备,此时总线会被dht11拉低,LED灯亮
	if(dht == 0)
	{
			led = 0;
	}
}
void main()
{
	led = 1;
	//dht11 上电后为了稳定要先等待1s
	Delay1000ms();
	check_dht();
	while(1);
}

读取DHT11数据的时序分析

image-20230110130518781

时序分析:

  • a点高电平
  • b点低电平
  • 至少延时15ms
  • 拉高总线
  • c:用while(dht)卡住,如果总线被拉低(有设备存在),则跳出循环。
  • d:用while(!dht)卡住,如果总线被拉高会跳出循环
  • e:用while(dht)卡住,总线被拉低则跳出循环
  • f:用while(!dht)卡住总共总线被拉高进入g点,开始接收数据。
  • g:g点延时50us后读取总线的状态。

注意:当单片机给DHT11开始信号的时候,DHT11只会读取一次数据。即40bit数据,所以每一次读取数据都要给DHT11开始信号。

代码实现:

再代码中要实现的是整形数据和字符数据的转换(例如:1和字符型 ‘1’)的转换,整形的1,2,3,...加上十六进制的30既可以得到字符型的1,2,3,....

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

sbit led = P3^7;
//dht11数据总线
sbit dht = P3^4;
char datas[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);
}

//dht 初始化
void dht_init()
{
	//- 首先总线拉高
	dht = 1;
	//- 总线拉低
	dht = 0;
	//- 等待至少18ms
	//此处为了保险 延时30ms
	Delay30ms();
	//- 总线拉高
	dht = 1;
	//- 等待20或者40us后,如果有设备,设备会主动将总线拉低80us,无设备则继续保持高电平
	//卡住 等待设备将其拉低
	while(dht);
	//卡住 等待设备将其拉高
	while(!dht);
	//卡住 等待设备将其拉低 此时进入f点
	while(dht);
}
//读取dht11的数据 读5个数据 1个数据8位 要读40次
void read_data_dht()
{
	int i;
	int j;
	char tmp;
	char data_mag;
	dht_init();
	for(i=0 ; i<5 ; i++)
	{
		for(j=0 ; j<8 ; j++)
		{
			//卡住g点
			while(!dht);
			//等60us后读取总线电平
			Delay40us();
			if(dht == 1)
			{
				tmp = 1;
				while(dht);
			}
			else
			{
				tmp = 0;
			}
			//将data_mag的数据左移动一位
			data_mag = data_mag << 1; 
			data_mag |= tmp;
		}
		*(datas+i) = data_mag;
	}

}
void UartInit(void)		//9600bps@11.0592MHz
{
	PCON &= 0x7F;		//波特率不倍速
	SCON = 0x50;		//8位数据,可变波特率
	TMOD &= 0x0F;		//清除定时器1模式位
	TMOD |= 0x20;		//设定定时器1为8位自动重装方式
	TL1 = 0xFD;		//设定定时初值
	TH1 = 0xFD;		//设定定时器重装值
	ET1 = 0;		//禁止定时器1中断
	TR1 = 1;		//启动定时器1
}
void seadByte(char byte)
{
 //再串口接收的时候只可以接收字符型的数据 所以要将datas中的数据转为字符型
	char data_mag;
	//定义一个静态局部变量
	static char cdata = 0;
	data_mag = byte/10;
	//将数字转为字符类型
	data_mag += 0x30;
	SBUF = data_mag;
	while(!TI);
	TI = 0;
	data_mag = byte%10;
	data_mag += 0x30;
	SBUF = data_mag;
	while(!TI);
	TI = 0;
	cdata++;
	if(cdata == 1)
	{
		SBUF = '.';
		while(!TI);
		TI = 0;
	}
	else
	{
		SBUF = ' ';
		while(!TI);
		TI = 0;
		cdata = 0;
	}
}
void sendStr()
{
	int i;
	int len;
	len = sizeof(datas)/sizeof(datas[0]);
	for(i=0 ; i<len-1 ; i++)
	{
		seadByte(*(datas+i));
	}
}
void main()
{
	led = 1;
	UartInit();
	Delay1000ms();
	Delay1000ms();
	while(1)
	{
		Delay1000ms();
		read_data_dht();
		//将数据传给串口
		sendStr();
	}
}

项目三 基于蓝牙的温度检测系统

功能介绍:

  • 利用DHT11检测温湿度
  • 利用蓝牙模块将温湿度发送给手机
  • LCD显示温湿度
  • 利用继电器控制电风扇,当温度大于25度,开启风扇

代码

#include "reg52.h"
#include "intrins.h"
/*
	p1^0   RS
	p1^1   R/W
	p1^2    E
	P0     DATA
	P1^3   Switch
*/

//dht11数据总线
sbit dht = P3^4;
sbit RS  = P1^0;
sbit RW  = P1^1;
sbit E   = P1^2;
sbit Switch = P1^3;
char datas[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 Delay5ms()		//@11.0592MHz
{
	unsigned char i, j;

	i = 9;
	j = 244;
	do
	{
		while (--j);
	} while (--i);
}
void Delay15ms()		//@11.0592MHz
{
	unsigned char i, j;

	i = 27;
	j = 226;
	do
	{
		while (--j);
	} while (--i);
}

//dht 初始化
void dht_init()
{
	//- 首先总线拉高
	dht = 1;
	//- 总线拉低
	dht = 0;
	//- 等待至少18ms
	//此处为了保险 延时30ms
	Delay30ms();
	//- 总线拉高
	dht = 1;
	//- 等待20或者40us后,如果有设备,设备会主动将总线拉低80us,无设备则继续保持高电平
	//卡住 等待设备将其拉低
	while(dht);
	//卡住 等待设备将其拉高
	while(!dht);
	//卡住 等待设备将其拉低 此时进入f点
	while(dht);
}
//读取dht11的数据 读5个数据 1个数据8位 要读40次
void read_data_dht()
{
	int i;
	int j;
	char tmp;
	char data_mag;
	dht_init();
	for(i=0 ; i<5 ; i++)
	{
		for(j=0 ; j<8 ; j++)
		{
			//卡住g点
			while(!dht);
			//等60us后读取总线电平
			Delay40us();
			if(dht == 1)
			{
				tmp = 1;
				while(dht);
			}
			else
			{
				tmp = 0;
			}
			//将data_mag的数据左移动一位
			data_mag = data_mag << 1; 
			data_mag |= tmp;
		}
		*(datas+i) = data_mag;
	}

}
void UartInit(void)		//9600bps@11.0592MHz
{
	PCON &= 0x7F;		//波特率不倍速
	SCON = 0x50;		//8位数据,可变波特率
	TMOD &= 0x0F;		//清除定时器1模式位
	TMOD |= 0x20;		//设定定时器1为8位自动重装方式
	TL1 = 0xFD;		//设定定时初值
	TH1 = 0xFD;		//设定定时器重装值
	ET1 = 0;		//禁止定时器1中断
	TR1 = 1;		//启动定时器1
}
void seadByte(char byte)
{
 //再串口接收的时候只可以接收字符型的数据 所以要将datas中的数据转为字符型
	char data_mag;
	//定义一个静态局部变量
	static char cdata = 0;
	static int tOrh = 0;
	tOrh++;
	cdata++;
	if(tOrh == 1)
	{
		SBUF = 'H';
		while(!TI);
		TI = 0;
		SBUF = ':';
		while(!TI);
		TI = 0;
	}
	if(tOrh == 3)
	{
		SBUF = 'T';
		while(!TI);
		TI = 0;
		SBUF = ':';
		while(!TI);
		TI = 0;
		tOrh = -1;
	}
	data_mag = byte/10;
	//将数字转为字符类型
	data_mag += 0x30;
	SBUF = data_mag;
	while(!TI);
	TI = 0;
	data_mag = byte%10;
	data_mag += 0x30;
	SBUF = data_mag;
	while(!TI);
	TI = 0;
	if(cdata == 1)
	{
		SBUF = '.';
		while(!TI);
		TI = 0;
	}
	else
	{
		//换行
		SBUF = '\r';
		while(!TI);
		TI = 0;
		SBUF = '\n';
		while(!TI);
		TI = 0;
		cdata = 0;
	}
}
void sendStr()
{
	int i;
	int len;
	len = sizeof(datas)/sizeof(datas[0]);
	for(i=0 ; i<len-1 ; i++)
	{
		seadByte(*(datas+i));
	}
}
void LCD_busy()
{
	char busy = 0x80;
	P0 = 0xff;
	while(busy & 0x80)
	{
		RS = 0;
		RW = 1;
		E = 0;
		_nop_();
		E = 1;
		_nop_();
		busy = P0;
		_nop_();
		E = 0;
		_nop_();
	}

}
void LCD_write_dir(char data_mag)
{
	LCD_busy();
	RS = 0;
	RW = 0;
	E = 0;
	P0 = data_mag;
	_nop_();
	E = 1;
	_nop_();
	_nop_();
	E = 0;
	_nop_();
}
void LCD_write_data(char data_mag)
{
	LCD_busy();
	RS = 1;
	RW = 0;
	E = 0;
	P0 = data_mag;
	_nop_();
	E = 1;
	_nop_();
	_nop_();
	E = 0;
	_nop_();
}
void LCD_init()
{
	//(1)延时 15ms
	Delay15ms();
	//(2)写指令 38H(不检测忙信号)
	LCD_write_dir(0x38);
	//(3)延时 5ms
	Delay5ms();
	//(4)以后每次写指令,读/写数据操作均需要检测忙信号
	//(5)写指令 38H:显示模式设置
	LCD_write_dir(0x38);
	//(6)写指令 08H:显示关闭
	LCD_write_dir(0x08);
	//(7)写指令 01H:显示清屏
	LCD_write_dir(0x01);
	//(8)写指令 06H:显示光标移动设置
	LCD_write_dir(0x06);
	//(9)写指令 0CH:显示开及光标设置
	LCD_write_dir(0x0c);
}
void sendLCD()
{
	//要显示的位置
	LCD_write_dir(0x80+0x03);
	//要显示的内容
	LCD_write_data('H');
	LCD_write_data(':');
	LCD_write_data(*datas/10 + 0x30);
	LCD_write_data(*datas%10 + 0x30);
	LCD_write_data('.');
	LCD_write_data(*(datas+1)/10 + 0x30);
	LCD_write_data(*(datas+1)%10 + 0x30);
	//要显示的位置
	LCD_write_dir(0x80+0x03+0x40);
	LCD_write_data('T');
	LCD_write_data(':');	
	LCD_write_data(*(datas+2)/10 + 0x30);
	LCD_write_data(*(datas+2)%10 + 0x30);
	LCD_write_data('.');
	LCD_write_data(*(datas+3)/10 + 0x30);
	LCD_write_data(*(datas+3)%10 + 0x30);
}
void main()
{
	Switch = 1;
	UartInit();
	LCD_init();
	Delay1000ms();
	while(1)
	{
		Delay1000ms();
		read_data_dht();
		//将数据传给串口
		sendStr();
		//将数据传给LCD
		sendLCD();
		if(*(datas+2) > 25)
		{
			Switch = 0;
		}
		else
		{
			Switch = 1;
		}
	}

}
posted @ 2023-09-01 14:01  徐博正  阅读(274)  评论(0)    收藏  举报