一、前言
煤气泄漏是一个严重的安全隐患,可能导致火灾、爆炸以及对人体健康的威胁。为了提高家庭和工业环境中煤气泄漏的检测和预防能力,设计了一种基于单片机的防煤气泄漏装置。
单片机选择STC89C52作为主控芯片。为了检测煤气泄漏,采用了MQ4传感器,能够快速、准确地检测煤气浓度。通过采集MQ4传感器的模拟信号,使用PCF8591模数转换芯片将模拟信号转换为数字信号。采用IIC接口的OLED显示屏,将采集到的数据显示出来,方便用户获取检测结果。用户可以通过两个独立按键设置煤气泄漏的报警阈值,以适应不同环境的需求。
当检测到煤气泄漏超过设定的阈值时,装置会触发蜂鸣器进行报警,并同时打开换气扇进行通风换气,以迅速排除煤气并降低安全风险。
这种基于单片机设计的防煤气泄漏装置具有以下优点:高效可靠的煤气泄漏检测能力、灵活的报警阈值设置、直观清晰的数据显示以及及时的安全响应措施。可以广泛应用于家庭、工业和商业场所,提供有效的煤气泄漏监测和安全保护。
二、硬件选型
在设计基于单片机的防煤气泄漏装置时,硬件选型是非常关键的。以下是详细介绍硬件选型的相关内容:
【1】主控芯片选择:STC89C52 STC89C52是一款8051架构的单片机,具有丰富的接口资源、较高的性能和稳定可靠的工作特性,广泛应用于各种嵌入式系统中。具有8位数据总线、16位地址总线和4KB的内部存储器。STC89C52具备多个通用I/O口、定时器/计数器、串口等功能,非常适合本项目需求。
【2】煤气传感器选择:MQ4 MQ4传感器是一种能够检测多种可燃气体,如天然气、甲烷等的传感器。具有高灵敏度和快速响应的特点,能够准确地检测煤气泄漏情况。MQ4传感器的输出为模拟信号,需要通过模数转换器将其转换为数字信号供主控芯片处理。
【3】模数转换器选择:PCF8591 PCF8591是一款集成了8位模数/数模转换和4个模拟输入通道的模数转换器。采用IIC总线通讯接口,能够将模拟信号转换为数字信号,并通过IIC协议发送给主控芯片。本项目中,PCF8591用于采集MQ4传感器输出的模拟信号,并将其转换为数字信号供STC89C52处理。
【4】显示屏选择:0.96寸OLED显示屏(IIC接口) 本设计采用基于IIC接口的OLED显示屏,具有高亮度、对比度和快速响应的特点。通过简单的通讯方式,可以将煤气浓度信息实时显示在屏幕上。OLED显示屏使用面积小、功耗低,在嵌入式系统中应用广泛。
【5】按键选择:独立按键 本设计采用两个独立按键来设置报警的阀值。一个按键用于递增阀值,另一个按键用于递减阀值。独立按键具有简单可靠、使用方便等特点,适合本项目需求。
【6】报警装置选择:蜂鸣器和换气扇 当检测到煤气泄漏超过设定的报警阀值时,蜂鸣器将发出警报,用于提醒周围人员。同时,为了降低煤气浓度,需要启动换气扇进行通风换气。具体的报警和换气扇电路可以根据实际需求设计。
三、设计思路
软件设计思路如下:
【1】初始化:在程序开始时,进行主控芯片STC89C52的初始化设置,包括引脚配置、定时器设置等。同时,初始化PCF8591和OLED显示屏,确保它们可以正常工作。
【2】传感器检测:通过MQ4传感器检测煤气是否泄漏。将MQ4传感器与STC89C52的模拟输入引脚连接,通过读取该引脚的模拟电压值,获取煤气浓度数据。
【3】数据采集与处理:使用PCF8591模数转换芯片,将MQ4传感器的模拟输出信号转换为数字信号,并通过STC89C52的IIC接口与PCF8591进行通信,获取转换后的数字数据。
【4】数据显示:将采集到的煤气浓度数据通过IIC接口的OLED显示屏进行显示。使用STC89C52的IIC通信功能,将数据发送给OLED显示屏,通过显示屏将数据以可读的方式展示给用户。
【5】阈值设置:通过两个独立按键实现报警阈值的设置。将按键与STC89C52的GPIO引脚连接,通过读取按键状态来判断用户是否进行阈值设置操作。当按键按下时,进入设置模式,用户可以通过按键的不同组合来调整报警阈值,并将设置的值保存在相应的变量中。
【6】报警与通风控制:根据当前采集到的煤气浓度数据和用户设置的报警阈值进行比较。如果煤气浓度超过设定的阈值,触发蜂鸣器进行报警,并控制换气扇打开进行通风换气。反之,当煤气浓度低于或等于设定的阈值时,停止报警并关闭换气扇。
【7】循环监测:使用主循环结构,不断进行煤气泄漏检测、数据采集、数据显示和阈值比较等操作,以实现持续的监测和反馈。
四、项目模块代码
4.1 PCF8591采集代码
下面是使用STC89C52单片机通过PCF8591读取MQ4传感器的ADC数据的代码。使用IIC总线进行PCF8591之间的通信,使用了自定义的IIC总线函数。通过readADC()
函数实现了读取MQ4传感器模拟量的ADC转换结果。
#include <reg52.h>
#define uchar unsigned char
#define uint unsigned int
sbit SDA = P2^0; // IIC总线数据线
sbit SCL = P2^1; // IIC总线时钟线
sbit MQ4_DOUT = P3^0; // MQ4传感器数字输出引脚
sbit MQ4_AIN = P3^1; // MQ4传感器模拟输入引脚
sfr IAP_DATA = 0xe2; // 定义IAP_DATA寄存器
sfr IAP_ADDRH = 0xe3; // 定义IAP_ADDRH寄存器
sfr IAP_ADDRL = 0xe4; // 定义IAP_ADDRL寄存器
sfr IAP_CMD = 0xe5; // 定义IAP_CMD寄存器
sfr IAP_TRIG = 0xe6; // 定义IAP_TRIG寄存器
sfr IAP_CONTR = 0xe7; // 定义IAP_CONTR寄存器
// 函数声明
void delay(uint ms);
void startIIC();
void stopIIC();
void sendByte(uchar dat);
uchar receiveByte();
void writeDAC(uchar dat);
uchar readADC();
void main() {
uchar mq4Value;
while (1) {
mq4Value = readADC(); // 读取ADC转换结果
// 处理mq4Value值,进行相应操作
delay(100); // 延时一段时间
}
}
// 延时函数
void delay(uint ms) {
uint i, j;
for(i = ms; i > 0; i--) {
for(j = 110; j > 0; j--);
}
}
// IIC总线起始信号
void startIIC() {
SDA = 1;
_nop_();
SCL = 1;
_nop_();
SDA = 0;
_nop_();
SCL = 0;
_nop_();
}
// IIC总线停止信号
void stopIIC() {
SDA = 0;
_nop_();
SCL = 1;
_nop_();
SDA = 1;
_nop_();
}
// 发送一个字节的数据
void sendByte(uchar dat) {
uchar i;
for (i = 0; i < 8; i++) {
SDA = dat >> 7;
_nop_();
SCL = 1;
_nop_();
SCL = 0;
_nop_();
dat <<= 1;
}
SDA = 1;
_nop_();
SCL = 1;
_nop_();
SCL = 0;
_nop_();
}
// 接收一个字节的数据
uchar receiveByte() {
uchar i, dat = 0;
SDA = 1;
for (i = 0; i < 8; i++) {
dat <<= 1;
SCL = 1;
_nop_();
dat |= SDA;
SCL = 0;
_nop_();
}
return dat;
}
// 写入DAC数值
void writeDAC(uchar dat) {
startIIC();
sendByte(0x90); // 地址和写命令
receiveByte(); // 接收应答
sendByte(0x40); // DAC通道A,并写入数据
receiveByte(); // 接收应答
sendByte(dat); // DAC数据
receiveByte(); // 接收应答
stopIIC();
}
// 读取ADC转换结果
uchar readADC() {
uchar adcValue;
startIIC();
sendByte(0x91); // 地址和读命令
receiveByte(); // 接收应答
adcValue = receiveByte(); // 读取ADC数据
stopIIC();
return adcValue;
}
4.2 OLED显示屏代码
以下是通过STC89C52控制IIC接口的OLED显示屏显示ADC数据实现代码。
#include <reg51.h>
#define SCL P1_0 // IIC时钟线
#define SDA P1_1 // IIC数据线
#define OLED_ADDR 0x78 // OLED显示屏的IIC地址
// OLED显示屏初始化函数
void OLED_Init() {
// 初始化OLED显示屏
// ...
// 发送初始化命令到OLED显示屏
// ...
}
// IIC总线开始信号
void IIC_Start() {
SDA = 1;
SCL = 1;
SDA = 0;
SCL = 0;
}
// IIC总线停止信号
void IIC_Stop() {
SDA = 0;
SCL = 1;
SDA = 1;
}
// IIC总线发送一个字节的数据
void IIC_WriteByte(unsigned char dat) {
unsigned char i;
for (i = 0; i < 8; i++) {
SCL = 0;
if (dat & 0x80)
SDA = 1;
else
SDA = 0;
SCL = 1;
dat <<= 1;
}
SCL = 0;
SDA = 1;
SCL = 1;
}
// 设置OLED显示屏光标位置
void OLED_SetPos(unsigned char x, unsigned char y) {
IIC_Start();
IIC_WriteByte(OLED_ADDR);
IIC_WriteByte(0xb0 + y);
IIC_WriteByte(((x & 0xf0) >> 4) | 0x10);
IIC_WriteByte((x & 0x0f) | 0x01);
IIC_Stop();
}
// 在OLED显示屏上显示一个字符
void OLED_ShowChar(unsigned char x, unsigned char y, unsigned char ch) {
unsigned char c = 0, i = 0;
c = ch - ' '; // 获取字符在字库中的偏移量
if (x > 128 - 8 || y > 64 - 16)
return; // 超出屏幕范围,退出函数
OLED_SetPos(x, y);
for (i = 0; i < 8; i++) {
IIC_Start();
IIC_WriteByte(OLED_ADDR);
IIC_WriteByte(0x40);
IIC_WriteByte(*(OLED_CharSet + c * 16 + i));
IIC_Stop();
x++;
}
}
// 在OLED显示屏上显示字符串
void OLED_ShowString(unsigned char x, unsigned char y, unsigned char *str) {
while (*str) {
OLED_ShowChar(x, y, *str);
x += 8;
str++;
}
}
// ADC模拟数值转换为字符串
void ADC_ToString(unsigned int adcValue, unsigned char *str) {
unsigned char i, j;
unsigned int temp;
temp = adcValue;
for (i = 0; i < 4; i++) {
str[i] = temp % 10 + '0';
temp /= 10;
}
// 反转字符串
i = 0;
j = 3;
while (i < j) {
temp = str[i];
str[i] = str[j];
str[j] = temp;
i++;
j--;
}
str[4] = '\0'; // 字符串结束符
}
// 主函数
void main() {
unsigned int adcValue = 0;
unsigned char str[5];
// 初始化OLED显示屏
OLED_Init();
while (1) {
// 读取ADC数据
adcValue = ADC_Read(); // 假设使用的函数为ADC_Read(),用于读取ADC数据
// 将ADC数据转换为字符串
ADC_ToString(adcValue, str);
// 在OLED显示屏上显示ADC数据
OLED_ShowString(0, 0, "ADC Value:");
OLED_ShowString(0, 2, str);
}
}
4.3 主代码逻辑
#include <reg51.h>
#define SCL P1_0 // IIC时钟线
#define SDA P1_1 // IIC数据线
#define OLED_ADDR 0x78 // OLED显示屏的IIC地址
#define MQ4_PIN P2 // MQ4传感器连接的引脚
sbit Buzzer = P3^0; // 蜂鸣器控制引脚
sbit VentilationFan = P3^1; // 换气扇控制引脚
// 全局变量
unsigned int alarmThreshold = 100; // 报警阈值,默认为100
unsigned int adcValue = 0; // 保存ADC采集到的数值
// IIC总线开始信号
void IIC_Start() {
SDA = 1;
SCL = 1;
SDA = 0;
SCL = 0;
}
// IIC总线停止信号
void IIC_Stop() {
SDA = 0;
SCL = 1;
SDA = 1;
}
// IIC总线发送一个字节的数据
void IIC_WriteByte(unsigned char dat) {
unsigned char i;
for (i = 0; i < 8; i++) {
SCL = 0;
if (dat & 0x80)
SDA = 1;
else
SDA = 0;
SCL = 1;
dat <<= 1;
}
SCL = 0;
SDA = 1;
SCL = 1;
}
// 设置OLED显示屏光标位置
void OLED_SetPos(unsigned char x, unsigned char y) {
IIC_Start();
IIC_WriteByte(OLED_ADDR);
IIC_WriteByte(0xb0 + y);
IIC_WriteByte(((x & 0xf0) >> 4) | 0x10);
IIC_WriteByte((x & 0x0f) | 0x01);
IIC_Stop();
}
// 在OLED显示屏上显示一个字符
void OLED_ShowChar(unsigned char x, unsigned char y, unsigned char ch) {
unsigned char c = 0, i = 0;
c = ch - ' '; // 获取字符在字库中的偏移量
if (x > 128 - 8 || y > 64 - 16)
return; // 超出屏幕范围,退出函数
OLED_SetPos(x, y);
for (i = 0; i < 8; i++) {
IIC_Start();
IIC_WriteByte(OLED_ADDR);
IIC_WriteByte(0x40);
IIC_WriteByte(*(OLED_CharSet + c * 16 + i));
IIC_Stop();
x++;
}
}
// 在OLED显示屏上显示字符串
void OLED_ShowString(unsigned char x, unsigned char y, unsigned char *str) {
while (*str) {
OLED_ShowChar(x, y, *str);
x += 8;
str++;
}
}
// ADC转换函数
unsigned int ADC_Convert(unsigned char channel) {
ADC_CONTR = ADC_POWER | ADC_START | channel | ADC_SPEED;
_nop_();
_nop_();
_nop_();
_nop_();
while (!(ADC_CONTR & ADC_FLAG))
;
ADC_CONTR &= ~ADC_FLAG;
return (ADC_RES * 256 + ADC_RESL);
}
// ADC模拟数值转换为字符串
void ADC_ToString(unsigned int adcValue, unsigned char *str) {
unsigned char i, j;
unsigned int temp;
temp = adcValue;
for (i = 0; i < 4; i++) {
str[i] = temp % 10 + '0';
temp /= 10;
}
// 反转字符串
i = 0;
j = 3;
while (i < j) {
temp = str[i];
str[i] = str[j];
str[j] = temp;
i++;
j--;
}
str[4] = '\0'; // 字符串结束符
}
主函数
void main() {
unsigned char str[5];
// 初始化OLED显示屏
OLED_Init();
// 设置初始报警阈值
SetAlarmThreshold();
while (1) {
// 读取MQ4传感器的ADC数值
adcValue = ADC_Convert(0); // 假设MQ4传感器连接到ADC的通道0
// 将ADC数值转换为字符串
ADC_ToString(adcValue, str);
// 在OLED显示屏上显示ADC数值
OLED_ShowString(0, 0, "Gas Level:");
OLED_ShowString(0, 2, str);
// 判断是否触发报警
if (adcValue > alarmThreshold) {
// 触发报警
ActivateAlarm();
} else {
// 取消报警
DeactivateAlarm();
}
// 检测是否按下设置阈值的按键
if (IsThresholdButtonPressed()) {
// 设置报警阈值
SetAlarmThreshold();
}
// 检测是否按下通风换气的按键
if (IsVentilationButtonPressed()) {
// 控制通风换气
ControlVentilation();
}
}
}