简介:本资源为基于STM32单片机的室内燃气监测报警系统完整设计资料,面向嵌入式初学者、电子竞赛参赛者及课程设计学生,帮助解决燃气浓度检测、超标报警与实时显示等实际问题。压缩包共202个文件,约3.89MB,以c与h源码、d与o编译中间文件、crf与lst链接列表、hex烧录文件、uvproj与uvopt工程配置为主,另含asm汇编、pdf文档、png图片及pdsprj仿真工程,覆盖从源码到仿真的完整链路。目前已有4071人学习下载。资料围绕STM32的ADC采样、中断处理、阈值比较算法展开,配合LCD1602显示浓度与报警状态,并驱动LED与蜂鸣器实现声光报警;同时提供Proteus仿真图与Keil工程,便于读者对照硬件连接、验证软件逻辑、理解外设接口配置与嵌入式编程思路,适合作为课程设计或竞赛项目的参考模板。
1. 从一份 STM32 燃气监测源码包说起:它到底能跑通什么
前阵子帮一个做消防电子的朋友看方案,他手里有个小项目要快速出原型:室内燃气泄漏监测,超标就声光报警,还要在本地屏上实时显示浓度值。他翻出一份流传挺广的 STM32 源码包,问我值不值得照着搭。我拆开看了一遍,核心链路其实很清晰——MQ 系列气敏传感器采集模拟量,STM32 的 ADC 做采样,LCD1602 显示实时数值,超阈值触发蜂鸣器加继电器。这套组合在毕设、课程设计和中小型安防产品原型里出现频率极高,原因也简单:器件便宜、逻辑直白、调试链路短。
这份资源适合两类人:一类是刚上手 STM32 外设、想找一个“有输入有输出有判断”的完整小系统练手的;另一类是要在几天内把燃气报警功能验证出来、不打算从零画板写驱动的。它不解决高精度标定和工业级可靠性问题,但能把“传感器→ADC→阈值判断→显示+报警”这条主线跑通。下面我按实际复现顺序拆,重点讲 ADC 采样配置、LCD1602 驱动时序和阈值逻辑这三块最容易翻车的地方。
2. 硬件链路与 ADC 采样:为什么你的浓度值一直在跳
2.1 传感器输出与 ADC 通道的对应关系
MQ 系列气敏元件本质是个可变电阻,随可燃气体浓度升高,电导率上升,负载电阻上的分压跟着变。常见模块会把分压点直接引出来,输出 0~3.3V 或 0~5V 的模拟电压。STM32 的 ADC 是 12 位逐次逼近型,参考电压通常取 VDDA(3.3V),所以满量程对应 4095。这里第一个坑就来了:如果你用的是 5V 供电的传感器模块,输出可能超过 3.3V,直接进 STM32 的 ADC 引脚会打坏 IO。稳妥做法是模块供电和 STM32 都走 3.3V,或者加电阻分压把上限压到 3.0V 以内。
通道选择上,源码包一般用 PA0 对应 ADC1_IN0,也有用 PA1 的。你拿到板子第一件事不是烧程序,是拿万用表量传感器输出脚在洁净空气下的静态电压,记下来。这个值决定了后面阈值怎么设,也决定了你判断“程序跑没跑对”的基准。
2.2 ADC 初始化代码与采样周期设置
下面这段是典型的 ADC1 单通道轮询初始化,我按可复现的方式重写了一遍,注释标了每个参数为什么这么给。
// ADC1_IN0 对应 PA0,单次转换,软件触发 void ADC1_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; ADC_InitTypeDef ADC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE); RCC_ADCCLKConfig(RCC_PCLK2_Div6); // ADC 时钟 = 72M/6 = 12MHz,不能超 14MHz GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; // 模拟输入,必须设 AIN GPIO_Init(GPIOA, &GPIO_InitStructure); ADC_DeInit(ADC1); 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; ADC_Init(ADC1, &ADC_InitStructure); ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); ADC_Cmd(ADC1, ENABLE); // 校准,顺序不能反 ADC_ResetCalibration(ADC1); while (ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while (ADC_GetCalibrationStatus(ADC1)); } u16 Get_ADC_Value(void) { ADC_SoftwareStartConvCmd(ADC1, ENABLE); while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)); // 等转换完成 return ADC_GetConversionValue(ADC1); }逻辑说明:采样时间给 55.5 周期是常见折中,气敏元件输出阻抗不低,采样时间太短会导致采样电容充不满,读数偏低且跳。校准那两步很多人省掉,结果就是不同板子之间偏差能到几十个 LSB。参数上,如果你发现读数抖动超过 ±30,先把采样时间加到 239.5 周期试,再考虑在软件里做滑动平均。
2.3 从 ADC 原始值到“浓度”的换算边界
源码包里通常有个Voltage = ADC_Value * 3300 / 4096的换算,然后直接拿电压和阈值比。这里要清醒:MQ 传感器的电阻-浓度曲线是非线性的,而且受温湿度影响大。没有标定条件时,用电压阈值做“有/无泄漏”判断是可行的,但别把它当 ppm 读数用。我一般会定义一个洁净空气基准值V0,用(V - V0)的增量做判断,比绝对电压稳。
提示:上电后前 20~30 秒传感器加热丝还没稳定,读数会从高往低走,这段时间的采样值不要参与判断,程序里加个延时或状态标志跳过。
3. LCD1602 驱动与显示刷新:为什么屏亮了但全是方块
3.1 4 位模式接线与初始化时序
LCD1602 用 4 位数据模式能省 4 根 IO,源码包基本都这么干。典型接法是 RS、RW、EN 加 D4~D7 共 7 根线。初始化时序是硬要求,延时不够就出方块或者只亮不显。下面这段初始化按 HD44780 手册的 4 位模式流程写,延时值我按 72M 主频调过。
// 假设 RS=PB0, RW=PB1, EN=PB2, D4~D7=PB4~PB7 void LCD1602_Init(void) { LCD_GPIO_Init(); // 配置上述引脚为推挽输出 Delay_ms(15); // 上电等待 >15ms LCD_Write_Cmd(0x33); // 强制复位序列 Delay_ms(5); LCD_Write_Cmd(0x32); // 切 4 位模式 Delay_ms(5); LCD_Write_Cmd(0x28); // 4 位、2 行、5x7 点阵 LCD_Write_Cmd(0x0C); // 开显示、关光标 LCD_Write_Cmd(0x06); // 写入后地址自增 LCD_Write_Cmd(0x01); // 清屏 Delay_ms(2); // 清屏指令需要较长延时 }逻辑说明:0x33和0x32这两条是 4 位模式的入口,缺了它后面所有指令都按 8 位解析,屏上就是乱码。0x01清屏后必须等 1.64ms 以上,很多人这里只给几十微秒,结果就是偶尔清不干净。参数上,EN 使能脉冲宽度要大于 450ns,72M 下一条LCD_Write_Cmd里的翻转加延时足够,但如果你把延时函数优化掉了,就可能出现时好时坏。
3.2 显示刷新与主循环的节奏控制
主循环里如果每次 ADC 采样完都刷整屏,屏会闪,而且 LCD 写入本身耗时。常见做法是只刷新数值变化的位,或者固定 200~500ms 刷一次。下面是个简单的显示函数,把电压值格式化成 4 位显示。
void LCD_Show_Value(u16 adc_val) { char buf[6]; u16 mv = (u32)adc_val * 3300 / 4096; // 转毫伏 buf[0] = mv / 1000 + '0'; buf[1] = '.'; buf[2] = mv % 1000 / 100 + '0'; buf[3] = mv % 100 / 10 + '0'; buf[4] = mv % 10 + '0'; buf[5] = '\0'; LCD_Set_Cursor(0, 0); LCD_Write_String("Gas: "); LCD_Write_String(buf); LCD_Write_String(" V"); }逻辑说明:mv计算用u32中转防止溢出,adc_val最大 4095,乘 3300 是 13 万多,u16装不下。显示格式固定宽度能避免残影。如果你要显示“浓度”两个字,记得 LCD1602 自带字库里没有中文,得用自定义 CGRAM 或者干脆用英文。
3.3 阈值判断与报警输出的联动
报警逻辑本身简单,但联动顺序有讲究。我一般让 ADC 采样、滤波、判断、显示、报警在一个 100ms 的定时节拍里顺序执行,避免显示阻塞导致报警延迟。阈值分两级:一级预警(比如电压增量超过 0.3V)只亮黄灯或短鸣,二级报警(超过 0.8V)拉继电器并长鸣。继电器吸合瞬间电流冲击大,如果和 STM32 共用一路 3.3V,最好给继电器单独供电并加续流二极管,否则 MCU 可能复位——这个坑我在两个板子上都见过。
4. 避坑与排查:那些让原型跑不起来的细节
4.1 现象:ADC 读数始终为 0 或 4095
原因:GPIO 模式没设成GPIO_Mode_AIN,或者传感器模块根本没输出。也有可能是 ADC 时钟超了 14MHz,转换结果无效。解决:先量 PA0 对地电压,正常应在 0.1~3.0V 之间;再查RCC_ADCCLKConfig的分频,72M 系统下 Div6 是 12MHz,Div4 是 18MHz 就超了。
4.2 现象:LCD 背光亮但无字符,或第一行显示黑块
原因:初始化延时不够,或者 4 位模式入口指令没发。RW 脚如果悬空或误接高电平,也会导致一直忙。解决:把LCD1602_Init里的延时翻倍试一次;用示波器看 EN 脚有没有脉冲;确认 RW 在写操作时拉低。
4.3 现象:报警阈值附近继电器反复吸合
原因:ADC 读数在阈值上下抖动,没有迟滞。解决:加软件迟滞,比如超过 0.8V 触发,降到 0.7V 以下才释放;同时对 ADC 做 8 次滑动平均。这个改动很小,但能消掉大部分“玄学”抖动。
4.4 现象:烧录后运行几秒就死机或复位
原因:继电器和 MCU 共用电源,吸合瞬间电压跌落。解决:继电器用独立 5V 供电,线圈两端反并一个 1N4148,MCU 电源脚加 100uF 电解加 0.1uF 瓷片。另外检查蜂鸣器是不是无源型,无源蜂鸣器直接接 IO 驱动电流可能不够,要加三极管。
4.5 现象:换一块同型号板子,读数差很多
原因:MQ 传感器个体差异大,负载电阻精度也有影响。解决:每块板子上电后先跑一段“基准采集”,把洁净空气下的 ADC 值存到 Flash 或 EEPROM,运行时用相对增量判断。没有这一步,阈值就得每块板子手调。
5. 进阶:把阈值判断做成可配置,以及一个验证习惯
源码包给的是固定阈值,实际用起来最想要的是能改。我的做法是在 Flash 里划一页存两个u16:基准值和报警增量,上电读出来,如果读到 0xFFFF 就写默认值。这样现场不用重新烧程序,通过串口发两条命令就能标定。串口协议不用复杂,SET BASE 512和SET TH 200两条文本指令足够,解析用sscanf就行。
验证方法上,我不建议直接拿打火机气体怼传感器,一来危险,二来会加速元件老化。常用做法是用酒精棉球靠近传感器,看读数是否平稳上升、报警是否在预期增量触发、移开后是否回落。整个过程用串口把 ADC 原始值和电压一起打出来,对照 LCD 显示是否一致。如果串口值和屏显值对不上,优先查显示函数的格式化,而不是怀疑 ADC。
| 验证项 | 操作 | 预期 |
|---|---|---|
| 基准采集 | 洁净空气下上电,等 30 秒 | 串口打印基准值稳定,波动 < ±10 |
| 响应测试 | 酒精棉球靠近 | 电压上升,超过增量阈值后报警 |
| 恢复测试 | 移开棉球 | 电压回落,报警在迟滞下限释放 |
| 显示一致性 | 对比串口电压与 LCD | 两者差值不超过 0.02V |
从那以后我每次拿到这类燃气监测源码包,都强制先跑一遍基准采集和串口对照,再动阈值。这个习惯帮我省掉了至少三次“明明代码一样但就是不对”的返工。希望帮到你。
本文还有配套的精品资源,点击获取