基于at89c52的矿井空气检测仪
主要功能
1.主控使用at89c52
2.ZPH01检测矿井粉尘浓度
3.MQ-4检测可燃气体浓度
4.通过1602打印出粉尘浓度和可燃气体浓度
5.使用独立按键加减报警阈值
6.使用LED+蜂鸣器来实现声光报警功能
7.使用PCF8591对MQ-4进行AD转换
模块详解
1.ZPH01有两种通讯方式1.PWM模式和2.串口模式
我们这里使用串口模式,将模块的MODE线接地切换到串口模式
波特率为9600,发包格式是
2.MQ-4和PCF8591
MQ-4通过检查空气中的可燃气体浓度来输出对应的电压值,AT89C52中并没有自带的ADC,所以我们用一个PCF8591来实现ADC,PCF8591转换后通过标准的IIC将数据发送给单片机处理
3.报警和阈值
程序中我们分别定义粉尘和可燃气体的报警阈值,可以通过独立按键来进行阈值的修改,当粉尘活可燃气体的浓度到达阈值,蜂鸣器发声警报,同时LED灯闪烁警报
程序
1.iic
void delay()
{
_nop_(); //I2C总线使用时一般都要延时5us左右
_nop_();
}
//================iic==============================================
void init() //初始化总线。将总线都拉高以释放
{
scl=1;
delay();
sda=1;
delay();
}
void start() //启始信号。 时钟信号为高电平期间,数据总线产生下降沿。
{ //为什么要下降沿,且sda先要为1。因为先要保证数据线为空才能工作
sda=1; //先释放数据总线。高电平释放
delay();
scl=1;
delay();
sda=0;
delay();
}
void stop()
{
sda=0; //先要有工作状态才能释放,sda=0时在工作状态
delay();
scl=1;
delay();
sda=1; //释放数据总线
delay();
}
void respons() //应答函数
{
uchar i=0;
scl=1; //每个字节发送完后的第九个时钟信号的开始
delay();
while((sda==1)&&(i<255)) //此处i的定义使用了uchar.只要填一个小于255的就行
i++; //此处的sda是从机的
scl=0; //表示主器件默认从器件已经收到而不再等待。不再等待之后,时钟的高电平过了就是低电平,所以scl=0
//此时第酒个时钟信号结束
}
void writebyte(uchar d) //写一字节,每次左移一位
{
uchar i,temp;
temp=d;
for(i=0;i<8;i++)
{
temp=temp<<1;
scl=0; //数据传输期间要想sda可变,先把时钟拉低。此处要给sda赋值
delay();
sda=CY; //CY为左移移入PSW寄存器中的的CY位。
delay();
scl=1; //sda有数据了。保持数据稳定
delay();
}
scl=0; //此处是写数据,是属于数据传输过程中。只有在时钟信号为低电平期间
delay(); //数据总线才可以变化。
sda=1; //所以要想释放数据总线,就必须先把时钟拉低
delay();
/*此处释放总线写在末尾是因为调用它时,前面有起始函数释放了总线*/
}
uchar readbyte()
{
uchar i,k;
scl=0;
delay();
sda=1;
delay();
/*此处释放总线放在前面是因为一般都是先写后读,保险起见,释放一下总线*/
for(i=0;i<8;i++)
{
scl=1; //一个时钟信号的开始
delay();
k=(k<<1)|sda; //实质是把sda的数据,最先传来的放在最高位,依次往下排
scl=0; //一个时钟信号结束
delay();
}
return k;
}
2.LCD1602.c
#include "lcd.h"
void lcdinit(void)//lcd1602初始化
{
lcdwrcmd(0x38);
lcdwrcmd(0x0c);
lcdwrcmd(0x06);
lcdwrcmd(0x01);
}
void lcdwrcmd(uchar cmd)//命令写入
{
lcdbusy();
RS=0;
RW=0;
DB=cmd;
E=1;
E=0;
}
void lcdwrdat(uchar dat)//数据写入
{
lcdbusy();
RS=1;
RW=0;
DB=dat;
E=1;
E=0;
}
void lcdbusy(void)//lcd1602忙碌判断
{
DB=0xff;
RS=0;
RW=1;
E =1;
while(DB&0x80);
E=0;
}
void lcdshow(uchar x,uchar y)//显示位置
{
uchar addr;
if(1==y)
{
addr=0x00+x;
}
if(2==y)
{
addr=0x40+x;
}
lcdwrcmd(addr+0x80);
}
3.LCD1602.h
#ifndef __LCD_H_
#define __LCD_H_
/**********************************
当使用的是4位数据传输的时候定义,
使用8位取消这个定义
**********************************/
//#define LCD1602_4PINS
/**********************************
包含头文件
**********************************/
#include<reg52.h>
//---重定义关键词---//
#ifndef uchar
#define uchar unsigned char
#endif
#ifndef uint
#define uint unsigned int
#endif
/**********************************
PIN口定义
**********************************/
#define DB P0
sbit E=P2^7;
sbit RW=P2^6;
sbit RS=P2^5;
/**********************************
函数声明
**********************************/
/*在51单片机12MHZ时钟下的延时函数*/
//void Lcd1602_Delay1ms(uint c); //误差 0us
///*LCD1602写入8位命令子函数*/
//void LcdWriteCom(uchar com);
///*LCD1602写入8位数据子函数*/
//void LcdWriteData(uchar dat);
///*LCD1602初始化子程序*/
//void LcdInit();
void lcdinit(void); //初始化
void lcdwrcmd(uchar cmd); //命令写入
void lcdwrdat(uchar dat); //数据写入
void lcdbusy(void); //忙碌判断
void lcdshow(uchar x,uchar y); //坐标
#endif
4.警报
//=====================警报控制函数==============
if((iic_data >= ch4)||(pm25if >= pm25))
{
bee = 0;
fan = 0;
flag = 1;
}
else
{
bee = 1;
fan = 1;
LED = 1;
flag = 0;
}
if(ch4_up_key == 0)
{
delay1ms(5);
if(ch4_up_key == 0)
while(!ch4_up_key);
ch4++;
if(ch4 > 99)
ch4 = 0;
}
if(ch4_down_key == 0)
{
delay1ms(5);
if(ch4_down_key == 0)
while(!ch4_down_key);
ch4--;
if(ch4 < 0)
ch4 = 99;
}
LED闪烁我们有定时器来实现
void Timer0Interrupt(void) interrupt 1 //定时器1ms
{
TH0 = 0x0FC;
TL0 = 0x66;
//add your code here!
if(flag == 1)
{
timer++;
if(timer == 100)
{
timer = 0;
LED = ~LED;
}
}
}
5.ZPH01
lcdinit(); //LCD初始化
SCON = 0x50;
TMOD = 0x20; //Set Timer1 as 8-bit auto reload mode
TH1 = TL1 = -(FOSC/12/32/BAUD); //Set auto-reload vaule
TR1 = 1; //Timer1 start run
ES = 1; //Enable UART interrupt
EA = 1;
bee = 1;
fan = 1;
LED = 1;
init();
//-----中断服务函数---串口接收函数-------
void Uart_Isr() interrupt 4
{
if (RI)
{
RI = 0; //Clear receive interrupt flag
rec[i1++] = SBUF;
if(i1 > 8)
i1 = 0;
// rec[i1++] = SBUF; //show UART data
}
if (TI)
{
TI = 0; //Clear transmit interrupt flag
busy = 0; //Clear transmit busy flag
}
}
6.主函数
void main() //主函数
{
uchar rec1;
uchar rec2;
char ch4 = 50;
int pm25 = 40;
uint iic_data;
uint iic_data1;
uchar i;
uchar pm25if;
lcdinit(); //LCD初始化
SCON = 0x50;
TMOD = 0x20; //Set Timer1 as 8-bit auto reload mode
TH1 = TL1 = -(FOSC/12/32/BAUD); //Set auto-reload vaule
TR1 = 1; //Timer1 start run
ES = 1; //Enable UART interrupt
EA = 1;
bee = 1;
fan = 1;
LED = 1;
init();
TMOD |= 0x01; //定时器初始化
TH0 = 0x0FC;
TL0 = 0x66;
EA = 1;
ET0 = 1;
TR0 = 1;
while(1)
{
//----------显示CH4----PM2.5--------
lcdshow(0,1);
for(i = 0;i<6;i++)
{
lcdwrdat(CH4[i]);
}
lcdshow(0,2);
for(i = 0;i<6;i++)
{
lcdwrdat(PM25[i]);
}
//------------pm2.5-------------------------
lcdshow(6,2);
lcdwrdat(rec1/10%10+'0');
lcdshow(7,2);
lcdwrdat(rec1%10+'0');
lcdshow(9,2);
lcdwrdat('.');
lcdshow(8,2);
lcdwrdat(rec2/10%10+'0');
lcdshow(10,2);
lcdwrdat(rec2%10+'0');
//------------ch4------------------------
iic_data1 = read(0x40);
iic_data = iic_data1*0.35;
lcdshow(6,1);
lcdwrdat(iic_data/10%10+'0');
lcdshow(7,1);
lcdwrdat(iic_data%10+'0');
lcdshow(8,1);
lcdwrdat('%');
//=====================警报控制函数==============
if((iic_data >= ch4)||(pm25if >= pm25))
{
bee = 0;
fan = 0;
flag = 1;
}
else
{
bee = 1;
fan = 1;
LED = 1;
flag = 0;
}
if(ch4_up_key == 0)
{
delay1ms(5);
if(ch4_up_key == 0)
while(!ch4_up_key);
ch4++;
if(ch4 > 99)
ch4 = 0;
}
if(ch4_down_key == 0)
{
delay1ms(5);
if(ch4_down_key == 0)
while(!ch4_down_key);
ch4--;
if(ch4 < 0)
ch4 = 99;
}
lcdshow(13,1);
lcdwrdat(ch4/10 + '0');
lcdshow(14,1);
lcdwrdat(ch4%10 + '0');
lcdshow(15,1);
lcdwrdat('%');
//=============ch4--完 ==================
//=========PM25==按键控制警报函数========
if(PM25_up_key == 0)
{
delay1ms(5);
if(PM25_up_key == 0)
while(!PM25_up_key);
pm25++;
if(pm25 > 300)
pm25 = 0;
}
if(PM25_down_key == 0)
{
delay1ms(5);
if(PM25_down_key == 0)
while(!PM25_down_key);
pm25--;
if(pm25 < 0)
pm25 = 300;
}
lcdshow(13,2);
lcdwrdat(pm25/100%10 + '0');
lcdshow(14,2);
lcdwrdat(pm25/10%10 + '0');
lcdshow(15,2);
lcdwrdat(pm25%10 + '0');
//========================================
if((rec[0] == 0xff))
{
rec1 = rec[3];
rec2 = rec[4];
pm25if = (rec[3]%10*10)+(rec[4]/10);
}
}
}