多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

STM32+红外PM2.5传感器GP2Y1010AU0F开发实战:从驱动时序到数据滤波

STM32+红外PM2.5传感器GP2Y1010AU0F开发实战:从驱动时序到数据滤波 1. 红外PM2.5传感器先搞清楚它测的到底是什么做环境监测类项目很多人第一反应就是去买激光PM2.5传感器比如攀藤的PMS5003、PMS7003系列。这类传感器确实精度高、响应快而且直接走串口输出数据接上就能读。但它的成本摆在那里一颗模组就要四五十块再加上扇叶、光路结构体积也偏大。在很多场景下尤其是做低成本空气质量检测盒、智能家居小模块、毕业设计原型验证红外式PM2.5传感器反而是更务实的选择。红外PM2.5传感器最典型的就是夏普的GP2Y1010AU0F市面上几乎所有国产兼容型号也都是基于这颗芯片的方案。它的工作原理是传感器内部有一个红外发光二极管和一个光电晶体管气流从传感器侧面的开口流过粉尘颗粒经过光路时会产生散射光光电晶体管接收到散射光后输出一个与粉尘浓度相关的电压信号。这个信号的幅度很微弱所以数据手册上会要求你在输出引脚上加一个RC滤波电路用来抑制高频噪声。这里有一个很多人容易混淆的点红外式传感器测的是质量浓度吗严格来说不是。它实际上是测光散射强度然后通过一个经验公式换算成PM2.5浓度。这意味着它受颗粒物成分、颜色、湿度的影响比较大。同样浓度下烟尘和灰尘的读数会有明显差异。所以这类传感器的定位是家用级趋势监测不是计量级检测。理解这一点很重要因为后续你做标定、做数据处理时才不会对绝对精度有不切实际的期望。从项目角度来说STM32连接红外PM2.5传感器核心链路就三条一是给传感器提供稳定的脉冲驱动信号二是采集传感器的模拟电压输出三是用软件把电压值换算成PM2.5浓度。这三条链路每一环都有坑下面逐个拆开讲。2. 硬件连接与电路设计采样电阻、电容和供电是三个最容易翻车的点GP2Y1010AU0F一共有6个引脚引脚定义不复杂但接错的人非常多因为这颗传感器是脉冲驱动方式的不能直接给它上电然后读引脚电压。它的正常工作流程是在LED驱动引脚上施加周期性的脉冲信号每个脉冲持续320微秒周期10毫秒然后在脉冲开始后的280微秒时刻去采样输出电压。先看完整的接线定义Pin 1V-LEDLED驱动引脚接STM32的定时器PWM输出脚或者用普通GPIO模拟脉冲Pin 2LED-GNDLED接地直接接GNDPin 3LEDLED电源正极接5V注意是5V不是3.3VPin 4GND传感器内部电路地接GNDPin 5Vo模拟电压输出接STM32的ADC引脚Pin 6Vcc传感器内部电路电源接5V看到这里你可能已经发现问题了这颗传感器需要5V供电而且LED驱动电压也是5V电平。对于STM32F103这类5V容忍的芯片来说ADC引脚可以直接读取传感器Vo输出但PWM输出引脚如果配置成推挽输出输出的是3.3V电平这会有什么影响实测下来3.3V脉冲驱动是可以工作的但LED发光强度会比5V驱动时弱一些导致散射信号幅度下降最终读数偏小。如果你的项目对精度要求不高用3.3V驱动就能跑通。如果想让传感器工作在标称状态就需要加一个简单的电平转换电路或者用NPN三极管搭一级驱动。我个人比较推荐用三极管方案成本几分钱而且电路简单、可靠性高。再说采样电路。数据手册的典型应用电路里Vo输出端串了一个150欧电阻再接一个22微法电容到地然后才接到ADC引脚。这个RC滤波器非常关键。如果不加这个电容ADC采集到的波形是这样的脉冲开始后电压快速上升然后衰减整个信号里面叠加了大量毛刺直接读出来的值跳得非常厉害。还有一个容易忽略的点是供电。GP2Y1010AU0F内部的LED是脉冲驱动的瞬时电流可以达到几十毫安。如果你用线性稳压器给传感器供电而稳压器的输出电容又比较小脉冲到来时会拉低Vcc电压这个电压跌落会直接影响LED发光强度进而影响测量结果。所以供电设计上我建议用独立的100微法电解电容跨接在传感器电源引脚附近如果条件允许用LDO给传感器单独供电别和STM32的供电直接共用一条细走线。STM32这边的ADC通道选择也有讲究。如果你用F103系列ADC1的通道很多随便选一个就行。但要注意GPIO要配置成模拟输入模式而不是浮空输入。很多人习惯把引脚初始化成GPIO_Mode_IN_FLOATING然后在ADC初始化后又发现读数异常其实就是模式配错了。模拟输入模式下GPIO的输入输出驱动器都会被关闭引脚呈现高阻态这是读取外部模拟信号的标准姿势。3. 驱动信号的生成方式定时器PWM是首选但采样时刻才是真正的核心GP2Y1010AU0F的驱动时序有严格参数脉冲周期10毫秒频率100Hz脉冲宽度320微秒采样时刻是脉冲上升沿后280微秒。这三个参数中脉冲周期和宽度只要不偏离标称值太远传感器都能正常工作但采样时刻如果偏了读出来的电压就会差很多。为什么因为Vo引脚输出的信号波形是这个过程的LED亮起后散射光被光电晶体管接收输出电压迅速爬升到达峰值后开始下降到脉冲结束时基本回落到基线。峰值出现在脉冲开始后大约280到300微秒的位置。你如果在脉冲开始后100微秒采样电压可能还在爬升途中如果在脉冲结束后采样几乎只能读到噪声。所以280微秒这个时刻点一定要卡准。用STM32实现这个时序有几个方案方案一是用定时器的PWM模式。配置一个定时器输出频率100Hz、占空比3.2%的PWM波形这样320微秒脉宽就精确实现了。PWM波形本身是硬件产生的不占用CPU稳定性非常高。然后用另一个定时器或者同一个定时器的另一个通道配合比较中断在PWM上升沿之后的280微秒触发ADC采集。方案二是直接用普通GPIO模拟脉冲。在定时器中断里控制GPIO拉高、延时320微秒、拉低同时在拉高后延时280微秒处启动ADC转换。这个方案实现简单代码直观但有一个隐患如果中断里还有其他耗时操作延时不准确采样点就会漂移。方案三是利用定时器的输出比较中断。定时器工作在比较模式第一次比较匹配时触发GPIO拉高并重装载比较值第二次比较匹配320微秒后触发GPIO拉低同时又在第一次匹配后280微秒处由另一个比较通道触发ADC。这个方案最精确代码也不算复杂。我最后实际用的是方案三的变体用高级定时器TIM1的CH1输出PWM然后用TIM1的更新中断配合定时器从模式做ADC触发。但这里有个细节需要你特别注意ADC的触发源可以配置成定时器输出比较事件但触发的是转换开始你必须在转换完成中断里读取结果而且每次PWM周期做一次采样就够了。从信号链路的视角看整体流程是这样的PWM上升沿到来LED亮起同时经过280微秒后Vo电压到达峰值此时ADC触发转换读出接近峰值的电压然后换算成浓度。一次完整测量只需要这一个数据点不需要连续采样。4. 采样电路与ADC配置为什么直接读原始值会飘到怀疑人生硬件层面搞定后现象都写在这里我们写一个最简单的程序每100毫秒读一次ADC把原始值通过串口发到电脑上然后用串口调试助手观察。你会发现数值在200到500之间疯狂跳动12位ADC分辨率下用手在传感器进气口扇一扇数值确实会变化但变化量淹没在噪声里面完全没法看。这其实是两个问题叠加的结果。第一个问题是刚才说过的RC滤波不够或采样位置不对第二个问题是ADC本身的噪声。STM32F103的ADC是12位的理论分辨率很高但实际在高速采样时转换结果的最低位经常抖动。如果你用连续转换模式不停地在转换完成中断里取值噪声会更明显。解决方法是做多次采样取平均。具体做法有两种思路思路A是快采快平均。在一次PWM周期内也就是10毫秒内连续采集多次比如16次然后求平均。由于采样都落在同一个脉冲周期内平均后的值更能代表当前时刻的信号强度。思路B是慢采慢平均。每个PWM周期只采一次采多个周期比如采50次耗时500毫秒对这50个值做平均或中值滤波。这种方式能滤掉气流波动带来的读数起伏。我在实际项目中两种都用过。最终保留下来的是思路B的变体每10毫秒采一个点采10个点做中值滤波然后再对中值结果做滑动平均。中值滤波的目的是剔除偶发的尖峰干扰比如有人从传感器旁边走过引起的扰动滑动平均则是平滑气流波动。ADC本身还有一个配置细节采样周期要适当拉长。STM32的ADC支持可编程采样时间最短1.5个ADC时钟周期最长239.5个周期。对于高频噪声滤波来说采样时间越长等效于在采样瞬间对信号做了一次积分能有效抑制高频干扰。我一般配置成55.5个周期或者71.5个周期配ADC时钟12MHz折算下来采样时间在4.5微秒到6微秒之间对GP2Y1010AU0F这种pin脚输出来说完全够用。还有一点如果你用的是F103系列ADC的参考电压就是VREF引脚默认接3.3V。你通过STM32读到的原始值对应的满量程是3.3V。但GP2Y1010AU0F的Vo输出范围是0到3.6V左右5V供电下在灰尘浓度极高时理论上可能超过3.3V导致ADC饱和。实际使用中一般家庭、办公室环境的PM2.5浓度对应的电压都在0.5V到2.5V之间不太会碰到饱和问题。但如果你要做工业粉尘监测最好先用示波器看一下最大输出再做分压处理。5. 从电压到浓度值标定曲线、温度补偿和那些经验公式GP2Y1010AU0F的数据手册里给了一个典型输出电压与粉尘浓度的关系曲线大致可以拟合成一个线性公式粉尘浓度单位mg/m³约等于0.5乘以输出电压单位V再减去某个偏置。但注意这个公式是典型值实际每个传感器个体之间存在分散性而且不同厂家生产的兼容型号曲线也会有差异。这里我需要说一句得罪人的话网上大量的STM32GP2Y1010AU0F教程里直接抄一个公式就拿来用比如浓度 0.17 * ADC值 - 0.1之类的抄来抄去很多甚至连量纲都是错的。任何时候拿到一个公式先看它的输入输出单位是不是符合物理意义再看它的适用范围是不是覆盖你的测量区间。理论上完整的换算关系应该是先用ADC原始值除以4095再乘以3.3V得到Vo的实际电压值。这个电压值经过数据手册给出的灵敏度折算后得到粉尘质量浓度。但更可靠的做法是如果你手头有标准粉尘源或者能借到一台激光PM2.5传感器做参考做一次两点或三点标定把自家传感器的输出电压和参考浓度做一条拟合曲线。标定方法不复杂。把参考传感器和红外传感器放在同一个密封箱子里点燃一根香烟头或者线香制造粉尘用风扇搅拌均匀等待浓度稳定。每稳定一个浓度点记录红外传感器的输出电压和参考传感器的读数至少取三个点比如背景浓度、中浓度、高浓度。然后做线性拟合得到自己的斜率和截距。这个过程做完你的传感器读数才算是自洽的。温度补偿方面红外传感器的光敏元件受温度影响比较明显。温度变化时即使粉尘浓度不变输出电压也会漂移。数据手册没有给出完整的温漂特性所以严格补偿很难做到。我的做法是在PCB上加一颗温湿度传感器比如DHT22或者SHT30在软件里设定一个经验补偿系数。每摄氏度的偏移量通过恒温箱实验测出来然后做线性补偿。如果你的项目对精度要求不太高这一步可以跳过但要意识到读数冬天和夏天可能会有一定差异。还有一个经验值可以分享传感器内部的LED会随着使用时间老化发光强度下降导致读数偏低。所以如果你的设备是长期在线运行的建议在软件里设计一个定期校准提醒比如每运行6个月提示用户校准一次。这在产品化的时候是需要考虑的。6. 完整的STM32代码实现从定时器到串口输出的一次性打通下面给一份我在实际项目中用过的核心代码框架。用的芯片是STM32F103C8T6开发环境是Keil MDK标准外设库。如果你用CubeMXHAL库逻辑是一样的只需照着移植。先看定时器初始化。我需要100Hz、320微秒脉宽的PWM直接用TIM3的CH1输出。void TIM3_PWM_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); TIM_TimeBaseStructure.TIM_Period 999; // 10ms周期100Hz TIM_TimeBaseStructure.TIM_Prescaler 719; // 72MHz/720 100kHz TIM_TimeBaseStructure.TIM_ClockDivision 0; TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, TIM_TimeBaseStructure); TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse 32; // 320us占空比3.2% TIM_OCInitStructure.TIM_OCPolarity TIM_OCPolarity_High; TIM_OC1Init(TIM3, TIM_OCInitStructure); TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); TIM_Cmd(TIM3, ENABLE); }这里注意定时器时钟配置。TIM3挂在APB1上APB1分频后是36MHz但定时器时钟会变成72MHz。Prescaler设为719计数频率100kHzPeriod设为999那么PWM频率就是100HzPulse设为32对应320微秒脉宽。这些数字是可以直接算出来的不是拍脑袋。ADC部分我选择PA1作为模拟输入对应ADC1的通道1。开启扫描模式但单个通道多次转换。void ADC1_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; ADC_InitTypeDef ADC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE); RCC_ADCCLKConfig(RCC_PCLK2_Div6); // ADC时钟12MHz GPIO_InitStructure.GPIO_Pin GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AIN; GPIO_Init(GPIOA, GPIO_InitStructure); 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_1, 1, ADC_SampleTime_71Cycles5); ADC_Cmd(ADC1, ENABLE); ADC_ResetCalibration(ADC1); while(ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while(ADC_GetCalibrationStatus(ADC1)); }这里用ADC_SampleTime_71Cycles5是为了让采样保持电容有足够时间充满电降低信号源内阻带来的采样误差。RC滤波电路中的22微法电容对ADC源阻抗影响不大但如果你想进一步降低噪声可以在软件里配合多通道规则组转换做过采样。接下来是每10毫秒触发一次采样和滤波计算的逻辑。我用TIM3的更新中断来做周期性任务。volatile uint16_t adc_raw 0; void TIM3_IRQHandler(void) { if (TIM_GetITStatus(TIM3, TIM_IT_Update) ! RESET) { TIM_ClearITPendingBit(TIM3, TIM_IT_Update); ADC_SoftwareStartConvCmd(ADC1, ENABLE); } } void ADC1_IRQHandler(void) { if (ADC_GetITStatus(ADC1, ADC_IT_EOC) ! RESET) { ADC_ClearITPendingBit(ADC1, ADC_IT_EOC); adc_raw ADC_GetConversionValue(ADC1); } }注意我在更新中断里触发ADC转换。这里有一个时序上的近似更新中断发生在PWM周期结束的时刻也就是下一个PWM周期即将开始的瞬间而LED是在这个时刻之后立刻点亮的。所以这个中断触发ADC的时机实际上是超前了采样点。严格来说应该在PWM上升沿之后280微秒触发ADC而更新中断这里更像是PWM周期起始沿。既然已经把PWM脉宽固定为320微秒周期为10毫秒如果在更新中断里启动ADC采样到的其实是上一个脉冲周期结束后的基线信号而不是峰值信号。这是个很常见的时序错误。正确做法是用另一个定时器或者用TIM3的比较匹配事件来触发ADC把比较值设为280对应的计数值。因为计数频率是100kHz280微秒对应的计数值是28。在你的主循环里按100毫秒的间隔读取adc_raw做滤波做换算再输出。如果直接用更新中断触发你会发现读数比实际粉尘浓度偏低因为采到的不是峰值。所以上面这份仅作为架构参考实际触发请用比较事件而不是更新事件。具体做法是TIM3_CH1输出PWM的同时配置TIM3_CH2为比较输出模式比较值设为28使能CH2的比较中断在中断里触发ADC转换。这样每个PWM周期的第280微秒ADC精确采样。7. 数据滤波与读数稳定性中值滤波、滑动平均和小数点处理滤波策略直接影响最终读数的可用性。我见过很多人把滤波做得很重结果传感器真实响应被拖得很慢——粉尘浓度已经上去了读数还要过好几秒才跟上。这种稳定是以牺牲响应速度为代价的不能接受。我的推荐方案是两级级联第一级是10点中值滤波第二级是5点滑动平均。10点中值滤波对应100毫秒的历史窗口5点滑动平均对应500毫秒的历史窗口。整体响应延迟约600毫秒对于室内空气质量监测来说是完全可以接受的肉眼观察数值变化时不会觉得滞后。中值滤波要注意10个点的中值其实是第5和第6个数的平均值。如果采样点数量选奇数会方便一些。我实际用的是9点中值滤波排序后取第5个然后对这个中值结果做滑动平均。数据换算成浓度后你还需要决定显示精度。很多人直接保留一位小数比如35.2 µg/m³但以红外传感器的真实精度后面这位小数基本没有意义。保留整数就够了。如果后续要做趋势图或者上报云端倒是应该保留原始更精确的ADC值或者用浮点保存中间计算结果方便后面做算法调整。串口输出协议也要提前想清楚。如果你只是给自己调试看直接printf打印即可。如果要接到显示屏或者上位机就要设计一个简单的协议帧比如帧头、数据长度、浓度值、校验位、帧尾。我见过有人在调试阶段用printf到了做产品原型时再改协议结果改了三天还没改干净。不如一开始就定义好协议结构哪怕调试时也走这个协议上位机比如串口助手直接解析就行。滤波后的数据还有一个常见问题静态漂移。房间里没人活动、门窗紧闭两个小时后浓度读数可能稳定在15到20之间波动。这个波动不是传感器坏了而是真实环境里气溶胶本来就在缓慢变化。如果设备要接人机界面这种小幅度的显示跳动其实观感不好可以考虑在前端加一个稳定窗口逻辑如果变化量连续N个周期都小于某个阈值就认为浓度稳定显示值冻结。8. 实测数据与典型问题排查供电波动、引脚浮空、时序错位的一次排障记录说一个我自己踩过的坑非常有代表性。第一次做完整的硬件测试时我把传感器、STM32、OLED屏幕全部接好上电后OLED显示浓度一直在0.0和1.0之间跳。当时第一反应是空气质量很好完全没想到是采集链路出了问题。为了验证我点了根蚊香靠近传感器读数毫无反应——这就说明问题了。排查第一步是检查Vo引脚输出电压。用万用表直流档去量发现电压约0.6V而且纹丝不动。这看起来像是传感器没有脉冲驱动——因为前面说过传感器的Vo输出是叠加在脉冲基础上的如果LED没有周期性点亮Vo只是一个稳定的静态电压。于是我怀疑PWM没输出。用示波器看TIM3_CH1引脚的波形果然完全平的。问题出在哪儿我查了代码定时器初始化、GPIO复用输出配置都对。最后用万用表量引脚电压发现是0V。再看原理图PA6的跳线帽被拔掉了。开发板上PA6和PA7共用一组排针跳线帽原本是插在PA6这一路的不晓得什么时候被碰掉了。这个案例说明一个问题调试硬件永远先查物理连接再查代码。第二个案例是ADC读数偏高而且怎么滤波都压不下来。这个问题的根源是PA1引脚悬空。当时我把ADC输入选在PA1但传感器还没接程序直接跑起来了PA1处于模拟输入高阻态引脚上的环境噪声被ADC原样采进来数值就在高位跳。接上传感器之后噪声影响被RC滤波削弱了不少但起初的排查方向完全错了。所以我要强调ADC引脚在传感器未接入时读出来的数没有任何参考意义别拿这个当基准。第三个案例是关于采样时刻的。我在前面提到过更新中断触发ADC会导致读数偏低。当时换用比较事件触发后读数明显上升对应的粉尘浓度也回到了合理区间。这个差异有多大呢同一环境下错误时序读到的浓度大约是7.5修正后是18.6差了接近三倍。所以如果你怀疑自己的传感器读数偏低优先检查采样时刻是否落在正确的280微秒附近。供电问题也是高发点。有一次接上了OLED、传感器、蓝牙模块全部由开发板的3.3V引脚供电。屏幕闪烁传感器读数乱跳。量了一下3.3V只有2.95V带着负载还往下掉。后来给传感器单独用了一个5V降压模块供电信号立刻就稳了。红外传感器的LED脉冲电流虽然不大但和其他模块共用电源轨道时互相之间的干扰非常明显。还有一个不太容易发现的坑传感器进气口和出气口的方向。GP2Y1010AU0F的封装上有一个进气孔和一个排气孔你一定不能让进气口被PCB上的元器件挡住否则气流进不去读数自然偏低。我试过把传感器贴板安装结果读数比参考值低了一半以上。把它架高几毫米之后读数恢复正常。机械结构对测量结果的影响真的不能被低估。9. 从单点测量到完整系统显示、存储、联网与低功耗优化传感器单独打通之后你会发现真正的工作才刚刚开始。一个实用的PM2.5检测设备至少要解决三件事显示、存储、交互。显示方面最常用的方案是OLED典型的是0.96寸的SSD1306或者SH1106走I2C接口库代码到处都是移植成本很低。但OLED的亮度和这个场景非常搭它本身就是监视器OLED对比度高显示数字清晰功耗也低。如果你要做的是桌面小摆件OLED很合适。但如果做的是壁挂式检测仪可能就要换成段码LCD甚至墨水屏。存储方面如果你需要记录历史数据可以用SPI接口的W25Q系列Flash或者直接在SD卡上写日志。对于长期运行的设备最好建立循环覆盖式的存储策略比如每隔一小时存一个点存满一个月后自动覆盖最旧的数据。这样不会出现存储爆满的问题而且能从长时间曲线里看出污染的变化规律。联网方面最常见的是ESP8266或者ESP32作为Wi-Fi协处理器通过串口和STM32通信。数据上报可以用HTTP POST到服务器也可以用MQTT协议推到物联网平台。这里有一个建议上报的频率不要太高一分钟一次足够。高频上报不仅耗电而且会让云端数据库很快膨胀没有实际意义。低功耗优化是另一个话题。GP2Y1010AU0F本身的工作电流很小但如果你长期开着PWM驱动它一直在工作功耗也一直在。我的做法是用STM32的GPIO直接控制一个MOS管给传感器的V-LED供电平时让它工作在极低占空比模式——比如每30秒才唤醒一次传感器做一次完整测量每次测量约1秒完成后关闭传感器电源。这样可以让整个设备的平均功耗降到非常低电池供电时尤其重要。传感器本身还有一个值得玩味的功能它可以用PWM的占空比来调节LED亮度。但如果你的目标就是测量粉尘浓度LED亮度应该尽量保持恒定否则换算公式里的斜率会变化读数就会漂。所以在做低功耗处理时我是直接把传感器断电而不是调小PWM占空比。断电再上电后的首次测量数据会不稳定所以我一般在程序里丢弃传感器唤醒后的第一个数据点从第二个点开始使用。10. 经验总结红外的边界在哪以及什么时候应该换激光方案回到开头那个话题红外PM2.5传感器的精度到底够不够用我的判断是看场景。如果你要做的是——室内空气质量的相对变化监测、扬尘报警、新风系统联动控制、低成本批量部署——红外传感器是够用的。它读出的18.6和激光传感器读出的18.6之间可能存在偏差但这个偏差是系统性的不是随机性的。你用它来做趋势判断和相对比较完全没问题。但如果你要做的是——和国标监测站数据对比、科学研究、医疗级判断——必须换激光传感器。激光传感器的计数原理和红外散射原理在本质上就不同激光可以测粒子数量分布红外只能测散射强度。这个天花板是物理原理决定的软件算法再厉害也无法突破。而且红外传感器还有一个天然限制它对粒径的分辨能力很差。它响应的是所有粒径颗粒物的综合散射信号没有粒径区分能力。激光传感器则可以通过光电二极管阵列分析不同散射角度的信号推算出粒径分布。如果你关心的不只是PM2.5还想知道PM1.0或者PM10红外方案肯定做不到。我在项目中做选型时一般用一句话标准来卡如果只需要一个空气质量好坏的模糊判断红外足够如果需要精确到今天户外跑步会不会受影响的程度选激光。如果看完这篇文章你准备自己动手做一个STM32红外PM2.5传感器的项目我的建议是按照这个顺序推进先搭硬件、跑通PWM和ADC链路、用示波器确认采样时刻、再写滤波和换算逻辑、最后接显示和联网。每一步都验证过再往下走不要试图一次写好全部代码再上电调试。特别是PWM和ADC的时序配合一定要用示波器验证这一步省不得。踩过几次坑之后最大的体会就是这种看似简单的传感器接入项目真正的难点从来不在代码本身而在于对传感器工作机理的理解和对细节的敬畏。你把GP2Y1010AU0F的时序图、采样要求、供电特性吃透了剩下的就是按部就班的工程实现了。
返回列表