☰
STM32连接红外PM2.5传感器:GP2Y1010采样时序与浓度换算指南
2026/9/26 16:27:03 网站建设 项目流程

STM32连红外PM2.5传感器,这个需求我在毕业设计和DIY环境监测项目里见得最多。红外式PM2.5传感器,典型代表是夏普GP2Y1010AU0F、GP2Y1014AU0F和韩系DSM501,核心原理是红外LED发射光束,照射到采样腔里的颗粒物后产生散射,光电二极管把散射光转成电信号,浓度越高信号越强。相比激光式传感器动辄几十上百的价格,这类传感器十几块钱就能搞定,接线简单,STM32一个ADC口、一个GPIO口就能跑起来,非常适合空气盒子、新风系统改造、校园创新项目这类场景。这篇文章把我从硬件接线、采样时序到浓度换算和标定全过程踩过的坑整理出来,给准备玩这个的人一份可以照着抄的作业。

1. 红外式PM2.5传感器到底是什么:原理、型号与选型思路

1.1 红外散射原理,以及它和激光式的本质区别

很多人第一次拿到这种传感器,会误以为它和红外遥控器是一回事。其实两者只是“都用了红外光”,原理和数据结构完全不一样。红外遥控器是发射端编码、接收端解码,传递的是控制指令;而PM2.5传感器是一个光学测量装置,它内部有一个红外LED、一个聚光结构、一个光电二极管,LED发出的光打在空气中的颗粒物上,颗粒把光散射到各个方向,其中一部分被光电二极管接收。颗粒越多、浓度越高,散射光越强,输出信号越大。

这个原理听起来很顺,但有一个绕不开的硬伤:红外LED的光不是单色相干光,散射强度对粒径不敏感,它分不清是0.3μm的细颗粒还是10μm的粗颗粒。所以这类传感器输出的并不是严格意义的PM2.5,而是“所有粒径颗粒的散射总量”,更接近PM10甚至TSP的概念。激光式传感器则不同,激光单色性和方向性好,配合多角度光电探测和Mie散射理论拟合,才能算出一个近似的粒径分布,输出PM1.0、PM2.5、PM10的区分数据。这也是为什么激光式贵那么多。理解了这一点,你就不会拿红外式的数据和气象站对比,也不会在项目答辩时把原理讲错。

1.2 常见型号怎么选:GP2Y1010、GP2Y1014、DSM501

市面上能买到的红外粉尘传感器主要就是这三个型号。我建议直接下单前先看输出类型,因为这决定了STM32侧的采集方案,差别很大。

型号输出形式供电典型特点
GP2Y1010AU0F模拟电压5V经典款,资料多,灵敏度约0.5V/(0.1mg/m³),输出范围0~约3.6V
GP2Y1014AU0F模拟电压5V1010的改进版,温漂小一些,引脚兼容,很多模块上用这个
DSM501PWM脉冲5V不需要ADC,低电平脉宽随浓度增大,只能做相对浓度判断

GP2Y1010和GP2Y1014的区别并不大,如果你买的是现成模块,看到哪个用哪个就行。DSM501则适合不想处理ADC、只想做一个“空气干净/不干净”指示的项目,比如自动开关净化器。需要注意一点:很多卖家页面写的是“PM2.5传感器”,但DSM501的数据手册明确说它只能做相对浓度判断,不能直接给出μg/m³的质量浓度。如果你毕设要求一定要输出PM2.5数值,选GP2Y系列,别选DSM501。

1.3 选模块还是自己搭电路:我的建议

淘宝上常见的GP2Y1010模块,通常已经把150Ω限流电阻、LED驱动三极管、运放和放大电路都集成好了,你只需要接四个引脚:VCC、GND、LED控制脚、Vout输出脚。我非常建议买这种模块,而不是买裸传感器自己去搭电路,因为那个LED驱动时序和模拟放大电路虽然不复杂,但自己搭容易受面包板接触电阻影响,导致读数飘得怀疑人生。模块贵也就贵几块钱,省下的调试时间远不止这个价。

2. STM32侧硬件接线与电路设计

2.1 引脚定义和接线表

如果你拿到的是裸传感器,GP2Y1010一共有6个引脚,这里按常规排列说明:V-LED接5V,并且必须串一个150Ω左右的限流电阻;LED-GND接地;LED是脉冲控制输入,由STM32的一个GPIO控制;GND接地;Vout是模拟输出;VCC接5V。真正需要STM32介入的只有两个点:一个GPIO输出给LED控制脚,一个ADC输入接Vout。

接线表大致如下:

传感器引脚接STM32/电源说明
V-LED5V经过150Ω电阻给红外LED供电,不能省略限流电阻
LED-GNDGND与系统共地
LEDPA0(任意GPIO)接收STM32的脉冲控制信号
GNDGND与系统共地
VoutPA1(ADC1_CH1)模拟信号输出到ADC
VCC5V传感器主供电

买模块的话,直接看模块丝印,一般会有VCC、GND、LED、Vout或AOUT标识,对照接就行。

2.2 电源和电平匹配:最容易被忽视的两个坑

第一坑:VCC到底用5V还是3.3V。GP2Y1010手册明确要求5V供电,输出电压最高能到3.6V左右,而STM32F103的ADC参考电压是3.3V,直接把Vout接到ADC引脚,轻则读数顶到4095,重则ADC模块长期超压受损。我见过不少新手在这里翻车,读数永远是4095,还以为是程序写错了。

解决方案有三个。一是传感器信号经10k+10k电阻分压后再进ADC,这样最高电压降到约1.8V,安全余量很足;二是干脆用3.3V给传感器供电,电路简单,但输出范围被压缩,灵敏度变差,红外式本来精度就一般,我不太推荐;三是选用模块上已经做了电平匹配的版本,买的时候问清楚。我自己最常用的是分压方案,毕竟红外式本身精度有限,再把供电电压降下来,数据就更没什么参考价值了。

第二坑:ADC参考电压的稳定性。F103的VDDA如果直接和系统3.3V连在一起,而系统里还有电机、继电器、Wi-Fi模块在跳动,ADC读数就会跟着电源纹波一起上下飘。硬件上至少要在VREF+引脚附近加一个100nF加10μF的电容,软件上可以在每次采集前顺便采一次VREF通道做归一化,把电源波动的影响降到最低。这个细节很多人忽略,但它对稳定性的提升非常明显。

2.3 采样时刻控制:为什么不能让LED一直亮着

红外PM2.5传感器对采样时序有严格要求。按GP2Y1010的数据手册,LED脉冲周期是10ms,脉冲宽度0.32ms,采样时刻在LED打开后的0.28ms处,采样窗口约0.04ms,采样完成后LED关闭。也就是说,传感器在LED亮起来后280μs时,内部光电转换信号才稳定到可测的状态,你必须在那个时间点附近完成ADC采样,早了晚了读数都不对。

如果你图省事,让LED常亮,传感器会进入过饱和状态,输出偏高,而且LED长时间工作发热,漂移会更严重。如果你采样点偏早,信号还没建立起来,读数偏低;偏晚,信号已经衰减了,也会偏低。这个280μs的时序窗口,是整个项目里最核心、也最容易出问题的地方,后面软件部分会重点展开。

3. 软件实现:从采样时序到浓度换算

3.1 方案A:GPIO延时法,快速验证

最简单可行的办法,就是用SysTick或者DWT做一个微秒级延时函数,在主循环里按10ms周期操作:先把LED引脚拉高,延时280μs,启动ADC转换并读取结果,然后把LED引脚拉低,等待剩下的时间进入下一个周期。

标准库版本的核心代码如下:

void PM25_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; ADC_InitTypeDef ADC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE); /* LED控制引脚 */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); /* Vout引脚设为模拟输入 */ 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_239Cycles5); ADC_Cmd(ADC1, ENABLE); ADC_ResetCalibration(ADC1); while (ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while (ADC_GetCalibrationStatus(ADC1)); } uint16_t ReadPM25(uint16_t *adc_raw) { GPIO_SetBits(GPIOA, GPIO_Pin_0); // LED打开 Delay_us(280); // 等待信号稳定 ADC_SoftwareStartConvCmd(ADC1, ENABLE); while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)); uint16_t raw = ADC_GetConversionValue(ADC1); GPIO_ResetBits(GPIOA, GPIO_Pin_0); // LED关闭 *adc_raw = raw; return raw; }

注意这里我把ADC采样时间设置成了239.5周期,而不是最快的1.5周期,原因在于前面的分压电阻增大了信号源阻抗,如果ADC内部采样电容没有足够时间充电,结果会偏低且不稳定。如果你没加分压电阻,直接接模块输出,可以适当缩短,但239.5周期在这个场景下并无坏处,多花的时间也就十几μs,完全可以接受。

这套方案的优点是逻辑直白、容易调通,缺点也明显:280μs的延时是阻塞式的,如果主循环里同时要跑OLED刷屏、读取DHT11、甚至RTOS任务切换,采样时刻就容易被拖偏,读数忽高忽低。所以它适合项目前期验证,不适合后续做复杂系统。

3.2 方案B:定时器PWM控制LED,ADC触发或中断采样

如果你的项目最终要加显示、联网、按键这些功能,最好从一开始就用定时器把采样时序固定下来。思路是:用定时器产生10ms周期的PWM波,脉宽0.32ms,其输出通道直接控制LED引脚;在PWM的上升沿之后,再通过另一个定时器或ADC外部触发,在280μs处触发一次ADC采样。

F103上没有现成的“PWM上升沿延迟280μs再触发ADC”的组合,我常用的是双定时器协作:TIM3产生10ms周期PWM控制LED,TIM4配置为单脉冲模式,由TIM3的上升沿作为触发源,经过280μs后输出一个比较事件,该事件触发ADC注入通道转换。代码体量比方案A大不少,但好处是采样时刻完全由硬件决定,CPU不参与,主循环哪怕卡了几十毫秒,数据也不会乱。

如果你的项目用不上这么复杂的方案,也可以做折中:保留方案A的GPIO控制,但把280μs延时改用定时器硬件延迟完成,或者把采样放到定时器中断里执行,避免被显示代码阻塞。核心思路只有一个:LED脉冲和ADC采样之间的280μs间隔必须稳定,任何软件阻塞都会直接反映在数据上。

3.3 DSM501的PWM读取:不用ADC也能量空气

如果你选的是DSM501,软件流程完全不同。DSM501在有粉尘时输出低电平脉冲,低电平持续时间越长、频率越高,代表颗粒越多。你可以用两个办法读取:

办法一是定时器输入捕获。把DSM501的输出接到STM32定时器的捕获通道,配置为捕获低电平脉宽,单位时间内累计低电平时间,除以总采样时间,得到浊度比例。办法二更简单:把输出接到任意GPIO,开一个1ms定时器中断,每次中断里判断引脚电平,如果是低电平就累加计数,统计30ms窗口内的低电平占比。

第二个办法的代码骨架类似:

uint16_t low_count = 0; uint16_t total_count = 0; void SysTick_Handler(void) { /* 假设SysTick配置为1ms */ total_count++; if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_10) == RESET) { low_count++; } if (total_count >= 30) { dust_ratio = (float)low_count / total_count; // 0.0 ~ 1.0 low_count = 0; total_count = 0; } }

DSM501的逻辑电平必须先确认:无尘时输出多为高电平,有尘时输出低电平脉冲。你要是按高电平去统计,会发现浓度越高读数反而越低,方向搞反是这类传感器最常见的低级错误。

3.4 浓度公式与现场标定方法

对于GP2Y系列的模拟电压输出,常规换算思路是:设粉尘浓度为C(mg/m³),输出电压为Vout,洁净空气下的输出电压为V0,则

C = (Vout - V0) / 0.5 × 0.1

其中0.5是典型灵敏度,代表每0.1mg/m³对应0.5V电压变化,单位是V/(0.1mg/m³)。数值再乘以1000就得到μg/m³。

但这里有个重要提醒:V0不同批次差异很大,网上流传的“C = 0.17 × Vout - 0.1”这类固定公式,都是针对某一颗传感器的经验拟合值,不能直接套在你手上这颗上。我整理过一个公式,都写在代码注释里了。

/* 已知灵敏度 sensitivity(V/(0.1mg/m3)) 和零点电压 v0(V) */ float PM25_Convert(float voltage) { if (voltage <= v0) return 0.0f; return (voltage - v0) / sensitivity * 0.1f * 1000.0f; // μg/m3 }

我的现场标定方法是:找一台激光式PM2.5传感器作为参考,把两台设备放在同一个采样点,先在净化后的环境采集多组V0平均值,然后用点燃的香烟、扬尘、滤网过滤等方式制造几个稳定浓度梯度,每档记录参考浓度和本机电压,最后用最小二乘拟合出你自己的V0和灵敏度。这个标定流程不仅能让数据更靠谱,写进毕设报告里还是一个加分项,评委一般看了都会觉得你考虑得周到。

4. 数据处理与稳定性优化

4.1 滤波:红外式的数据天生就带毛刺

红外式传感器输出噪声比较大,同一个环境下的连续采样,相邻两帧差几十μg/m³太正常了。如果直接把原始值显示在屏幕或串口上,曲线看起来就是心电图。我建议至少做一层滑动平均,窗口取5到10帧即可:

#define FILTER_LEN 8 uint16_t filter_buf[FILTER_LEN]; uint8_t filter_index = 0; uint32_t filter_sum = 0; uint16_t PM25_Filter(uint16_t raw) { filter_sum -= filter_buf[filter_index]; filter_buf[filter_index] = raw; filter_sum += raw; filter_index = (filter_index + 1) % FILTER_LEN; return filter_sum / FILTER_LEN; }

如果数据还有偶尔的尖峰毛刺,可以再加一个3帧中值滤波,先中值再滑动平均,效果更好。但注意,滤波窗口越大响应越慢,如果项目要做空气净化器自动启停,窗口拉太大可能净化器半天不动,这个度要根据实际场景调整。

4.2 预热和采样位置对读数的影响

我必须提醒你一个很容易被忽视的问题:红外式传感器上电后需要稳定时间,尤其是内部LED灯珠发热会导致光路和电路温漂,前1到2分钟读数会明显偏高并缓慢下降。所以系统启动后,先等一两分钟再开始统计平均或者上传数据,否则上报的前几条数据基本没有参考价值。

采样位置同样重要。传感器要放在气流能流通的地方,不要贴墙、不要闷在封闭壳子里,否则测到的永远是采样腔里的死气。很多人把传感器装在自制空气盒子里,四周用热熔胶全部封死,只留一个小孔,结果读数异常高还不波动,后来拆开重新开了对流孔就好了。粉尘传感器不是温度传感器,它必须“呼吸”。

5. 常见问题与排查实录

5.1 典型问题速查表

我从实操中整理出一份高频问题清单,大部分项目调试遇到的坑都能在里面找到参照:

现象可能原因排查手段
读数一直是0Vout没接对ADC通道、LED引脚没初始化用万用表测Vout静态电压;单独写代码让LED常亮,看ADC是否变化
读数满偏4095输出电压超过3.3V,或通道配置错误检查供电方式和分压电路,确认信号在ADC量程内
读数乱跳,数值波动大采样点不在280μs窗口、有软件阻塞、无滤波用示波器抓LED和Vout波形;改硬件触发采样;加滤波
上电前几分钟读数偏高传感器预热不足上电后等待1~2分钟再开始统计
和参考激光传感器对不上没有标定、公式系数固定按前文方法做现场两点标定
放在净化器旁边数值不降采样位置气流不通开对流孔,让传感器接触外部空气
DSM501读数方向反了统计了高电平而非低电平确认输出逻辑,改为统计低电平脉宽

5.2 调试时最好先抓波形,别急着写换算

没有经验的人拿到传感器,第一件事往往是写代码、看串口数据,然后发现读数像疯了一样,就开始改滤波参数,改了半天也没用。我的建议是:如果手里有示波器,先把LED控制引脚的脉冲波形和Vout波形一起抓出来看一下,确认脉冲周期、脉宽和采样点位置是否正确。Vout会在LED打开后出现一个很尖的脉冲,你要采的是脉冲顶部稍微稳定下来的那段,如果示波器显示采样点落在了上升沿,那读数当然不对。

没有示波器也不要紧,可以临时写一段测试代码:LED引脚置高电平,持续读取ADC,看输出电压是否稳定、是否接近满偏。如果此时读数异常,说明问题出在硬件电路或通道配置,而不是采样时序。如果LED常亮时读数正常,只在脉冲模式下乱跳,那基本可以断定是时序或阻塞问题。

串口打印是另一件调试利器。开发阶段把原始ADC值、滤波后ADC值、电压值、浓度值全都打印出来,一次采完整流程的数据,不要只输出最终浓度。很多时候你一看原始电压,问题就清楚了。

5.3 关于红外式传感器“精度”的心理预期

最后说一个很多人不愿意面对的事实:红外式PM2.5传感器不是一个精密的空气质量监测仪器,它的定位是低成本、低功耗的相对浓度感知器。用它来判断“开门通风后数值下降”“房间里抽了烟数值升高”“净化器开了半小时数值在降”,这些场景完全没问题;但如果你要和国标站数据、和实验室激光仪器做绝对对比,那基本做不到。

项目报告里该怎么写就稳一点:明确标注传感器型号和原理,说明数据是“红外散射法估算的粉尘浓度”,不做数据精度承诺。这样一来,答辩被追问传感器原理时你能讲清楚,二来不会因为数据和外部参考差异过大被质疑。我见过有同学拿着红外式的数据跟官方PM2.5监测站对比,差了一倍多,差点被打回来,其实就是没理解这个局限。

我在这个项目上反复折腾最久的,就是那280μs的采样时序。刚开始用HAL库的阻塞延时,后来加了OLED刷屏,刷一次屏要几毫秒,采样点跟着跑,读数忽高忽低。最后改成定时器PWM控制LED、中断触发采样,才彻底稳定下来。所以如果你后续要做显示、联网、存储这类功能,从一开始就尽量用硬件定时器方案,不要在主循环里用阻塞延时做采样。最后再分享一个小技巧:做完传感器标定后,把V0和灵敏度两个参数写死在程序里,但在调试串口里提供一个“标定模式”,输入指令后可以重新测量并更新这两个参数,这样以后换了一颗传感器,不用重新烧写程序,直接在调试工具里就能完成校准。红外PM2.5传感器的价值,就在于用最简单的方案解决“空气大概干不干净”的问题,把时序、电源、滤波这三件事做好,你的数据就会比市面上很多廉价成品设备更靠谱。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询