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

STM32驱动GP2Y1010AU0F红外PM2.5传感器:从采样时序到浓度标定

发布时间:2026/9/24 23:28:55

资讯中心
01
ARTICLE

STM32驱动GP2Y1010AU0F红外PM2.5传感器:从采样时序到浓度标定

STM32驱动GP2Y1010AU0F红外PM2.5传感器:从采样时序到浓度标定
不知道多少人跟我一样第一次拿到GP2Y1010AU0F这颗红外粉尘传感器想当然地把AOUT接到STM32的ADC引脚上然后串口打印出来的数字像抽风一样乱跳。更崩溃的是网上查一圈资料全都停在“注意采样时序”这句话上至于这个时序到底是几微秒、在代码里怎么把握、电压怎么换算成µg/m³几乎没有人展开说。这篇文章就把我从接线到标定的完整折腾过程聊透针对STM32 红外PM2.5传感器这套组合把原理、CubeMX配置、HAL库驱动代码、实测结果和踩坑点一次性讲清楚。不管你是跟着视频教程做环境监测项目还是准备拿它当毕业设计的一环这篇应该能帮你少走不少弯路。1. 红外PM2.5传感器的选型逻辑为什么是GP2Y1010AU0F1.1 红外散射和激光散射的本质差异市面上常见的PM2.5传感器大致分两类一类是激光散射式典型代表是攀藤PMS5003、SDS011这类自带风扇、串口输出的模块另一类就是本文要说的红外散射式最经典的就是夏普GP2Y1010AU0F以及它的改进型号GP2Y1014AU0F。它们的测量原理有一点相同让空气里的颗粒物穿过一束光颗粒物会把光散射到各个方向传感器旁边放一个光电接收管接收到的散射光强度就能间接反映颗粒物浓度。区别在于光源和接收结构。激光传感器用的是波长更短、方向性更好的激光二极管配合气泵或风扇强制气流通过能比较准确地区分不同粒径所以它能告诉PM1.0、PM2.5、PM10分别是多少。红外传感器的光源则是一颗红外LED波长大散射效率对细颗粒不敏感而且没有风扇靠自然扩散进风。你可以把它理解成“用一个手电筒去照灰尘”大颗粒能看见小颗粒就未必。因此GP2Y1010AU0F标称的检测范围是0~0.5mg/m³也就是0~500µg/m³灵敏度大概是0.5V/0.1mg/m³。严格讲它测的是“粉尘浓度”不是真正意义上的“PM2.5浓度”但放在空气质量检测的开源项目里用来观察室内PM2.5变化趋势、控制空气净化器开关是够用的。如果你是想写论文、做计量级设备别犹豫直接上激光传感器。如果是学习STM32的ADC、定时器、中断外加做一个看得见摸得着的环境监测Demo红外方案便宜、简单、功耗低反而是很合适的切入点。1.2 数据手册读不透的原因时序远比公式重要GP2Y1010AU0F的数据手册并不长但第一次看很容易被一张时序图劝退。它真正难的地方不在于那几行公式而在于这颗传感器的输出不像普通模拟传感器那样提供一个稳定的直流电压。它的工作方式是每隔10ms给红外LED一个0.32ms的脉冲在脉冲点亮后的280µs左右传感器内部的采样保持电路会把光电接收信号锁存到输出引脚上让AOUT引脚输出一个与该时刻颗粒物浓度对应的电压值。你如果拿示波器去看AOUT引脚看到的不是一条平稳的线而是一个跟着脉冲节奏起伏的波形。也就是说ADC采样必须精准地落在LED脉冲期间的某个时间窗口里。采早了LED还没完全点亮信号没建立好采晚了LED关闭信号已经衰减。很多人第一步就挂在“为什么这个传感器读出来的数一直不对”上根本不是硬件坏了而是采样时机没对上。那为什么非要用脉冲方式驱动LED因为这颗红外LED瞬间电流不小持续点亮会发热而且没有脉冲调制的话环境红外光、LED自身温漂都会混进信号里测量就没法看了。脉冲驱动相当于给测量过程加了一个“时间闸门”只在固定窗口里读取其他时间一律忽略这样抗干扰能力才强。2. 硬件接线与等效电路驱动、采样和电源三件事分别处理2.1 模块引脚与STM32F103C8T6的接线清单现在市面上买到的GP2Y1010模块大部分已经把传感器本体、外围电阻电容、有时还带一个运放跟随器集成在一块小板子上。模块引脚一般是这样VCC、GND、AOUT模拟输出、ILEDLED脉冲控制有些模块还会引出DIG数字输出口那是模块内部比较器做出来的不走ADC。我用的是一块常见的蓝板STM32F103C8T6最小系统板接线如下传感器模块引脚连接到STM32说明VCC5V传感器供电按数据手册要求是5VGNDGND必须共地AOUTPA1ADC1通道1经分压和RC滤波后接入ILEDPA0推挽输出产生10ms周期、0.32ms高电平的脉冲这里要特别提醒一句GP2Y1010AU0F的数据手册上ILED驱动高电平典型值是5V而STM32的IO口是3.3V。如果你买的是带三极管驱动的成品模块3.3V通常能点亮内部LED但如果是裸传感器最好加一级NPN三极管用STM32的IO去控制5V通断别直接把IO怼上去。我最早用裸传感器直接接3.3V读数偏弱且不稳定后来加了2N2222三极管才正常。拿不准的时候先看卖家电路图或者直接用手头模块试读数不对就优先怀疑驱动电平。2.2 为什么ILED脉冲里藏着整颗传感器的命门ILED这根线是整个项目里最容易翻车的地方。先说结论它不能一直输出高电平也不能一直低电平软件上必须给一个周期10ms、脉宽0.32ms的脉冲。有些急性子同学拿到传感器后想着“LED亮不就能测了吗”直接把ILED接3.3V结果发现AOUT输出一个非常大的固定电压怎么吹风都不变。这是因为持续点亮时LED自身发热带来的散射和光电接收管的暗电流全部混了进来传感器内部的采样保持电路也认为现在是“常亮状态”输出的电压和颗粒物浓度不再有对应关系。另一个极端是ILED保持低电平这时候传感器完全没工作AOUT输出接近零读数自然是0。正确做法是让ILED跟着定时器节奏走。我用TIM6产生10ms更新中断每次中断里拉高PA0延时280µs后启动ADC转换转换完再延时40µs左右拉低PA0保持到下一个10ms到来。这样ILED引脚上就是一个标准的0.32ms脉冲和手册里的时序图严格对齐。2.3 AOUT进ADC前的一级分压和RC滤波参数GP2Y1010AU0F输出电平最高能到3.6V左右而STM32的ADC参考电压是3.3V直接接会有超量程风险。稳妥的做法是在AOUT和PA1之间串联一个2kΩ电阻PA1对地再接一个1kΩ电阻组成分压比1/3的电路。这样即使传感器输出3.6VPA1上也只有1.2V远在ADC安全范围内。同时这2kΩ电阻和PA1对地的0.1µF电容组成一个一阶低通滤波器截止频率大约在800Hz能滤掉输出引脚上的高频毛刺。很多同学用面包板飞线读出来的数据跳得厉害多半就是没用RC滤波或者用了10kΩ1µF这种把信号拉平的大时间常数。记住RC参数不能拍脑袋这里200µs的时间常数和280µs的采样窗口是匹配的如果时间常数过大脉冲还没建立起来采样就发生了读数会整体偏低。ADC分辨率方面12位ADC在3.3V参考下1个LSB约0.806mV。分压比1/3回退到传感器端等效约2.42mV对应颗粒物浓度大约0.5µg/m³。所以不用担心分压之后分辨率不够这个精度对红外传感器来说已经绰绰有余。3. 基于CubeMX和HAL库的驱动代码把280µs采样时机变成程序3.1 外设配置清单TIM6、ADC1、USART1的初始化要点我用的是STM32CubeMX生成工程用的STM32F103C8T6HAL库。核心外设只有三个TIM6做10ms时基、ADC1做模拟采样、USART1打印结果。TIM6配置预分频PSC719自动重装载ARR999。如果你的APB1定时器时钟是72MHz那么定时器计数频率就是72MHz/(7191)100kHz再计1000个数就是10ms。这里要注意CubeMX里如果APB1被分了频定时器时钟会自动×2所以最终看到的时钟源可能已经是72MHz不用再额外乘2。ADC1配置选择PA1为ADC1_IN1扫描模式关闭连续转换关闭采样时间拉到最长239.5周期。有的教程为了省事用连续扫描模式但这里必须单次转换因为每次采样只发生在280µs窗口内转换完就该停等下一个10ms周期再来。USART1115200-8-N-1用于把浓度值发到PC串口助手。GPIO配置PA0是推挽输出初始电平设为低这里接ILEDPA1被ADC占用不用再手动配GPIO。生成工程后在stm32f1xx_it.c的TIM6_IRQHandler里HAL库会自动调用HAL_TIM_IRQHandler最终走到HAL_TIM_PeriodElapsedCallback回调。我们就在这个回调里完成拉高、延时、采样、拉低的动作。3.2 基于DWT的微秒延时为什么不能依赖HAL_Delay这是很多人代码“看起来对、实际不对”的关键点。HAL库自带的HAL_Delay是毫秒级延时底层基于SysTick精度不够而且它在中断上下文里使用很容易出问题。我们需要一个微秒级延时函数最好基于DWT数据观察点计数器它直接用CPU周期计数不依赖SysTick也不会被其他中断影响精度。static void delay_us(uint32_t us) { DWT-CYCCNT 0; DWT-CTRL | DWT_CTRL_CYCCNTENA_Msk; uint32_t start DWT-CYCCNT; uint32_t ticks us * (SystemCoreClock / 1000000U); while ((DWT-CYCCNT - start) ticks); }使用条件是SystemCoreClock要和实际系统时钟一致STM32F103C8T6在CubeMX里如果配了72MHz这个值就是72000000。算一下72MHz主频下CPU跑一个周期约13.9ns延时280µs需要约20160个周期这种基于周期的延时在中断里用非常稳。建议把TIM6的中断优先级设为最高。因为采样时机要求拉高后280µs就必须读ADC如果这段窗口被其他中断插进来拖了太久读到的就不是280µs时刻的有效信号了。主循环里那些串口打印、OLED刷新优先级都往后退。3.3 浓度换算公式与分压比回退计算每次采样后HAL_ADC_GetValue()拿到的是0~4095的原始值。换算的第一步是还原成PA1引脚电压float v_adc (float)adc_raw * 3.3f / 4096.0f;第二步因为前面用了1/3分压真实传感器输出电压是float v_sensor v_adc / 0.333f;第三步用数据手册的灵敏度和零漂把电压变成浓度。GP2Y1010AU0F的典型零漂是0.6V灵敏度是0.5V对应0.1mg/m³也就是0.5V对应100µg/m³那么每1V对应200µg/m³#define SENS_SLOPE_UG_PER_V 200.0f #define DUST_ZERO_V 0.6f float conc (v_sensor - DUST_ZERO_V) * SENS_SLOPE_UG_PER_V; if (conc 0.0f) conc 0.0f;假设采样到ADC原始值是512那么v_adc0.4125V回退到v_sensor1.238V浓度就是(1.238-0.6)*200≈127.6µg/m³。这个数值在室内香烟烟雾环境下是合理量级。完整的中断回调加浓度计算逻辑如下volatile uint16_t g_adc_raw 0; void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim-Instance TIM6) { HAL_GPIO_WritePin(ILED_GPIO_Port, ILED_Pin, GPIO_PIN_SET); delay_us(280); HAL_ADC_Start(hadc1); HAL_ADC_PollForConversion(hadc1, 100); g_adc_raw HAL_ADC_GetValue(hadc1); delay_us(40); HAL_GPIO_WritePin(ILED_GPIO_Port, ILED_Pin, GPIO_PIN_RESET); } }主循环里只做数值读取和串口打印不要在中断回调里直接调用printf或HAL_UART_Transmit否则串口阻塞会拖垮采样时序。我一般是这样采样回调只更新全局变量主循环用HAL_Delay(1000)控制1秒打印一次顺便对100次采样的结果取平均再输出。4. 实测与标定出厂曲线只是参考零漂和斜率必须自己校准4.1 干净空气下的零点基准调整数据手册给的0.6V零漂是典型值不是每个传感器的出厂实测值。同一批次的模块在同样干净空气里零漂可能落在0.5V到0.8V之间。如果你的代码固定用0.6V做减法有些模块会在干净空气下算出几十µg/m³的假浓度。正确做法是上电后先让传感器预热几分钟等内部LED和光电二极管温度稳定再在相对干净的室内采集200~500次AOUT取平均作为实测零漂。我手上这块模块实测零漂是0.63V和手册的0.6V差了0.03V换算成浓度就是6µg/m³在标准AQI分级里可能直接把“优”误判成“良”所以这个标定不能省。如果你所处的环境本身就雾霾很重找不到干净空气做基准可以用一个带HEPA滤网的空气净化器把传感器放进净化器出风口附近等读数稳定后采集零漂。没有净化器的话把传感器放在通风处、远离明显灰尘源也能凑合。4.2 用香烟烟雾做阶跃响应观察完整的冲高回落过程标定斜率比较粗糙但很有效的办法是香烟法。点一支烟放在传感器侧面20~30厘米处不要直接怼着传感器吹让烟雾慢慢飘散过去然后观察串口打印的浓度曲线。我的实测过程是这样的干净环境下浓度稳定在3~8µg/m³左右烟头靠近后浓度在几秒内冲到150~250µg/m³把烟拿走后浓度缓慢回落但不会立刻回到零要等一两分钟。这个“冲高、维持、回落”的过程就是传感器的典型动态响应如果你看到的是读数不动、或者一碰烟直接顶格500µg/m³且不回落基本说明采样时序或供电有问题。如果想做更精确的斜率修正需要一台激光PM2.5检测仪作为参考让两种传感器并排放置在香烟烟雾环境里记录多组数据然后用最小二乘法拟合出真实斜率。比如我拟合出来是“实测浓度 (Vout - 0.63) * 220”和手册的200有明显偏差。这个修正会让数据在100µg/m³以上时更好看一点但对精度要求不高的趋势监测直接用200其实也能接受。4.3 温湿度、摆放角度和气流对数据的影响红外散射传感器对环境因素比想象中敏感。湿度大的时候颗粒物表面会吸水变大散射信号增强同一批颗粒物显示出来的浓度会偏高。所以阴雨天和干燥天的读数可能有10%~20%的偏差不要以为是传感器坏了。摆放角度也很有讲究。GP2Y1010的检测腔是侧边开孔的颗粒物要靠自然气流飘进去。如果传感器紧贴墙壁或者进气口朝下采样到的空气是停滞的读数会明显偏低。我后来把传感器固定在壳体外侧进气口朝前斜向下45度远离人的呼吸区读数的重复性好了很多。另外空调风口、暖气旁边、门窗缝隙这些位置瞬时气流会把局部灰尘吹起来导致读数毛刺特别大。尽量把传感器放在能代表房间整体空气的位置高度离地面1.2~1.5米和人呼吸带接近。5. 踩坑实录串口读数为零、固定满量程、数值乱跳的排查链路5.1 读数为零不是接线问题是时序没打进去有一次我把代码烧进去串口一直打印0.0第一反应是接线松了重新插了一遍还是0。后来用示波器看PA0引脚发现根本没有脉冲输出。查了CubeMX配置才发现TIM6初始化生成的代码在MX_TIM6_Init里正常但我忘了调用HAL_TIM_Base_Start_IT(htim6)定时器中断根本就没跑起来ILED一直是低电平。这类问题的排查顺序可以固定下来先看ILED引脚有没有10ms周期的脉冲再看AOUT在脉冲窗口里有没有对应波形最后才怀疑ADC配置。GPIO推挽输出初始化后默认低电平PA0如果一直为低传感器整个不工作ADC读到0很正常。代码逻辑上记得在main函数里把TIM6中断启动函数加上。5.2 满量程输出的元凶ILED被当成了普通LED还有一次同事把ILED直接接到了3.3V说“我一直让它亮着不就行了”。结果传感器输出一直在3.2V左右换算成浓度接近满量程。这个坑前面已经讲过原因是LED持续点亮后传感器内部采样保持电路被“常数”信号淹没输出不再对应真实颗粒物浓度。如果出现固定满量程先查ILED波形。用示波器看如果是一个方波说明驱动正常如果是一条直线高电平那就是时序问题。没有示波器的话用万用表测ILED对地平均电压正常情况下应该在0.1V到0.5V之间跳变如果稳定在3.3V基本就是持续点亮了。5.3 数据乱跳的电源与采样稳定性分析排除时序问题后数据乱跳最常见的两个原因电源纹波和采样时机抖动。GP2Y1010的红外LED脉冲瞬间电流不小如果传感器VCC直接从STM32核心板的5V引脚取而核心板的稳压电路裕量又不够LED点亮瞬间会让5V跌下去AOUT跟着波动。我的解决办法是在传感器模块的VCC和GND之间并联一个100µF电解电容再并一个0.1µF陶瓷电容电解电容兜底瞬时电流陶瓷电容滤高频。加了电容之后读数毛刺肉眼可见地减小。另一种“乱跳”是采样时机抖动。如果主循环里有重负载中断或者TIM6中断优先级不够高280µs采样窗口会被挤占。表现为读数一会儿正常一会儿偏低波形图上看是稀疏的尖刺。这种问题把TIM6中断调到最高优先级即可解决。另外ADC转换不要用中断方式在中端回调里同步等待转换完成反而更可控。还有一个小细节PA1悬空的时候ADC输入是高阻态读出来的数可能随机漂。如果暂时不接传感器先PA1接地能排除干扰。接好传感器后如果读数奇大检查PA1是不是没焊牢、或者分压电阻虚焊。6. 这套方案下一步怎么用从空气质量检测节点到传感器选型复盘6.1 软件滤波和滑动平均过滤毛刺但不掩盖突变传感器每10ms产生一个采样点主循环1秒取100次平均得到的是平滑的1秒浓度值。想要更快响应可以用滑动平均维护一个10~20个点的环形缓冲区每次进来一个新值就更新平均。也可以用一阶低通滤波float filtered 0.0f; float alpha 0.2f; filtered alpha * current (1.0f - alpha) * filtered;alpha越大越跟手越小越平滑。红外传感器本身响应就偏慢alpha取0.1~0.3之间比较合适。要记住滤波目的是去掉高频毛刺不是把烟雾冲高这个信号也抹平如果为了“数据好看”把滤波调到完全平滑真实污染事件反而看不出来了。6.2 什么时候必须换成激光PM2.5传感器这个项目做完之后我对红外方案的边界有了更清楚的认知。如果只是做一个桌面空气监测小盒子、自动空气净化器红外方案完全够用而且成本低、功耗小、不用风扇安静。但如果你想做空气质量研究、或者要输出PM1.0/PM2.5/PM10的分粒径数据红外方案就无能为力了。激光传感器的数据是数字串口输出的STM32读起来反而更简单一个UART接上按协议解析帧就行不需要操心280µs采样窗口这些事。它们的价格已经从几年前的几百块降到了几十块比如PMS5003、SDS011这类模块几乎成了DIY空气站的标准配置。所以我的建议是学习阶段玩红外理解传感器底层测量逻辑正式做产品直接上激光方案省下的时序调试时间远大于的硬件成本差距。6.3 和显示、联网、OTA组合时的模块划分建议如果你想把PM2.5传感器和其他功能组合成完整项目比如加载LVGL做屏幕显示、ESP8266联网上传、甚至OTA远程升级那么采样模块最好独立封装。我一般会把传感器驱动拆成两个文件dust_sensor.c和dust_sensor.h对外只暴露一个初始化函数和一个获取浓度函数ADC、TIM6、DWT延时都藏在文件内部。这样主循环里想显示就显示、想联网就联网不会因为屏幕刷新或网络阻塞反过来影响采样时序。OTA升级这类功能更是如此如果风扇、屏幕、网络全都堆在同一个处理循环里Flash擦除期间中断被长时间阻塞传感器数据就会断档把采样独立出来会稳得多。至于下一步扩展我建议优先加一个温湿度传感器做数据补偿再用ESP8266把数据推上MQTT最后搭一个简单的Web页面。整套链路跑通你就有了一台真正属于自己的空气质量检测节点比单纯在串口助手看数字有意思得多。
02
RELATED NEWS

相关资讯

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

03
WHY YAOTU

想打造同款高转化官网?

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

场景化定制

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

营销型架构

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

全周期服务

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

免费获取你的建站方案

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