基于STM32的粮仓环境安防监测系统:从传感器到Proteus仿真实战 做了那么多单片机项目回头再看粮仓环境安防监测这个题目我觉得它几乎是STM32入门到进阶最适合复现的一个综合案例。单看功能——温湿度采集、烟雾检测、火焰报警、光照判断、风扇排风、加热除湿、声光告警、按键调阈值每一个单独拎出来都不算难但组合在一起就是一个完整的物联网终端节点雏形。这套系统我在实际调试中花了两周时间从原理图绘制到Proteus仿真跑通再到打板焊接真机运行中间踩了不少坑也积累了很多对新手特别友好的经验。这篇文章我会把源码结构、原理图设计思路、仿真搭建步骤和常见问题全部拆开讲基本上你照着做从零到跑通整个系统不会超过一个周末。先说清楚这套系统到底是干什么的。粮仓存储最怕三件事高温高湿导致粮食发霉、烟雾或明火引发火灾、以及夜间或无人值守时段的安全隐患。粮仓环境安防监测系统就是针对这些痛点实时采集仓内温湿度、烟雾浓度、火焰信息和环境光照当数值超过预设阈值时自动启动排风扇、加热器或声光报警器实现“监测—判断—执行”的闭环。对于学生做课程设计、毕业设计或者工程师想快速验证一套传感器执行器的联动方案这套系统的软硬件架构都很有参考价值。1. 整体方案设计与选型思路1.1 系统需求拆解做项目第一步不是写代码而是把需求拆成一个个明确的功能点。我当时把粮仓安防系统拆成了四路传感输入、三种执行输出、一组人机交互环境温湿度检测仓库内温度和相对湿度是粮食存储的核心指标温度超过30℃、湿度超过70%RH就需要通风除湿。烟雾浓度检测早期火灾隐患通常先以烟雾形式出现需要实时监测仓内烟雾浓度。火焰检测明火是粮仓最致命的威胁必须快速识别并触发声光报警。光照检测白天和夜晚的安防策略不同光照值也用于判断是否需要开启补光或进入夜间警戒模式。执行机构排风扇散热排烟、加热器除湿、蜂鸣器LED声光报警。人机交互LCD1602实时显示所有参数独立按键用来调整报警阈值。1.2 主控选型为什么是STM32F103C8T6可能有同学会问这种系统用51单片机或者Arduino不也行确实行但我最终选STM32F103C8T6核心原因是这套系统的扩展性和教学价值。C8T6拥有72MHz主频、64KB Flash、20KB RAM外设接口丰富以后你想往这个项目上加ESP8266做WiFi上报、加OLED屏显示曲线、加舵机联动灭火装置资源依然够用。更重要的是STM32的HAL库和标准外设库让代码层级清晰传感器驱动、业务逻辑、执行控制可以分层编写这种工程化思维是以后做复杂项目的基础。C8T6在Proteus里的仿真模型也非常成熟可以直接用hex文件跑仿真这一点在后面会详细讲。1.3 传感器选型与替代方案传感器选型我遵循一个原则仿真能跑通、真机好购买、电路够经典。DHT11温湿度传感器单总线协议数据格式固定代码实现简单。精度虽然一般±2℃、±5%RH但粮仓环境监测的精度需求并不苛刻。如果你想提高精度可以替换成DHT22或SHT30代码只需要修改驱动层的时序解析部分。MQ-2烟雾传感器这是一个模拟量输出的气敏传感器内部是二氧化锡半导体当环境中可燃气体或烟雾浓度升高时电导率发生变化输出电压随之改变。接ADC通道读取电压值即可。注意MQ-2需要预热刚上电的几分钟内输出不稳定代码里要做延时规避。火焰传感器我用的是一路数字量输出的红外火焰传感器模块检测波长范围在760nm到1100nm的红外光有火焰时输出低电平。模块上带可调电位器可以调节检测灵敏度。光敏电阻模块同样是模拟量输出光照越强输出电压越低或越高取决于电路接法。我用的是电压随光照增强而升高的接法方便直观判断。1.4 执行机构设计继电器隔离与驱动执行机构风扇、加热器属于大功率设备不能直接接在STM32的GPIO上必须通过继电器隔离驱动。继电器选型时注意线圈电压我用的5V继电器和触点容量10A 250VAC足够带动小型风扇和加热器。驱动电路上我最初直接用GPIO推继电器结果发现STM32GPIO的灌电流能力带不动继电器线圈系统一吸合就复位。后来改用NPN三极管S8050放大驱动GPIO高电平通过1kΩ基极电阻驱动三极管导通继电器线圈通电吸合同时在线圈两端并联一个1N4007续流二极管吸收断电时的反向电动势保护三极管和MCU。关于这个续流二极管的作用后面排查章节我会用一个具体的故障案例讲清楚。2. 原理图设计每个模块的关键连接2.1 最小系统电路STM32F103C8T6的最小系统包括电源、复位、晶振和BOOT设置四部分。电源部分我用AMS1117-3.3将5V降压到3.3V输入输出各接一个10μF电解电容和一个100nF陶瓷电容滤波。晶振用8MHz无源晶振两个20pF负载电容注意晶振下方不要铺铜否则容易起振不稳定。复位电路是经典的10kΩ上拉电阻100nF电容到地NRST引脚低电平复位。BOOT0和BOOT1各接一个10kΩ下拉电阻到地确保从主Flash启动。实际上BOOT1在F103系列中有的封装没有独立引脚C8T6的BOOT1就是PB2需要处理一下避免浮空导致启动模式异常。2.2 传感器接口电路设计传感器的接口电路我在设计时重点考虑了三个问题电平匹配、噪声抑制、接口防呆。DHT11的数据引脚接PB0需要外接一个4.7kΩ上拉电阻到VCC因为DHT11的数据线是开漏输出必须靠上拉电阻才能输出高电平。数据线和电源线之间并联一个100nF电容滤除总线上的高频噪声这个电容在长线连接时尤其重要。MQ-2传感器模块的AO输出接PA0ADC1通道0DO输出接PA1作为辅助数字量检测。虽然我只用AO做浓度判断但DO预留出来可以做一个硬件阈值的冗余保护。光敏模块的AO接PA1。不过这里有一个细节需要特别注意我用的传感器模块输出电压范围是0到5V而STM32的ADC输入范围是0到3.3V直接接会烧引脚。我用电阻分压1kΩ2kΩ将最大电压降到3.3V左右也可以用两个二极管做钳位保护。火焰传感器DO接PB1配置为上拉输入。正常情况下模块输出高电平检测到火焰时输出低电平代码里检测下降沿触发报警。2.3 执行机构与继电器驱动电路风扇和加热器分别接在继电器1和继电器2的公共端与常开端之间。继电器驱动电路我采用上面提到的三极管方案具体参数基极串联1kΩ电阻三极管用S8050NPN发射极接地集电极接继电器线圈负极线圈正极接5V。继电器线圈两端反向并联1N4007二极管阴极接5V阳极接集电极。实际上我在这个电路上吃过亏第一次焊接时忘记装续流二极管继电器每次吸合和释放的瞬间单片机都会复位。用示波器测量发现继电器线圈断电瞬间产生了将近50V的反向尖峰直接通过地线干扰了MCU的电源。加上续流二极管后反向尖峰被钳位在0.7V左右系统稳定运行。这个教训让我深刻理解了为什么厂商手册反复强调续流二极管必不可少。蜂鸣器我用的是有源蜂鸣器接PB5通过S8050三极管驱动。有源蜂鸣器内部自带振荡源GPIO给高电平就会发声不需要PWM驱动代码里用定时器做间歇鸣叫即可。LED指示灯接PC13板载LED和PB6、PB7外接两个LED分别表示报警状态和正常运行状态。2.4 LCD1602与按键电路LCD1602采用4位数据线模式占用PB8-PB11四个数据脚D4-D7RS接PB12RW接PB13EN接PB14。之所以选PB口是为了和传感器用的PA口、PC口分开方便布线和后期调试。LCD的背光通过一个三极管控制这样可以在夜间自动关闭背光进入省电模式。按键电路我设计了3个独立按键KEY1设置键、KEY2加、KEY3减分别接PB3、PB4、PA8。按键接法采用内部上拉按键另一端接地按下时引脚读到低电平。这里要注意一个细节PB3和PB4在STM32上默认是JTAG引脚如果要用作普通GPIO需要在初始化代码中关闭JTAG功能只保留SWD。这个问题让不少新手卡了很久后面我会专门讲。3. STM32代码架构与核心实现3.1 工程结构与初始化流程代码基于标准外设库SPL编写不用HAL库的原因是我个人觉得SPL的逻辑更直观适合学习底层原理。工程目录划分为HARDWARE外设驱动、SYSTEM系统配置、USER主逻辑、CORE启动文件。主程序的初始化流程是这样的int main(void) { HAL_Init(); SystemClock_Config(); // 配置系统时钟为72MHz delay_init(72); // 延时函数初始化 GPIO_Init(); // 初始化所有GPIO ADC1_Init(); // 初始化ADC1通道0和通道1 Timer2_Init(); // 定时器2用于DHT11时序控制 USART1_Init(115200); // 串口调试可选 LCD1602_Init(); // LCD初始化 KEY_Init(); // 按键初始化 EEPROM_Init(); // 使用内部Flash模拟EEPROM Read_Threshold(); // 读取保存的报警阈值 while(1) { DHT11_Read_Data(temperature, humidity); MQ2_Read_Value(); Flame_Detect(); Light_Read(); Process_Control(); // 核心逻辑阈值判断与执行器控制 Display_Update(); // LCD刷新显示 Key_Scan(); // 按键扫描 delay_ms(200); // 200ms刷新周期 } }3.2 DHT11驱动单总线时序的精确控制DHT11用的是单总线协议所有的时序控制都基于微秒级别的延时函数。主机发送起始信号后DHT11响应并连续输出40位数据湿度整数、湿度小数、温度整数、温度小数、校验和。每一位数据用不同的高电平持续时间区分26到28μs代表“0”70μs代表“1”。我在真机调试中遇到的典型问题是用普通延时函数模拟时序时由于编译器优化级别不同延时不准确导致读到的数据全为0。解决方案是使用定时器做微秒级延时并且在读取时序时关闭中断防止其他中断打断时序采集。uint8_t DHT11_Read_Byte(void) { uint8_t i, dat 0; for(i 0; i 8; i) { while(DHT11_DQ_IN 0); // 等待低电平结束 delay_us(40); // 如果40us后仍为高说明是1 if(DHT11_DQ_IN 1) { dat | (1 (7 - i)); while(DHT11_DQ_IN 1); // 等待高电平结束 } } return dat; }3.3 ADC采集烟雾浓度与光照的量化处理MQ-2传感器的输出是模拟电压对应的物理含义是烟雾浓度。我在代码中进行了校准处理先将ADC采集的12位数字量转换为实际电压值再通过传感器手册中的灵敏度特性曲线将电压值映射到烟雾浓度等级。uint16_t ADC_Read_Value(ADC_TypeDef* ADCx, uint8_t channel) { ADC_RegularChannelConfig(ADCx, channel, 1, ADC_SampleTime_55Cycles5); ADC_SoftwareStartConvCmd(ADCx, ENABLE); while(ADC_GetFlagStatus(ADCx, ADC_FLAG_EOC) RESET); return ADC_GetConversionValue(ADCx); }我采用的是多通道轮询模式PA0采集烟雾电压PA1采集光照电压每次切换通道后加一个短延时等待ADC稳定。这里踩过一个坑ADC连续采集的数值跳动比较大尤其在电机或继电器动作时数值波动能到几十个ADC单位。后来我在软件上加了滑动平均滤波连续采8次取平均配合硬件上的RC滤波100Ω10μF数据就稳定了。烟雾浓度的电压阈值我设定为1.5V对应中等烟雾浓度光照阈值设定为1.2V对应环境明显变暗。这两个值不是固定的通过按键可以进入设置模式调整。3.4 核心联动逻辑阈值判断与执行策略系统联动逻辑是整个项目的灵魂我把它设计成一个状态机。状态划分为正常模式、预警模式、报警模式。正常模式温度低于温度上限、湿度低于湿度上限、烟雾浓度低于烟雾阈值、无火焰信号。所有执行机构关闭LCD显示“NORMAL”绿色LED闪烁表示系统正常运行。预警模式温度或湿度超过上限。启动排风扇温度高时或加热器湿度高时LCD显示预警类型。此时蜂鸣器不响但橙色LED常亮。报警模式烟雾浓度超标或检测到火焰。立即启动蜂鸣器间歇鸣叫响0.5秒、停0.5秒排风扇全速运转排烟红色LED快速闪烁周期100ms并通过串口输出报警信息“FIRE ALARM! Txx C Hxx%”。预警和报警之间可以同时触发比如温度高和烟雾浓度高同时出现优先处理报警逻辑。状态机的好处是逻辑清晰、不容易出现多个if判断互相覆盖的情况也便于后续扩展语音播报或短信报警功能。3.5 按键调阈值与参数断电保存阈值可调是我认为这个项目最容易被忽略但实用性很强的功能。实际粮仓环境中不同季节、不同存储物的温湿度要求不同写死阈值等于功能残疾。我的实现思路是按下KEY1进入设置模式LCD第二行显示当前可调参数温度阈值→湿度阈值→烟雾阈值→光照阈值KEY2和KEY3分别执行加减。连续按KEY1可以循环切换参数长按KEY1超过2秒退出设置模式并保存参数。参数保存使用STM32内部Flash模拟EEPROM。F103的Flash支持按页擦除每页1KB我将最后一页作为参数存储区。写入前先擦除整页再写入参数结构体。读取时直接指针访问指定地址。注意Flash写入前必须确认主频稳定且写入过程中不能断电否则容易损坏Flash。实际上我测试了连续100次写入Flash都能正常读写但官方手册说Flash擦写寿命大约1万次所以代码里我加了一个判断——只有参数值发生变化时才执行写Flash操作。4. Proteus仿真搭建与调试4.1 仿真元件清单与接线要点Proteus仿真可以说是这套系统快速验证的最佳拍档不用焊接硬件就能先跑通逻辑。我用的版本是Proteus 8.9元件清单如下STM32F103C8在元件库搜索“STM32F103C8”即可找到DHT11Proteus 8.6以上版本自带DHT11模型搜索“DHT11”MQ-2Proteus没有直接的MQ-2模型我用一个电位器POT-HG模拟其模拟输出手动调节电压模拟烟雾浓度变化火焰传感器用一个按键开关模拟按下为低电平模拟检测到火焰释放为高电平光敏电阻同样用POT-HG模拟调节电位器改变光照电压LCD1602搜索“LM016L”或“LCD1602”继电器用“RELAY”模型搭配NPN三极管驱动蜂鸣器搜索“BUZZER”注意选有源型接线时注意Proteus中STM32F103C8模型默认没有内部上拉所以按键外接上拉电阻我用10kΩ或者把GPIO配置为内部上拉但仿真模型的内部上拉不一定生效最好还是外接电阻保险。4.2 Keil生成HEX文件并烧录仿真在Keil中需要先配置生成HEX文件Options for Target - Output - 勾选“Create HEX File”。Proteus仿真烧录步骤很简单双击STM32F103C8元件在Program File中选择生成的.hex文件点击“OK”后开始仿真。这里有个关键点Proteus仿真运行后如果代码中有等待外部中断或者死循环的延时仿真速度会明显变慢。可以在Debug菜单中调整仿真速度或者使用“Run Simulation”的加速模式。不过我在仿真中遇到一个很典型的问题DHT11在Proteus中的时序模型和真机有差异有时候读出来全是0xFF。排查后发现是仿真模型对起始信号的时间要求比数据手册更严格需要把主机起始信号拉低时间从18ms缩短到10ms左右。真机上18ms没问题但仿真就过不去我修改了延时参数后仿真正常了。这说明一个问题仿真不是万能的它只能验证逻辑框架时序细节必须拿真机调。4.3 仿真方案扩展用虚拟仪表辅助调试仿真调试时我习惯加几个虚拟仪表辅助观察在ADC输入引脚挂一个电压表直观看到模拟量变化在继电器输出端挂LED方便确认执行器动作。这些虚拟仪表不会影响逻辑但对排查问题非常有帮助。更好的做法是开启Proteus的“Digital/Analog Graph”实时绘制ADC输入电压波形。我在调试烟雾阈值时就是通过示波器视图观察电位器调节过程中的电压变化进而确定合理的ADC采样周期和滤波参数。5. 常见问题与排查实操记录5.1 问题速查表我把这个项目从仿真到真机过程中遇到的高频问题整理成一张表每个问题背后都是一次真实的排查经历。现象可能原因排查方法与解决思路系统反复复位继电器无续流二极管反向电动势干扰电源加1N4007续流二极管测量继电器线圈两端的反向尖峰是否被钳位DHT11读数全为0延时函数不精确或被中断打断关闭中断读时序改用定时器精确延时LCD显示花屏4位模式下初始化时序错误或EN引脚抖动检查LCD初始化代码的延时保证EN至少保持1μs高电平并在写入前清零ADC数值跳动大电源噪声或采样频率过高硬件加RC滤波软件加滑动平均滤波采样间隔增大到10ms以上按键无效JTAG引脚冲突PB3/PB4在GPIO初始化中关闭JTAGGPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE)MQ-2初始读数偏高传感器预热不充分上电后延时5分钟再进入正常检测循环或者代码里做开机预热倒计时仿真运行缓慢Proteus中频繁刷新LCD延长刷新周期到300ms或在代码中只在数据变化时刷新LCD火焰传感器误报阳光中的红外成分干扰调节模块上的电位器降低灵敏度代码中连续检测3次低电平才确认报警5.2 一个典型故障的完整排查过程我印象最深的是继电器误动作问题。现象是系统运行一段时间后排风扇会无缘无故启动。起初以为是代码判断逻辑有bug反复检查阈值判断代码逻辑上没有任何问题。用串口打印传感器数据后发现温度值在正常范围内波动但偶尔会有一个异常大的跳变。进一步排查发现异常跳变总是出现在继电器动作的瞬间。继电器吸合时的大电流导致电源电压瞬间跌落ADC参考电压也跟着波动采集到的数据就出现了尖峰。软件上的滑动平均滤波虽然能过滤随机噪声但对这种周期性尖峰效果有限。最终的解决方案是双管齐下硬件上用大容量电解电容470μF做电源储能降低负载突变时的电压跌落软件上在继电器动作后增加20ms的静默期期间不进行ADC采样。从这个案例中我的体会是嵌入式系统的很多问题不是代码逻辑的问题而是电源完整性和信号完整性出了问题。遇到软件判断异常时先检查硬件环境往往能更快定位根因。6. 系统扩展方向与开源资料获取6.1 从“能跑”到“好用”的演进路径这套基础系统跑通之后你可以从以下方向继续演进难度逐步递增接入ESP8266/ESP32实现WiFi远程监控手机端通过MQTT协议查看实时温湿度和报警状态。这是最推荐的第二步因为软件上只需增加一个串口协议解析模块把传感器数据格式化成JSON字符串通过串口发给WiFi模块即可。用DHT22或SHT30替换DHT11提高温湿度采集精度。SHT30用I2C接口和现有代码的改动集中在驱动层业务逻辑完全不用动能直观感受到接口设计对可维护性的价值。增加OLED显示屏在一屏内显示所有数据曲线LCD1602的显示能力确实有限OLED用I2C接口只需占用两个引脚。增加GSM模块实现短信报警在没有WiFi的偏远粮仓短信是更可靠的报警手段。增加备用锂电池和充放电管理电路断电时系统仍能工作数小时如果配合NB-IoT模块甚至可以做到长期离线监控。6.2 开源内容说明这个项目我整理成完整开源包包含三部分内容源码工程完整的Keil工程带详细注释针对标准外设库和HAL库分别提供一份版本方便不同学习背景的人对照阅读。原理图文件AD绘制的原理图和PCB文件PDF版本方便直接查看。原理图按模块分区标注每个模块有中文注释新手可以直接照着抄。Proteus仿真文件已经调通所有模块的仿真工程下载后安装Proteus 8.6以上版本选择HEX文件路径即可直接运行。获取方式我放在文章末尾的说明中。这套系统我前前后后调试了两周最终稳定运行了一整夜没有异常才敢把代码和图纸放出来。如果你在复现过程中遇到问题欢迎在评论区带上你的现象描述和代码截图我基本每天都会看评论。做这个项目给我最大的感受是嵌入式系统开发的核心能力不是背下某个芯片的寄存器而是建立一套“分析现象—定位原因—验证解决”的思维闭环。任何一个看似简单的传感器模块接上主控、加上执行器、放到具体场景中之后都会暴露出你在Datasheet里永远找不到的问题。粮仓安防系统的价值不在于技术多前沿而在于它完整呈现了一个真实产品从需求到实现的全过程——数据采集、逻辑判断、执行输出、人机交互、异常处理一个都不少。你把它完全吃透了再去看那些带操作系统、带云平台的大型项目底层思路其实是相通的。