STM32F10x MQ-2烟雾传感器驱动程序

一、MQ-2传感器工作原理

1.1 MQ-2特性参数

参数 数值 说明
检测气体 烟雾、液化气、丙烷、氢气、甲烷 多种可燃气体
工作电压 5V DC 加热电压
预热时间 ≥48小时 达到稳定工作状态
响应时间 ≤10秒 传感器响应速度
恢复时间 ≤30秒 传感器恢复时间
负载电阻 10kΩ 典型值

1.2 输出特性

  • 模拟输出:电压随气体浓度变化(0.1V-4.5V)
  • 数字输出:DOUT引脚输出TTL电平(浓度超过阈值时输出低电平)
  • 加热电路:需要5V加热电压,预热后才能稳定工作

二、硬件连接

2.1 STM32F103C8T6与MQ-2连接

复制代码
MQ-2模块引脚    STM32F103引脚    说明
─────────────────────────────────────────────
VCC            5V              5V电源(加热需要)
GND            GND             电源地
AO             PA0             ADC1通道0,模拟输出
DO             PB0             数字输出(阈值报警)
─────────────────────────────────────────────

2.2 电路设计要点

  1. 加热电路:MQ-2需要5V加热电压,不能直接接3.3V
  2. 分压电路:模块内部已有分压电阻,AO输出0-5V
  3. 电平转换:STM32 ADC输入范围0-3.3V,需要分压或电平转换

三、完整驱动代码实现

3.1 头文件(mq2_sensor.h)

c 复制代码
#ifndef __MQ2_SENSOR_H
#define __MQ2_SENSOR_H

#include "stm32f10x.h"

// MQ-2配置参数
#define MQ2_ADC_CHANNEL    ADC_Channel_0    // PA0 - ADC1通道0
#define MQ2_DIGITAL_PORT   GPIOB
#define MQ2_DIGITAL_PIN    GPIO_Pin_0
#define MQ2_ANALOG_PORT    GPIOA
#define MQ2_ANALOG_PIN     GPIO_Pin_0

// 校准参数(根据实际传感器调整)
#define MQ2_CLEAN_AIR_RATIO  9.83    // 洁净空气中的Rs/R0比值
#define MQ2_LOAD_RESISTANCE  10.0     // 负载电阻RL (kΩ)
#define MQ2_VOLTAGE_SUPPLY  5.0      // 电源电压 (V)

// 气体浓度转换系数(根据datasheet调整)
#define MQ2_LPG_COEFF_A     574.25
#define MQ2_LPG_COEFF_B     -2.222
#define MQ2_CO_COEFF_A      36974.0
#define MQ2_CO_COEFF_B      -3.109
#define MQ2_SMOKE_COEFF_A   3616.1
#define MQ2_SMOKE_COEFF_B   -2.675

// 报警阈值
#define MQ2_ALARM_THRESHOLD 1000      // 烟雾浓度报警阈值 (ppm)

// 函数声明
void MQ2_Init(void);
uint16_t MQ2_ReadADC(void);
float MQ2_GetVoltage(void);
float MQ2_GetResistance(void);
float MQ2_GetRatio(void);
float MQ2_GetLPGppm(void);
float MQ2_GetCOppm(void);
float MQ2_GetSmokeppm(void);
uint8_t MQ2_GetDigitalAlarm(void);
void MQ2_CalibrateR0(float clean_air_ratio);
void MQ2_SetAlarmThreshold(uint16_t threshold);
void MQ2_Task(void);

#endif /* __MQ2_SENSOR_H */

3.2 源文件(mq2_sensor.c)

c 复制代码
#include "mq2_sensor.h"
#include "delay.h"
#include "usart.h"

// 全局变量
static float R0 = 10.0;  // 传感器在洁净空气中的电阻值 (kΩ)
static uint16_t alarm_threshold = MQ2_ALARM_THRESHOLD;

/**
  * @brief  MQ-2传感器初始化
  * @param  无
  * @retval 无
  */
void MQ2_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    ADC_InitTypeDef ADC_InitStructure;
    
    // 1. 使能时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | 
                          RCC_APB2Periph_ADC1, ENABLE);
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);  // ADC时钟分频
    
    // 2. 配置模拟输入引脚 (PA0)
    GPIO_InitStructure.GPIO_Pin = MQ2_ANALOG_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;  // 模拟输入
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(MQ2_ANALOG_PORT, &GPIO_InitStructure);
    
    // 3. 配置数字输出引脚 (PB0)
    GPIO_InitStructure.GPIO_Pin = MQ2_DIGITAL_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;  // 上拉输入
    GPIO_Init(MQ2_DIGITAL_PORT, &GPIO_InitStructure);
    
    // 4. ADC参数配置
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;  // 独立模式
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;         // 单通道
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;    // 单次转换
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // 软件触发
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; // 右对齐
    ADC_InitStructure.ADC_NbrOfChannel = 1;              // 1个通道
    ADC_Init(ADC1, &ADC_InitStructure);
    
    // 5. 配置ADC通道
    ADC_RegularChannelConfig(ADC1, MQ2_ADC_CHANNEL, 1, ADC_SampleTime_239Cycles5);
    
    // 6. 使能ADC
    ADC_Cmd(ADC1, ENABLE);
    
    // 7. ADC校准
    ADC_ResetCalibration(ADC1);
    while(ADC_GetResetCalibrationStatus(ADC1));
    ADC_StartCalibration(ADC1);
    while(ADC_GetCalibrationStatus(ADC1));
    
    printf("MQ-2传感器初始化完成\r\n");
}

/**
  * @brief  读取ADC值
  * @param  无
  * @retval ADC转换结果 (0-4095)
  */
uint16_t MQ2_ReadADC(void)
{
    uint16_t adc_value = 0;
    
    // 启动ADC转换
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
    
    // 等待转换完成
    while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
    
    // 读取ADC值
    adc_value = ADC_GetConversionValue(ADC1);
    
    // 清除标志位
    ADC_ClearFlag(ADC1, ADC_FLAG_EOC);
    
    return adc_value;
}

/**
  * @brief  获取电压值
  * @param  无
  * @retval 电压值 (V)
  */
float MQ2_GetVoltage(void)
{
    uint16_t adc_value = MQ2_ReadADC();
    float voltage = (float)adc_value * 3.3 / 4095.0;  // 假设参考电压3.3V
    return voltage;
}

/**
  * @brief  获取传感器电阻值
  * @param  无
  * @retval 传感器电阻值 (kΩ)
  */
float MQ2_GetResistance(void)
{
    float voltage = MQ2_GetVoltage();
    float rs = 0.0;
    
    if(voltage > 0.1 && voltage < 4.9) {
        // Rs = (Vc - Vout) / Vout * RL
        rs = (MQ2_VOLTAGE_SUPPLY - voltage) / voltage * MQ2_LOAD_RESISTANCE;
    }
    
    return rs;
}

/**
  * @brief  获取Rs/R0比值
  * @param  无
  * @retval Rs/R0比值
  */
float MQ2_GetRatio(void)
{
    float rs = MQ2_GetResistance();
    float ratio = rs / R0;
    return ratio;
}

/**
  * @brief  获取液化气浓度 (LPG)
  * @param  无
  * @retval LPG浓度 (ppm)
  */
float MQ2_GetLPGppm(void)
{
    float ratio = MQ2_GetRatio();
    float ppm = MQ2_LPG_COEFF_A * pow(ratio, MQ2_LPG_COEFF_B);
    return ppm;
}

/**
  * @brief  获取一氧化碳浓度 (CO)
  * @param  无
  * @retval CO浓度 (ppm)
  */
float MQ2_GetCOppm(void)
{
    float ratio = MQ2_GetRatio();
    float ppm = MQ2_CO_COEFF_A * pow(ratio, MQ2_CO_COEFF_B);
    return ppm;
}

/**
  * @brief  获取烟雾浓度
  * @param  无
  * @retval 烟雾浓度 (ppm)
  */
float MQ2_GetSmokeppm(void)
{
    float ratio = MQ2_GetRatio();
    float ppm = MQ2_SMOKE_COEFF_A * pow(ratio, MQ2_SMOKE_COEFF_B);
    return ppm;
}

/**
  * @brief  读取数字报警信号
  * @param  无
  * @retval 0: 报警, 1: 正常
  */
uint8_t MQ2_GetDigitalAlarm(void)
{
    return GPIO_ReadInputDataBit(MQ2_DIGITAL_PORT, MQ2_DIGITAL_PIN);
}

/**
  * @brief  校准R0值(在洁净空气中运行)
  * @param  clean_air_ratio: 洁净空气中的Rs/R0比值
  * @retval 无
  */
void MQ2_CalibrateR0(float clean_air_ratio)
{
    float rs_sum = 0.0;
    uint8_t samples = 10;
    
    printf("开始校准R0值,请将传感器置于洁净空气中...\r\n");
    
    for(uint8_t i = 0; i < samples; i++) {
        rs_sum += MQ2_GetResistance();
        Delay_ms(200);
    }
    
    float rs_avg = rs_sum / samples;
    R0 = rs_avg / clean_air_ratio;
    
    printf("校准完成!R0 = %.2f kΩ\r\n", R0);
}

/**
  * @brief  设置报警阈值
  * @param  threshold: 报警阈值 (ppm)
  * @retval 无
  */
void MQ2_SetAlarmThreshold(uint16_t threshold)
{
    alarm_threshold = threshold;
}

/**
  * @brief  MQ-2传感器主任务
  * @param  无
  * @retval 无
  */
void MQ2_Task(void)
{
    static uint8_t calib_flag = 0;
    static uint32_t last_time = 0;
    
    // 上电后先预热
    if(!calib_flag && (millis() > 30000)) {  // 预热30秒
        MQ2_CalibrateR0(MQ2_CLEAN_AIR_RATIO);
        calib_flag = 1;
    }
    
    // 每秒钟读取一次数据
    if(millis() - last_time > 1000) {
        last_time = millis();
        
        if(calib_flag) {
            float voltage = MQ2_GetVoltage();
            float resistance = MQ2_GetResistance();
            float ratio = MQ2_GetRatio();
            float smoke_ppm = MQ2_GetSmokeppm();
            uint8_t alarm = MQ2_GetDigitalAlarm();
            
            printf("MQ-2: 电压=%.2fV, 电阻=%.2fkΩ, Rs/R0=%.2f\r\n", 
                   voltage, resistance, ratio);
            printf("烟雾浓度: %.1f ppm, 数字报警: %s\r\n", 
                   smoke_ppm, alarm ? "正常" : "报警");
            
            // 检查是否超过报警阈值
            if(smoke_ppm > alarm_threshold) {
                printf("警告!烟雾浓度超标!\r\n");
                // 这里可以添加报警动作,如点亮LED、蜂鸣器等
            }
        } else {
            printf("MQ-2传感器预热中...\r\n");
        }
    }
}

3.3 主程序示例(main.c)

c 复制代码
#include "stm32f10x.h"
#include "delay.h"
#include "usart.h"
#include "mq2_sensor.h"

int main(void)
{
    // 初始化系统时钟
    SystemInit();
    
    // 初始化延时函数
    Delay_Init();
    
    // 初始化串口
    USART_Init(115200);
    printf("STM32 MQ-2烟雾传感器测试\r\n");
    
    // 初始化MQ-2传感器
    MQ2_Init();
    
    while(1)
    {
        // 执行MQ-2传感器任务
        MQ2_Task();
        
        // 延时
        Delay_ms(10);
    }
}

3.4 延时函数(delay.c)

c 复制代码
#include "delay.h"

static uint8_t fac_us = 0;
static uint16_t fac_ms = 0;

void Delay_Init(void)
{
    SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
    fac_us = SystemCoreClock / 8000000;
    fac_ms = (uint16_t)fac_us * 1000;
}

void Delay_us(uint32_t us)
{
    uint32_t temp;
    SysTick->LOAD = us * fac_us;
    SysTick->VAL = 0x00;
    SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
    
    do {
        temp = SysTick->CTRL;
    } while((temp & 0x01) && !(temp & (1 << 16)));
    
    SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
    SysTick->VAL = 0x00;
}

void Delay_ms(uint32_t ms)
{
    uint32_t temp;
    SysTick->LOAD = ms * fac_ms;
    SysTick->VAL = 0x00;
    SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
    
    do {
        temp = SysTick->CTRL;
    } while((temp & 0x01) && !(temp & (1 << 16)));
    
    SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
    SysTick->VAL = 0x00;
}

四、关键算法说明

4.1 传感器电阻计算

MQ-2传感器的输出电压与气体浓度关系通过电阻变化体现:

复制代码
Rs = (Vc - Vout) / Vout * RL

其中:

  • Vc:电源电压(5V)
  • Vout:传感器输出电压
  • RL:负载电阻(10kΩ)

4.2 气体浓度计算

根据MQ-2数据手册,气体浓度与Rs/R0的关系为:

复制代码
ppm = A * (Rs/R0)^B

不同气体的A、B系数:

气体类型 A系数 B系数
LPG 574.25 -2.222
CO 36974.0 -3.109
烟雾 3616.1 -2.675

4.3 R0校准方法

  1. 将传感器置于洁净空气中

  2. 测量Rs值(多次测量取平均)

  3. 根据洁净空气中的Rs/R0比值计算R0:

    R0 = Rs / Rs/R0_clean_air

参考代码 MQ-2烟雾传感器模块STM32F10x源程序 www.youwenfan.com/contentcsu/56118.html

五、使用注意事项

5.1 预热要求

  • 首次使用:需要预热≥48小时才能达到稳定
  • 日常使用:上电后预热30秒-2分钟
  • 长期存放:再次使用前需要重新预热

5.2 环境因素影响

因素 影响 应对措施
温度 高温会降低灵敏度 温度补偿算法
湿度 高湿度会影响电阻值 湿度补偿或干燥剂
老化 长期使用灵敏度下降 定期重新校准R0

5.3 常见问题解决

问题 原因 解决方案
读数不稳定 预热不充分 延长预热时间
读数偏高 传感器污染 清洁传感器表面
读数偏低 R0值漂移 重新校准R0
无反应 加热电路故障 检查5V加热电压

六、扩展功能建议

6.1 多气体检测

c 复制代码
// 扩展支持多种气体检测
typedef enum {
    GAS_LPG = 0,
    GAS_CO,
    GAS_SMOKE,
    GAS_HYDROGEN,
    GAS_PROPANE
} GasType;

float MQ2_GetGasConcentration(GasType gas_type)
{
    float ratio = MQ2_GetRatio();
    float ppm = 0.0;
    
    switch(gas_type) {
        case GAS_LPG:
            ppm = 574.25 * pow(ratio, -2.222);
            break;
        case GAS_CO:
            ppm = 36974.0 * pow(ratio, -3.109);
            break;
        case GAS_SMOKE:
            ppm = 3616.1 * pow(ratio, -2.675);
            break;
        // 其他气体...
    }
    
    return ppm;
}

6.2 报警联动控制

c 复制代码
// 报警联动功能
void MQ2_AlarmHandler(void)
{
    float smoke_ppm = MQ2_GetSmokeppm();
    
    if(smoke_ppm > alarm_threshold) {
        // 1. 点亮红色报警LED
        GPIO_SetBits(GPIOA, GPIO_Pin_1);
        
        // 2. 启动蜂鸣器报警
        GPIO_SetBits(GPIOA, GPIO_Pin_2);
        
        // 3. 发送报警短信/邮件(需要GSM/WiFi模块)
        SendAlarmMessage(smoke_ppm);
        
        // 4. 关闭燃气阀门(需要继电器控制)
        GPIO_ResetBits(GPIOA, GPIO_Pin_3);
    } else {
        // 恢复正常状态
        GPIO_ResetBits(GPIOA, GPIO_Pin_1);
        GPIO_ResetBits(GPIOA, GPIO_Pin_2);
        GPIO_SetBits(GPIOA, GPIO_Pin_3);
    }
}

七、总结

这个STM32F10x MQ-2烟雾传感器驱动程序具有以下特点:

  1. 完整功能:支持模拟电压读取、数字报警、多种气体浓度计算
  2. 自动校准:支持R0值自动校准,提高测量精度
  3. 稳定可靠:包含预热检测、数据滤波等机制
  4. 易于扩展:模块化设计,方便添加新功能

使用建议

  • 首次使用前务必进行R0校准
  • 确保足够的预热时间
  • 定期检查传感器状态
  • 根据实际环境调整报警阈值

这套驱动可以直接用于智能家居、工业安全、消防报警等场景的烟雾检测应用。

相关推荐
山木嵌入式5 小时前
【STM32实战】轻量级任务调度器实现
stm32·单片机·rtos·任务调度器·裸机开发
guygg886 小时前
基于霍尔传感器的BLDC控制源码
单片机·嵌入式硬件
ytttr8736 小时前
DSP 28335 CAN总线通信程序
开发语言·stm32·单片机
一枝小雨8 小时前
RISC-V架构sp寄存器 & RISC-V架构下FreeRTOS任务上下文保存与恢复
单片机·架构·嵌入式·risc-v·rtos·内核原理
BW.SU9 小时前
PackagingTool 嵌入式资源打包合并工具
单片机·二进制·嵌入式开发·资源合并软件·图片打包
长安第一美人10 小时前
工业级实时监控系统开发:PHP+ZMQ+JS 前后端分离架构全解析
前端·嵌入式硬件·架构·交互·rk3588·zmq后端
田甲10 小时前
STM32开发环境迁移实践:从 CubeMX 生成 CMake 工程到 VS Code 编译与调试
stm32·单片机·嵌入式硬件
hoiii18710 小时前
在 STM32F1上读取 BMX055 三轴加速度
stm32·单片机·嵌入式硬件
嵌入式小站10 小时前
STM32 零基础可移植教程 04:按键输入,为什么按下去读到的是 0 或 1
chrome·stm32·嵌入式硬件
三佛科技-1873661339711 小时前
BP8522D贴片SOP7,5V150mA高集成度无VCC电容降压型恒压芯片解析
单片机·嵌入式硬件