基于单片机设计的防煤气泄漏装置

一、前言

煤气泄漏是一个严重的安全隐患,可能导致火灾、爆炸以及对人体健康的威胁。为了提高家庭和工业环境中煤气泄漏的检测和预防能力,设计了一种基于单片机的防煤气泄漏装置。

单片机选择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转换结果。

cpp 复制代码
#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数据实现代码。

cpp 复制代码
#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 主代码逻辑

cpp 复制代码
#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();
        }
    }
}
相关推荐
智者知已应修善业37 分钟前
【51单片机用数码管显示流水灯的种类是按钮控制数码管加一和流水灯】2022-6-14
c语言·经验分享·笔记·单片机·嵌入式硬件·51单片机
智商偏低7 小时前
单片机之helloworld
单片机·嵌入式硬件
青牛科技-Allen8 小时前
GC3910S:一款高性能双通道直流电机驱动芯片
stm32·单片机·嵌入式硬件·机器人·医疗器械·水泵、
森焱森10 小时前
无人机三轴稳定控制(2)____根据目标俯仰角,实现俯仰稳定化控制,计算出升降舵输出
c语言·单片机·算法·架构·无人机
白鱼不小白10 小时前
stm32 USART串口协议与外设(程序)——江协教程踩坑经验分享
stm32·单片机·嵌入式硬件
S,D11 小时前
MCU引脚的漏电流、灌电流、拉电流区别是什么
驱动开发·stm32·单片机·嵌入式硬件·mcu·物联网·硬件工程
芯岭技术14 小时前
PY32F002A单片机 低成本控制器解决方案,提供多种封装
单片机·嵌入式硬件
youmdt14 小时前
Arduino IDE ESP8266连接0.96寸SSD1306 IIC单色屏显示北京时间
单片机·嵌入式硬件
嘿·嘘14 小时前
第七章 STM32内部FLASH读写
stm32·单片机·嵌入式硬件
Meraki.Zhang14 小时前
【STM32实践篇】:I2C驱动编写
stm32·单片机·iic·驱动·i2c