功能实现
本次设计的空气质量检测系统主要有以下功能。
(1)单片机上电能够正常启动和复位,按键模块无异常。
(2)按下按键能够进行相应模式的报警阈值调整上限。
(3)能够对空气中PM2.5的浓度、环境温湿度、有害气体浓度进行实时数据采集。
(4)LCD1602可以实时显示检测到的粉尘浓度、温湿度、有害气体浓度的环境参数数值和设定的各项环境参数的报警值。
(5)检测环境参数值超设定值时进行声光报警。
硬件设计
本次设计采用模块设计,51单片机最小系统模块,1602液晶显示模块,DHT11数字温湿度模块,ADC0832采集模块,PM2.5检测模块和MQ-135气体检测模块,4位独立按键模块,蜂鸣器模块。
软件设计
1602模块软件设计
本次设计通过1602显示字符函数来显示所有数据。通过ADC0832模块AD采样数据标准转换显示PM2.5模块和MQ-135气体检测模块反馈数据;DHT11数字温湿度模块通过规定的通信协议来进行数据采集;最后通过主函数,来实现按键模块控制实现界面的跳转以及蜂鸣器模块的报警。下面展示的是各模块对应的.C文件。希望能帮到大家!
1.LCD1602.c文件
#include <reg52.h>
#include "LCD1602.H"
/* 等待液晶准备好 */
void LcdWaitReady()
{
unsigned char sta;
LCD1602_DB = 0xFF;
LCD1602_RS = 0;
LCD1602_RW = 1;
do {
LCD1602_E = 1;
sta = LCD1602_DB; //读取状态字
LCD1602_E = 0;
} while (sta & 0x80); //bit7等于1表示液晶正忙,重复检测直到其等于0为止
}
/* 向LCD1602液晶写入一字节命令,cmd-待写入命令值 */
void LcdWriteCmd(unsigned char cmd)
{
LcdWaitReady();
LCD1602_RS = 0;
LCD1602_RW = 0;
LCD1602_DB = cmd;
LCD1602_E = 1;
LCD1602_E = 0;
}
/* 向LCD1602液晶写入一字节数据,dat-待写入数据值 */
void LcdWriteDat(unsigned char dat)
{
LcdWaitReady();
LCD1602_RS = 1;
LCD1602_RW = 0;
LCD1602_DB = dat;
LCD1602_E = 1;
LCD1602_E = 0;
}
/* 设置显示RAM起始地址,亦即光标位置,(x,y)-对应屏幕上的字符坐标 */
void LcdSetCursor(unsigned char x, unsigned char y)
{
unsigned char addr;
if (y == 0) //由输入的屏幕坐标计算显示RAM的地址
addr = 0x00 + x; //第一行字符地址从0x00起始
else
addr = 0x40 + x; //第二行字符地址从0x40起始
LcdWriteCmd(addr | 0x80); //设置RAM地址
}
/* 在液晶上显示字符串,(x,y)-对应屏幕上的起始坐标,str-字符串指针 */
void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str)
{
LcdSetCursor(x, y); //设置起始地址
while (*str != '\0') //连续写入字符串数据,直到检测到结束符
{
LcdWriteDat(*str++);
}
}
/* 初始化1602液晶 */
void InitLcd1602()
{
LcdWriteCmd(0x38); //16*2显示,5*7点阵,8位数据接口
LcdWriteCmd(0x0C); //显示器开,光标关闭
LcdWriteCmd(0x06); //文字不动,地址自动+1
LcdWriteCmd(0x01); //清屏
}
delay.c文件
#include "delay.h"
/*****************************************************************************
延迟函数
/****************************************************************************/
void delay_ms(U16 ms)
{
U16 i,j;
for(i=ms;i>0;i--)
for(j=110;j>0;j--);
}
/*******************************************************************************
* 函 数 名 : delay_10us
* 函数功能 : 延时函数,ten_us=1时,大约延时10us
* 输 入 : ten_us
* 输 出 : 无
*******************************************************************************/
void delay_10us(U16 ten_us)
{
while(ten_us--);
}
DHT11.c文件
#include "dht11.h"
#include "intrins.h"
#include "DELAY.h"
/************************************
DHT11初始化
返回0:初始化成功,
返回1:失败
*******************************/
U8 DHT11_Init(void)
{
DHT11_DQ=1;
DHT11_Rst();
return DHT11_Check();
}
//复位DHT11
void DHT11_Rst(void)
{
DHT11_DQ=1;
delay_10us(1);
DHT11_DQ=0; //拉低DQ
delay_ms(25); //拉低至少18ms
DHT11_DQ=1; //DQ=1
delay_10us(3); //主机拉高20~40us
}
//等待DHT11的回应
//返回1:未检测到DHT11的存在
//返回0:存在
U8 DHT11_Check(void)
{
U8 retry=0;
while (!DHT11_DQ&&retry<100)//判断从机发出 80us 的低电平响应信号是否结束
{
retry++;
_nop_();
};
if(retry>=100)return 1;
else retry=0;
while (DHT11_DQ&&retry<100)//判断从机发出 80us 的高电平是否结束如结束则主机进入数据接收状态
{
retry++;
_nop_();
};
if(retry>=100)return 1;
return 0;
}
//从DHT11读取一个字节
//返回值:读到的数据
U8 DHT11_Read_Byte(void)
{
U8 i,temp;
U8 data_byte=0;
U8 retry=0;
for(i=0;i<8;i++)//接收8bit的数据
{
// while(!DHT11_DQ);//等待50us的低电平开始信号结束
while (!DHT11_DQ&&retry<50)//等待50us的低电平开始信号结束
{
retry++;
_nop_();
};
retry=0;
delay_10us(2);//等待40us (等待时间已改短)
temp=0;//时间为26us-28us表示接收的为数据'0'
if(DHT11_DQ==1)
temp=1; //如果26us-28us之后还为高电平则表示接收的数据为'1'
// while(DHT11_DQ);//等待数据信号高电平'0'为26us-28us'1'为70us
while (DHT11_DQ&&retry<100)//等待数据信号高电平'0'为26us-28us'1'为70us
{
retry++;
_nop_();
};
data_byte<<=1;//接收的数据为高位在前右移
data_byte|=temp;
}
return data_byte;
}
//从DHT11读取一次数据
//temp:温度值(范围:0~50°)
//humi:湿度值(范围:20%~90%)
//返回值:0,正常;1,读取失败
U8 DHT11_Read_Data(U8 *temp,U8 *humi)
{
U8 buf[5];
U8 i;
DHT11_Rst();
if(DHT11_Check()==0)
{
for(i=0;i<5;i++)//读取40位数据
{
buf[i]=DHT11_Read_Byte();
}
if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4])
{
*humi=buf[0];
*temp=buf[2];
}
}else return 1;
return 0;
}
void WSDXS() //温湿度显示
{
DHT11_Read_Data(&U8T_data_H,&U8RH_data_H);
str_T[0]=U8T_data_H%100/10+0x30; // 整数十位
str_T[1]=U8T_data_H%10+0x30; // 整数个位
str_T[2] = '\0';
str_H[0]=U8RH_data_H%100/10+0x30; // 整数十位
str_H[1]=U8RH_data_H%10+0x30; // 整数个位
str_H[2]= '\0';
LcdShowStr(2, 0, str_T); //显示到液晶屏上
LcdShowStr(2, 1, str_H); //显示到液晶屏上
}
3.ADC0832.c文件
#include "ADC0832.h"
/****************************************************************************
函数功能:ADC0832转换子程序(模块VCC:5V)
入口参数:CH通道选择
出口参数:dat
****************************************************************************/
U8 adc0832(U8 CH)
{
U8 i,test,adval;
adval = 0x00;
test = 0x00;
Clk = 0; //初始化
DATI = 1;
_nop_();
CS = 0;
_nop_();
Clk = 1;
_nop_();
if ( CH == 0x00 ) //通道选择
{
Clk = 0;
DATI = 1; //通道0的第一位
_nop_();
Clk = 1;
_nop_();
Clk = 0;
DATI = 0; //通道0的第二位
_nop_();
Clk = 1;
_nop_();
}
else
{
Clk = 0;
DATI = 1; //通道1的第一位
_nop_();
Clk = 1;
_nop_();
Clk = 0;
DATI = 1; //通道1的第二位
_nop_();
Clk = 1;
_nop_();
}
Clk = 0;
DATI = 1;
for( i = 0;i < 8;i++ ) //读取前8位的值
{
_nop_();
adval <<= 1;
Clk = 1;
_nop_();
Clk = 0;
if (DATO)
adval |= 0x01;
else
adval |= 0x00;
}
for (i = 0; i < 8; i++) //读取后8位的值
{
test >>= 1;
if (DATO)
test |= 0x80;
else
test |= 0x00;
_nop_();
Clk = 1;
_nop_();
Clk = 0;
}
if(adval == test) //比较前8位与后8位的值,如果不相同舍去。若一直出现显示为零,请将该行去掉
dat = test;
nop_();
CS = 1; //释放ADC0832
DATO = 1;
Clk = 1;
return dat;
}
/* 整型数转换为字符串,str-字符串指针,dat-待转换数,返回值-字符串长度 */
U8 IntToString(U8 *str, int dat)
{
signed char i = 0;
U8 len = 0;
U8 buf[6];
if (dat < 0) //如果为负数,首先
{
dat = -dat;
*str++ = '-';
len++;
}
do { //先转换为低位在前的十进制数组
buf[i++] = dat % 10;
dat /= 10;
} while (dat > 0);
len += i; //i最后的值就是有效字符的个数
while (i-- > 0) //将数组值转换为ASCII码反向拷贝到接收指针上
{
*str++ = buf[i] + '0';
}
*str = '\0'; //添加字符串结束符
return len; //返回字符串长度
}
KEY.C文件
#include "KEY.h"
/**************
按键扫描函数
*************/
void Key_set_scan()
{
if(quxiao==0)
{
delay_ms(20);
if(quxiao==0)
{
while(!quxiao);
qx_flag =~ qx_flag;
}
}
if(shezhi==0)
{
delay_ms(20);
if(shezhi==0)
{
while(!shezhi);
LcdWriteCmd(0x01); //清屏
LcdWriteCmd(0x01); //清屏
moshi++;
if(moshi >= 5)moshi = 0;
if(moshi == 0)
{
LcdWriteCmd(0x01); //清屏
delay_ms(10);
LcdShowStr(0, 0,"T: C");
LcdShowStr(0, 1,"H: %RH ");
LcdShowStr(6, 0,"P: ug/m3");
LcdShowStr(8, 1,"C: ppm");
}
else if(moshi == 1)
{
LcdWriteCmd(0x01);LcdWriteCmd(0x01);LcdWriteCmd(0x01); //清屏
LcdWriteCmd(0x01);LcdWriteCmd(0x01);LcdWriteCmd(0x01);
LcdShowStr(0, 1,"Set_T: C ");
}
else if(moshi == 2)
{ LcdWriteCmd(0x01); //清屏
delay_ms(10);
LcdShowStr(0, 0," ");
LcdShowStr(0, 1,"Set_H: %RH ");
}
else if(moshi == 3)
{ LcdWriteCmd(0x01); //清屏
delay_ms(10);
LcdShowStr(0, 0," ");
LcdShowStr(0, 1,"Set_P: ug/m3 ");
}
else if(moshi == 4)
{ LcdWriteCmd(0x01); //清屏
delay_ms(10);
LcdShowStr(0, 0," ");
LcdShowStr(0, 1,"Set_C: ppm ");
}
}
}
if(jia==0)
{
delay_ms(100);
if(jia==0)
{
if(moshi==1)
{
T_baojing++ ;
if( T_baojing>=99 )T_baojing =99;
}
if(moshi==2)
{
H_baojing++ ;
if( H_baojing>=99 )H_baojing =99;
}
if(moshi==3)
{
P_baojing++ ;
if( P_baojing>=999 )P_baojing =999;
}
if(moshi==4)
{
C_baojing++ ;
if( C_baojing>=999 )C_baojing =999;
}
}
}
if(jian == 0)
{
delay_ms(100);
if(jian == 0)
{
if(moshi==1)
{
T_baojing-- ;
if( T_baojing<=0 )T_baojing =0;
}
if(moshi==2)
{
H_baojing-- ;
if( H_baojing<=0 )H_baojing =0;
}
if(moshi==3)
{
P_baojing-- ;
if( P_baojing<=0 )P_baojing =0;
}
if(moshi==4)
{
C_baojing-- ;
if( C_baojing<=0 )C_baojing =0;
}
}
}
}
定时器配置,开启定时器,用于PM2.5模块中LED输入脉冲引脚
time.c文件
#include "time.h"
/*------------------------------------------------
定时器初始化子程序
------------------------------------------------*/
void Init_Timer0(void)
{
TMOD |= 0x01; //使用模式1,16位定时器,使用"|"符号可以在使用多个定时器时不受影响
TH0=(65536-3000)/256; //重新赋值 30ms
TL0=(65536-3000)%256;
EA=1; //总中断打开
ET0=1; //定时器中断打开
TR0=1; //定时器开关打开
PT0=1; //优先级打开
}
/*------------------------------------------------
定时器中断子程序(定时1ms)
------------------------------------------------*/
void Timer0_isr(void) interrupt 1
{
TH0=(65536-3000)/256; //重新赋值 10ms
TL0=(65536-3000)%256;
PM_LED=~PM_LED;//通过取反产生脉冲
}
最后就是main函数整体编译
main.c
#include <reg52.h>
#include <intrins.h>
#include "DHT11.h"
#include "KEY.h"
#include "LCD1602.h"
#include "ADC0832.h"
#include "time.h"
U8 str_T[6]; //温度值
U8 str_H[8]; //湿度值
U8 str_pm[5]; //pm2.5值
U8 str_co[5]; //有害气体浓度值
U8 T_baojing = 40; //温度报警值
U8 H_baojing = 80; //湿度报警值
U16 P_baojing = 80; //PM2.5报警值
U16 C_baojing = 100;//有害气体报警值
U8 T_buff[3]; //报警缓存
U8 H_buff[3]; //报警缓存
U8 P_buff[4]; //报警缓存
U8 C_buff[4]; //报警缓存
U8 moshi=0; //模式0~4
U8 data1;
float Value1,Value2; //AD采集转换后变量
U8 dat = 0x00; //AD值
bit qx_flag = 1; //报警开关 默认开
sbit LED_H = P1^4;// 温湿度报警指示灯
sbit LED_P = P1^5;// PM2.5报警指示灯
sbit LED_C = P1^6;// CO报警指示灯
sbit BUZZER = P2^2;// 蜂鸣器信号脚
sbit fengshan = P2^5;// 风扇信号脚
unsigned int sum=0;
U8 U8FLAG,k;
U8 U8count,U8temp;
U8 U8comdata;
U8 U8T_data_H=0;//温度值
U8 U8RH_data_H=0;//湿度值
/************主函数****************/
void main()
{
U16 len;//长度
U8 m;
Init_Timer0(); //定时器初始化
InitLcd1602(); //初始化液晶
while(DHT11_Init()) //检测DHT11是否存在
{
LcdShowStr(0,0,"Error");//显示提示报错信息
}
//显示提示字符信息
LcdShowStr(0, 0,"T: C");
LcdShowStr(0, 1,"H: %RH");
LcdShowStr(6, 0,"P: ug/m3");
LcdShowStr(8, 1,"C: ppm");
while(1)
{
Key_set_scan();//按键扫描
if(moshi == 0) //设置模式切换
{
WSDXS(); //温湿度显示
for(m=0;m<50;m++)
{
Value1 = adc0832(0); //PM2.5检测
delay_ms(4);
sum=sum+Value1;
}
Value1=sum/50;
sum=0;
Value1 = Value1*(float)(Value1/5);//浓度校准
if(Value1>999)Value1=999;
len = IntToString(str_pm,Value1); //转换成字符串
while (len < 3) //用空格补齐到5个字符长度
{
str_pm[len++] = ' ';
}
str_pm[len] = '\0'; //添加字符串结束符
LcdShowStr(8, 0, str_pm); //烟雾浓度显示到液晶屏上
Value2 = adc0832(1); //CO检测
Value2 = (float)(Value2*1.5);//浓度校准
len = IntToString(str_co,Value2); //转换成字符串
while (len < 3) //用空格补齐到3个字符长度
{
str_co[len++] = ' ';
}
str_co[len] = '\0'; //添加字符串结束符
LcdShowStr(10, 1, str_co); //烟雾浓度显示到液晶屏上
if(qx_flag == 1) //按键触发
{
if((U8T_data_H>T_baojing)||(U8RH_data_H>H_baojing)||(Value1>=P_baojing)||(Value2>=C_baojing))
{BUZZER = 1;fengshan = 0;}
else{BUZZER = 0;fengshan = 1;}
}
else {BUZZER = 0;fengshan = 1;}
//if(U8T_data_H>T_baojing) LED_T = 0; else LED_T = 1;
if((U8RH_data_H>H_baojing)||(U8T_data_H>T_baojing)) LED_H = 0; else LED_H = 1;
if(Value1>=P_baojing) LED_P = 0; else LED_P = 1;
if(Value2>=C_baojing) LED_C = 0; else LED_C = 1;
}
else if(moshi == 1) //设置温度上限模式
{
T_buff[0] = T_baojing/10+0x30;
T_buff[1] = T_baojing%10+0x30;
T_buff[2] = '\0';
LcdShowStr(6, 1,T_buff);
}
else if(moshi == 2) //设置湿度上限模式
{
H_buff[0] = H_baojing/10+0x30;
H_buff[1] = H_baojing%10+0x30;
H_buff[2] = '\0';
LcdShowStr(6, 1,H_buff);
}
else if(moshi == 3) //设置PM2.5浓度上限模式
{
P_buff[0] = P_baojing/100+0x30;
P_buff[1] = P_baojing%100/10+0x30;
P_buff[2] = P_baojing%10+0x30;
P_buff[3] = '\0';
LcdShowStr(6, 1,P_buff);
}
else if(moshi == 4) //设置CO浓度上限模式
{
C_buff[0] = C_baojing/100+0x30;
C_buff[1] = C_baojing%100/10+0x30;
C_buff[2] = C_baojing%10+0x30;
C_buff[3] = '\0';
LcdShowStr(6, 1,C_buff);
}
}
}
本次设计经过实物验证,功能正常实现,该次设计用于毕业设计,若有不足,希望指正交流学习。
下面是百度网盘资料分享下载链接: