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校准
  • 确保足够的预热时间
  • 定期检查传感器状态
  • 根据实际环境调整报警阈值

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

相关推荐
bubiyoushang8883 小时前
STM32F030 多路ADC采样实现
stm32·单片机·嵌入式硬件
三佛科技-187366133974 小时前
LP8841SC+LP35118N (72W SiC双电源方案),全电压认证,体积直降 20%
单片机·嵌入式硬件
metaRTC5 小时前
metaRTC8 成功适配 RTOS:开启 MCU/嵌入式实时音视频新时代
单片机·嵌入式硬件·webrtc·实时音视频·rtos
d111111111d5 小时前
UAER问题+修复小bug
前端·javascript·笔记·stm32·单片机·嵌入式硬件·学习
嵌入式的飞鱼6 小时前
SD NAND vs eMMC:嵌入式存储方案怎么选?
嵌入式硬件·mcu·sd nand
进击的小头6 小时前
第19篇:嵌入式定点与浮点运算科普:核心差异、精度控制与开发技巧
单片机·嵌入式硬件
M158227690557 小时前
老 PLC 秒接工业以太网|三格电子串口转网口模块,让设备改造零门槛、一步上云
单片机·嵌入式硬件
zhmc7 小时前
电解电容的ESR定义与测量
嵌入式硬件
神一样的老师8 小时前
【兆易创新GD32VW553开发板试用】开发板资料汇总
单片机