从PM2.5传感器到代码:PWM通讯的实战解码

张开发
2026/4/14 13:04:50 15 分钟阅读

分享文章

从PM2.5传感器到代码:PWM通讯的实战解码
1. 从PM2.5浓度到PWM信号一个完整的感知链路想象一下你正在装修新家担心室内空气质量。这时候你可能会买一个PM2.5检测仪但你知道这个小盒子内部是如何工作的吗其实整个过程就像是一个微型翻译官在忙碌传感器负责闻空气中的颗粒物浓度然后通过PWM信号说给单片机听。我拆解过市面上常见的激光式PM2.5传感器发现它们内部有个精妙的光学迷宫。当空气被小风扇抽进检测腔激光二极管发出的光束遇到颗粒物会产生散射光电探测器捕捉这些散射光强度经过放大电路处理后最终转化为电信号。这里有个有趣的细节大多数传感器输出的其实是电压信号但为什么我们常见PWM接口因为PWM抗干扰能力更强特别适合这种长距离传输的工况。以某型号传感器为例它的PWM输出规范是这样的周期固定为2000ms0.5Hz高电平持续时间代表浓度值典型输出范围100-2000μs对应0-500μg/m³这种设计比模拟电压输出更可靠。我实测过在电磁环境复杂的工业现场电压信号可能漂移10%但PWM信号始终稳定。这也是为什么很多工业级传感器首选PWM接口。2. PWM通讯的本质用时间编码信息第一次接触PWM时我总把它想象成摩尔斯电码。只不过这里不是滴答声而是用高低电平的持续时间来传递信息。PWM有三个关键参数频率就像说话的快慢传感器常用0.5-1kHz占空比高电平占整个周期的比例分辨率相当于说话的精确度8位分辨率只能区分256级在PM2.5传感器场景中厂商往往采用周期固定、脉宽变化的方案。这样做的好处是接收端处理简单只需要测量高电平持续时间。我对比过几种编码方式脉宽编码如100-2000μs对应0-500μg/m³占空比编码如5%-95%对应量程频率编码变化脉冲频率第一种方案最抗干扰因为只需要测量时间不受电压波动影响。这也是为什么你在代码中看到的是直接测量低电平持续时间。3. STC单片机PWM模块实战配置拿到传感器后我选择STC8H系列单片机作为接收端。这个国产MCU的PWM模块配置起来比STM32更简单特别适合快速验证。先来看硬件连接传感器PWM输出 → 单片机任意IO需支持外部中断共地连接必不可少关键配置步骤如下// 定时器2初始化用于测量脉宽 void Timer2_Init(void) { T2L 0; // 定时器2低字节 T2H 0; // 定时器2高字节 AUXR | 0x04; // 定时器2时钟为Fosc/12 IE2 | 0x04; // 允许定时器2中断 } // 外部中断初始化下降沿触发 void INTx_Init(void) { IT0 1; // 下降沿触发 EX0 1; // 允许INT0中断 EA 1; // 全局中断开启 }实际调试时我踩过一个坑没有开启定时器的自动重装模式导致测量值偶尔跳变。后来发现STC的定时器在中断服务函数中需要手动重装初值void timer2_isr() interrupt 12 { static unsigned int capture_count; capture_count (T2H 8) | T2L; T2H 0; // 必须手动重装 T2L 0; // 后续处理测量值... }4. 从原始数据到PM2.5浓度算法处理的艺术你以为测到脉宽就完事了太天真了原始数据需要经过三重处理才能用滑动滤波我习惯用8点滑动平均#define FILTER_LEN 8 uint16_t filter_buf[FILTER_LEN]; uint8_t filter_index 0; uint16_t sliding_filter(uint16_t new_val) { filter_buf[filter_index] new_val; if(filter_index FILTER_LEN) filter_index 0; uint32_t sum 0; for(uint8_t i0; iFILTER_LEN; i) { sum filter_buf[i]; } return (uint16_t)(sum / FILTER_LEN); }线性转换将脉宽映射到浓度值// 假设200-2000μs对应0-500μg/m³ float convert_to_pm25(uint16_t pulse_width) { if(pulse_width 200) return 0; return (pulse_width - 200) * (500.0 / 1800.0); }单位转换根据AQI标准转换为空气质量指数实测中发现传感器在低浓度区线性度较差我在项目里额外增加了分段补偿算法。具体参数要根据传感器型号通过实验确定建议用标准PM2.5源进行校准。5. 抗干扰设计与异常处理在工厂环境实测时电磁干扰导致数据偶尔跳变。我总结了几个实用技巧硬件滤波在信号线上加100nF电容到地软件校验连续3次超范围视为无效超时机制超过2个周期无信号触发报警异常处理代码示例#define MAX_VALID_PULSE 2500 // 最大合理脉宽(μs) #define MIN_VALID_PULSE 80 // 最小合理脉宽 uint8_t is_valid_pulse(uint16_t width) { if(width MAX_VALID_PULSE || width MIN_VALID_PULSE) { return 0; } return 1; } void process_pm25_data(void) { static uint8_t error_count 0; if(!is_valid_pulse(raw_width)) { error_count; if(error_count 3) { trigger_alarm(); } return; } error_count 0; // 正常处理流程... }6. 进阶应用PWM转串口输出很多场景需要把数据上传到上位机这时候可以增加串口转发功能。我设计了一个双缓冲结构typedef struct { uint16_t pm25_value; uint8_t updated; } sensor_data_t; sensor_data_t data_buf[2]; uint8_t active_buf 0; void uart_send_task(void) { if(!data_buf[!active_buf].updated) return; printf(PM2.5: %dμg/m³\r\n, data_buf[!active_buf].pm25_value); data_buf[!active_buf].updated 0; } // 在测量中断中切换缓冲区 void measurement_isr() { data_buf[active_buf].pm25_value processed_value; data_buf[active_buf].updated 1; active_buf !active_buf; }这种设计避免了数据竞争实测在115200波特率下工作稳定。如果需要更可靠传输可以增加CRC校验。7. 低功耗优化技巧对于电池供电的设备我摸索出一套省电方案间歇工作模式每30秒唤醒测量一次动态时钟调整测量时用24MHz空闲时降频到1MHz外设电源管理不使用时关闭传感器电源STC单片机低功耗配置示例void enter_idle_mode(void) { PCON | 0x01; // 进入空闲模式 // 通过外部中断唤醒 } void setup_low_power(void) { WDT_CONTR 0; // 关闭看门狗 AUXR | 0x80; // 分频器使能 CLK_DIV 0x03; // 时钟1/8分频 PWR_MODE 0x02; // 选择低功耗模式 }实测这些改动能让设备续航从3天提升到3周代价是响应速度降低。需要根据应用场景权衡。

更多文章