STM32F103C8T6连接ZH03B传感器:一个串口采集PM2.5数据的完整流程(附代码)
STM32F103C8T6与ZH03B传感器实战:从零搭建PM2.5监测系统
最近在做一个室内空气质量监测的小项目,发现ZH03B这款激光粉尘传感器性价比超高,但网上资料比较零散。今天就把整个开发过程整理成保姆级教程,特别适合刚接触STM32的新手。我用的是最常见的STM32F103C8T6最小系统板(蓝色药丸板),成本不到20元,搭配ZH03B传感器,教你如何通过串口获取精确的PM2.5数据。
1. 硬件准备与连接
先清点下需要的器材:
- STM32F103C8T6开发板(正点原子Mini板或类似蓝色药丸板均可)
- ZH03B激光粉尘传感器(注意要买3.3V版本的)
- USB转TTL模块(如CH340)
- 杜邦线若干
- 万用表(非必须,但建议备一个)
重要提醒:ZH03B有5V和3.3V两种版本,STM32的IO口耐压一般是3.3V,务必确认你的传感器是3.3V电平的!我刚开始就买错了版本,导致数据异常,后来用逻辑电平转换器才解决。
接线示意图:
| STM32引脚 | ZH03B引脚 | 功能说明 |
|---|---|---|
| PA2 | PIN5(TXD) | 传感器数据输出 |
| PA3 | PIN4(RXD) | 传感器控制输入 |
| 3.3V | PIN1(VCC) | 电源正极 |
| GND | PIN2(GND) | 电源地线 |
注意:很多教程只接了TXD,但实际上RXD也需要连接,因为ZH03B的主动输出模式需要MCU发送配置指令。
2. 开发环境搭建
推荐使用Keil MDK+STM32CubeMX组合,对新手特别友好:
- 安装Keil MDK-ARM(记得注册避免代码大小限制)
- 下载STM32CubeMX并安装F1系列HAL库
- 新建工程,选择STM32F103C8型号
- 配置时钟树(外部晶振8MHz,主频设为72MHz)
时钟配置是新手最容易出错的地方,这里给出一个稳定配置方案:
// SystemClock_Config() 关键参数 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;3. 串口配置与传感器初始化
我们需要配置两个串口:
- USART1:用于调试输出(连接电脑)
- USART2:与ZH03B通信
在CubeMX中的配置步骤:
- 使能USART1和USART2
- USART1参数:9600波特率,8位数据,无校验
- USART2参数:9600波特率,8位数据,无校验
- 开启USART2全局中断
关键点:USART1在APB2总线,USART2在APB1总线,时钟使能时要特别注意:
__HAL_RCC_USART1_CLK_ENABLE(); // APB2 __HAL_RCC_USART2_CLK_ENABLE(); // APB1ZH03B传感器需要先发送初始化指令才能进入主动输出模式:
// ZH03B初始化指令 uint8_t init_cmd[] = {0xFF, 0x01, 0x78, 0x40, 0x00, 0x00, 0x00, 0x00, 0x47}; HAL_UART_Transmit(&huart2, init_cmd, sizeof(init_cmd), 100);4. 数据解析与处理
ZH03B的数据帧格式固定为32字节,包含PM1.0、PM2.5、PM10等参数:
0 1 2 3 4~5 6~7 8~9 10~15 16~31 0x42 0x4D Len Data PM1.0 PM2.5 PM10 保留 校验和在USART2中断服务函数中添加解析逻辑:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { static uint8_t rx_buf[32], index = 0; static bool frame_start = false; if(huart->Instance == USART2) { uint8_t byte = rx_byte; // 接收到的字节 if(!frame_start && byte == 0x42) { frame_start = true; rx_buf[0] = byte; index = 1; } else if(frame_start) { rx_buf[index++] = byte; // 完整帧接收完成 if(index >= 32 && rx_buf[1] == 0x4D) { uint16_t pm25 = (rx_buf[6] << 8) | rx_buf[7]; printf("PM2.5: %d ug/m3\r\n", pm25); frame_start = false; } } } }5. 常见问题排查
问题1:接收不到任何数据
- 检查接线是否正确(TXD-RX交叉连接)
- 用万用表测量传感器VCC电压是否为3.3V
- 尝试降低波特率(如4800)
问题2:数据乱码
- 确认双方波特率一致
- 检查STM32时钟配置是否正确
- 尝试在传感器TXD线上加1K上拉电阻
问题3:数据偶尔丢失
- 增加接收缓冲区大小
- 优化中断优先级(建议设置USART2中断优先级高于其他外设)
- 在传感器电源端并联100μF电容
6. 进阶优化建议
- 数据平滑处理:传感器数据可能有波动,可以添加滑动平均滤波
#define FILTER_SIZE 5 uint16_t pm25_buffer[FILTER_SIZE] = {0}; uint8_t filter_index = 0; // 在数据解析后添加 pm25_buffer[filter_index++] = pm25; if(filter_index >= FILTER_SIZE) filter_index = 0; uint32_t avg = 0; for(int i=0; i<FILTER_SIZE; i++) avg += pm25_buffer[i]; avg /= FILTER_SIZE;低功耗模式:适合电池供电场景
- 间隔唤醒采集(如每5分钟唤醒一次)
- 采集完成后进入Stop模式
多传感器融合:可搭配温湿度传感器(如DHT22)获取更全面的环境数据
7. 完整项目代码结构
建议按模块化方式组织代码:
├── Core │ ├── Src │ │ ├── main.c │ │ ├── stm32f1xx_it.c │ │ └── usart.c │ └── Inc │ ├── main.h │ └── zh03b.h ├── Drivers └── zh03b_driver ├── zh03b.c └── zh03b.h关键数据结构封装:
// zh03b.h typedef struct { UART_HandleTypeDef *huart; uint16_t pm1_0; uint16_t pm2_5; uint16_t pm10; } ZH03B_HandleTypeDef; void ZH03B_Init(ZH03B_HandleTypeDef *hzh03b); HAL_StatusTypeDef ZH03B_ReadData(ZH03B_HandleTypeDef *hzh03b);在调试过程中发现,ZH03B对电源稳定性要求较高,建议在VCC和GND之间并联一个100μF的电解电容加一个0.1μF的陶瓷电容,这样数据稳定性会明显提升。另外,传感器的激光头容易积灰,建议每隔几个月用棉签轻轻清洁,否则测量精度会逐渐下降。
