做空气质量监测项目时最常被翻牌的组合就是STM32连接红外PM2.5传感器。我手上这块夏普GP2Y1010AU0F就是典型的红外散射式粉尘传感器价格便宜、接口简单配合STM32最小系统板半小时就能跑通数据采集。这篇笔记不是抄手册而是把我实际接线、写代码、踩坑的过程完整记录下来包括ADC采集、PWM捕获、软件滤波、串口输出这些关键点希望对正在做环境监测小项目或毕业设计的同学有参考价值。很多人一上来就想上激光传感器那个确实精度高但单价摆在那里而且对风道、光路要求也高调试起来比红外麻烦得多。红外传感器的原理其实很直白传感器内部有一个红外LED持续发光当空气里的颗粒物经过光路时会发生散射旁边的光电二极管接收到散射光输出电压就会变化。颗粒物浓度越高散射光越强输出电压越高。这种结构决定了它测的是相对浓度绝对精度一般但做室内空气质量参考、灰尘报警、颗粒物趋势监测完全够用。1. 项目概述与设计思路1.1 为什么选择红外PM2.5传感器红外PM2.5传感器的最大优势是成本和易用性。像GP2Y1010这种型号几十块就能拿下而激光传感器普遍要贵好几倍。如果只做室内灰尘浓度监测、空气净化器联动或者毕业设计演示红外方案完全满足要求。它的功耗也不高传感器本体工作电流也就20mA左右STM32F103C8T6最小系统板加传感器一个USB口供电就够了。性价比之外信号接口简单是我的第二考量。这类传感器的输出一般是模拟电压少数型号输出PWM波形不管哪一种STM32都能轻松处理。模拟输出电压直接用ADC采集PWM输出就用定时器输入捕获测占空比。我在项目里用的是GP2Y1010AU0F它输出的是模拟电压所以我走ADC这条路但后面也会讲PWM型传感器的采集方法因为很多同学手上的是PWM输出的型号。1.2 整体方案选型先交代一下硬件配置。主控用的是STM32F103C8T6蓝色Pill板的经典配置72MHz主频12位ADC多个定时器做这类传感器采集绰绰有余。传感器选择GP2Y1010AU0F它需要一个脉冲信号驱动内部红外LED输出模拟电压接到STM32的ADC引脚。整体数据流是这样的GP2Y1010输出与粉尘浓度成正比的模拟电压STM32用ADC1采集这个电压通过电压与浓度的换算关系得到质量浓度值再经过软件滤波去掉短时波动最后通过串口输出到电脑。如果愿意后面还能加OLED显示或者ESP8266上传云端这个项目天然可以扩展成一个小型环境监测节点。之所以用标准外设库而不是HAL库一方面是这个项目代码量不大标准库足够另一方面网上大量STM32例程都是基于标准库照着改更容易。如果你习惯HAL库逻辑完全一样就是函数名和初始化结构体不同不影响下面的核心思路。2. 硬件连接与信号接口2.1 引脚定义与接线表GP2Y1010AU0F并不像普通传感器那样直接上电就能输出它内部有一颗红外LED这颗LED需要按特定时序点亮不然光电二极管只能收到环境杂光。传感器一共6个引脚其中1脚是V-LED接红外LED的电源2脚是LED-GND接LED的地3脚是LED-Drive用来给LED提供驱动脉冲4脚是Vout是模拟电压输出5脚是GND接系统地6脚是Vcc接5V电源。我在实际项目里把LED-Drive接到了STM32的定时器PWM输出脚PA0用固定频率和占空比去驱动红外LED。传感器系统连接如下传感器引脚功能接STM32/外部1 V-LED红外LED正极通过150Ω电阻接5V2 LED-GND红外LED负极GND3 LED-DriveLED驱动脉冲PA0TIM2_CH1 PWM4 Vout模拟电压输出PA1ADC1_IN15 GND信号地GND6 Vcc传感器电源5V有一个细节特别容易翻车V-LED这个引脚不能直接接5V手册要求在V-LED和5V之间串一个150Ω电阻而且在V-LED和LED-GND之间要跨接一个220uF的电容。这个电容不是滤波用的摆设它是给红外LED瞬间大电流提供储能让LED驱动电流稳定。我第一次搭电路时偷懒没加这个电容结果ADC读数上下跳了三百多个LSB加了电容之后明显稳下来。2.2 PWM驱动信号与LED脉冲GP2Y1010的LED驱动要求是脉冲信号周期10ms高电平时间0.32ms也就是占空比约3.2%。在这0.32ms的脉冲期间红外LED点亮光电二极管采样其余时间LED熄灭用来区分环境红外光。如果你的主控输出一个恒定高电平传感器也能工作但会引入环境光干扰所以还是按手册来。用STM32实现这个驱动很简单配置TIM2_CH1输出PWM频率100Hz占空比3.2%。代码里主要设置预分频器和自动重载寄存器。以72MHz主频为例要让定时器输出100HzPSC设置为71ARR设置为9999那定时器时钟就是1MHz实际频率就是100Hz。占空比3.2%就是在比较寄存器CCR1里写320。这个数值不需要特别精确但不要偏离太多。2.3 Vout输出与电压范围GP2Y1010的Vout引脚输出的是0~3.6V左右的电压无尘环境下大约0.6V随着粉尘浓度升高电压跟着涨。STM32的ADC输入范围是0~3.3V虽然Vout标称可达3.6V但在正常室内环境下很少超过2.5V所以可以直接接PA1采集。如果你担心极端环境超过3.3V可以在Vout和PA1之间加一个电阻分压或者加一个稳压管钳位。还有一个细节要注意Vout输出阻抗不低如果直接连一个很长的杜邦线到PA1线间电容可能会让ADC采样值滞后。建议Vout引脚到地并联一个100nF的陶瓷电容同时尽量缩短连接线。这个电容一方面滤掉了高频噪声另一方面也让ADC采样时不会因为源阻抗过高导致建立错误。3. STM32驱动与核心代码实现3.1 ADC初始化与单次转换STM32F103的ADC是12位的满量程对应3.3V所以理论上每个LSB对应约0.8mV。如果拿万用表量Vout是1.2VADC读数应该在1450左右。实际使用中Vref引脚接的是开发板自带的3.3V稳压输出精度一般作为室内监测够用。初始化代码我放在一个gpio_adc.c文件里。第一步开启GPIOA和ADC1时钟第二步把PA1配成模拟输入第三步配置ADC1为独立模式、单次转换、右对齐通道1采样时间选55.5周期。这里为什么要选55.5周期而不是最快的1.5周期因为GP2Y1010的输出信号带有比较慢的响应特性采样时间太短容易把上一位电容充放电的残留误采进来。实际调试下来55.5周期是稳定性和转换速度的一个很好平衡点。// gpio_adc.c ADC初始化 void ADC_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; ADC_InitTypeDef ADC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE); 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_55Cycles5); ADC_Cmd(ADC1, ENABLE); ADC_ResetCalibration(ADC1); while(ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while(ADC_GetCalibrationStatus(ADC1)); }校准这一步很多人会忽略。STM32的ADC在首次上电后内部电容有失调官方数据手册也建议先校准再转换。这个校准是硬件自动完成的我们只需要按上面代码调用即可。如果你发现ADC读数整体偏移先检查是不是漏了校准。读取ADC值的代码很简单软件触发一次转换等待EOC标志置位后读取结果// 读取一次ADC转换结果 uint16_t ADC_GetValue(void) { ADC_SoftwareStartConvCmd(ADC1, ENABLE); while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) RESET); return ADC_GetConversionValue(ADC1); }3.2 电压到浓度的换算思路ADC读到的数字量需要先换算成电压再换算成浓度。电压公式就是Vout ADC值 * 3.3 / 4096换算成电压后怎么得到粉尘浓度不同厂家的传感器标定曲线不一样。GP2Y1010的数据手册上有输出电压和粉尘浓度的大致关系很多应用笔记给的经验公式是D (Vout - 0.6) / 0.5其中D的单位是mg/m³。但我建议不要盲信这个公式因为传感器个体差异、光学老化、温湿度变化都会让这个斜率发生变化。更稳妥的做法是自己做两点标定至少确认零点位置。我的做法第一次拿到传感器先在干净空气下测一个电压V0然后把点燃的香烟靠近传感器进气口看电压最大能到多少记作V1。假设香烟烟雾浓度大约2mg/m³那标定斜率K (V1 - V0) / 2实际浓度就是D (Vout - V0) / K。这样虽然不精确但至少趋势和量级是可信的。软件里我实现了一个简单的滑动平均滤波对ADC原始值做去极大值和极小值后的平均代码如下#define FILTER_N 20 float get_density_mgm3(void) { uint16_t buf[FILTER_N]; uint16_t i, min, max; uint32_t sum 0; for(i 0; i FILTER_N; i) { buf[i] ADC_GetValue(); delay_ms(10); } min buf[0]; max buf[0]; for(i 0; i FILTER_N; i) { if(buf[i] min) min buf[i]; if(buf[i] max) max buf[i]; } for(i 0; i FILTER_N; i) { sum buf[i]; } sum - min; sum - max; float avg_adc sum / (float)(FILTER_N - 2); float vout avg_adc * 3.3f / 4096.0f; float density (vout - 0.6f) / 0.5f; if(density 0) density 0; return density; }这里每两次采样间隔10ms主要考虑到传感器本身响应速度比较慢你间隔1ms采20次和间隔10ms采20次平均值几乎一样但后者能覆盖更长的时间窗口滤除短时气流波动更有效。此外多次采样的过程也可以顺便让ADC内部电容完全稳定下来。3.3 用定时器输入捕获测PWM输出的传感器如果你的红外PM2.5传感器输出的是PWM方波而不是模拟电压那就不能走ADC了要用定时器输入捕获。这类传感器的输出很好识别只有两根信号线一根VCC一根OUTOUT上输出频率固定、高电平宽度随浓度变化的方波。例如有的传感器周期固定为1kHz左右高电平宽度从几十微秒到几百微秒变化占空比正比于粉尘浓度。测量占空比最常用的办法是定时器双通道捕获。我用TIM2的CH1和CH2分别接在同一个输出脚上CH1配置为上升沿捕获CH2配置为下降沿捕获这样CH1捕获值记录一个周期开始的时间CH2记录高电平结束的时间。两次捕获值相减再除以定时器计数频率就是高电平时间相邻两次CH1捕获值之差就是周期时间。占空比等于高电平时间除以周期时间再转成浓度。// 输入捕获中断服务函数里记录边沿计数值 void TIM2_IRQHandler(void) { if(TIM_GetITStatus(TIM2, TIM_IT_CC1) ! RESET) { TIM_ClearITPendingBit(TIM2, TIM_IT_CC1); if(rising_edge_flag) { rise_value TIM_GetCapture1(TIM2); } else { period TIM_GetCapture1(TIM2) - rise_value; } rising_edge_flag !rising_edge_flag; } }用输入捕获而不是普通的EXTI外部中断优势在于捕获动作完全由硬件完成不依赖CPU响应速度即使中断响应不够快或被打断计数器值也能在边沿瞬间被锁存到寄存器。这对PWM频率较高的情况特别重要。实际配置时要注意两点一是定时器的预分频器不能设太大否则计数分辨率不够。比如PWM频率1kHz如果计数频率只有100kHz那一个周期的计数值是100占空比分辨率只有1%不够用。我一般让计数频率至少1MHz以上。二是捕获极性配置要正确。CH1和CH2分别捕获上升沿和下降沿后还要在软件里区分哪一次是周期开始。用上面的二进制标志位即可。3.4 串口输出与printf重定向采集到的浓度数据最终要让人看到最简单的就是通过串口打印到电脑。我习惯用USART1PA9和PA10波特率115200。为了让printf直接输出到串口在工程里重写fputc函数// 重定向printf到串口1 int fputc(int ch, FILE *f) { while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) RESET); USART_SendData(USART1, (uint8_t)ch); return ch; }注意要包含stdio.h并且在Keil中勾选MicroLIB否则重定向后printf还是从Semihost走串口不会输出任何东西。这个坑我见过太多人踩。如果你不想用printf也可以直接用USART_SendData封装一个发送字符串的函数效果一样。主程序里每500ms采一次浓度然后打印一行printf(PM2.5: %.2f mg/m3\r\n, get_density_mgm3());串口助手能看到每秒打印两行数据。如果数据稳定在0.00附近说明传感器接入正常如果数值持续跳变就要回到滤波和电源上去查问题。4. 调试过程中的常见问题与排查技巧4.1 ADC读数一直是0或4095这种问题最常见的原因不是代码而是引脚配置错误。先拿万用表量PA1对GND的电压如果Vout本身有电压但ADC读数是0那大概率是GPIO没有初始化成模拟输入或初始化顺序不对。如果读数一直是4095说明引脚被内部上拉到了高电平或者传感器Vout悬空这时候检查杜邦线是否虚接。还有一个小概率情况PA1默认不是纯粹的GPIO功能。但PA1本身没有复用冲突问题多半出在ADC时钟或ADC通道号对应错误。查一下芯片数据手册的ADC通道表PA1对应ADC1_IN1通道号一定填1不是2。我一开始就填错过结果读出来一直是0。4.2 数值跳变剧烈如果你把传感器放在桌面上开风扇读数波动很大这其实不全是干扰问题。红外传感器测量的是局部空气的颗粒浓度人的走动、风扇气流、空调风向都会让浓度快速变化。所以采样值有波动是正常的关键是区分真实波动和测量噪声。我处理的方式分两级第一级硬件上在Vout引脚并联100nF电容第二级软件上做滑动滤波。如果还觉得波动大可以增大滤波窗口到50次或者改用一阶低通滤波。一阶低通滤波的写法filtered (float)0.1 * raw (float)0.9 * filtered;这个算法比滑动平均更平滑但响应会滞后适合周期性读取的场景。如果你的项目需要实时报警窗口不能开太大否则当火灾烟雾发生时读数会慢半拍。我建议滑动窗口不超过30次滤波系数在0.1到0.2之间。4.3 传感器上电后电压慢慢漂移很多人第一次用GP2Y1010上电后立刻读数发现电压从0.7V慢慢爬升到1.0V以为坏了。其实这是红外LED的温漂和光电二极管的稳定过程。传感器上电后需要预热官方没有明确规定但实测大概30秒到1分钟数值才会稳定。所以在你的主程序里上电后延时30秒再开始正式采样或者用串口打印“warming up”提示让系统在稳定后再工作。如果预热完成后电压还在缓慢漂移那就要怀疑V-LED的220uF电容容量不够了。电解电容长期使用容量会下降影响LED驱动电流稳定这种情况下直接换新电容。4.4 PWM输入捕获周期测不准用输入捕获测PWM占空比最常见的错误是定时器溢出没有处理。如果你的定时器是16位计数频率设得太高而PWM周期本身又比较长计数器可能在一个周期内溢出多次导致周期值出错。解决方法是把预分频器算好让计数器在一个PWM周期内最多计数到满量程的80%左右。比如PWM频率1kHz计数频率1MHz一个周期计数1000没有溢出完全够用。如果是低频PWM比如50Hz1MHz计数周期是20000也不会溢出。但如果计数频率设成72MHz且分频不当就很容易溢出。此外中断服务函数里做周期计算时要注意读两次捕获值是否被高优先级中断打断可以在进入函数后关闭一下定时器中断或者在开始时读取、结束时再读取确保用的都是同一周期的计数值。4.5 JTAG引脚冲突导致程序下不进去这个项目里如果用PA15、PB3、PB4做PWM或ADC就会遇到“stm32禁用jtag”的热搜词。GP2Y1010的LED-Drive我选的是PA0ADC选PA1都不涉及JTAG所以没冲突。但如果你的开发板引脚紧缺想用PB3或PB4复用功能必须在启动代码里禁用JTAG只保留SWD不然程序根本下不进去。禁用JTAG后SWD调试功能仍可用这一点不用担心。在代码里调用GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);这样PB3、PB4就能当普通IO用了。不过要注意这个函数必须在GPIO时钟使能之后调用否则可能无效。5. 实测效果与扩展方向5.1 实测数据表现我把整套系统焊在洞洞板上传感器探头悬在房间里室内没人走动时串口打印的浓度值稳定在0.0到0.1之间。点燃一根香烟放到传感器进气口附近读数2秒内跳到1.5左右烟雾消散后慢慢降到0.3附近。虽然这个绝对值和专业仪器的读数没法比但趋势非常明显用来做PM2.5相对变化监测完全没问题。通过串口打印的曲线还能看出一个小规律有人在室内走动或扫地时浓度会有一个短暂的尖峰几分钟后回落。这说明传感器对扬尘有响应不是只有烟雾才能测到。如果你想监测室内空气质量可以把传感器放在距地面1.2米左右的位置避开空调出风口这样数据更具代表性。5.2 后续可以扩展的方向这个项目的代码和硬件结构都非常好扩展。最简单的是加一块0.96寸OLED用SPI或I2C接口显示实时浓度和温度。稍微进阶一点加一个SHT30温湿度传感器把环境数据一起打包形成一个完整的气象节点。再进阶加ESP8266模块通过串口把数据上传到本地MQTT服务器这样手机就能查看家里的空气质量变化曲线。如果做毕业设计还可以加一个继电器控制的排风扇当浓度超过阈值时自动启动排风浓度降下来后自动关闭。这种闭环控制逻辑很讨喜而且理论上也不复杂只是把阈值判断加到主循环里。做这类扩展时电源设计要重新评估因为继电器和ESP8266的功耗都比传感器大很多最好单独用电源轨避免互相干扰。5.3 我个人的一些体会这个项目我总共做了两版第一版偷懒没加220uF电容也没做软件滤波数据抖得不像话。第二版老老实实按手册把电容加上再配合滑动平均效果立竿见影。所以说玩STM32连接红外PM2.5传感器硬件上把传感器供电处理好比在代码里花心思调滤波算法更有效。还有一点体会是不要迷信公式里的浓度绝对数值红外传感器的精度就那样但只要自己做过标定知道零点电压和量程范围拿它来做相对监测和趋势判断是完全靠谱的。如果你也是刚接触这类传感器建议先搭一个最简单的版本串口看到数据波形动了再慢慢加OLED、上云、控制这些功能一步步来比一次性堆满要顺利得多。
企业数字化 ERP 产品动态
相关推荐
Claude Code 深度拆解:远程模式 3 — 消息路由与传输层配置实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 14:44:33
端侧模型推理工程实战:ONNX Runtime 量化、打包与线程治理 明白了,我不再逐段解读提示词,而是以资深从业者身份,围绕“端侧模型推理工程:ONNX Runtime 打包、量化与推理线程治理”这个标题直接开写。全文按实战经验分享的调性组织,结构清晰、经验密集,符合社区发布习… · 2026/9/26 14:44:33
AI全栈安全渗透测试平台:从碎片化工具到智能协作的实战搭建 做安全测试这几年,我最大的感受不是漏洞变多了,而是手里的工具和流程碎了一地:资产收集要用一套脚本,漏洞扫描要开另一个平台,报告又得手动整理半夜才能发出去。正因如此,我花了大半年时间搭了一个自己的“… · 2026/9/26 14:44:33
Python调用各家大模型API统一示例:鉴权、流式与Token管理 简介:这份源码合集整合了国内十余家主流AI平台的Python调用示例,覆盖文心一言、通义、ChatGLM、Kimi、Deepseek、Baichuan、讯飞、腾讯、字节等常见服务商,面向需要快速接入各家API的开发者与学习者。针对不同平台接口的认证规则与返回格式差… · 2026/9/26 16:22:21
Windows下MCP服务配置:TaoToken统一Key接入与settings.json骨架实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 16:22:21
基于YOLOv8的校园能耗检测:数据集、训练与部署全流程 简介:这份资源面向计算机、人工智能、自动化等专业的在校学生与教师,提供一套可直接运行的YOLOv8校园能耗智能检测项目,适合作为毕业设计、课程设计或大作业的完整方案,也便于初学者进阶学习。压缩包共8个文件,包含3个… · 2026/9/26 16:22:21
深度解析MESI与 C++内存序列 MESI 协议(Modified, Exclusive, Shared, Invalidated)是用于解决多核 CPU 缓存一致性问题的协议。它定义缓存行四种状态,保证多个 CPU 核心访问共享数据最终一致。Modified(已修改):缓存行数据已经被修改&… · 2026/9/26 16:22:15
VS Code Agents 新玩法:先闲聊,后绑定项目 不知道大家平时用vscode Agents的时候有没有遇到这种尴尬。
想先跟AI唠唠思路,讨论下方案构思。可只要你想聊代码相关内容,就必须先打开对应的项目文件夹。
如果没加载工作区,Agent会话完全看不到本地代码;一旦切换工程࿰… · 2026/9/26 16:22:15
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍 简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践 一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46