STM32实战:MQ-7一氧化碳检测报警器设计与串口调试

发布时间:2026/9/16 16:36:24
STM32实战:MQ-7一氧化碳检测报警器设计与串口调试 简介这套STM32单片机MQ-7一氧化碳监测系统源代码完整实现了气体浓度采集、OLED屏幕显示、蜂鸣器超限报警以及将浓度数据通过串口发送到调试助手的全过程。工程采用标准Keil开发环境适合嵌入式入门者、课设毕设选题者以及环境安全监测项目开发者参考可直接加载硬件进行功能验证或二次改造。压缩包共229个文件整体体积6.34MB核心内容包括C语言源码、头文件、Keil工程配置、链接脚本以及编译生成的中间文件解压后即可用MDK打开编译省去重新搭建外设驱动框架的功夫。目前已有964人浏览学习工程内驱动代码齐全涵盖定时器、ADC采集、I2C通信、OLED绘图、串口发送等模块并附有编译生成的可执行文件能边看代码边联调直观理解一氧化碳浓度从模拟信号到数字显示报警的完整处理链路。若正在寻找STM32气体检测类参考设计这份源码包提供了从外设初始化到业务逻辑的清晰示例可作为项目起点快速移植使用。1. 一块板子把 CO 浓度变成“看得见、叫得出、可回看”的告警MQ-7 这种一氧化碳传感器在气体类项目里出镜率不低但很多人焊完板子才发现问题不在“读不到数”而在“读数放到哪里、什么时候该响、数据怎么留痕”。这块板子的完整链路是MQ-7 的模拟电压输出进 STM32 的 ADC换算成 ppm 之后一屏 OLED 实时显示浓度蜂鸣器按阈值分级报警同时每一帧 CO 数据通过串口发到 PC 上的串口调试助手。三个输出端互为备份OLED 管现场、蜂鸣器管提醒、串口管事后分析这才是“能交付”的项目形态。适合正在做环境监测类课程设计、毕业设计或者是想给实验室加一个低成本气体告警节点的工程师。下面按硬件信号链、HAL 库工程、串口协议、报警参数、标定排错这条线一步步讲清楚。2. MQ-7 信号链与 STM32 侧硬件连接2.1 MQ-7 的加热周期和输出信号MQ-7 属于半导体气敏传感器内部有一根加热丝和一层二氧化锡材料。它对一氧化碳的响应依赖“高低温循环加热”在一个完整周期里先是约 60 秒的低温加热让材料表面吸附 CO再是约 90 秒的高温加热把吸附物氧化掉使阻值恢复。新手最容易犯的错是上电就读数据发现数值乱跳甚至为 0就开始怀疑 ADC 配置。实际上传感器需要约 3 分钟预热窗口等加热循环跑完两三轮阻值才稳定。信号链路本身很简单传感器内部阻值 Rs 随 CO 浓度升高而下降配合板载的分压电阻 R_L 形成分压Vout 端电压随之升高。常见模块上 R_L 是 10kΩ供电用 5V输出范围大致在 0.1V 到 3V 之间。注意这里有两个坑第一模块输出电压和浓度不是线性关系规格书里给的是双对数坐标下的灵敏度曲线。工程上通常做分段线性拟合或者干脆只分“正常 / 超标 / 严重超标”三档不追求精确读数。第二如果模块供电 5V而 STM32 的 ADC 参考电压是 3.3V就必须用电阻分压或者让模块改从 3.3V 供电。MQ-7 加热丝允许电压范围较宽3.3V 供电只是加热变慢不伤器件许多低成本方案直接让传感器和 MCU 共用 3.3V。2.2 STM32 的 ADC 采集、OLED 和蜂鸣器接线先把硬件接口定下来后面写代码才有依据。以下是一份常见的引脚分配尺寸兼容市面上多数 MQ-7 模块和 0.96 寸 OLED外设信号STM32 引脚说明MQ-7 模块VCC3.3V / 5V注意和 ADC 参考电压匹配MQ-7 模块VOUTPA1ADC1 通道 1MQ-7 模块GNDGND与 MCU 共地OLEDSCLPB8I2C1 时钟OLEDSDAPB9I2C1 数据蜂鸣器模块I/OPB12高电平触发低电平关闭串口调试助手侧TX/RXPA9/PA10USART1 或通过 USB 转 TTL 接 PC蜂鸣器这里要区分有源和无源。有源蜂鸣器内置振荡电路给高电平就响适合只做告警的场景无源蜂鸣器需要外部给方波代码里用定时器 PWM 改变频率可以发出“滴滴”节奏声。如果选有源蜂鸣器GPIO 直接推挽输出即可如果选无源建议 PB12 接到 TIM4 的 CH1 引脚用 PWM 输出 2kHz~4kHz 方波驱动。两者在 CubeMX 里的配置逻辑完全不同下面的示例按有源设计代码更短、更容易跑通。2.3 供电和接地ADC 读数抖动的隐藏来源MQ-7 的加热丝冷态电阻很小上电瞬间电流接近 180mA这个脉冲会拉低整条 3.3V 电源轨直接影响 ADC 参考电压。如果发现读数在浓度不变时仍有 ±50ppm 级别的毛刺先查供电再查滤波代码。常见做法是传感器模块单独从 5V 取电输出端通过 10kΩ 1kΩ 电阻分压后再进 PA1这样既躲开了加热电流冲击也把电压适配进 3.3V 量程。分压后的等效输入电阻约 9kΩ对 STM32 ADC 的采样保持电容来说是安全的不需要额外加运放跟随。另外PA1 到 GND 之间并联一个 0.1μF 陶瓷电容能滤掉一部分开关噪声代价是信号响应变慢但对秒级变化的气体浓度没有影响。3. 用 HAL 库把 MQ-7、OLED、串口跑通的最小工程3.1 CubeMX 引脚与时钟配置这几年网上搜“stm32hal库点亮 oled 屏幕全流程”的开发者越来越多说明 HAL 库已经是主流工作方式。用 CubeMX 生成工程只需要四步选择芯片型号以 STM32F103C8T6 为例RCC 打开外部高速晶振 HSE。PA1 设为 ADC1_IN1PB8/PB9 设为 I2C1 SCL/SDAPA9/PA10 设为 USART1 TX/RX。PB12 设 GPIO_Output初始输出低电平。ADC1 配置采样时间选 55.5 周期以上连续转换模式打开USART1 参数 115200、8 位数据、无校验、1 位停止位。生成代码后在main.c的MX_I2C1_Init()之后确认 SCL/SDA 引脚都设成了开漏输出OLED 的高电平靠外部上拉实现。不少“OLED 白屏不显示”的问题根源就是引脚配置成了推挽输出电平匹配但 I2C 时序异常。3.2 主循环里的完整流程采样、滤波、显示、串口发送核心代码在main()的while(1)里逻辑顺序是读 ADC → 转电压 → 转 ppm → 写入 OLED → 判断阈值驱动蜂鸣器 → 格式化串口帧发送。下面这段可编译到标准 HAL 工程里// main.c 主循环未启用 RTOS 时的轮询写法 uint16_t adc_val; float voltage, ppm; char tx_buf[64]; uint8_t oled_str[16]; while (1) { // 启动一次 ADC 转换并等待完成超时 50ms HAL_ADC_Start(hadc1); if (HAL_ADC_PollForConversion(hadc1, 50) HAL_OK) { adc_val HAL_ADC_GetValue(hadc1); } HAL_ADC_Stop(hadc1); // 12 位 ADC3.3V 参考电压输入前有 10k/1k 分压 voltage (float)adc_val * 3.3f / 4096.0f * 11.0f; ppm mq7_voltage_to_ppm(voltage); // 查表或分段拟合 // 判断报警阈值PB12 高电平触发蜂鸣器 if (ppm 100.0f) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET); snprintf(oled_str, sizeof(oled_str), CO:%dppm!ALARM, (int)ppm); } else { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET); snprintf(oled_str, sizeof(oled_str), CO:%dppm NORMAL, (int)ppm); } // 刷新 OLED显示两行 OLED_ShowString(0, 0, oled_str); OLED_ShowString(0, 2, UART:CO_alarm); // 串口发送格式为 CO:ppm ALARM:0/1\r\n int alarm_flag (ppm 100.0f) ? 1 : 0; snprintf(tx_buf, sizeof(tx_buf), CO:%dppm ALARM:%d\r\n, (int)ppm, alarm_flag); HAL_UART_Transmit(huart1, (uint8_t *)tx_buf, strlen(tx_buf), 100); HAL_Delay(500); }代码里有几个参数直接决定行为HAL_ADC_PollForConversion的 50ms 超时只影响等待时长不影响精度voltage最后乘的系数 11.0 是因为前级分压比是 (10k1k)/1k如果硬件上没加分压电阻这里要改回 1.0HAL_Delay(500)控制整个周期的刷新频率500ms 一次对气体检测足够还能给 OLED 留出写入余量。mq7_voltage_to_ppm()是留给大家自己补的函数两行代码的核心思路电压减去零点电压洁净空气中测到的初始值后乘一个灵敏度系数 K。精确做法是拿 50ppm 和 300ppm 标准气体做两点标定解出 K。3.3 OLED 显示 I2C 驱动的初始化序列OLED 屏幕的驱动芯片一般是 SSD13060.96 寸 128x64。HAL 库下点亮屏幕不需要移植复杂库初始化序列核心是三条指令// I2C OLED 初始化基于 SSD1306 static void OLED_Init(I2C_HandleTypeDef *hi2c) { // 关闭显示设置显示时钟分频和复用比例 OLED_WriteCmd(hi2c, 0xAE); // display off OLED_WriteCmd(hi2c, 0xD5); // set display clock divide OLED_WriteCmd(hi2c, 0x80); OLED_WriteCmd(hi2c, 0xA8); // set multiplex ratio OLED_WriteCmd(hi2c, 0x3F); // 64 行 OLED_WriteCmd(hi2c, 0x8D); // charge pump 使能 OLED_WriteCmd(hi2c, 0x14); // 开启内部升压必须 OLED_WriteCmd(hi2c, 0xAF); // display on OLED_Fill(hi2c, 0x00); // 清屏 }其中0x8D/0x14这两条是最容易漏的不开 charge pump 的话OLED 显示区会留一片黑。OLED_WriteCmd实际是一次 I2C 写操作地址 0x780x3C 左移一位控制字节 0x00 表示写命令0x40 表示写数据。显示数字时先取字模数据再逐页写入 GDDRAM。字符字模一般 8x16 大小一行显示 16 西文字符写中文则要二级字库字模文件工程上一律建议界面用英文。4. 串口上报格式设计与报警阈值参数化4.1 一帧数据在串口调试助手里该是什么样串口调试助手是排错时最直接的观测窗口打开 SSCOM 或者正点原子串口助手波特率设 115200就能看到设备周期上报的文本帧。项目里建议统一成下面的字符串帧格式CO:126ppm ALARM:1\r\n这个格式有三个好处人类可直接阅读用strstr()匹配也不容易误判\r\n结尾让串口助手按行显示不用手动开启“时间戳”也能看出上报周期文本长度固定十六进制字节流的解析被规避了对串口调试助手这类工具最友好。如果要传给上位机做曲线展示文本帧解析效率偏低改成二进制帧更合适例如 4 字节一帧uint8_t frame[4]; frame[0] 0xAA; // 帧头 frame[1] (uint8_t)(ppm); // ppm 整数部分 frame[2] (uint8_t)(ppm * 10) % 10; // 小数位 frame[3] (frame[1] ^ frame[2]) 0xFF; // 校验两种格式建议保留一个宏开关文本模式用于调试二进制模式用于对接上位机。帧率要克制500ms 到 1s 发一次足够发太快串口助手会被刷得没法看。4.2 报警阈值、回差和报警持续时间的配置位置把报警相关参数集中成几个宏或者结构体是项目能否“参数化”的关键。建议放在config.h里// config.h 报警策略参数 #define ALARM_CO_HIGH 100.0f // 报警上限 ppm #define ALARM_CO_HYSTERESIS 20.0f // 回差避免阈值附近抖动 #define ALARM_DELAY_MS 2000 // 持续超标 2s 才报警 #define ALARM_SEND_ACTIVE 1 // 1报警帧重复发送ALARM_CO_HYSTERESIS这一项很多人不写导致浓度在 98~102ppm 之间震荡时蜂鸣器跟着反复开关。加入回差后只有浓度超过 120ppm 才解除报警这个“迟滞”让报警行为稳定得多。ALARM_DELAY_MS是时间窗口实现时在循环里累计超标时长static uint32_t alarm_ticks 0; if (ppm ALARM_CO_HIGH) { alarm_ticks 500; if (alarm_ticks ALARM_DELAY_MS) HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET); } else { if (ppm ALARM_CO_HIGH - ALARM_CO_HYSTERESIS) { alarm_ticks 0; HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET); } }这样配置的好处是现场调试时不用改一句业务代码只调config.h里的几个值就能应对不同场景厨房里做饭的油烟也会让 MQ-7 读数上升阈值就得比工业环境更高。4.3 RS485 场景下的串口接线差异如果现场环境要求远距离传输比如在厂区把数据送到几十米外的控制室常见做法是 STM32 的 UART 输出接一个 MAX485 转接板再走双绞线到 PC 端的 USB-485 转换器。有人会用“485串口调试助手”这类工具做收发测试但要注意 485 是半双工总线发送数据时必须让 DE/RE 引脚在发送期间拉高。用 HAL 库的阻塞发送很容易丢掉最后一字节解决办法是发送前手动把 DE 拉高发送完成后把 DE 拉低HAL_GPIO_WritePin(DE_PIN_GPIO_Port, DE_PIN_Pin, GPIO_PIN_SET); HAL_UART_Transmit(huart1, data, len, 100); HAL_GPIO_WritePin(DE_PIN_GPIO_Port, DE_PIN_Pin, GPIO_PIN_RESET);如果串口调试助手上看到最后一个字符反复丢失先查 DE 引脚时序再查终端电阻是否接在总线两端。120Ω 终端电阻是标配不加会出现波形振铃误码率随线长直线上升。5. 进阶MQ-7 预热点基线校准、滑动滤波与排错手段5.1 预热窗口与零点基线读数不从零开始怎么办MQ-7 在洁净空气中的输出电压并不是 0V而是随环境温湿度在一个区间里漂。把公式里的零点电压zero_voltage写死成 0.5V是很多抖动问题的来源。正确的做法是设备上电后进入 3~5 分钟的“预热标记”状态OLED 上显示WARM UP倒计时期间持续采样取平均得到一个 baseline// 预热结束时的基线记录掉电不保存每次上电重新采 float baseline_voltage 0.0f; for (int i 0; i 30; i) { baseline_voltage read_mq7_voltage(); HAL_Delay(500); } baseline_voltage / 30.0f;预热结束后ppm 换算全基于“当前电压减去基线电压”的相对值而不是绝对电压。环境从 20°C 换到 30°C基线电压会移动所以每隔 8 小时建议让系统重新采 5 分钟基线这个操作可以放在串口调试助手里发一个特定字符如B触发。5.2 滑动平均滤波与串口丢帧校验ADC 读数毛刺在气体检测里非常普遍滑动平均是最轻量且有效的滤波方式。缓冲 4 个采样值新值进来踢掉最旧值输出平均值。STM32 上用一个 4 元素数组即可不需要引入复杂算法// 4 点滑动平均滤波 static float buf[4] {0}; static uint8_t idx 0; static float sum 0; float smooth_filter(float val) { sum - buf[idx]; buf[idx] val; sum val; idx (idx 1) 0x03; return sum / 4.0f; }代码里idx 0x03是取模 4 的快速写法数组越界问题被天然规避。串口侧的丢帧排查则要先分辨是“没发出”还是“没收到”在 HAL_UART_Transmit 返回后立即读 USART_ISR 的 TXE 位看发送移位寄存器是否空。如果 HAL 函数明确返回 HAL_OK就用示波器看 TX 引脚波形如果波形正常换一个串口调试助手版本测试部分助手对不支持的状态字节处理有问题。5.3 常见故障与处置对照现象串口助手完全不显示数据。查 PA9/PA10 是否被 PC 端驱动占用部分开发板上 CH340 和 ST-Link 虚拟串口同时存在需要确认当前用的是哪一个口。下载器连上时“stm32 virtual com port 叹号”提示驱动缺失重装 USB 串口驱动后拔插设备即可。现象OLED 亮但无字符。I2C 地址错误是最常见原因0.96 寸模块默认地址是 0x3C但部分厂家的模块是 0x3D。写一个 I2C 扫描函数把总线上所有地址打印到串口通电后看收到的地址比盲猜快得多。现象蜂鸣器一上电就响。先查 PB12 外部是否被拉低/拉高再确认代码里初始化后有没有显式设置初始电平。GPIO 的默认状态在复位后是浮空输入任何噪声都可能触发高电平必须在MX_GPIO_Init末尾强制赋值一次。现象ADC 读出的电压始终 3.3V。分压电阻虚焊或 PA1 被复用成其他功能导致读到的不是外部电压。测量 PA1 引脚本身的直流电压如果测量为 0.7V 但代码读到 3.3V则确认 CubeMX 里 PA1 的 ADC 通道号是否写错。现象flash 烧录时报 “error: no stm32 target found!”。一般是调试接口被关闭或者 SWD 引脚被占用。PB3/PB4 被程序占用为 GPIO 后第一次烧录能成第二次就再也连不上。解决办法是拉 BOOT0 到高电平复位进系统存储器用串口 ISP 擦除后恢复 SWD。当你能在串口调试助手里稳定看到一行行 CO 数据、OLED 同步刷新、蜂鸣器在设定阈值点前后行动这套系统的骨架就算真正立住了。再做多一步把ALARM_CO_HIGH从 100 改到 200重新烧录后看响应是否变迟钝——阈值判断本来就不该在五秒内做出反应气体扩散是慢过程控制节奏跟着慢下来整个系统才耐看。本文还有配套的精品资源点击获取