一、项目介绍
井下瓦斯监控系统是煤矿安全生产中非常重要的一部分,防止井下瓦斯爆炸事故的发生,保障煤矿工人的人身安全。由于地下环境特殊,需要特殊的监测系统来实时监测瓦斯浓度等关键指标,并及时报警以便采取措施进行处理。
瓦斯气体,又称沼气,是一种轻质烃类气体,主要成分是甲烷(CH4),也包含少量的乙烷、丙烷等。它是在地下煤炭层与泥岩等岩石中通过微生物作用或者煤炭化学反应形成的。在煤矿等地下工程中,瓦斯常常是一种具有危险性的气体,如果采取不当的措施,就有可能发生瓦斯爆炸事故。
基于 51 单片机的井下瓦斯监控系统,可以通过传感器检测瓦斯气体浓度,将检测到的数据通过 AD 转换后送入单片机处理,再通过 LCD 显示器显示出来。如果瓦斯浓度超过了预设阈值,系统会自动启动报警装置进行警示。同时,这种系统具有适用面广、成本低、可靠性高等特点。
在目前环保意识提高的背景下,煤炭企业和政府对于井下瓦斯监控系统的需求越来越大,系统的市场潜力巨大。
二、设计原理
2.1 传感器选型
(1)瓦斯气体检测
MQ2 传感器是一种常用于气体检测的半导体传感器,主要用于检测多种易燃、易爆气体,如瓦斯、丙烷、液化气等。它采用了半导体氧化物层敏感元件技术,当检测到目标气体时,其电阻值会发生变化,从而可以通过测量电阻值的变化来检测目标气体的浓度。MQ2 传感器具有灵敏度高、响应速度快、使用方便等特点,因此在气体检测领域广泛应用。
MQ2 传感器包括热敏电阻、电化学传感器、半导体敏感元件等部分,其中半导体敏感元件是其核心部件,也是影响传感器性能的关键因素。在使用前需要进行预热处理,一般预热时间为 1-2 分钟,然后将待测气体与传感器接触,即可读取传感器的输出信号并进行浓度计算。
(2)ADC 采集模块
PCF8591 模块是一种集成了 AD 转换器和 DA 转换器的模块,通过 I2C 总线可以连接到单片机或其他电子设备上,用于模拟信号的输入和输出。其主要特点是集成度高、精度高、使用方便、成本低廉等。
模块由 PCF8591 芯片和相关外围电路组成,其中 PCF8591 芯片是一个具有 4 个模拟输入通道和 1 个模拟输出通道的集成电路,内部集成了 128 级 AD 转换器和 8 位 DA 转换器,并且支持外部基准电压输入。同时,该模块还包括 4 个可变电阻,可以通过调节来改变模拟输入通道的电阻值,从而实现对信号的增益和衰减。通过 I2C 总线,可以方便地读取和输出模拟信号。
在实际应用中,PCF8591 模块广泛用于传感器信号的采集和处理,例如温度、光强、声音等信号的转换和传输。
2.2 设计思路
基于 51 单片机设计的井下瓦斯监控系统的原理如下:
(1)传感器检测瓦斯浓度:使用瓦斯传感器检测井下瓦斯浓度,并将检测结果转换为电信号输出。
(2)单片机采集数据:使用 ADC 模块将传感器输出的电信号转换为数字信号,并将其存储到单片机内部的 RAM 中。
(3)数据处理:单片机通过对采集到的数据进行处理,可以实现瓦斯浓度的实时监测,并根据预设阈值进行报警处理。
(4)报警处理:当瓦斯浓度超过预设阈值时,单片机会触发报警器进行报警。同时,可以通过 OLED 显示屏实时显示瓦斯浓度,并通过蜂鸣器发出警报声音。
(5)数据存储:单片机还可以将采集到的数据存储到外部存储器中,以便后续的数据分析和处理。
基于 51 单片机设计的井下瓦斯监控系统通过传感器检测瓦斯浓度,单片机采集数据并进行处理,实现了对瓦斯浓度的实时监测和报警处理,同时还可以将数据存储到外部存储器中,方便后续的数据分析和处理。
三、代码实现
3.1 采集 MQ2 浓度打印到串口
以下是基于 STC89C52 通过 PCF8591 采集 MQ2 烟雾传感器的值,并转为浓度打印到串口的详细代码。
#include <reg52.h>
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
sbit SCL=P1^0; // I2C时钟线
sbit SDA=P1^1; // I2C数据线
sbit MQ2=P3^2; // MQ2烟雾传感器
// 函数声明
void delay(uint n);
void I2C_Start();
void I2C_Stop();
void I2C_SendACK(bit ack);
bit I2C_RecvACK();
void I2C_SendByte(uchar dat);
uchar I2C_RecvByte();
void PCF8591_Write(uchar addr, uchar dat);
uchar PCF8591_Read(uchar addr);
void InitPCF8591();
uint ReadMQ2();
// 主函数
void main()
{
InitPCF8591(); // 初始化PCF8591
while(1)
{
uint mq2val = ReadMQ2(); // 读取MQ2传感器的值
float mq2con = (mq2val / 255.0) * 100.0; // 将传感器的值转换为浓度
printf("MQ2烟雾浓度:%f%%\n", mq2con); // 打印浓度到串口
delay(1000); // 延时1秒
}
}
// 延时函数
void delay(uint n)
{
uint i, j;
for(i = 0; i < n; i++)
for(j = 0; j < 125; j++);
}
// I2C总线函数
// I2C起始信号
void I2C_Start()
{
SDA = 1;
SCL = 1;
_nop_();
_nop_();
_nop_();
SDA = 0;
_nop_();
_nop_();
_nop_();
SCL = 0;
}
// I2C停止信号
void I2C_Stop()
{
SDA = 0;
SCL = 1;
_nop_();
_nop_();
_nop_();
SDA = 1;
_nop_();
_nop_();
_nop_();
}
// I2C发送应答信号
void I2C_SendACK(bit ack)
{
SDA = ack;
SCL = 1;
_nop_();
_nop_();
_nop_();
SCL = 0;
}
// I2C接收应答信号
bit I2C_RecvACK()
{
SCL = 1;
_nop_();
_nop_();
_nop_();
bit ack = SDA;
SCL = 0;
return ack;
}
// I2C发送一个字节
void I2C_SendByte(uchar dat)
{
uchar i;
for(i = 0; i < 8; i++)
{
SDA = (dat & 0x80) >> 7;
dat <<= 1;
SCL = 1;
_nop_();
_nop_();
_nop_();
SCL = 0;
}
I2C_RecvACK();
}
// I2C接收一个字节
uchar I2C_RecvByte()
{
uchar i, dat = 0;
for(i = 0; i < 8; i++)
{
dat <<= 1;
SCL = 1;
_nop_();
_nop_();
_nop_();
dat |= SDA;
SCL = 0;
}
I2C_SendACK(1);
return dat;
}
// PCF8591函数
// 初始化PCF8591
void InitPCF8591()
{
PCF8591_Write(0x40, 0x00); // 设置PCF8591控制字节,模拟输入通道为0
}
// 向PCF8591写入一个字节
void PCF8591_Write(uchar addr, uchar dat)
{
I2C_Start(); // 发送起始信号
I2C_SendByte(0x90); // 发送设备地址,并写入模式
I2C_RecvACK();
I2C_SendByte(addr); // 发送寄存器地址
I2C_RecvACK();
I2C_SendByte(dat); // 发送数据
I2C_RecvACK();
I2C_Stop(); // 发送停止信号
}
// 从PCF8591读取一个字节
uchar PCF8591_Read(uchar addr)
{
uchar dat;
I2C_Start(); // 发送起始信号
I2C_SendByte(0x90); // 发送设备地址,并写入模式
I2C_RecvACK();
I2C_SendByte(addr); // 发送寄存器地址
I2C_RecvACK();
I2C_Start(); // 发送起始信号
I2C_SendByte(0x91); // 发送设备地址,并读取数据
I2C_RecvACK();
dat = I2C_RecvByte(); // 读取数据
I2C_SendACK(1);
I2C_Stop(); // 发送停止信号
return dat;
}
// 读取MQ2传感器的值
uint ReadMQ2()
{
uchar val = PCF8591_Read(0x40); // 读取PCF8591的模拟输入值
if(MQ2 == 0) // 如果MQ2传感器检测到烟雾
return (uint)(val * 2.55); // 返回模拟输入值的百分比
else
return 0; // 否则返回0
}
复制代码
上面代码里,主要包括了 I2C 总线函数和 PCF8591 函数,用于与 PCF8591 芯片进行通信。其中,InitPCF8591()函数用于初始化 PCF8591 芯片,PCF8591_Write()函数用于向 PCF8591 芯片写入数据,PCF8591_Read()函数用于从 PCF8591 芯片读取数据。另外,ReadMQ2()函数用于读取 MQ2 传感器的值,并将其转换为浓度值。最后,在主函数中,通过调用 ReadMQ2()函数读取 MQ2 传感器的值,并将其转换为浓度值,然后通过 printf()函数将浓度值打印到串口。
3.2 采用烟雾浓度显示到 OLED
下面代码是 STC89C52 通过 PCF8591 采集 MQ2 烟雾传感器的值,并转为浓度显示到 IIC 接口的 OLED 显示屏上。
#include <reg52.h>
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
typedef enum { false = 0, true = !false } bool;
sbit SCL=P3^6; //IIC总线时钟引脚定义
sbit SDA=P3^7; //IIC总线数据引脚定义
sbit LED = P1^0; // 测试用LED灯,可选
uchar code tabel[]="Smog: "; //OLED屏幕显示内容
uchar code dis[]="%";
/*------------------函数声明------------------*/
void delayms(uint); //毫秒延时函数
void IIC_Start(); //IIC起始信号
void IIC_Stop(); //IIC停止信号
void IIC_Send_Byte(uchar); //发送一个字节
uchar IIC_Read_Byte(bool); //读取一个字节
void LCD_SetPos(uchar,uchar); //设置位置
void LCD_WriteCmd(uchar); //写命令
void LCD_WriteData(uchar); //写数据
void LCD_Init(); //初始化
void MQ2_Init(); //MQ2传感器初始化
int MQ2_Read(); //读取MQ2传感器的值并返回浓度值
/*------------------主函数------------------*/
void main()
{
uchar i=0,j=0,k=0;
int result = 0; //存储MQ2传感器读取的值
uchar buf[5] = {0}; //存储MQ2浓度值字符串
uchar MQ2_data[10]={0}; //存储OLED屏幕显示数据
EA = 1; //开放总中断
IIC_Init();
LCD_Init();
MQ2_Init();
while(1)
{
result = MQ2_Read(); //读取MQ2传感器的值
if(result >= 0) //读取成功
{
itoa(result,buf,10); //将读取结果转为字符串格式
for(i=0;i<6;i++) //将OLED显示内容清空,准备写入新数据
MQ2_data[i]=0;
for(i=0;i<6;i++) //拼接OLED显示内容
{
if(tabel[i]!=0) //判断是否有显示内容
MQ2_data[i]=tabel[i];
else
break;
}
j=0; //记录MQ2字符串长度
while(buf[j]!=0 && i+j<7) //拼接MQ2浓度值字符串,最多显示5位数
{
MQ2_data[i+j] = buf[j];
j++;
}
if(j<5) //MQ2浓度值不足5位数字,换行再拼接“%”字符
{
MQ2_data[i+j] = '\n';
MQ2_data[i+j+1] = dis[0];
}
for(k=0;k<i+j+2;k++) //将OLED屏幕显示MQ2浓度值
{
LCD_WriteData(MQ2_data[k]);
}
delayms(1000); //延时1秒(可根据实际需求调整)
}
}
}
/*------------------IIC总线控制函数------------------*/
void IIC_Init()
{
SCL = 1; //初始化,总线空闲状态时SCL和SDA都为高电平
SDA = 1;
}
void IIC_Start()
{
SDA = 1;
_nop_();
SCL = 1;
_nop_();
SDA = 0; //起始信号的形成:在SCL高电平期间,SDA从高电平转为低电平
_nop_();
SCL = 0;
}
void IIC_Stop()
{
SDA = 0;
_nop_();
SCL = 1;
_nop_();
SDA = 1; //停止信号的形成:在SCL高电平期间,SDA从低电平转到高电平
_nop_();
}
void IIC_Send_Byte(uchar dat)
{
uchar i;
for(i=0;i<8;i++)
{
SDA = dat & 0x80;
dat <<= 1;
SCL = 1;
_nop_();
SCL = 0;
_nop_();
}
}
uchar IIC_Read_Byte(bool ack)
{
uchar i,dat=0;
for(i=0;i<8;i++)
{
SCL = 1;
_nop_();
dat <<= 1;
dat |= SDA;
SCL = 0;
_nop_();
}
if(ack)
SDA = 0; //发送ACK
else
SDA = 1; //不发送ACK
SCL = 1;
_nop_();
SCL = 0;
_nop_();
SDA = 1;
return dat;
}
/*------------------OLED屏幕控制函数------------------*/
void LCD_SetPos(uchar x,uchar y)
{
LCD_WriteCmd(0xb0+y);
LCD_WriteCmd(((x&0xf0)>>4)|0x10);
LCD_WriteCmd((x&0x0f)|0x00);
}
void LCD_WriteCmd(uchar cmd)
{
IIC_Start();
IIC_Send_Byte(0x78);
IIC_Send_Byte(0x00); //写命令
IIC_Send_Byte(cmd);
IIC_Stop();
}
void LCD_WriteData(uchar dat)
{
IIC_Start();
IIC_Send_Byte(0x78);
IIC_Send_Byte(0x40); //写数据
IIC_Send_Byte(dat);
IIC_Stop();
}
void LCD_Init()
{
LCD_WriteCmd(0xae);
LCD_WriteCmd(0x00);
LCD_WriteCmd(0x10);
LCD_WriteCmd(0x40);
LCD_WriteCmd(0xb0);
LCD_WriteCmd(0x81);
LCD_WriteCmd(0xcf);
LCD_WriteCmd(0xa1);
LCD_WriteCmd(0xa6);
LCD_WriteCmd(0xa8);
LCD_WriteCmd(0x3f);
LCD_WriteCmd(0xc8);
LCD_WriteCmd(0xd3);
LCD_WriteCmd(0x00);
LCD_WriteCmd(0xd5);
LCD_WriteCmd(0x80);
LCD_WriteCmd(0xd9);
LCD_WriteCmd(0xf1);
LCD_WriteCmd(0xda);
LCD_WriteCmd(0x12);
LCD_WriteCmd(0xdb);
LCD_WriteCmd(0x40);
LCD_WriteCmd(0x20);
LCD_WriteCmd(0x02);
LCD_WriteCmd(0xaf);
LCD_WriteCmd(0xff);
}
/*------------------MQ2传感器控制函数------------------*/
void MQ2_Init()
{
IIC_Start();
IIC_Send_Byte(0x90); //写入设备地址 1001A2A1A0(0) R/W = 0(PCF8591 写操作)
IIC_Send_Byte(0x40); //写入控制字节,选择通道0,并开启模拟转换器
IIC_Stop();
}
int MQ2_Read()
{
int result=0;
uchar buf[10]={0};
IIC_Start();
IIC_Send_Byte(0x90); //写入设备地址
IIC_Send_Byte(0x41); //读取数据
result = IIC_Read_Byte(true)*256; //读取高位数据
result += IIC_Read_Byte(true); //读取低位数据
IIC_Stop();
if(result < 0)
return -1; //读取失败
else
return result; //返回读取的值
}
/*------------------辅助函数------------------*/
void delayms(uint n)
{
uint i, j;
for(i=0;i<n;i++)
for(j=0;j<114;j++);
}
复制代码
评论