基于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,发包格式是

ZigBee矿井气体监测系统架构图包括哪些_初始化


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); 
			
		}
	}	
}

ZigBee矿井气体监测系统架构图包括哪些_数据总线_02

ZigBee矿井气体监测系统架构图包括哪些_初始化_03