目录
前言
程序思路
DHT11
一、DHT11基础储备
二、接口说明
三、协议及数据格式
编辑
四、DHT11代码
MQ-2烟雾传感器
一、MQ-2烟雾传感器介绍
二、工作原理
三、时序配置
四、指令配置
编辑
五、代码
LCD1602
一、LCD1602介绍
二、LCD1602指令介绍 编辑
三、LCD1602代码
HC-05
一、HC-05介绍
三、uart代码
主程序
总结
文章来源地址https://uudwc.com/A/rZA1D
前言
基于51单片机开发板的DHT11温湿度传感器和MQ2烟雾报警器,DHT11温湿度传感器和MQ2烟雾报警器的数据传入单片机后进行数据处理,通过串口发送到手机端和LCD1602显示屏显示,可以通过手机端进行阈值控制。
文章来源:https://uudwc.com/A/rZA1D
程序思路
DHT11
一、DHT11基础储备
DHT11 数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器,内部由一个 8 位单片机控制一个电阻式感湿元件和一个 NTC 测温元件。DHT11 虽然也是采用单总线协议,但是该协议与 DS18B20 的单总线协议稍微有些不同之处。
相比于 DS18B20 只能测量温度,DHT11 既能检测温度又能检测湿度,不过 DHT11 的精度和测量范围都要低于 DS18B20,其温度测量范围为 0~50℃,误差在±2℃;湿度的测量范围为 20%~90%RH(Relative Humidity 相对湿度—指空气中水汽压与饱和水汽压的百分比),误差在±5%RH。DHT11 电路很简单,只需要将 DATA 引脚连接单片机的一个 I/O 即可,不过该引脚需要上拉一个 5K 的电阻,DHT11 的供电电压为 3~5.5V。
二、接口说明
接口有两种一种为3个引脚的
Pin | 名称 | 注释 |
1 | +(VCC) | 供电3.3-5V |
2 | out(DATA) | 输出串行数据 |
3 | -(GND) | 接地 |
Pin | 名称 | 注释 |
1 | VDD | 供电3.3~5.5V DC |
2 | DATA | 串行数据,单总线 |
3 | NC | 空脚 |
4 | GND | 接地,电源负极 |
2. 电源引脚
DHT11的供电电压为3~5.5 V。传感器上电后,要等待 1s 以越过不稳定状态,在此期间无需发送任何指令。电源引脚(VDD,GND)之间可增加一个100nF 的电容,用以去耦滤波。
3. 串行接口(单线双向)
DATA 用于微处理器与 DHT11之间的通讯和同步,采用单总线数据格式,一次通讯时间4ms左右,数据分小数部分和整数部分
三、协议及数据格式
DHT11 采用单总线协议与单片机通信,单片机发送一次复位信号后,DHT11 从低功耗模式转换到高速模式,等待主机复位结束后,DHT11 发送响应信号,并拉高总线准备传输数据。一次完整的数据为 40bit,按照高位在前,低位在后的顺序传输。
数据格式为:8bit 湿度整数数据+8bit 湿度小数数据+8bit 温度整数数据+8bit 温度小数数据+8bit 校验和,一共 5 字节(40bit)数据。由于 DHT11 分辨率只能精确到个位,所以小数部分是数据全为 0。校验和为前 4 个字节数据相加,校验的目的是为了保证数据传输的准确性。
DHT11 只有在接收到开始信号后才触发一次温湿度采集,如果没有接收到主机发送复位信号,DHT11 不主动进行温湿度采集。当数据采集完毕且无开始信号后,DHT11 自动切换到低速模式。
注意:由于 DHT11 时序要求非常严格,所以在操作时序的时候,为了防止中断干扰总线时序,先关闭总中断,操作完毕后再打开总中断。
我们可以通过延迟的方式达到高低电平保持的效果,通过STC-ISP的软件延时计算器生成在11.0592MHz系统频率下的延迟函数
使用_nop_();时必须包含头文件 intrins.h,一个
_nop_();指令延迟2us
11.0592Mhz:
时钟周期:1/11.0592Mhz,1单位是秒所以11.0592Mhz要转为秒为11059200hz
1/11059200≈0.00000009s
机器周期:12×时钟周期=0.00000009s×12=0.00000109s
转为us就是1.09us
在调用此函数时,需要一个调用指令,此指令消耗 2
个机器周期(即 2.18us)
;函数执行完 毕时要返回主调函数,需要一个返回指令,此指令消耗 2 个机器周期(即 2.18us
)。调用和返回消耗了
2.18us + 2.18us = 4.36us
。即5us,所以我们调用us延迟函数时,延迟时间等于2*n+5us
//延时us --2*n+5us
void DHT11_delay_us(unsigned char n)
{
while(--n);
}
//延时ms
void DHT11_delay_ms(unsigned int z)
{
unsigned int i,j;
for(i=z;i>0;i--)
for(j=114;j>0;j--);
}
//DHT11起始信号
void DHT11_start()
{
Temp_data=1;
DHT11_delay_us(2);//高电平保持时间
Temp_data=0;
DHT11_delay_ms(20);//低电平保持20ms
Temp_data=1;
DHT11_delay_us(13);//拉高
}
由于51单片机在11.0592MHz系统频率下延迟无法精确到1us,所以我们统一为延迟85us作为应答信号,实时证明85us应答信号也可以接收到数据。
如图所示我们可以通过从机信号的高电平保持时间来判断位数据为“0”或“1”我们通过调用 DHT11_delay_us(9);2*9+5=23us;然后进行判断是否为高电平(因为执行一条语句需要1.09us)如果为高电平接收到的数据为1,否则为0。
//接收一个字节
unsigned char DHT11_rec_byte()
{
unsigned char i,dat;
for(i=0;i<8;i++)
{
while(!Temp_data);
DHT11_delay_us(9); //延迟23us
dat<<=1; //数据移位,为新一位数据赋值做准备
if(Temp_data==1) //如果为高电平,dat为1
{
dat+=1;
}
while(Temp_data); //为低电平退出循环,接收下一位数据
}
return dat;
}
然后将得到的数据进行储存
//接收温湿度数据
void DHT11_receive()
{
DHT11_start();
Temp_data=1;
if(Temp_data==0)
{
while(Temp_data==0); //等待拉高
DHT11_delay_us(40); //拉高后延时85us
R_H=DHT11_rec_byte(); //接收湿度高八位
R_L=DHT11_rec_byte(); //接收湿度低八位
T_H=DHT11_rec_byte(); //接收温度高八位
T_L=DHT11_rec_byte(); //接收温度低八位
revise=DHT11_rec_byte(); //接收校正位
DHT11_delay_us(25); //结束
if((R_H+R_L+T_H+T_L)==revise) //校正
{
RH=R_H;
RL=R_L;
TH=T_H;
TL=T_L;
}
/*数据处理,方便显示*/
rec_dat[0]=RH;
rec_dat[1]=RL;
rec_dat[2]=TH;
rec_dat[3]=TL;
}
}
四、DHT11代码
dht11.c 代码
#include "dht11.h"
unsigned int rec_dat[9];
unsigned int R_H,R_L,T_H,T_L;
unsigned char RH,RL,TH,TL,revise;
//延时us --2*n+5us
void DHT11_delay_us(unsigned char n)
{
while(--n);
}
//延时ms
void DHT11_delay_ms(unsigned int z)
{
unsigned int i,j;
for(i=z;i>0;i--)
for(j=114;j>0;j--);
}
//DHT11起始信号
void DHT11_start()
{
Temp_data=1;
DHT11_delay_us(2);
Temp_data=0;
DHT11_delay_ms(20);
Temp_data=1;
DHT11_delay_us(13);
}
//接收一个字节
unsigned char DHT11_rec_byte()
{
unsigned char i,dat;
for(i=0;i<8;i++)
{
while(!Temp_data);
DHT11_delay_us(9); //延迟23us
dat<<=1; //数据移位,为新一位数据赋值做准备
if(Temp_data==1) //如果为高电平,dat为1
{
dat+=1;
}
while(Temp_data); //为低电平退出循环,接收下一位数据
}
return dat;
}
//接收温湿度数据
void DHT11_receive()
{
DHT11_start();
Temp_data=1;
if(Temp_data==0)
{
while(Temp_data==0); //等待拉高
DHT11_delay_us(40); //拉高后延时85us
R_H=DHT11_rec_byte(); //接收湿度高八位
R_L=DHT11_rec_byte(); //接收湿度低八位
T_H=DHT11_rec_byte(); //接收温度高八位
T_L=DHT11_rec_byte(); //接收温度低八位
revise=DHT11_rec_byte(); //接收校正位
DHT11_delay_us(25); //结束
if((R_H+R_L+T_H+T_L)==revise) //校正
{
RH=R_H;
RL=R_L;
TH=T_H;
TL=T_L;
}
/*数据处理,方便显示*/
rec_dat[0]=RH;
rec_dat[1]=RL;
rec_dat[2]=TH;
rec_dat[3]=TL;
}
}
dht11.h
#ifndef __UART_H__
#define __UART_H__
void InitUART(void);
unsigned char UART_Receive_Data();
#endif
MQ-2烟雾传感器
一、MQ-2烟雾传感器介绍
MQ-2传感器所使用的气敏材料是在清洁空气中电导率较低的二氧化锡(SnO2)。当传感器所处环境中存在可燃气体时,传感器的电导率随空气中可燃气体浓度的增加而增大。使用简单的电路即可将电导率的变化转换为与该气体浓度相对应的输出信号。MQ-2气体传感器可用于家庭和工厂的气体泄漏检测,适宜对液化气、丁烷、丙烷、甲烷、酒精、氢气、烟雾等的探测,对天然气和其它可燃蒸汽的检测也很理想。这种传感器可检测多种可燃性气体,是一款适合多种应用的低成本传感器
特点:
1、具有信号输出指示。
2、双路信号输出(模拟量输出及TTL电平输出)
3、TTL输出有效信号为低电平。(当输出低电平时信号灯亮,可直接接单片机)
4、模拟量输出0~5V电压,浓度越高电压越高。
5、对液化气,天然气,城市煤气有较好的灵敏度。
6、具有长期的使用寿命和可靠的稳定性
7、快速的响应恢复特性
二、工作原理
MQ-2型烟雾传感器属于二氧化锡半导体气敏材料,属于表面离子式N型半导体。处于200~3000摄氏度时,二氧化锡表面吸附空气中的氧,形成氧的负离子吸附,使半导体中的电子密度减少,从面使其电阻值增加。当与烟雾接触时,如果晶粒间界处的势垒收到烟雾的调至面变化,就会引起表面导电率的变化。利用这一点就可以获得这种烟雾存在的信息烟雾浓度越大导电率越大,输出电阻越低,则输出的模拟信号就越大。
Pin | 名称 | 注释 |
1 | VCC | 电源正极接口,可外接3.3~5v供电电源 |
2 | GND | 电源负极接口,可外接电源负极或地线(GND) |
3 | DO | 数字信号输出接口(0和1),可外接单片机的GPIO |
4 | AO | 模拟信号输出接口,可外接单片的ADC采样通道 |
3. 烟雾检测
当可燃气体浓度小于指定的阈值时,DO输出高电平,大于指定的阈值时则输出低电平。
4. 阈值调节
模块中蓝色的电位器是用于调节阀值,顺时针旋转,阈值会越大,逆时针越小。
5. 使用AO接口
与DO不同,AO会输出模拟信号,因此需要与单片机的ADC采样通道连接。单片机可以通过此模拟信号来获取可燃气体浓度大小。
我们可以通过接收到的AD值转换为电压后在转换为ppm值。
AD转换过程我们使用开发板上的XPT2046,XPT2046 是一款 4 线制电阻式触摸屏控制器,内含 12 位分辨率 125KHz 转换速率逐步逼近型 A/D 转换器。
因为AIN0,AIN1,AIN2给开发板上的可调电阻,NTC和GR占用了,开发板上留出AIN3供我们使用,通过接线把AO口接到图中
三、时序配置
//引脚定义
sbit XPY2046_DIN=P3^4;
sbit XPY2046_CS=P3^5;
sbit XPY2046_DCLK=P3^6;
sbit XPY2046_DOUT=P3^7;
然后我们根据时序图对其进行读取AD值,!CS为0时,才可以进行有效操作,所以开始时吧CS脚置0,对DCLK赋初值0,然后开始进行写操作,由时序图可以看出,DIN进行赋值时给DCLK一个上升沿,把信号发送出去,发送完给DCLK一个下降沿,由时序规范表可以看到tACQ最小获取时间为1.5us所以我们可以直接给DCLK置1后置0,使DCLK形成一个下降沿。然后通过循环移位的方式把8位指令从高到低全部写入xpt2046,写入后有BUSY忙碌信号,我们可以通过延迟函数来形成,也可以不加,因为我们tACQ已经满足了时序条件,最后读出DOUT由时序图可以看出DOUT读出在DCLK的下降沿,我们可以先给DCLK置1后置0,形成一个下降沿后读取DOUT,数据也是从高到低读取,读取16位数据
/**
* @brief ZPT2046读取AD值
* @param Command 命令字,范围:头文件内定义的宏,结尾的数字表示转换的位数
* @retval AD转换后的数字量,范围:8位为0~255,12位为0~4095
*/
unsigned int XPT2046_ReadAD(unsigned char Command)
{
unsigned char i;
unsigned int Data=0;
XPY2046_DCLK=0; //DCLK赋初值
XPY2046_CS=0; //时序开始,读取一组有效数据
for(i=0;i<8;i++)
{
XPY2046_DIN=Command&(0x80>>i);//写入1位数据
XPY2046_DCLK=1; //写入完置1发送
XPY2046_DCLK=0; //发送完置0,准备接收下一数据
}
for(i=0;i<16;i++)
{
XPY2046_DCLK=1; //置1,形成上升沿
XPY2046_DCLK=0; //置0,形成下降沿,在下降沿读取数据
if(XPY2046_DOUT){Data|=(0x8000>>i);}
//如果数据为1,则赋值为1,为0赋值为0,
//这里为0不赋值,直接移位进行赋值
}
XPY2046_CS=1;//完成一组数据读取后拉高,结束时序
return Data>>8;
//我们读取的是8位数据,把16位数据右移8位,如果为12位,右移4位
}
时序部分已经完成,接下来是写入的指令。
四、指令配置
我们只看单端模式下的输入配置A2-A0
//如果读取12位 把C改为4 例如#define XPT2046_VBAT 0xA4
#define XPT2046_VBAT 0xAC
#define XPT2046_AUX 0xEC
#define XPT2046_XP 0x9C
#define XPT2046_YP 0xDC
这里配置了AIN0(X+),AIN1(Y+),AIN2(VBAT),AIN3(AUX)的输入配置,我们MQ-2接入的是AIN3(AUX),所以我们调用XPT2046_ReadAD(XPT2046_AUX);就可以得到MQ-2的AO 口输入的AD值,通过AD值转换为电压后,再转换为ppm
有图可以看出我们转换后的电压值和万用表测量值在一定误差范围内,可以忽略误差。
五、代码
XPT2046.c
#include <REG51.H>
#include <INTRINS.H>
//引脚定义
sbit XPY2046_DIN=P3^4;
sbit XPY2046_CS=P3^5;
sbit XPY2046_DCLK=P3^6;
sbit XPY2046_DOUT=P3^7;
/**
* @brief XPT2046读取AD值
* @param Command 命令字,范围:头文件内定义的宏,结尾的数字表示转换的位数
* @retval AD转换后的数字量,范围:8位为0~255,12位为0~4095
*/
unsigned int XPT2046_ReadAD(unsigned char Command)
{
unsigned char i;
unsigned int Data=0;
XPY2046_DCLK=0; //DCLK赋初值
XPY2046_CS=0; //时序开始,读取一组有效数据
for(i=0;i<8;i++)
{
XPY2046_DIN=Command&(0x80>>i);//写入1位数据
XPY2046_DCLK=1; //写入完置1发送
XPY2046_DCLK=0; //发送完置0,准备接收下一数据
}
for(i=0;i<16;i++)
{
XPY2046_DCLK=1; //置1,形成上升沿
XPY2046_DCLK=0; //置0,形成下降沿,在下降沿读取数据
if(XPY2046_DOUT){Data|=(0x8000>>i);}
//如果数据为1,则赋值为1,为0赋值为0,
//这里为0不赋值,直接移位进行赋值
}
XPY2046_CS=1;//完成一组数据读取后拉高,结束时序
return Data>>8;
//我们读取的是8位数据,把16位数据右移8位,如果为12位,右移4位
}
XPT2046.h
#ifndef __XPT2046_H__
#define __XPT2046_H__
//如果读取12位 把C改为4 例如#define XPT2046_VBAT 0xA4
#define XPT2046_VBAT 0xAC
#define XPT2046_AUX 0xEC
#define XPT2046_XP 0x9C
#define XPT2046_YP 0xDC
unsigned int XPT2046_ReadAD(unsigned char Command);
#endif
LCD1602
一、LCD1602介绍
LCD1602液晶显示屏
LCD ( Liquid Crystal Display 的简称)液晶显示器。能够同时显示16x2,32个字符,是一种专门用来显示字母、数字、符号等的点阵型液晶模块。
LCD1602液晶显示器是广泛使用的一种字符型液晶显示模块。它是由字符型液晶显示屏(LCD)、控制驱动主电路HD44780及其扩展驱动电路HD44100,以及少量电阻、电容元件和结构件等装配在PCB板上而组成。该显示屏的优点是耗电量低、体积小、辐射低。
LCD1602主要用来显示数字、字母、图形以及少量自定义字符。可以显示2行16个字符,拥有16个引脚,其中8位数据总线D0-D7,和RS、R/W、EN三个控制端口,工作电压为5V,并且带有字符对比度调节V0和背光源AK。
二、LCD1602指令介绍
三、LCD1602代码
lcd1602.c
#include"lcd1602.h"
void Lcd1602_Write_Cmd(unsigned char cmd) //写命令
{
LCD1602_RS = 0;
LCD1602_RW = 0;
LCD1602_DB = cmd;
LCD_Delay10ms(1);
LCD1602_EN = 1;
LCD_Delay10ms(1);
LCD1602_EN = 0;
}
void Lcd1602_Write_Data(unsigned char dat) //写数据
{
//Read_Busy();
LCD1602_RS = 1;
LCD1602_RW = 0;
LCD1602_DB = dat;
LCD_Delay10ms(1);
LCD1602_EN = 1;
LCD_Delay10ms(1);
LCD1602_EN = 0;
}
//指定位置开始显示数据!
void LcdSetCursor(unsigned char x,unsigned char y) //坐标显示
{
unsigned char addr;
if(y == 0)
addr = 0x00 + x;//第一行开始,x表示一行的第x个
else
addr = 0x40 + x;//第二行开始,x表示一行的第x个
Lcd1602_Write_Cmd(addr|0x80);
}
void LcdShowStr(unsigned char x,unsigned char y,unsigned char *str) //显示字符串
{
LcdSetCursor(x,y); //当前字符的坐标
while(*str != '\0')
{
Lcd1602_Write_Data(*str++);
}
}
int LCD_Pow(int X,int Y)
{
unsigned char i;
int Result=1;
for(i=0;i<Y;i++)
{
Result*=X;
}
return Result;
}
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i;
LcdSetCursor(Line,Column);
for(i=Length;i>0;i--)
{
Lcd1602_Write_Data(Number/LCD_Pow(10,i-1)%10+'0');
}
}
void InitLcd1602() //1602初始化
{
Lcd1602_Write_Cmd(0x38); //打开,5*8,8位数据
Lcd1602_Write_Cmd(0x0c);
Lcd1602_Write_Cmd(0x06);
Lcd1602_Write_Cmd(0x01); //清屏
}
void LCD_Delay10ms(unsigned int c) //误差 0us
{
unsigned char a,b;
for(;c>0;c--)
for(b=38;b>0;b--)
for(a=130;a>0;a--);
}
lcd1602.h
#ifndef __LCD1602_H_
#define __LCD1602_H_
#include<reg51.h>
#define LCD1602_DB P0 //data bus 数据总线
sbit LCD1602_RS = P2^6;
sbit LCD1602_RW = P2^5;
sbit LCD1602_EN = P2^7;
void LCD_Delay10ms(unsigned int c);
void Lcd1602_Write_Cmd(unsigned char cmd); //写命令
void Lcd1602_Write_Data(unsigned char dat); //写数据
void LcdSetCursor(unsigned char x,unsigned char y); //坐标显示
void LcdShowStr(unsigned char x,unsigned char y,unsigned char *str); //显示字符串
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);//显示数字
void InitLcd1602(); //1602初始化
#endif
HC-05
一、HC-05介绍
HC-05蓝牙模块是一种基于蓝牙协议的简单无线通信设备。该模块基于BC417单芯片蓝牙IC,符合蓝牙v2.0标准,支持UART和USB接口。
具有两种工作模式:命令响应工作模式和自动连接工作模式。
当模块处于命令响应工作模式(或者AT模式)时能才能执行 AT 命令,用户可向模块发送各种 AT指令,为模块设定控制参数或发布控制命令。(AT指令就是我们PC与一些终端设备(例如蓝牙,WiFi模块)之间进行通信的,配置这些终端设备参数的一套指令。)在自动连接工作模式下模块又可分为主(Master)、从(Slave)和回环(Loopback)三种工作角色。当模块处于自动连接工作模式时,将自动根据事先设定的方式连接的数据传输。主模式:该模块可以主动搜索并连接其它蓝牙模块并接收发送数据。从模式:只能被搜索被其它蓝牙模块连接进行接收发送数据。回环:蓝牙模块就是将接收的数据原样返回给远程的主设备。
Pin | 名称 | 注释 |
1 | STATE | 状态指示。未连接时输出低电平,连接时输出高电平。 |
2 | RXD | UART接收引脚 |
3 | TXD | UART发射引脚 |
4 | GND | 地 |
5 | VCC | 接电源,可以用+5V。 |
6 | EN | 使能。接地禁用模块,悬空或接3.3V使能。 |
如何进入AT模式:
HC-05与HC-06不同,HC-06没有连接上时,就是AT模式,而HC-05需要有个开关,注意到模块上的小黑点没有,不按的时候是可配对状态,1秒闪2次。按它再上电就使HC-05进入AT模式了,严格的讲,它只是一个开的作用,无法退出AT模式。(退出的话可以在串口发送AT+RESET复位,或者重新上电)此时指示灯慢闪(2秒亮一次),模块进入AT状态。
进入AT状态后,通过串口转换器连接和电脑进行AT指令操作
基本配置(所有AT指令都必须换行)
模块默认波特率为9600、默认配对密码为1234、默认名称为HC-05,
AT模式波特率固定38400,8位数据位、1位停止位、无奇偶校验的通信格式。
(1)发送AT\r\n,回复OK;
(2)发送AT+UART ?\r\n,回复+UART 9600,1,0
(3)发送AT+UART=9600,0,0\r\n,回复OK。即为波特率配置成功。
(4)发送AT+NAME="xzxq",更改蓝牙名字返回+NAME: xzxq OK
通过这几个简单的配置,蓝牙模块基本配置完成
单片机端通过配置好相同的9600波特率进行串口通信
通过软件生成的方式进行配置,获取定时器初值
定时器初值为16进制的FD
我们使用方式二进行串口通信,还要允许串行接收,所以SCON = 0x50;或者对每个位进行单独配置
我们使用的是定时器1,8位自动重载定时器,TMOD = 0x20;
三、uart代码
uart.c
#include "uart.h"
#include "reg51.h"
void InitUART(void)//使用定时器1作为串口波特率发生器
{
SCON=0x40; //串口通信工作方式1
REN=1; //允许接收
TMOD |=0x20; //定时器1的工作方式2
TH1=0xFD;
TL1=0xFD;
TF1 = 0; //清除TF1标志
TR1=1;
TI=1;
EA=1; //总中断允许
}
unsigned char UART_Receive_Data()
{
unsigned char temp;
if(RI)//等待串口接收到数据(RI被置1)
{
RI=0;//清空接收中断标志位,为下次接收做准备
temp=SBUF;
}
return temp;
}
uart.h
#ifndef __UART_H__
#define __UART_H__
void InitUART(void);
unsigned char UART_Receive_Data();
#endif
主程序
main.c
#include <reg51.h>
#include <intrins.h>
#include <stdio.h>
#include "lcd1602.h"
#include "dht11.h"
#include "Key.h"
#include "Buzzer.h"
#include "XPT2046.h"
#include "uart.h"
#include "Delay.h"
#include "math.h"
/**********************
模块接口
DHT11 P1^1
HC-05 RXD P3^1
HC-05 TXD P3^0
MQ2传感器AO IN3
MQ2传感器DO P2^2
风扇 P1^0
**********************/
sbit LED1 = P2^0; //运行指示灯
sbit LED2 = P2^1; //运行指示灯
sbit fan = P1^0; //风扇控制引脚
bit flag;
float ppm;
float Vrl;
extern unsigned int rec_dat[9];
unsigned char rec_dat_lcd0[6];
unsigned char rec_dat_lcd1[6];
unsigned char rec_dat_lcd2[6];
unsigned char rec_dat_lcd3[6];
unsigned char humi_t = 60;
unsigned char temp_t = 35;
unsigned int mq2_data_t = 100;
void control();
void mq2_warning();
//主函数
void main()
{
InitUART();
P1=0xf0;
InitLcd1602();
LcdShowStr(0,0,"Temp:");
LcdShowStr(12,0,"Humi:");
LcdShowStr(25,0,"Smoke:");
LcdShowStr(35,0,"mv");
LcdShowStr(35,1,"mv");
while(1)
{
Lcd1602_Write_Cmd(0x06);
DHT11_receive(); //获取温湿度数据
sprintf(rec_dat_lcd0,"%d",rec_dat[0]);
sprintf(rec_dat_lcd1,"%d",rec_dat[1]);
sprintf(rec_dat_lcd2,"%d",rec_dat[2]);
sprintf(rec_dat_lcd3,"%d",rec_dat[3]);
//温度
LcdShowStr(5,0,rec_dat_lcd2);
LcdShowStr(7,0,".");
LcdShowStr(8,0,rec_dat_lcd3);
LcdShowStr(9,0," C");
LCD_ShowNum(5,1,temp_t,2);
LcdShowStr(9,1," C");
//湿度
LcdShowStr(17,0,rec_dat_lcd0);
LcdShowStr(19,0,".");
LcdShowStr(20,0,rec_dat_lcd1);
LcdShowStr(21,0," %");
LCD_ShowNum(17,1,humi_t,2);
LcdShowStr(21,1," %");
Vrl=XPT2046_ReadAD(XPT2046_AUX)*5.0/255; //读取AIN3的AD值,转换为电压,MQ2传感器
ppm = pow(11.5428 * 35.904 * Vrl/(25.5-5.1* Vrl),0.6549);
LCD_ShowNum(31,0,Vrl*1000,4);
LCD_ShowNum(31,1,mq2_data_t,4);
//下面通过串口助手打印温度
printf("Humi:%d.%d %S\n",rec_dat[0],rec_dat[1],"%");
printf("Temp:%d.%d °C\n",rec_dat[2],rec_dat[3]);
printf("Smoke:%f%S\n",ppm,"ppm");
control();
mq2_warning();
}
}
void mq2_warning()
{
if(Vrl*1000 >= mq2_data_t)//当浓度高于设定值时 ,执行条件函数
{
Delaynms(3000);//延时抗干扰
if(Vrl*1000 >= mq2_data_t)//确定 浓度高于设定值时 ,执行条件函数
{
flag = 1;
}
}
else
{
flag = 0;
}
if(flag)
{
Buzzer_Time(500);
fan = 1;
}
else
{
fan = 0;
}
sprintf(rec_dat_lcd0,"%d",rec_dat[0]);
sprintf(rec_dat_lcd2,"%d",rec_dat[2]);
if(rec_dat[2]> temp_t)
LED1 = 0;
else
LED1 = 1;
if(rec_dat[0] > humi_t)
LED2 = 0;
else
LED2 = 1;
}
void control()
{
switch(UART_Receive_Data())
{
case 0x01:humi_t--;break;
case 0x02:humi_t++;break;
case 0x03:temp_t--;break;
case 0x04:temp_t++;break;
case 0x06:mq2_data_t--;break;
case 0x07:mq2_data_t++;break;
case 0x08:Lcd1602_Write_Cmd(0x18);break;//通过指令控制屏幕画面左右移动
case 0x09:Lcd1602_Write_Cmd(0x1c);break;
default: humi_t =humi_t;temp_t=temp_t;mq2_data_t=mq2_data_t;Lcd1602_Write_Cmd(0x06); break;
}
switch(Key())
{
case 3:Lcd1602_Write_Cmd(0x18);break;
case 4:Lcd1602_Write_Cmd(0x1c);break;
default:Lcd1602_Write_Cmd(0x06);break;
}
}
Key.c
#include <REGX52.H>
#include "Delay.h"
/**
* @brief 获取独立按键键码
* @param 无
* @retval 按下按键的键码,范围:0~4,无按键按下时返回值为0
*/
unsigned char Key()
{
unsigned char KeyNumber=0;
if(P3_1==0){Delaynms(20);KeyNumber=1;}
if(P3_0==0){Delaynms(20);KeyNumber=2;}
if(P3_2==0){Delaynms(20);KeyNumber=3;}
if(P3_3==0){Delaynms(20);KeyNumber=4;}
return KeyNumber;
}
Key.h
#ifndef __KEY_H__
#define __KEY_H__
unsigned char Key();
#endif
Delay.c
void Delaynms(unsigned int xms)
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
Delay.h
#ifndef __DELAY_H__
#define __DELAY_H__
void Delaynms(unsigned int xms);
#endif
Buzzer.c
#include <REGX52.H>
#include <INTRINS.H>
//蜂鸣器端口:
sbit Buzzer=P2^5;
/**
* @brief 蜂鸣器私有延时函数,延时500us
* @param 无
* @retval 无
*/
void Buzzer_Delay500us() //@12.000MHz
{
unsigned char i;
_nop_();
i = 247;
while (--i);
}
/**
* @brief 蜂鸣器发声
* @param ms 发声的时长,范围:0~32767
* @retval 无
*/
void Buzzer_Time(unsigned int ms)
{
unsigned int i;
for(i=0;i<ms*2;i++)
{
Buzzer=!Buzzer;
Buzzer_Delay500us();
}
}
Buzzer.h
#ifndef __BUZZER_H__
#define __BUZZER_H__
void Buzzer_Time(unsigned int ms);
#endif
总结
程序可能出现的一些问题比如定义的变量太多,导致地址空间溢出
*** ERROR L107: ADDRESS SPACE OVERFLOW
SPACE: DATA
SEGMENT: _DATA_GROUP_
LENGTH: 001CH
Program Size: data=105.1 xdata=0 code=5548
Target not created.
Build Time Elapsed: 00:00:01
把Memory Model 修改为第二个,或者第三个就行。
全部代码
https://download.csdn.net/download/m0_64355812/87837706