尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

STM32实战:GP2Y1010AU0F红外PM2.5传感器驱动与数据滤波全流程

发布时间:2026/9/26 16:11:07

资讯中心
01
ARTICLE

STM32实战:GP2Y1010AU0F红外PM2.5传感器驱动与数据滤波全流程

STM32实战:GP2Y1010AU0F红外PM2.5传感器驱动与数据滤波全流程
1. 项目概述与方案选型做嵌入式开发这几年最常被问到的一句话就是“想做个空气质量监测从哪里下手”。大家的首选几乎都是STM32而传感器则绕不开PM2.5。用STM32连接红外PM2.5传感器是一个成本不高、逻辑清晰、又能把GPIO、定时器、ADC、滤波算法全串起来的典型项目。这篇文章就以夏普GP2Y1010AU0F这颗红外粉尘传感器和STM32F103C8T6最小系统板为例把从硬件接线、驱动原理、PWM脉冲时序、ADC采样、数据滤波到浓度标定的完整流程逐个拆开讲。适合正在做传感器课程设计、单片机入门练习或者想给家里DIY一台低成本空气质量监测仪的同学参考。这项目最大的好处在于它不依赖昂贵的模块不需要复杂的通信协议核心就是你用单片机把一个模拟小信号读进来再换算成人能理解的数字。整个过程走一遍你对ADC采样时机、定时器PWM、滤波算法和数据标定的理解都会上一个台阶。1.1 红外PM2.5传感器的工作原理得先把原理讲透。GP2Y1010AU0F这类红外粉尘传感器用的是光散射法。传感器内部有一个红外LED和一个光电二极管两者成一定角度布置。红外LED把一束光打向采样腔空气里的颗粒物会把光散射到各个方向其中一部分就落到了光电二极管上。颗粒物越多、越密集散射光越强光电二极管输出的电压就越高。你可以这样理解黑屋子里拿手电筒往前照你能清晰看到光柱里灰尘飞舞就是因为灰尘把光散射到了你的眼睛方向。汽车大灯在大雾天能看到光柱也是同样的原理。传感器做的事情就是把“你的眼睛”换成“光电二极管”再把“光强变化”折算成“颗粒物浓度”。这里特别提醒一点很多同学一看到“红外传感器”就去搜“38kHz红外发射芯片参数”这是被红外遥控器带偏了。遥控器那种红外是载波调制通信38kHz是载波频率。而PM2.5传感器的红外LED是低频脉冲驱动周期10ms频率只有100Hz驱动方式和载波调制完全是两码事。选型的时候别被“红外”两个字搅浑了。1.2 为什么选GP2Y1010AU0F和STM32F103C8T6GP2Y1010AU0F几乎是红外粉尘传感器里最经典的一颗。六脚直插模拟电压输出灵敏度约0.5V/(0.1mg/m³)输出范围大概在0.6V到3.7V对应0到0.6mg/m³的粉尘浓度。它便宜散件二十到四十块钱资料多课程设计用再合适不过。有人问为什么不直接上激光传感器比如攀藤PMS5003或者SDS011激光传感器精度高、响应快但价格翻一倍输出是UART数字信号内部自带风扇和算法单片机这边基本不涉及信号调理学到的底层知识反而少。所以我的建议很直接如果是做产品、要精度上激光如果是课程设计、学习原理红外模拟输出的GP2Y1010AU0F更能帮你把传感器数据链路的每个环节吃透。控制端选STM32F103C8T6理由也很朴素板子十块钱左右72MHz主频12位ADC多个定时器64KB Flash20KB RAM应对这个项目绰绰有余。再加上资料铺天盖地遇到问题随便一搜就有答案。工具链方面Keil MDK5配合ST-Link Utility烧录是常规操作网上关于Keil5兼容C51和STM32的安装教程也很多这里就不展开细说了。2. 硬件连接与电路设计GP2Y1010AU0F一共六个引脚接线并不复杂但有几个细节不注意就是一路坑。下面先列出引脚定义和推荐连接方式。引脚号名称功能推荐连接1V-LEDLED供电正极串联150Ω电阻接5V并接220μF电解电容到GND2LED-GNDLED地接GND3LEDLED控制端通过NPN三极管接STM32 GPIO4GND传感器地接GND5Vout模拟电压输出经分压电阻接STM32 ADC引脚6Vcc传感器电源接5V并接33μF和0.1μF去耦电容到GND典型电路里V-LED要通过150Ω限流电阻接5V旁边并一颗220μF的大电解电容这颗电容的作用是稳住LED脉冲瞬态电流。Vcc则需要并联33μF和0.1μF去耦高频和低频噪声。LED控制端推荐用NPN三极管驱动GPIO输出高电平让三极管导通LED回路接通点亮GPIO输出低电平则关闭。如果你手头没有三极管拿GPIO直接灌电流去控LED也能实验但STM32 GPIO灌电流能力有限长期运行不够可靠不推荐作为正式方案。Vout是模拟输出不能直接接到3.3V供电的STM32 ADC上。这颗传感器的Vcc是5VVout最大能到3.6V左右超过STM32的3.3V量程。稳妥做法是加一级电阻分压比如10k和20k串联分压比1.5把3.6V降到2.4V再进ADC。同时ADC引脚对地并联一颗0.1μF电容能滤掉一部分高频噪声。2.1 接线速查表为了方便照抄我直接把完整接线列出来。STM32F103C8T6最小系统板为例STM32引脚连接对象PB0NPN三极管基极经1k电阻控制传感器Pin3 LEDPA1ADC通道1接分压中点读取VoutPA9 (TX)USB-TTL的RX串口打印数据PA10 (RX)USB-TTL的TX3.3V不接传感器电源只给MCU供电5V经150Ω接传感器Pin1直接接Pin6GND传感器Pin2、Pin4USB-TTL地线必须全部共地传感器Vout分压的具体做法Vout串一颗10k电阻再接一颗20k电阻到GND两颗电阻的连接点引入PA1。这样Vout电压在ADC端就是原来的2/3换算的时候乘回1.5即可。ADC端再并一颗0.1μF电容到GND。2.2 容易踩坑的电路细节第一个坑是共地。传感器GND、STM32 GND、USB-TTL的GND必须连在一起。很多同学串口打印出来全是一堆乱码排查半天发现是TTL模块没跟开发板共地这种低级错误最让人哭笑不得。第二个坑是电源走线。LED脉冲点亮时瞬态电流比平均电流大不少如果传感器和开发板共用一条又细又长的杜邦线供电电压跌落会在Vout上体现出来读数忽高忽低。建议传感器供电从稳压输出端单独拉线尽量短别跟电机、继电器之类的感性负载共用电源。第三个坑是Vout直接接ADC。这不是不能用而是有概率读坏。STM32 ADC引脚虽然有一定耐压能力但长期超过3.3V会影响采样精度严重时损伤引脚。分压只多两颗电阻别省。第四个坑是安装位置。光电传感器靠近热源时内部电路温漂会让零点电压移动哪怕颗粒物浓度没有变化读数也会慢慢飘。安装时让传感器进风口避开芯片散热区域探头侧面留出气流通道。另外光学镜头沾灰后读数会明显偏高定期清洁是必须的。3. 软件实现从PWM脉冲到浓度换算软件部分是这个项目的核心。线路接对了、电源稳了但如果采样时序不对读出的数据照样不能用。3.1 为什么必须用320μs脉冲驱动打开GP2Y1010AU0F的数据手册可以看到LED驱动要求脉冲周期10ms脉冲宽度0.32ms也就是320μs。很多人不理解LED直接常亮不行吗答案是不行。红外LED如果常亮第一发热严重连续工作一段时间后光电二极管的漂移会大得离谱第二寿命会缩短第三功耗也高。所以传感器设计成低占空比脉冲驱动每次点亮只有320μs隔10ms再点一次。这样做还有一个额外好处采样可以同步在LED点亮窗口内进行避开环境光的干扰。时序上还有一个关键点LED点亮之后散射光信号不会瞬间稳定需要一小段时间建立。数据手册上的输出电压特性表明在点亮后约0.28ms左右输出进入稳定区间。因此采样时机应该放在点亮后的280μs到320μs之间。我一般取300μs留出余量。这个机制可以类比成相机的闪光灯你要拍暗处静物闪光灯闪一下在闪光的瞬间按下快门得到一张受环境光干扰最小的照片。传感器也是这个逻辑LED闪一下ADC在闪光的稳定期内采一次。3.2 STM32定时器PWM配置产生10ms周期、320μs高电平的脉冲最直观的做法是定时器PWM输出。我用TIM3通道1举例STM32F103C8T6主频72MHz。先算分频系统时钟72MHzPSC设为71得到1MHz的计数频率也就是每个计数1μs。再算周期ARR设为9999则计数周期为10000μs正好10ms。通道比较值CCR设为320这样PWM高电平持续320μs。占空比只有3.2%在示波器上看起来就是一条很窄的脉冲别以为没输出。void MX_TIM3_Init(void) { TIM_OC_InitTypeDef sConfigOC {0}; __HAL_RCC_TIM3_CLK_ENABLE(); htim3.Instance TIM3; htim3.Init.Prescaler 71; // 72MHz / 72 1MHz htim3.Init.CounterMode TIM_COUNTERMODE_UP; htim3.Init.Period 9999; // 10ms htim3.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; HAL_TIM_PWM_Init(htim3); sConfigOC.OCMode TIM_OCMODE_PWM1; sConfigOC.Pulse 320; // 320us sConfigOC.OCPolarity TIM_OCPOLARITY_HIGH; HAL_TIM_PWM_ConfigChannel(htim3, sConfigOC, TIM_CHANNEL_1); HAL_TIM_PWM_Start(htim3, TIM_CHANNEL_1); }如果你不想让LED一直以100Hz频率闪烁也可以只在需要采样时开启PWM采样完关闭。这样更省电也方便后续做成低功耗版本。3.3 采样时序与ADC读取PWM脉冲已经产生接下来要让ADC在正确的时间窗口里采样。有两种做法。第一种是高级做法用PWM的比较事件去同步触发ADC。TIM3通道1的比较输出可以作为ADC的外部触发源这样PWM翻转的时刻会自动启动一次ADC转换时间误差极小。不过这种配置对初学者来说有点绕需要对STM32的ADC触发机制比较熟。第二种是简单做法软件控制一个GPIO自己模拟脉冲时序。在需要采样时GPIO拉高LED开始点亮延时300μs启动ADC读完数据后拉低LED再延时9.7ms凑满10ms周期。这种做法足够课程设计用而且整个过程都在自己的掌控里遇到问题好排查。我推荐先用这个方案跑通理解了时序再回去优化。GPIO模拟采样的核心代码uint16_t read_pm25_raw(void) { uint16_t adc_val 0; HAL_GPIO_WritePin(LED_CTRL_GPIO_Port, LED_CTRL_Pin, GPIO_PIN_SET); delay_us(300); // 等散射光稳定 HAL_ADC_Start(hadc1); HAL_ADC_PollForConversion(hadc1, 100); adc_val HAL_ADC_GetValue(hadc1); HAL_ADC_Stop(hadc1); HAL_GPIO_WritePin(LED_CTRL_GPIO_Port, LED_CTRL_Pin, GPIO_PIN_RESET); delay_us(9700); // 补全10ms周期 return adc_val; }这里有个细节HAL_ADC_Start和HAL_ADC_Stop每次调用都有寄存器操作开销但在这个频率下完全不影响性能。如果你追求效率可以省略Stop等下一次直接用软件触发启动也行。3.4 滤波算法与浓度换算公式拿到ADC原始值后先换算成真实的传感器输出电压。设ADC读数为rawSTM32的ADC参考电压是3.3V12位分辨率满量程4095分压比是1.5则float vout (float)raw / 4095.0f * 3.3f * 1.5f;接下来算浓度。查数据手册灵敏度0.5V对应0.1mg/m³也就是100μg/m³。所以0.5V→100μg/m³1V→200μg/m³。换算公式float density_ugm3 (vout - v0) * 200.0f; if (density_ugm3 0) density_ugm3 0;v0是传感器零点电压也就是在干净空气中的输出值。数据手册典型值给的是0.6V附近但不同个体差异很大最好自己实测标定。这个后面专门讲。浓度值算出来之后别直接用一次采样波动太大。我用滑动平均滤波维护一个长度为20的环形缓冲区每来一个新值就挤掉一个旧值然后取平均值。这样做的好处是响应够快平均20次也就200ms同时能明显压住随机噪声。滑动平均参考实现#define FILTER_SIZE 20 float filter_buf[FILTER_SIZE]; float filter_sum 0; uint8_t filter_idx 0; uint8_t filter_filled 0; float moving_average(float new_value) { if (!filter_filled) { for (int i 0; i FILTER_SIZE; i) filter_buf[i] new_value; filter_sum new_value * FILTER_SIZE; filter_filled 1; return new_value; } filter_sum - filter_buf[filter_idx]; filter_buf[filter_idx] new_value; filter_sum filter_buf[filter_idx]; filter_idx (filter_idx 1) % FILTER_SIZE; return filter_sum / FILTER_SIZE; }第一次调用时先把缓冲区填满避免前面20个值全是0导致输出被压低。这个细节很多人会漏掉。4. 完整示例工程框架HAL库下面给一套能直接用的工程骨架基于STM32CubeMX生成的HAL库代码开发环境是Keil MDK5。4.1 初始化代码要点用CubeMX创建工程时要选对芯片STM32F103C8Tx。时钟配置选择外部8MHz晶振主频拉到72MHz。需要开启的外设GPIO、ADC1、USART1、TIM3。ADC1使用PA1采样时间设置到239.5周期这样采样保持时间足够长读数稳定些。USART1设置115200-8-N-1。初始化顺序没有严格要求但建议遵循时钟 → GPIO → UART → ADC → TIM。GPIO层面PB0配置为推挽输出初始输出低电平PA1配置为模拟输入。TIM3先初始化但不需要启动PWM如果你采用GPIO模拟方案TIM3可以不用初始化。下面的代码按GPIO模拟方案来写结构更简单。void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); // LED控制脚 PB0 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); GPIO_InitStruct.Pin GPIO_PIN_0; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull GPIO_NOPULL; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOB, GPIO_InitStruct); // PA1 模拟输入 GPIO_InitStruct.Pin GPIO_PIN_1; GPIO_InitStruct.Mode GPIO_MODE_ANALOG; HAL_GPIO_Init(GPIOA, GPIO_InitStruct); }ADC初始化用CubeMX生成即可注意关闭连续转换模式因为我们需要每次手动触发一次。如果开了连续转换ADC会不停采样虽然也能读但和LED脉冲时序对不上读数就会乱飘。4.2 主循环与串口输出主循环的逻辑很直接间隔一段时间采集一次原始值换算成浓度滤波然后通过串口发出去。为了观察传感器本身的稳定性我在调试阶段会同时打印原始ADC值、电压和浓度三个数据。串口打印用printf重定向在工程里加一个fputc函数#include stdio.h int fputc(int ch, FILE *f) { HAL_UART_Transmit(huart1, (uint8_t *)ch, 1, 0xFFFF); return ch; }注意在Keil里面要勾选Use MicroLIB否则printf会占用太多资源。主循环示例int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_ADC1_Init(); MX_USART1_UART_Init(); while (1) { uint16_t raw read_pm25_raw(); float vout (float)raw / 4095.0f * 3.3f * 1.5f; float concentration (vout - 0.6f) * 200.0f; if (concentration 0) concentration 0; if (concentration 999) concentration 999; float filtered moving_average(concentration); printf(ADC:%4d, Vout:%.2fV, PM2.5:%.1f ug/m3\r\n, raw, vout, filtered); HAL_Delay(500); } }这里每500ms采样打印一次。要说明的是500ms间隔里会错过很多LED脉冲周期但由于read_pm25_raw函数内部自己模拟了完整的10ms时序所以每次调用读到的都是有效数据。如果你想让数据更平滑可以缩短打印间隔多采几次再平均。实际调试时我习惯用串口助手观察波形。把采样间隔改成100ms然后复制数据贴到Excel里画折线图能非常直观地看到传感器响应和滤波效果。这一步做完对数据的把握会强很多。5. 常见问题与排查技巧实录这个项目踩坑点比较集中我把最常见的现象、原因和解决办法整理成一张表方便对照排查。现象可能原因排查方法ADC读数一直为0LED没点亮、三极管接反、ADC引脚配置错用万用表测LED两端电压确认PB0电平变化输出电压乱跳采样时机不对、电源纹波大、传感器未预热稳定电源、加滤波电容、预热5分钟后再读浓度一直显示负值零点电压V0设得偏高在干净空气中实测V0写入代码数值比实际高很多光学镜头沾灰、有水汽烟雾干扰清洁透镜远离蒸汽源串口输出乱码波特率不匹配、TTL没共地检查串口参数连好GND线两根传感器读数差异大个体零点差异、灵敏度差异逐个标定V0和灵敏度系数数据稳定但响应慢滑动平均窗口太大减小滤波窗口到5~10次5.1 手把手排障路径如果你第一次上电发现数据不对别急着改代码按下面的顺序来。先测硬件。万用表切到直流电压档直接量传感器Vout到GND。在干净空气里正常应该有零点几伏的输出。如果量出来接近0V说明LED可能没亮检查PB0到三极管基极的电路确认GPIO输出波形正常。也可以写一段测试代码让PB0固定输出高电平LED常亮几秒钟同时看Vout是否上升。能上升就说明传感器通路是好的问题出在时序或配置。再测数据链路。串口打印原始ADC值用手在传感器进风口前面扇一扇ADC值应该有明显抖动。如果原始值纹丝不动大概率ADC没配好或者PA1被复用成别的功能了。如果原始值有变化但换算后不敏感检查分压比系数是不是写错了。然后遮光测试。用一个不透光的盖子套住传感器进风口读数应该很接近零。这一步是排除环境光干扰。如果遮住后数值还是很高多半是透镜污染或者传感器本身有问题。最后做烟雾测试。点一根香烟把烟轻轻吹向进风口浓度值应该迅速飙升到几百甚至爆表。注意别把烟直接吸进去传感器内部会积灰。测试完用电吹风冷风档吹一下采样腔。5.2 实测中容易被忽略的三个小问题第一传感器预热。GP2Y1010AU0F刚上电的前几分钟输出电压会有明显漂移。我的做法是程序启动后先空跑30秒只让LED脉冲工作但不记录数据等热稳定了再开始正式采样。如果你不加这个预热逻辑前几分钟打印出来的数据会呈现一条缓慢下降的曲线容易误判成传感器故障。第二电源纹波。传感器对Vcc上的纹波非常敏感尤其是LED脉冲瞬间的电流冲击。有一次我图省事用一个面包板电源给传感器和舵机共用供电结果舵机一转PM2.5读数就开始跳。后来把舵机挪走单独用LDO给传感器供电问题立刻消失。凡是发现读数和设备启停强相关先怀疑电源。第三线材长度。Vout模拟信号线如果走得太长又没有屏蔽很容易耦合干扰。尽量让传感器靠近STM32杜邦线控制在10cm以内。实在要拉长线用屏蔽线或者双绞线ADC引脚对地电容加大到1μF试试。6. 实测校准与项目扩展代码跑通、数据能打印出来这只是第一步。要让数据在真实场景里有参考价值标定这一步省不了。6.1 自己动手标定传感器先理解为什么必须标定。数据手册给的是典型参数但每个传感器实际出厂零点电压和灵敏度都不一样。你手里这颗可能Vout在干净空气里是0.63V我手里这颗可能是0.55V。直接用0.6V带入公式误差可能达到20%。标定的目的就是把自己这颗传感器的两个关键参数测出来。标定分两步。第一步测零点V0。找一个相对干净的室内环境最好远离窗户、油烟和灰尘源把传感器放进去等五分钟稳定后连续采样20次取平均值就是V0。实测我手里这颗V0是0.62V比手册典型值略高。第二步做单点校准。找一个参考源最简单的是点一根香烟让它自然燃烧把烟引导到传感器进风口附近等读数稳定后记录电压V1。同时用手头真正靠谱的设备测一下当前环境的参考浓度C_ref。然后算出实际灵敏度系数float k (v1 - v0) / C_ref; // 单位 V/(ug/m3)把代码里的200换成1/k即可。举个例子如果测得v11.58V参考浓度为60μg/m³则k (1.58-0.62)/60 0.016V/(μg/m³)换算系数就是1/k 62.5。这个系数跟手册给的200差了不少说明这颗传感器个体差异确实明显。标定完成后浓度计算公式改为float density_ugm3 (vout - 0.62f) * 62.5f;如果你手头没有参考仪器用国家标准限值做个半定量粗标也行把香烟烟雾稀释到体感“轻度污染”的程度对应读数调到75~110μg/m³区间。这不算精确但至少能让设备读数在一个合理的量级。6.2 从课程设计到智能家居小端传感器数据能稳定输出了后续扩展方向非常多。最基础的是加一块0.96寸I2C OLED屏实时显示PM2.5浓度做一个桌面空气质量小摆件。OLED走I2CSCL和SDA分别接PB6、PB7用SSD1306驱动库几十行代码就能显示数字。想把数据发到手机上看可以加一个ESP8266或ESP32模块通过串口和STM32通信在上位机端跑MQTT协议数据上报到Home Assistant或者腾讯云、阿里云物联网平台。STM32这边只需要把浓度值打包成固定格式发给WiFi模块云端解析后就能展示历史曲线。这个方向很适合作为毕业设计的前期基础。还有一个同学们问得很多的方向是低功耗。GP2Y1010AU0F的LED不采样的时候可以完全关闭。STM32F103C8T6进入STOP模式后电流能降到微安级别。设计成每60秒醒来一次采样3秒算个均值然后继续休眠。一块18650电池撑几个月没问题。低功耗改造的要点是采样前唤醒采样完毕关闭传感器电源和MCU外设时钟进入STOP模式前把所有GPIO配置成低功耗状态。我个人在实际操作中的体会是GP2Y1010AU0F这类红外传感器的绝对精度确实拼不过激光传感器但它最大的价值在于把模拟信号采集的全链路都暴露在你面前。LED脉冲时序、ADC采样窗口、滤波平滑、电压转浓度、标定修正这一整套流程走下来你对“传感器数据是怎么来的”理解会变得非常完整。之后再接激光传感器、接数字传感器本质上就是换一个数据源处理套路完全一致。这也是我建议新手从红外PM2.5传感器起步的原因——它足够简单但也足够让你学到真东西。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。