资讯动态

STM32驱动红外PM2.5传感器:从ADC采集到数据标定实战

发布时间:2026/9/24 10:04:24 来源:尧图企业网站定制
1. 从一颗红外粉尘传感器说起为什么选它、能做什么搞环境监测类的小项目PM2.5检测几乎是绕不开的一环。我最早接触这类需求是在做一个室内空气质量盒子的时候当时摆在面前的有两条路一是用激光散射式传感器数据准、能测到0.3微米颗粒物但价格普遍偏高而且对气路结构有要求风扇、激光头、光电接收管一套下来体积不小二是用红外光学式粉尘传感器比如常见的GP2Y1010AU0F这一类价格便宜、接口简单、体积小巧虽然精度和一致性比不上激光方案但用来做趋势判断、超标报警、教学演示完全够用。这次要聊的就是后者——STM32连接红外PM2.5传感器。这个组合在嵌入式入门、课程设计、毕业设计里出现频率极高原因很直接STM32的ADC资源丰富、定时器灵活、GPIO驱动能力强而红外粉尘传感器本质上就是一个模拟电压输出器件两者搭配起来硬件成本低、软件逻辑清晰非常适合拿来练手也适合快速做出一个能用的空气质量检测原型。红外PM2.5传感器的工作原理并不复杂。它内部有一个红外发光二极管和一个光电接收管两者成一定角度布置。当空气中存在悬浮颗粒物时颗粒会把发光管发出的红外光散射到接收管上颗粒浓度越高散射光越强接收管输出的电压信号就越大。传感器内部通常还会集成一个运放电路把微弱的光电流转换成0~5V左右的模拟电压输出。STM32要做的就是采集这个电压经过换算得到对应的PM2.5浓度值。这里有一个关键点很多人一开始会忽略红外粉尘传感器输出的电压和PM2.5浓度之间并不是严格的线性关系而且不同批次、不同厂家的传感器曲线还不一样。所以如果你追求“精确测量”这个方案本身就不合适但如果你要的是“有没有超标”“趋势怎么变”“能不能触发报警”那它完全胜任。我在实际项目里一般会把它定位成“定性或半定量检测”这个定位想清楚了后面的标定和代码设计就不会走偏。适合看这篇内容的人我大致分三类第一类是刚学完STM32的GPIO和ADC想找个综合小项目练手的第二类是在做课程设计或毕业设计需要快速搭出一个能演示、能出数据的空气质量检测模块的第三类是有一定嵌入式基础想把这个传感器接入自己现有系统做联动控制的。不管你是哪一类下面的内容都会从硬件接线、时序驱动、ADC采集、数据换算到常见坑一步步拆开讲。2. 硬件方案设计与核心器件选型思路2.1 红外PM2.5传感器的引脚与驱动特性市面上常见的红外粉尘传感器型号五花八门但引脚定义大同小异。以最典型的GP2Y1010AU0F为例它一般有6个引脚V-LED、LED-GND、LED、S-GND、Vo、Vcc。别看引脚多真正需要接的也就那么几根但每一根都有讲究。V-LED和LED-GND这是内部红外发光管的供电引脚注意它不是直接接3.3V或5V常供电的。发光管需要脉冲驱动通常要求以一定频率典型是280微秒周期其中脉冲宽度约320微秒的采样窗口间歇点亮。为什么要脉冲驱动一是降低功耗二是延长发光管寿命三是配合接收端做同步采样避开环境光的干扰。LED这是发光管的控制引脚接STM32的一个GPIO通过输出高低电平来控制发光管的通断。实际电路中这个引脚和V-LED之间通常会串一个限流电阻有些模块已经集成好了有些需要自己加。Vcc和S-GND传感器内部运放和接收电路的供电一般接5V。这里要注意传感器输出Vo的电压范围可能超过STM32的ADC参考电压如果STM32是3.3V供电ADC满量程就是3.3V而传感器在浓度较高时输出可能接近4V甚至更高直接接进去会打坏引脚。所以要么给传感器供3.3V但可能影响内部运放工作点要么在Vo和ADC引脚之间加一个分压电阻网络。Vo模拟电压输出接STM32的ADC通道。我个人的习惯是传感器Vcc接5VVo经过两个电阻分压后再进STM32的ADC引脚。分压比怎么算假设传感器最大输出4.5VSTM32 ADC参考3.3V那么分压比至少要4.5/3.3≈1.36取1.5倍比较稳妥。用两个电阻上电阻10k下电阻20k分压后电压是原来的2/34.5V变成3.0V安全落在量程内。这个计算过程看着简单但实际调试时如果忘了分压轻则数据满量程卡死重则烧引脚我见过不止一个新手栽在这里。2.2 STM32最小系统与ADC通道选择STM32这边用最小系统板就够了。F103C8T6这类经典款ADC1有多个通道选一个方便接线的就行比如PA0对应ADC1_IN0。为什么优先选ADC1因为标准库和HAL库对ADC1的例程最多配置起来最省事遇到问题也容易搜到答案。ADC的配置有几个参数需要想清楚分辨率STM32F103的ADC是12位的采集范围0~4095对应0~3.3V。这个精度对红外传感器来说绰绰有余因为传感器本身的误差远大于ADC的量化误差。采样时间这个参数很关键。传感器输出阻抗不低如果采样时间太短ADC内部的采样电容充不满读数会偏低且不稳定。我一般会把采样时间设成55.5个周期或239.5个周期宁可慢一点也要稳。实测下来采样时间设短了数据跳动能到几十个LSB设长了就稳定很多。转换模式单次转换就够了配合定时器触发或者软件触发。如果要做连续监测可以用定时器定期触发ADC这样采样间隔均匀数据做滤波也方便。参考电压直接用STM32的VDDA作为参考前提是VDDA供电要干净。如果板子上VDDA和VDD之间没有磁珠或电感隔离ADC读数可能会随系统负载波动。这个问题在低成本最小系统板上很常见解决办法是在VDDA引脚附近加一个0.1uF和1uF的电容能明显改善。2.3 驱动时序为什么不能一直点亮LED前面提到发光管要脉冲驱动这里展开说一下时序。传感器的数据手册里一般会给一个时序图LED控制引脚先拉低一段时间比如0.28ms然后拉高点亮发光管等待一段时间比如0.32ms让接收管输出稳定在这个窗口内采集Vo然后再拉低LED等待下一个周期。整个周期大约10ms左右也就是100Hz的采样率。这个时序如果用软件延时来做简单但占用CPU用定时器PWM来做解放CPU但配置稍复杂。我两种都试过对于这个项目软件延时完全够用因为10ms的周期对STM32来说很宽松主循环里干别的事也不耽误。但如果你后续要加显示屏、无线模块主循环任务多了还是建议用定时器中断来驱动LED和触发ADC这样时序更稳。注意LED控制引脚拉低时发光管是熄灭的此时采集到的Vo是“暗电压”也就是环境光造成的底噪。有些驱动方案会同时采集亮电压和暗电压两者相减得到有效信号这样能进一步抑制环境光干扰。如果你的使用环境光线变化大这个做法值得加上。3. 从接线到出数完整实操流程拆解3.1 硬件连线与供电检查先把硬件连起来。我按自己的习惯列一个接线表你可以对照着接传感器引脚接到哪里备注V-LED5V通过限流电阻有些模块内部已集成电阻LED-GNDGND与STM32共地LEDSTM32 GPIO如PB0控制发光管通断Vcc5V传感器内部电路供电S-GNDGND与STM32共地Vo分压网络后接PA0分压比约1.5:1接完线先别急着上电用万用表量一下几个关键点Vcc是不是5VVo在LED熄灭时是不是接近0V分压后的电压是不是在3.3V以内。这一步花两分钟能避免后面烧板子。上电后先写一个最简单的测试程序让LED引脚以10ms周期翻转用示波器或者逻辑分析仪看波形。如果没有示波器可以用STM32的另一个ADC通道去采LED引脚前提是分压看数值是不是在跳变。确认时序对了再去看Vo的ADC读数。3.2 ADC采集与软件滤波ADC采集的代码框架大概是这样主循环里先拉低LED延时0.28ms拉高LED延时0.32ms启动ADC转换等转换完成读值拉低LED延时到10ms周期结束。读到的原始值先存到一个数组里做滑动平均滤波。滑动平均的窗口取多大我试过8、16、32最后常用16。窗口太小滤波效果不明显太大响应变慢。16个点做平均既能压住随机噪声又不会让数据滞后太多。如果环境干扰特别大可以在滑动平均之前先做一次中值滤波去掉明显的尖峰。#define FILTER_LEN 16 uint16_t adc_buf[FILTER_LEN]; uint8_t adc_idx 0; uint16_t get_filtered_adc(void) { uint32_t sum 0; for (int i 0; i FILTER_LEN; i) { sum adc_buf[i]; } return sum / FILTER_LEN; }这段代码很简单但有个细节数组初始化时最好先填满有效数据否则前16次读数的平均值会偏小。我一般在上电初始化时连续采16次填满数组再进入正常循环。3.3 电压换算与浓度标定拿到滤波后的ADC值先换算成电压voltage adc_value * 3.3 / 4095。注意这里用的是分压后的电压如果要还原传感器Vo的实际电压还要乘以分压比。比如分压比是1.5那么vo_actual voltage * 1.5。接下来是电压到浓度的换算。不同传感器的曲线不同常见的一种近似关系是在洁净空气中传感器输出一个基准电压比如0.9V左右随着浓度升高电压线性增加每增加0.1V大约对应一定浓度的PM2.5。但这个线性关系只在低浓度段成立高浓度段会饱和。我一般会做两点标定一点是洁净空气比如用密封袋装干净空气记录电压值V0另一点是已知污染源比如点一根香用激光传感器做参考记录电压值V1和对应的浓度C1。然后用线性插值C (V - V0) * C1 / (V1 - V0)。这个标定很粗糙但比直接用厂家给的通用曲线靠谱因为每个传感器的个体差异真的不小。实操心得标定时不要只测一次每个点测10组数据取平均而且要在传感器预热5分钟后测。红外传感器刚上电时输出会漂移预热后稳定很多。这个预热时间很多人会忽略导致标定数据不可重复。3.4 数据输出与联动逻辑数据算出来后可以通过串口打印到电脑上看也可以接OLED屏显示。串口输出最简单用printf重定向到USART1格式化成一行行的数据方便用串口助手或者Python脚本记录。如果要做联动比如超标报警逻辑也很直接设定一个阈值比如浓度超过75微克每立方米就点亮LED或驱动蜂鸣器。这里要注意加迟滞否则浓度在阈值附近波动时报警会频繁开关。迟滞的做法是超过阈值5才开报警低于阈值-5才关报警。#define THRESHOLD 75 #define HYSTERESIS 5 if (concentration THRESHOLD HYSTERESIS) { alarm_on(); } else if (concentration THRESHOLD - HYSTERESIS) { alarm_off(); }这个迟滞逻辑看着简单但在实际产品里非常有用能避免报警器“抽风”。4. 调试路上绕不开的那些坑与排查技巧4.1 ADC读数跳动大、不稳定这是最常见的问题。原因可能有几个一是采样时间太短前面说过加长采样时间能改善二是VDDA供电不干净加滤波电容三是传感器供电和STM32供电共地不良检查地线连接四是环境光干扰尤其是日光灯和阳光会直接调制到传感器输出上。排查顺序我一般是先看电源用示波器看VDDA和传感器Vcc的纹波再看地线确认所有GND都接在一起且线尽量短最后看环境光用手遮住传感器看读数是否稳定。如果遮住就稳了那就是环境光问题解决办法是在传感器周围加遮光罩或者采用亮暗电压相减的方案。4.2 数据始终满量程或接近零满量程通常是分压没做或者分压比不对传感器输出超过3.3V直接进ADC。接近零则可能是LED驱动引脚没接对发光管根本没亮或者传感器供电没上。用万用表量一下Vo在LED点亮时的电压如果一直是0检查LED引脚波形和限流电阻。还有一种情况是ADC通道配置错了比如代码里写的是ADC1_IN0实际接的是ADC1_IN1读到的就是悬空引脚的噪声。这个错误很低级但很常见尤其是用CubeMX生成代码后手动改引脚的时候。4.3 串口打印乱码或没输出串口问题一般出在波特率、时钟配置和引脚映射上。STM32F103的USART1默认在PA9和PA10如果重映射了要记得改配置。波特率用115200最通用但要注意系统时钟和APB2分频如果系统时钟不是72MHz波特率会偏。用CubeMX的话这些都会自动算好手动配标准库就要自己核对RCC配置。4.4 常见问题速查表现象可能原因排查方法ADC读数跳动大采样时间短、电源纹波、环境光加长采样时间、加滤波电容、遮光读数满量程未分压、分压比不对量Vo电压检查分压电阻读数接近零LED未驱动、传感器未供电量LED引脚波形、量Vcc串口乱码波特率不对、时钟配置错核对RCC和USART配置数据不变化ADC通道错、传感器坏换通道测试、换传感器预热后数据漂移正常现象预热5分钟后再标定4.5 几个容易被忽略的细节第一传感器的安装方向。红外粉尘传感器对气流方向有一定敏感性进气口和出气口不要被遮挡否则读数会偏低。如果放在盒子里要留通风孔但孔不要正对风扇或空调出风口。第二长期运行的积灰问题。红外传感器内部的光学腔体长期暴露在空气中会积灰导致基准电压漂移。我一般建议每三个月用干净的气吹一下进气口或者在设计时就加一层无纺布做预过滤。这个问题在短期演示中看不出来但做长期监测就必须考虑。第三温度影响。红外发光管的发光效率随温度变化接收管的暗电流也随温度变化所以传感器输出会有温漂。如果项目对温度范围要求宽要么加温度补偿要么选用带温度补偿的型号。我实测过同一浓度下冬天和夏天读数能差10%到15%。第四STM32的ADC参考电压。如果板子上有独立的VREF引脚最好外接一个精准的3.3V基准比如REF3033。用VDDA做参考的话VDDA本身的波动会直接反映到读数上。这个改动成本不高但对数据稳定性提升明显。5. 从能用到好用几个进阶优化方向5.1 用定时器触发ADC实现等间隔采样前面说的软件延时方案在主循环任务多的时候时序会抖。更稳的做法是用一个定时器比如TIM2设置成10ms更新中断在中断里控制LED引脚和启动ADC。这样采样间隔由硬件保证CPU只需要在ADC转换完成中断里读数据。这个改动代码量不大但数据的一致性会好很多尤其适合后续要做FFT或者趋势分析的场景。5.2 加入温湿度补偿如果手头有DHT11或SHT30这类温湿度传感器可以把温度和湿度也采进来做一个简单的补偿模型。虽然红外传感器对湿度的敏感度不如激光传感器但在高湿环境下颗粒物吸湿膨胀会改变散射特性读数会偏高。补偿模型不用很复杂一个线性修正就够了C_corrected C_raw * (1 k * (T - 25))k取经验值比如0.002每摄氏度。5.3 数据上云与OTA升级如果项目需要远程监测可以加一个无线模块把数据传到服务器。这里不展开具体协议只说一个思路STM32通过串口和无线模块通信定时上报浓度值服务器端做存储和可视化。如果后续要更新STM32的程序可以预留OTA接口通过无线模块接收固件包写入内部Flash再跳转执行。这个功能对毕业设计来说是个不错的加分项但实现起来要注意Flash分区和跳转地址的配置搞不好会变砖。5.4 用FreeRTOS做任务划分当项目功能变多比如同时要采传感器、刷屏幕、传数据、做报警裸机的主循环会越来越臃肿。这时候可以上FreeRTOS把不同功能拆成独立任务用队列传递数据。ADC采集任务优先级设高一点显示任务设低一点报警任务用事件标志触发。这样代码结构清晰也方便后续扩展。不过对于单纯测PM2.5来说FreeRTOS有点杀鸡用牛刀看你项目整体复杂度决定。6. 我个人在实际操作中的几点体会这个项目我从最早用洞洞板搭到后来画PCB前后做过好几版。最大的感受是红外PM2.5传感器的价值在于“便宜和够用”不要指望它给你实验室级别的数据。把它定位成“空气质量趋势指示器”你的心态会好很多项目也更容易做成。另一个体会是标定比代码重要。很多人代码写得漂亮但数据不准问题就出在标定上。花半个小时认真做两点标定比调一天滤波参数都管用。标定的时候记得记录环境条件温度、湿度、传感器预热时间这些都会影响结果。最后分享一个小技巧如果你手头没有激光传感器做参考可以用一个简单的办法做相对标定——在密闭小空间里点一根香记录传感器电压从低到高的变化曲线同时用手机摄像头对着香火拍照用图像里的烟雾浓度做粗略参考。虽然不精确但能帮你判断传感器是否工作正常、响应是否合理。这个方法我在早期调试时用过挺管用。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价