资讯详情

基于STM32的多传感器环境监测系统:从原理图到Proteus仿真实战

📅 2026/9/13 15:01:37 | 华诺云谱 👁 阅读
基于STM32的多传感器环境监测系统:从原理图到Proteus仿真实战
这套项目其实是我去年年底整理完毕的包含完整的STM32工程代码、可生产的原理图以及一套Proteus仿真工程。最初只是想给自己实验室做一台环境监测小设备后来陆续有朋友和同学找我要资料干脆抽出时间把所有文件补全、注释写清楚整理成一套开源项目放了出来。如果你正在做课设、毕设或者刚入手STM32想找个综合性强但又不至于啃不动的实战项目这套东西会很对你的胃口——它覆盖了GPIO、定时器、ADC、I2C、单总线通信、中断、状态机这些嵌入式基本功同时又是一套能真实跑起来、能测出数据的完整系统而不是那种点个灯就结束的demo。1. 这套系统到底做了什么事1.1 我最初想解决的实际问题实验室里放了几台仪器常年没人管夏天温度一高有些设备就开始不稳定但等发现的时候往往已经晚了。我需要的不是一台专业的工业环境监测仪而是一台能实时盯着温度、湿度、空气质量超限了能主动通知我最好还能把数据留下来的小设备。市面上成品环境监测仪价格从几百到几千都有功能倒是齐全但没法按我的需求二次开发数据接口也不开放。自己动手做一个其实不复杂关键是把“能用的系统”和“能跑通的demo”区分开。市面上的STM32开发板套件很多但大部分例程都是单个传感器单独演示真正组合在一起的时候时序冲突、ADC通道切换、电源噪声这些问题就全冒出来了。这套项目就是把传感器数据采集、本地显示、超限报警、串口数据输出串成一条完整的链路你拿到手之后烧进去就能跑也能很清楚地看到每一层是怎么工作的。1.2 监测指标与硬件构成总览环境质量这个说法很大真要全测齐全那得上一套气象站了。所以我在立项的时候做了取舍选了四类最有代表性的指标监测项传感器选型接口方式说明温度、湿度DHT11单总线经典入门传感器时序典型适合学习空气质量烟雾/可燃气体MQ-2ADC模拟量能测液化气、丁烷、烟雾等PM2.5/粉尘浓度GP2Y1010AUADC模拟量带PWM驱动夏普的经典粉尘传感器光照强度BH1750I2C数字输出直接读lux主控用的是STM32F103C8T6这是一颗Cortex-M3内核、64KB Flash、20KB RAM的小芯片资源虽然不算大但跑这套系统绰绰有余。输出端搭配一块0.96寸OLED屏I2C接口做实时显示一个无源蜂鸣器做报警三个按键用于切换显示页面和设置报警阈值一路串口用于和上位机通信。1.3 代码、原理图、仿真三份交付物各自的价值开源资料里我放了三个相对独立的部分。工程代码是核心用的是标准外设库Standard Peripheral Library之所以不用HAL库是我个人习惯问题后面会细说。原理图是PDF加工程文件两种格式用嘉立创EDA画的你可以直接打开看也可以照着画板。仿真工程则是Proteus 8 Professional版本里面已经把固件烧好了打开就能跑。三份东西对应三种使用场景如果你想改功能、学代码就看工程源码如果你想自己打板做实物就照原理图走如果你暂时手上没有硬件或者想先快速验证逻辑仿真工程可以直接在电脑上跑。对于正在做课设的同学这三份东西凑齐了从设计报告到实物演示再到仿真截图基本就完整了。2. 硬件选型这套组合背后的取舍逻辑2.1 为什么是STM32F103C8T6而不是别的选主控的时候很多人会有疑问测个温湿度、空气质量用STC89C52或者Arduino Nano不就行了吗确实能做51单片机资源太紧张跑个DHT11驱动加数码管显示就到头了想上OLED、想加联网模块、想做多路ADC采集Flash和RAM立刻捉襟见肘。Arduino倒是开发快但它的抽象层把底层寄存器全包住了做完一个项目你对MCU本身还是一无所知这对学习来说反而是损失。STM32F103C8T6这颗芯片现在价格已经打到很低的水平散片甚至不到十块钱它恰好站在“资源够用”和“有学习深度”的交叉点上。APB1/APB2两条总线上的外设时钟可以独立配置ADC支持规则组和注入组定时器有PWM和输入捕获这些机制是嵌入式的通用知识你在这里学会了以后换GD32、换N32、换其他Cortex-M芯片都能平滑迁移。而且这颗芯片的资料密度是全网最高的遇到问题随便一搜就能找到答案对新手极其友好。2.2 传感器逐个拆解接口、精度与成本DHT11是单总线数字温湿度传感器单总线是一条数据线既做供电又要传数据时序要求很严格。它的精度是湿度±5%RH、温度±2℃这个精度放在实验室环境监测里只能说够看谈不上准但它便宜、例程多、时序非常典型用来学习单总线协议是很合适的。如果你的项目对精度有要求可以平替成DHT22或者SHT30代码层只需要改驱动上层逻辑不用动。MQ-2属于半导体气敏传感器内部有一个加热电阻和一个二氧化锡半导体气敏层。工作时加热电阻要保持通电所以模块整体功耗不小实测大约150mW左右比DHT11高一个数量级。它的输出有模拟量AO和数字量DO两种我接的是AO通过STM32的ADC读取电压值再查气体浓度曲线换算成PPM。注意这个东西是“定性半定量”的它告诉你“气体浓度上升了”是准确的但具体是多少PPM误差可能有三四成用途定位是报警而不是精密计量。GP2Y1010AU这个传感器有点意思它有六个引脚其中一个引脚需要接PWM脉冲驱动内部红外LEDLED导通时照射到粉尘颗粒产生反射光接收管输出的电压经放大后送到VO引脚。所以它不只是简单接个ADC还需要定时器产生一个周期10ms、脉宽320μs的脉冲而且采样电压要在脉冲开始后某个特定时间窗口内读取时序上有个讲究后面代码部分会细讲。BH1750是用I2C接口的数字光照传感器芯片内部集成了光电二极管、积分放大器和16位ADC直接通过I2C寄存器读出来的就是 lux 值不需要自己做电压换算。I2C总线需要外接上拉电阻模块上一般自带了如果自己画板就要记得加。2.3 供电与电平匹配的注意事项整套系统的供电路径是这样的USB的5V进来一部分直接给传感器模块供电DHT11、MQ-2、蜂鸣器都支持5V另一路通过AMS1117-3.3稳压到3.3V给STM32、OLED和BH1750。这个设计是故意的——如果所有东西都统一3.3V供电也行但MQ-2的加热丝在5V下工作状态最标准DHT11在5V下的时序参数也最典型。但这里有个非常容易踩的坑5V供电的传感器信号线输出到3.3V的STM32引脚电平会不会超DHT11的数据引脚在5V供电时高电平接近5V直接接在STM32的PB口上长期工作有损坏风险。我实测的情况是短时间问题不大但不太稳妥。项目中做的处理比较简单实用DHT11的数据线上串联一个1kΩ电阻再对地接一个3.3V稳压管。如果你是直接用现成模块很多模块板上已经做了电平转换那就省心很多。另外系统内部所有传感器、主控必须共地否则I2C和单总线通信会出现各种随机性错误。提示自己画板的时候电源入口处一定要加一个100μF电解电容和0.1μF瓷片电容并联放在USB座附近。这个位置少两个电容ADC采样的噪声会让你怀疑人生。3. 原理图设计从最小系统到各传感器接口电路3.1 STM32F103C8T6最小系统的四个必要部分所谓最小系统就是芯片能运行起来的最少外围电路。它有四个必要部分电源、晶振、复位、启动模式配置。电源部分STM32F103C8T6有VDD、VDDA、VBAT等电源引脚每一个VDD引脚旁边都要就近放一个100nF去耦电容VDDA引脚要接一个1μF0.01μF的组合滤波电容。曾经见过有人图省事几个VDD共用一颗电容结果系统经常莫名复位查了半天最后才发现是电源纹波问题。晶振部分我用的是8MHz主晶振加32.768kHz副晶振。8MHz经过芯片内部的PLL倍频后得到72MHz主频这是F103的最高工作频率。晶振的两个引脚各接一个20pF负载电容到地这个电容的取值不是随便拍的它取决于晶振本身的负载电容参数一般在数据手册里会标。如果负载电容偏大偏小最直接的表现就是RTC走时不准或者系统偶尔启动失败。32.768kHz副晶振主要是给RTC用的如果你不需要RTC功能这颗晶振可以直接省掉我用上是为了以后加时间戳功能。复位电路就是一个10kΩ上拉电阻加一个0.1μF电容到地NRST引脚低电平复位。启动模式配置就是BOOT0和BOOT1两个引脚BOOT0通过10kΩ电阻下拉到地BOOT1同样下拉这样芯片从内置Flash启动正常工作。3.2 传感器接口电路怎么设计最可靠DHT11的接口电路看起来就一个上拉电阻但细节在下拉。DHT11的数据引脚内部是开漏输出必须靠外部上拉电阻才能输出高电平典型值是4.7kΩ。上拉电阻太小总线拉低时电流太大上拉电阻太大总线恢复高电平的速度变慢在长线传输的时候容易出时序错误。项目中我在数据线上串了一个1kΩ电阻做人机交互口的保护实测对时序影响很小。MQ-2模块的AO输出直接进STM32的ADC输入引脚就行但要注意模块的AO输出电压范围。MQ-2模块的AO在干净空气中大约是0.1V到0.5V浓度升高时电压上升最高能到4V左右。STM32的ADC输入范围是0到3.3V以VDDA为参考如果直接把4V送到PA1引脚超出ADC量程不说长期还可能损伤引脚。所以硬件上我加了一级电阻分压AO经过一个10kΩ串联电阻和6.6kΩ并联电阻分压到地把满量程压到3.3V以内。实际如果你买的模块自带电压比较器也可以从DO引脚接数字量但那就丢了浓度连续变化的信息我不推荐。GP2Y1010AU的驱动电路稍微复杂一点它需要PWM驱动LED。按数据手册推荐的电路LED驱动端引脚2通过一个150Ω电阻接MCU的PWM输出LED供电端引脚1接5V中间还要加一个220μF的电解电容。VO输出引脚引脚5通过一个100kΩ电阻和一个1.5nF电容组成低通滤波器进入ADC。我在这里加了两个细节一是在采样软件里加了多次平均二是在PCB布局时让这个模拟输出走线尽量短、远离PWM走线不然粉尘数据会随着你PWM频率周期性跳动看起来像是电路坏了。3.3 去耦与布线的实战经验原理图设计完成后画PCB时还要注意几点这些经验来自我自己打样回来的教训。供电走线遵循“星型接地”思路5V电源进来先到传感器供电节点再到稳压器3.3V出来优先经过主控附近的大电容再分配到OLED、BH1750等外设。模拟地和数字地不强制分离但对于GP2Y1010AU和MQ-2这种模拟信号ADC参考电压的稳定性直接决定了测量精度。如果PCB空间允许在LQFP48封装的芯片背面放一个2.2μF的钽电容对ADC基准稳定性有明显改善。我项目的原理图里还有几个排母扩展口把USART1、I2C1、SPI1、SWD、3.3V、5V、GND都引出来了。这套系统做完了之后必然想加功能预留这几个口子后面想挂ESP8266做联网上报或者挂GPS模块都不用重新设计板子直接飞线就行。4. 代码架构与关键驱动实现4.1 工程结构怎么分代码五个模块各干什么工程代码我按功能拆成了5个模块目录Project/ ├── Core/ // 启动文件、系统时钟配置 ├── BSP/ // 板级支持包LED、蜂鸣器、按键 ├── Driver/ // 传感器驱动DHT11、GP2Y1010、MQ2、BH1750 ├── APP/ // 应用逻辑数据显示、报警判断、页面切换 └── System/ // 中断处理、延时函数延时函数我强烈建议用定时器实现不要用简单的循环空转。DHT11的时序要求非常精确us级别的延时如果靠for循环估算在编译器优化等级不同的情况下误差很大同一段代码在-O0和-O2下跑出来的延时能差一倍。我封装了一个基于SysTick的延时函数us和ms两个粒度都有在系统时钟72MHz下精度足够满足DHT11和GP2Y1010的时序需求。4.2 DHT11单总线时序最容易翻车的一段代码DHT11的数据读取是这套系统中第一个容易翻车的地方。它的通信协议大概是这样的主机先把总线拉低至少18ms然后释放总线延时20-40μs等待DHT11响应。DHT11会把总线拉低80μs作为响应信号然后再拉高80μs之后开始传输40位数据。40位数据里每一位的表示方法和常见的UART不太一样DHT11用高电平持续时间来区分0和1。数据位开始前先拉低50μs然后拉高高电平持续26-28μs表示0持续70μs表示1。读取的代码如下uint8_t DHT11_ReadByte(void) { uint8_t i, data 0; for(i 0; i 8; i) { while(DHT11_DQ_READ() 0); // 等待50us低电平结束 delay_us(40); // 延时40us判断电平 if(DHT11_DQ_READ() 1) data | (1 (7 - i)); // 是高电平且延续判定为1 while(DHT11_DQ_READ() 1); // 等待位结束 } return data; }这段代码的坑在于delay_us(40)这一步必须在两次电平读取之间做得很准。延时太短会把0误判成1延时太长又会把1误判成0。我实测下来在4.7kΩ上拉、数据线长度不超过20cm时40μs是个比较居中的值。另一个大坑是两次读取之间的间隔。DHT11数据手册明确要求两次读取间隔大于1秒实际上如果间隔太短传感器会不响应总线一直保持高电平读出来全是0xFF。我刚开始测试的时候误以为程序跑飞了后来查了手册才发现是节奏太快。所以我代码里加了个状态机每2秒才触发一次温湿度采集。4.3 MQ-2和GP2Y1010的ADC采集要注意什么ADC这部分我用的ADC1的规则组开启DMA同时采集MQ-2的AO、GP2Y1010的VO两个通道还有一个通道接在3.3V分压电阻上做参考校准。使用DMA的好处是CPU不用每次等转换完成数据自动搬运到内存数组里主循环直接读数组就行。MQ-2的浓度换算不是线性的。它测的是气体浓度导致的电导率变化换算关系接近对数曲线。如果要做比较准确的PPM显示需要查传感器手册中的灵敏度特性曲线取几个关键点做对数插值。我这套代码里给了一个简化版的换算函数把电压映射到相对浓度百分比足够做趋势展示和阈值报警。如果你要精确测量建议还是用专门的电化学传感器或者红外传感器。GP2Y1010的ADC采集时机同样有讲究。它靠PWM脉冲驱动LEDLED发光后反射光信号要经过一段传播时间到达接收管所以VO输出比LED脉冲有一个相位延迟。数据手册推荐的时序是PWM周期10ms高电平时间320μs而ADC采样要在PWM上升沿之后280μs开始、再往后推一段时间读取。我的实现是用定时器输出PWM的同时在PWM中断里设置一个标志位ADC的定时器触发延迟280μs后启动规则组转换。等效的代码结构大致是这样的void TIM3_PWM_IRQHandler(void) { if(TIM_GetITStatus(TIM3, TIM_IT_CC1) ! RESET) { TIM_ClearITPendingBit(TIM3, TIM_IT_CC1); ADC_SoftwareStartConvCmd(ADC1, ENABLE); // 此时开始采样 } }GP2Y1010的电压与粉尘浓度的关系数据手册给了一个典型曲线大概在0到0.5V对应0到500μg/m³左右但这是特定粉尘类型下的参考值实际上不同粉尘的反射特性差别很大。我把原始电压和折算浓度都通过串口输出校准的时候你拿一个标准粉尘仪在旁边比对着改系数就行了。4.4 OLED显示与报警状态机的设计思路OLED用的0.96寸SSD1306驱动芯片I2C接口128x64分辨率。驱动代码用的标准SSD1306命令集我把显示页面拆成四个首页显示温湿度第二页显示空气质量第三页显示粉尘浓度和光照度第四页显示报警阈值设置。按键短按切换页面长按进入设置模式。报警部分我用了个简单的状态机没有用裸奔的if-else堆逻辑。三个状态正常、注意、报警。温湿度、气体、粉尘、光照四个维度各自有上下限阈值任何一个指标越限就把状态往上推一级。报警时蜂鸣器不同频率鸣叫OLED上也同步显示是哪个参数出问题。设置界面上可以用按键调整某个维度的阈值调整完的参数我存在Flash的最后几页里掉电之后不丢。5. Proteus仿真搭建在电脑上先把逻辑跑通5.1 为什么这套项目要配一份仿真工程很多嵌入式项目做完之后只发源代码和原理图对于手头没硬件、或者还处在学习阶段的朋友来说门槛还是高。我配了一份Proteus仿真工程目的就是让大家在没有实物的情况下也能完整体验这套系统的工作流程——烧录固件、观察OLED显示变化、调节传感器输入看报警响应、打开虚拟串口看数据。做课设的时候仿真的截图和视频甚至可以直接作为设计报告的材料省去拍照录视频的麻烦。Proteus里STM32的元件库其实有几种不同的模型我用的版本是Proteus 8 Professional元件搜索STM32F103C8T6就能找到。DHT11、MQ-2这些传感器Proteus元件库里也有对应的仿真模型DHT11会实时计算出温湿度返回MQ-2模型可以通过滑动变阻器模拟气体浓度变化。GP2Y1010没有现成模型我用了可调电阻分压电路来模拟它的输出电压用不同电压值代表不同粉尘浓度。5.2 仿真工程一步步搭建过程搭建步骤大致是这样的从元件库拖出STM32F103C8T6、DHT11、LM016LLCD、滑动变阻器、虚拟终端等元件摆放到原理图编辑区。按我提供的仿真原理图连接电路注意DHT11数据线要加上拉电阻OLED这里仿真里用LCD1602替代显示效果等同更直观。双击STM32芯片在Program File里选择用Keil编译生成的hex文件设置晶振频率为8MHz。点击运行LCD开始显示温湿度数据调节滑动变阻器改变模拟量输入观察报警和数据显示的变化。用Virtual Terminal连接USART1的TX/RX引脚可以实时看到串口输出的传感器数据报文。仿真里最需要注意的一点是Proteus的STM32模型对GPIO初始化代码反应比较敏感。如果你在工程配置里开了RTOS或者在main函数里用了某些特殊的启动方式可能仿真跑不起来。我的工程里没有上RTOS就是裸机轮询加中断仿真兼容性很好。5.3 仿真和实物之间的差异Proteus仿真最大的价值是验证逻辑但千万别以为仿真跑通了实物就没问题。我遇到过的情况是DHT11在仿真里读得稳稳的上实物后偶尔读到乱码仿真里ADC数值纹丝不动实物上一看波动能有两三个百分点的幅度。这些差异来自仿真模型把传感器理想化了省掉了电气噪声、时序漂移、器件离散性这些真实因素。所以我对大家的建议是仿真工程用于功能验证和设计报告但最终一定要回到实物上测试一遍。项目里的代码和原理图都是经过实物验证的我实打实焊了三块板子测试大方向不会跑偏但你自己的实际布局走线、电源质量会影响一些模拟量数据需要现场微调。6. 实测调参中的几个坑与后续扩展思路6.1 DHT11误码率高的排查链路我第一版板子打样回来DHT11读取十次大概要错两三次表现为温度突变到几十度或者湿度出现负值。排查过程我一步步列出来给大家一个参考第一步看供电。用万用表量DHT11的VCC和GND之间电压是否稳定在5V±0.2V。我这边实测电压一直是稳的排除。第二步看时序。用逻辑分析仪抓DHT11数据线的波形和手册上的时序图做对比。我抓到的波形显示主机拉低时间够长传感器响应正常但是数据位的0和1不好区分高电平持续时间介于临界值。第三步查上拉电阻。把4.7kΩ换成10kΩ之后数据线高电平恢复变慢问题不但没解决还更严重了。换回4.7kΩ问题依旧。第四步查线路长度和干扰。DHT11的数据线我走了大约15cm的排线把它缩短到5cm后误码率立刻下降。原来是排线过长导致信号边沿变缓大概属于IO翻转沿不干净从而干扰了延时判断。最后我改了驱动代码把时序判断的延时参数从35μs调整到45μs配合缩短排线问题彻底解决。6.2 ADC数据波动的几种对策MQ-2和GP2Y1010的输出在实物上都有波动MQ-2是加热丝温度波动引起的GP2Y1010是气流中粉尘颗粒分布不均引起的。我给ADC读数加了一个滑动平均滤波器保持最近10次采样的队列每次新数据进来去掉最老的数据再取均值效果很明显波动从±5%压到±1%左右。报警判断必须加回差。假设你设置温度上限35℃报警如果不加回差温度在34.9和35.1之间来回抖动时蜂鸣器会反复启停很烦人。我的做法是温度升到35℃或以上才触发报警但回落到33℃即阈值减2℃才解除报警。这样2℃的回差就避免了临界值抖动。6.3 低功耗和联网扩展方向如果你想把系统做成电池供电的可以考虑三件事第一把MQ-2的加热丝改成间歇供电每30秒加热一次、测量一次其余时间断电功耗立刻降一大半第二MCU进入Stop模式用RTC定时唤醒每两分钟采集一轮数据然后继续睡眠第三把OLED换成E-ink或者只在唤醒时点亮能进一步压低功耗。联网扩展是最多人问的。STM32F103C8T6没有以太网MAC最简单的方式是串口挂一个ESP8266或者ESP01模块AT指令把数据通过Wi-Fi上报。上位机可以接到阿里云物联网平台或者EMQX这类私有MQTT broker再配合一个小程序做远程查看。项目原理图里我已经预埋了UART1的排针接口就是给这个扩展留的。实际用下来这台设备在我实验室已经稳定跑了两个多月没重启过。中间有几天温度异常报警一查是空调坏了也算侧面验证了这套系统的价值。你把这个项目做完之后很自然会想着加联网、加数据存储、加更多传感器——到那个时候你会发现底层驱动全部是现成的扩展工作只是往上叠加逻辑而已。最后提一句代码里所有模块都有详细的注释原理图文件和仿真工程放在一起。如果你照着做遇到问题先看串口输出然后量传感器供电、查时序基本都能解决。祝顺利。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。