简介:一套基于R7F0C001G2单片机的便携式PM2.5检测仪完整设计方案,面向嵌入式开发者与电子工程学习者;属于瑞萨RL78系列低功耗微控制器,内置Flash、SRAM、定时器和串行接口,适合作为环境监测终端的核心处理器。资源共50个文件,约2.64MB,包含11个.c与11个.h源码、原理图、PCB、BOM、用户手册及设计说明,还有HEX、MAP、LMF等编译调试文件;软件部分覆盖数据采集、处理与显示逻辑,硬件部分包含传感器模块、电源管理与模拟数字转换电路,结构清晰便于按需查阅。通过软件源码、原理图与PCB图可完整还原设备硬件设计,设计文档与BOM列表对选型、调试和二次开发均有参考价值;高精度AD转换相关设计可帮助理解PM2.5模拟信号采集与量化处理。目前已有796人学习,适合单片机应用、嵌入式系统及环境检测类项目设计参考。
1. R7F0C001G2 便携式 PM2.5 检测仪:从原理图、PCB 到源码一次说透
很多人拿到“基于R7F0C001G2单片机便携式PM2.5检测仪”这类设计资料时,第一反应是打开原理图找接口,却忽略了整个设计真正的难点不在 MCU 本身,而在传感器供电、数据帧解析和低功耗调度。R7F0C001G2 是瑞萨 RL78/G12 系列 16 位单片机,1.8V 即可运行,HALT/STOP 模式下静态电流能压到微安级,很适合电池供电的便携环境监测设备。这篇文章按一套能直接跑通的方案来讲:硬件怎么选型、原理图怎么画、PCB 怎么布、源码里的帧接收状态机怎么写,最后落在整机校准和常见坑上。无论做课程设计、毕业设计,还是想把这个方案改成量产产品,照着这个思路走比反复焊板子调线高效得多。
2. 硬件原理图设计:R7F0C001G2 资源分配、传感器选型与电池供电
2.1 激光 PM2.5 传感器选型:为什么串口输出比 PWM 更合适
PM2.5 检测仪的核心是激光粉尘传感器,原理是用激光照射流过采样腔的气流,颗粒物产生散射光,光电二极管把散射光转换成电信号,再经过放大和算法换算出质量浓度。早期模块(比如夏普 GP2Y1010)输出的是模拟电压,MCU 用 ADC 采样后查表换算;后来很多模块改成 PWM 输出,通过测量脉冲宽度得到浓度;现在主流是串口 UART 输出,激光传感器内部直接完成光路校准和粒径统计,输出 PM1.0、PM2.5、PM10 三组数值,使用门槛最低。
便携式设计里,我一般会优先选 UART 输出的模块,比如攀藤 PMS5003 或其兼容型号。原因是它自带风扇,采样腔封闭,对外围光路结构几乎没要求;MCU 不需要额外做放大电路,原理图大大简化。PWM 输出虽然接线更少,但要频繁用定时器捕获脉宽,采样周期长,而且拿不到粒径分级数据。如果课程设计或毕业设计里想展示“单片机原理及应用”的完整链路,UART 方式刚好能把串口、中断、状态机都用上。
下表是三种输出方式的实际对比:
| 输出方式 | 典型模块 | 拿到什么数据 | MCU 侧工作量 | 适用场景 |
|---|---|---|---|---|
| 模拟电压 | GP2Y1010 | 电压值,自行查表 | ADC 采样加换算,需标定 | 模拟电路教学、低成本风速计 |
| PWM | 部分国产模块 | 占空比对应浓度 | 需要定时器捕获脉宽,代码耦合高 | 对数据精度要求不高的场合 |
| UART | PMS5003、SDS011 | PM1.0/2.5/10,帧格式固定 | 串口中断加状态机,代码清晰 | 便携检测仪、气象站、空气质量监测 |
选 UART 模块还有一个好处:传感器内部自带的 MCU 已经做了温湿度补偿,数据比自己做后端滤波再查表更稳定,这对后续软件源码里的数据质量是决定性的。
2.2 R7F0C001G2 的最小系统与引脚分配
R7F0C001G2 的资源并不多:Flash 和 RAM 按型号有差异,外设包含 UART、I2C、SPI、ADC、定时器,IO 口需要精打细算。做 PM2.5 检测仪,最常用的外设是 UART0 接传感器、ADC 接电池电压检测、GPIO 接 LCD 和按键。下面是一套常见分配方案:
| 功能 | 接口 | 说明 |
|---|---|---|
| PM2.5 传感器数据 | UART0 RXD/TXD | 波特率 9600,3.3V TTL,传感器空闲时引脚为高 |
| LCD 显示 | P6 并口或 SPI 引脚 | 如果选 1602/12864 这类并口屏,占用 6~11 个 IO |
| 电池电压采样 | ANI 通道 | 通过电阻分压后接入 ADC,量程 0~3.3V |
| 测量状态 LED | GPIO 输出 | 低功耗时关闭 |
| 按键“切换显示” | GPIO 输入 | 使用内部上拉,按下为低电平 |
R7F0C001G2 最小系统比 STC 单片机多注意两点:去耦电容要靠近电源引脚,复位引脚用 10kΩ 上拉到 VDD 再串 0.1μF 到地;电源地要单点接入系统地。晶振方面,便携设备不需要长时间连续高精度计时,如果只是做浓度采样,可以直接用内部高速时钟,省掉外部晶振占用的 PCB 面积;但如果要加 RTC 定时唤醒,建议使用内部低速时钟配合定时器,毕竟外壳做不到手表那么小,节省一个晶振对 layout 帮助很大。
给出一段典型的 GPIO 和串口初始化代码,方便对照原理图检查接线:
void system_init(void) { // R7F0C001G2: 关闭看门狗, 配置时钟到内部高速 PM4 |= 0x02; // P42 设为输入 (PM4寄存器操作示例) PM0 &= 0xFE; P0 &= 0xFE; // 传感器电源控制引脚, 初始为低 // 串口0: 9600, 8N1, 中断接收 S0 &= 0x0F; S0 |= 0x00; // CK00 设为内部时钟输出 ST0 |= 0x01; }这段代码不是某个芯片的完整寄存器手册,而是想强调:原理图上每个引脚的信号方向、上下拉、初始电平状态,在软件初始化里都要“再确认一遍”。很多新人把 RX/TX 接反,或者把传感器电源控制引脚默认拉高,导致传感器通电就耗电,便携设备电池半天就没电了。
2.3 锂电池供电与传感器分时供电设计
便携式 PM2.5 检测仪通常是 3.7V 锂电池供电,系统逻辑电平 3.3V,而部分激光传感器模块标称 5V 或 3.3~5V 兼容。如果传感器需要 5V,原理图上就要加一个 DC-DC 升压;如果传感器可以在 3.3V 下工作,直接用电池经 LDO 降压到 3.3V 即可,整个系统只有一路电源,设计最省事。以 PMS5003 为例,标称工作电压 5V,但很多兼容模块在 3.3V 也能启动,只是激光头亮度略微下降、风扇转速变低,数据稳定性会差一点。产品化建议升压到 5V 给传感器独立供电,MCU、LCD、按键用 3.3V。
大电流路径要单独处理。锂电池充电用 TP4056 这类国产充电芯片,输入 5V,充电电流设 500mA 或 1A;电池输出经过保护板后,用 MOS 管控制传感器供电。MOS 管栅极由 MCU GPIO 驱动,原理图里要加一个 100kΩ 下拉电阻,防止 MCU 未初始化期间传感器意外启动。下面是一个典型的分时供电思路:
void sensor_power(uint8_t on) { if (on) { P0 |= 0x01; // 打开传感器电源 MOS delay_ms(20); // 等待风扇启动、光路稳定 } else { P0 &= 0xFE; } }传感器在测量时电流约 60~100mA,电池电压检测电阻分压要选大阻值,比如 100kΩ 分压到 MCU ADC 引脚,待机漏电流控制在微安级。整个系统的低功耗策略在原理图阶段就要确定:传感器电源可控制、LCD 背光可关闭、MCU 能在 STOP 模式定时唤醒。如果这三件事做好了,两节 18650 电池支撑一周以上待机是没问题的。
3. PCB 布局布线:干扰、电流与结构三者怎么兼顾
3.1 便携设备板框结构与功能分区
便携检测仪的外壳通常是一个长条形小盒子,正面开 LCD 窗口,侧面留传感器进风口。PCB 第一步不是在 AD20 里乱拖器件,而是把外壳的板框导入,确定连接器、按键、LCD、传感器风口的位置,再开始分区布局。常见做法是:板子从进风端到出风端依次放传感器模块、传感器电源、MCU、LCD 和电池接口,模拟区域靠近风口,数字区域靠电池。
PCB layout 里最容易被忽略的是传感器模块的风道。激光传感器内部有风扇和弯道,如果 PCB 上元器件太高挡住风口,数据会跳动。选择在 PCB 边缘留出 20mm 以上的净空,传感器模块用排针或板对板连接器立装,与主板成 90 度角,这样风道完全避开了主板。空间实在紧张时,可以把传感器放在 PCB 反面,正面继续放 MCU 和 LCD。
功能分区是三块:电源区(电池接口、充电芯片、LDO)、数字区(MCU、晶振、LCD)、模拟传感器接口区。电源区的地和数字区的地不能用整片铜直接连死,建议单点连接,即在 MCU 下方用 0 欧电阻或磁珠把模拟地、数字地、电源地汇合。如果不做分区,激光传感器的风扇电机会把 EMI 耦合到电池电压采样线,ADC 读数会随着风扇转速抖动,这个坑在整机测试时特别难查。
3.2 走线宽度、层叠厚度与铜皮挖空
便携设备层数不必太多,双面板足够,板厚 1.0mm 到 1.6mm 都行。层叠越薄,回流地路径越短,但过薄的板子在传感器排针处容易断裂,一般选 1.2mm 以上。有人问 PCB 0.3mm 能过多少电流,这个问题要看铜厚和温升:1oz 铜厚、外层走线、温升 10℃ 的条件下,0.3mm 宽大约能过 0.5A;考虑到电池充电电流通常 1A,充电路径至少要 1mm 宽。传感器供电线虽然不是大电流,但它是脉冲式负载,线宽不要低于 0.5mm,否则线上压降会让传感器风扇转速波动,影响采样稳定性。
用 IPC-2221 的经验公式可以快速计算走线载流能力:
import math def trace_current(width_mm, thickness_oz=1, temp_rise=10): # 外层走线, IPC-2221 公式 area = width_mm * 0.035 * thickness_oz # 铜截面积 mm^2 current = 0.048 * (temp_rise ** 0.44) * (area ** 0.725) return current for width in [0.2, 0.3, 0.5, 1.0, 1.5]: print(f"{width}mm, 1oz, 10C -> {trace_current(width):.2f}A")在 AD20 中画 PCB 时,把上述计算结果作为规则输入,而不是凭感觉给线宽。另一个实用技巧是铜皮挖空:在激光传感器的光电二极管下方、电池电压分压电阻下方,将顶层或底层的整片铜皮挖空。原因是传感器内部信号微弱,PCB 上的铜箔会形成寄生电容,把数字噪声耦合进去。挖空方式不是删掉走线,而是在 Keep-Out 层画一个区域,再在铺铜时排除该区域;AD20 里用“Place → Polygon Pour Cutout”或在铺铜属性里添加 Exclusion 区域都行。走线挖空区周围不要走高频信号线,否则铜皮挖空反而形成天线。下表是便携 PM2.5 检测仪常用走线规则:
| 信号 | 建议线宽 | 说明 |
|---|---|---|
| 电池正负极 | 1.0mm~1.5mm | 充电和放电共用 |
| 传感器 5V 供电 | 0.6mm~0.8mm | 需承受峰值 100mA |
| UART 数据线 | 0.3mm | 低速信号,无需屏蔽 |
| ADC 分压信号 | 0.3mm 并包地 | 左右两侧铺地铜 |
| LCD 并口数据线 | 0.25mm~0.3mm | 等长要求不高 |
3.3 制板前的 DRC 检查与典型规则设置
画完 PCB 先别急着生成 Gerber,AD20 的 DRC 要按板厂工艺来设。最小线宽设 0.2mm,最小间距 0.2mm,过孔外径 0.6mm、孔径 0.3mm,丝印不要压在焊盘上,否则装配后丝印模糊、位号看不清。把这些写进规则后跑一遍 DRC,常见错误集中在“Un-Routed Net Constraint”和“Silk To SolderMask Clearance”。
AD20 中执行 DRC 的路径是“Design → Rules → 设置 Electrical 和 Manufacturing 类目 → Tools → Design Rule Check → Run DRC”。报告里重点看三类:开路、间距违例、丝印侵占。还有一种情况是同一网络有多个名字,比如 GND 被拉分成 GND_A、GND_B,DRC 不会报错,但板子焊完就是地回路异常。习惯性在原理图里用同一种 GND 符号,然后靠 0 欧电阻或磁珠做单点汇合,不要改网络名。制板文件导出时顺便把 BOM 对一遍,尤其是电阻封装,0603 和 0805 混用会让贴片厂额外收费。
4. 软件源码:帧接收状态机、数据滤波与低功耗显示
4.1 传感器串口帧的接收状态机实现
PMS5003 这类 UART 传感器上电后会主动上报数据,一帧 32 字节,帧头是 0x42 0x4D,紧接着是帧长度、数据段、最后两字节校验。MCU 只需要被动接收,但如果用“等串口读到 0x42 再读后续字节”的串行阻塞方式,一旦中间丢一个字节,后面所有帧就全错位了。正确做法是收到一个字节触发一次中断,在中断里用状态机拆帧,也就是“当单片机遇上状态机”这一思路的典型应用。
#define FRAME_HEAD0 0x42 #define FRAME_HEAD1 0x4D #define FRAME_LEN 32 static uint8_t rx_buf[FRAME_LEN]; static uint8_t rx_cnt = 0; static uint8_t state = 0; // 0=等待0x42, 1=等待0x4D, 2=接收数据 void uart_rx_isr(uint8_t ch) { switch (state) { case 0: if (ch == FRAME_HEAD0) { rx_buf[0] = ch; state = 1; } break; case 1: if (ch == FRAME_HEAD1) { rx_buf[1] = ch; rx_cnt = 2; state = 2; } else { state = 0; // 帧头错误, 重新找 } break; case 2: rx_buf[rx_cnt++] = ch; if (rx_cnt >= FRAME_LEN) { state = 0; process_pm_frame(rx_buf); // 帧满, 交给上层校验 } break; default: state = 0; break; } }这个状态机有几个值得注意的参数:FRAME_LEN 必须和传感器数据手册里的帧长完全一致,有的传感器帧长是 32,有的是 24,定义错了会永远拼不出一整帧。其次,状态机跳转必须处理偏移,比如第一字节就收到 0x4D,不能硬等 0x42,而是直接重新开始。最后,如果传感器重启后发送半帧,MCU 会进入 state 2 但收不到剩余字节,所以上层要加一个“超过 50ms 没收到新数据就复位状态机”的超时机制。
4.2 数据合法性校验与滑动平均滤波
收到完整帧以后直接取第 4、5 字节当作 PM2.5 是一个常见错误。PMS 系列协议里,数据段最后两字节是校验和,定义是“帧头之后所有字节之和取低 16 位”。不做校验就使用数据,万一通信被电机噪声干扰,显示数值会偶尔跳到 999。校验和滑动平均滤波放在同一个函数里:
static uint16_t pm_buf[8]; static uint8_t pm_idx = 0; static uint16_t pm_sum = 0; static uint8_t pm_cnt = 0; uint16_t pm2p5_filtered(uint8_t *frame) { uint16_t sum = 0; uint16_t cal = 0; uint16_t raw; int i; // 累加校验: 帧头之后所有字节 for (i = 0; i < FRAME_LEN - 2; i++) { sum += frame[i]; } cal = (frame[FRAME_LEN - 2] << 8) | frame[FRAME_LEN - 1]; if (sum != cal) { return 0xFFFF; // 校验失败, 调用方应丢弃 } raw = (frame[4] << 8) | frame[5]; // PM2.5 浓度, 单位 ug/m3 if (pm_cnt < 8) { pm_buf[pm_cnt++] = raw; pm_sum += raw; return pm_sum / pm_cnt; } pm_sum -= pm_buf[pm_idx]; pm_sum += raw; pm_buf[pm_idx] = raw; pm_idx = (pm_idx + 1) % 8; return pm_sum / 8; }滑动平均窗口选 8 次,每次间隔约 2.4 秒,对应传感器自身采样周期。窗口太短,数据还是抖;窗口太长,人拿着检测仪从室内走到室外,数值要 30 秒才稳定,用户会认为“坏掉了”。实际调试时可以把原始值和滤波值同时通过串口打印到电脑上,比较一下抖动幅度。另一个细节是校验失败返回 0xFFFF,上层收到这个值要直接丢弃,而不是把上一次数据再补上来,否则最后一次有效数值会“粘”在界面上,让用户误以为浓度恒定。
4.3 低功耗调度与 LCD 显示刷新
便携式 PM2.5 检测仪的软件框架通常是:STOP 模式休眠,RTC 定时唤醒(比如每 15 秒),唤醒后开传感器电源、等 30 秒?传感器稳定后再读数据,刷新 LCD,然后继续休眠。这个流程里最大的功耗浪费是等待传感器稳定,因为风扇转一圈需要时间,光路也要预热。改进办法是提前开传感器:唤醒前 5 秒先供电,MCU 在 STOP 模式下可以用定时器中断先打开传感器电源,再延时进入 STOP,这样真正测量时传感器已经稳定。
void rtc_wakeup_isr(void) { sensor_power(1); // 不立即进入测量, 而是让传感器先工作 pm_wait_ready = 1; set_delay(5000); // 启动一个 5 秒软定时器 } void timer_delay_isr(void) { if (pm_wait_ready) { pm_wait_ready = 0; start_uart_receive(); // 开始采集 lcd_refresh_async(); // 异步刷屏 } }LCD 刷屏是另一个被低估的耗电项。12864 这类并口屏每一次刷新都要遍历几百个字节,如果每 2 秒全屏刷新一次,MCU 大部分时间都耗在这上面。改成只在数值变化超过 1μg/m³ 时才刷新,数值不变时只更新时间或背光状态就行。同时段式 LCD 或 OLED 在低功耗上表现更好,适合进阶改造。
5. 整机校准与常见问题排查:零点偏移、收不到帧、数值乱跳
5.1 零点与标准值偏移的校准方法
PM2.5 传感器出厂前做过标定,但拿到新 PCB 上以后,光路位置、进风口风阻、传感器供电电压都和原厂测试台不同,直接读出的数值会和标准仪器有偏差。校准分两步:第一步做零点,找一截洁净空气环境,或者用 HEPA 滤网包住进风口,读取传感器的底数,正常应该在 0±2μg/m³ 范围内。如果底数偏大,多数是传感器装配时被焊烟污染了,用酒精棉签轻轻擦拭采样腔进风口,注意不要碰到内部光电检测面。
第二步做标准值偏移,把检测仪和一台已校准的仪器放同一个采样点,每隔一分钟记录一组数据,连续记录半小时,用最小二乘法算 y = ax + b 里的 a 和 b。这里的 x 是本地检测仪读数,y 是标准仪器读数,注意不要把两个轴的对应关系搞反了。算出的系数可以存在单片机的 Data Flash 里,固件里留一个校准模式:
void apply_calibration(uint16_t raw) { int32_t val; if (raw == 0xFFFF) return; val = (int32_t)raw * cal_slope / 1000 + cal_offset; if (val < 0) val = 0; if (val > 999) val = 999; display_value = (uint16_t)val; }校准系数存储时要注意:Data Flash 擦写寿命一般在 1 万次左右,不能每次开机都写,只有确认新系数后再写入。另外不要把 a、b 存成 float,用整型加缩放因子(上面代码里的 /1000)更稳,避免浮点库把 Flash 撑爆。
5.2 常见硬件与软件问题排查一览
| 现象 | 可能原因 | 排查步骤 |
|---|---|---|
| 串口完全收不到数据 | 传感器没供电、RX/TX 接反、波特率不对 | 先用 USB-TTL 直接接传感器,确认模块本身能出数据;再量主板传感器电源脚电压;最后看 MCU 串口引脚电平 |
| 数据帧能收到但校验总失败 | 供电电压偏低导致传感器输出时序劣化 | 示波器看 UART 波形,重点看波形上升沿是否过缓;传感器供电端并 100μF 电容 |
| 数值稳定但明显偏低 | 进风口风道被 PCB 遮挡 | 检查传感器采样嘴周围 10mm 内是否有元器件,用热熔胶重新固定风道 |
| 电池续航不到一天 | 传感器一直供电、LCD 频繁刷新 | 万用表串联测整机平均电流,定位是 5V 升压空载损耗还是 MCU 没进入 STOP |
第二类问题里,很多开发板用 Arduino 或 STC 单片机调试没问题,但一换上 R7F0C001G2 就收不到数,原因往往是引脚复用没配置:UART0 的 RX 引脚默认是数字输入,没有切换到串口功能。排查时先看寄存器,再看波形。
5.3 一个提升量产可靠性的技巧:传感器异常自恢复
激光传感器工作时间长了,风扇轴承磨损、进风口积灰,偶尔会出现“上电后不发数据”的假死状态。与其让用户断电重来,不如在固件里做一个定时监控:如果 60 秒内没有收到一帧合法数据,就把传感器电源关闭 1 秒再打开,同时清掉串口状态机。这个做法比单纯看门狗复位 MCU 更精准,因为问题出在传感器,不需要重启整个系统。
void pm_supervisor(void) { static uint16_t no_data_time = 0; if (last_pm_rx_tick != 0) { if (now - last_pm_rx_tick > 10000) { no_data_time = 0; last_pm_rx_tick = 0; } } else { no_data_time += 100; if (no_data_time > 60000) { sensor_power(0); delay_ms(1000); sensor_power(1); uart_state_reset(); no_data_time = 0; } } }注意传感器断电再上电以后,状态机必须复位,否则旧帧残留会让第一帧数据错位。这个监控函数放在主循环里每 100ms 调用一次,代码量不大,但能把“长时间运行后数据消失”这一类售后问题大幅降低。配合设计文档里的测试记录,整机在稳定性上的表现会明显上一个台阶。
本文还有配套的精品资源,点击获取