ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

基于STM32的智能空气净化器控制系统设计详解

2026/9/16 16:44:16 拓冰建站 浏览量
基于STM32的智能空气净化器控制系统设计详解 简介这是一份以STM32为核心的智能空气净化器控制系统设计资料包主要面向电子与嵌入式方向的毕业设计、课程设计、工程实训或项目练手场景。方案完整涵盖DHT22温湿度采集、ZPH04 PM2.5检测、风扇启停控制、RTC实时时钟、摇杆模块和USART串口通信等多类驱动所有源码均经过实际测试可直接编译运行有助于快速复刻具有环境监测与自动调节功能的净化器样机。压缩包共1065个文件大小16.28MB其中以574个C源码、258个H头文件为主体另含uvprojx工程文件、链接脚本、库文件、编译产物及说明文档目录结构清晰便于对照学习与二次开发。已有258人学习下载资料内包含完整工程配置与调试记录遇到编译或接线问题可与博主交流能有效缩短环境搭建时间。整体方案从源码到工程配置覆盖完整适合初学者按步骤走通项目流程。1. 从传感器到风机联动这套 STM32 控制系统该先想清楚什么智能空气净化器听起来是「传感器采集 风机转起来」的简单组合实际做下来绝大多数毕设和课设翻车都翻在控制逻辑和硬件选型的耦合上PM2.5 传感器采样慢了风机响应快了系统来回震荡或者 PWM 调速和传感器共用一个 ADC 通道数据根本没法看。基于 STM32 的智能空气净化器控制系统设计核心不是把器件接起来而是把「空气质量感知 → 档位决策 → 风机执行」这条链路做成一个闭环并且让这个闭环在真实房间环境里稳定工作。这套系统适合三类人做毕设或课设的学生需要一套能讲清原理、能跑通实物、能写出设计文档的完整方案做小型智能家居项目的工程师想用 STM32 而不是 WiFi 模组裸跑降低成本和功耗以及想从「点灯」进阶到「闭环控制」的嵌入式学习者。这里我不会贴一份虚构的完整工程代码而是把架构、关键模块、参数设计和排障路径讲透让你拿到任何一款 STM32 开发板都能自己搭出来。先给一个反直觉的结论空气净化器控制系统里最难的往往不是 PM2.5 传感器读数而是风机的闭环调速。传感器数据可以滤波、可以校准但风机一开气流会直接扰动传感器周围的微环境这个「自激干扰」才是让系统表现不稳定的根因。所以整套设计的成败取决于你对「采样周期、滤波窗口、PWM 频率、反馈阈值」这四个参数的理解深度。2. 系统架构与核心选型用 STM32 搭出可复用的净化器控制框架2.1 控制对象拆解净化器里到底有哪些执行器和传感器先明确控制对象。一台具备自动模式的空气净化器最少包含一个 PM2.5/PM10 粉尘传感器、一个温湿度传感器辅助判断环境状态、一个可调速风机、一个指示灯或 OLED 显示屏、以及按键输入。扩展一点还可以有负离子发生器、紫外线杀菌灯、滤网更换提醒和 WiFi 远程控制。对毕设而言核心控制对象就是风机其它的都是状态输入和反馈输出。按功能划分系统可以分成感知层、决策层和执行层。感知层负责把 PM2.5 浓度、温湿度等物理量变成数字信号决策层在 STM32 内部完成数据处理和控制算法执行层通过 PWM 驱动风机并反馈转速。我一般建议把「传感器数据采集」和「控制决策」在代码层面拆成两个模块用标志位或消息队列通信这样做的好处是调试传感器时不会干扰电机控制反过来也一样。从选型角度看STM32F103C8T6 是毕设和课设最常见的芯片性价比高、资料多、CubeMX 支持完善。如果你的设计需要同时跑 RTOS 屏幕刷新 传感器采集 电机控制建议用 STM32F407 系列主频更高、外设更丰富。但要注意选型不是越贵越好控制空气净化器这种低频系统F103 的 72MHz 主频绰绰有余关键是把 DMA、定时器和 ADC 这几个外设用好。2.2 传感器选型GP2Y1010AU 与 PMS5003 的取舍PM2.5 传感器是整套系统的眼睛选型直接决定数据质量和成本。市面上两种主流方案夏普 GP2Y1010AU 红外粉尘传感器价格便宜十几元输出模拟电压需要自己搭建信号处理电路精度一般适合低成本课设攀藤 PMS5003 激光传感器走 UART 串口输出数字浓度值精度高、响应快但价格贵一些。我个人的建议是预算允许就优先用 PMS5003因为它省掉了模拟前端设计还能直接拿到 PM1.0、PM2.5、PM10 三组数据写论文时数据维度也更丰富。GP2Y1010AU 的采样电路有一个典型坑传感器内部有 LED 脉冲驱动输出信号需要经过 RC 滤波后才能进 ADC而且采样时机要避开 LED 导通瞬间的尖峰。具体操作是在 STM32 中用一个定时器生成 10ms 周期的脉冲在脉冲开始后 0.28ms 处触发 ADC 采样。这个时序要求如果没满足读到的数据会明显偏高且抖动剧烈。PMS5003 的数字输出是 32 字节的被动帧格式帧头0x42 0x4D第二和第三个字节是帧长之后依次是 PM1.0、PM2.5、PM10 的浓度值。用 STM32 的串口 DMA 接收这 32 字节做帧头校验和 CRC 校验然后解析出 PM2.5 浓度。注意传感器上电后需要 30 秒左右的稳定时间前 30 秒的数据应丢弃或显示「预热中」。2.3 风机驱动与 PWM 调速为什么不能直接 GPIO 驱动风机是执行器的核心直接 GPIO 控制风扇只有「开/关」两态没法做多档调速所以必须用 PWM。STM32 的定时器输出 PWM 信号经过 MOS 管或电机驱动芯片放大后驱动风机电源。常见的驱动方案有两种一是用 N-MOS 管 续流二极管做低压侧开关适合 5V/12V 小风机二是用专门的电机驱动芯片如 L9110S、DRV8870带过流保护和逻辑电平转换适合实验板快速搭通。PWM 频率选择是这里最容易忽视的参数。对于普通有刷直流风机PWM 频率选 20kHz 以上可以避开人耳可听噪声但会让 MOS 管开关损耗增加选 1kHz10kHz 则可能听到啸叫。我一般用 20kHz理由有两点第一净化器放在卧室噪声体验极其重要第二F103 的定时器时钟最高 72MHz配 20kHz PWM 的分频和重载值都在合理范围内不会产生太大的定时器中断开销。电机电枢电感能起到续流滤波作用配合续流二极管电流纹波可以接受。以下是使用 STM32 高级定时器 TIM1 输出两路互补 PWM其中一路用于风机调速的初始化代码示例// 使用 TIM1 CH1 输出 PWM频率 20kHz分辨率 10 位 // 时钟树APB2 定时器时钟 72MHz void FAN_PWM_Init(uint16_t arr, uint16_t psc) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; // 1. 开启 TIM1 和 GPIOA 时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1 | RCC_APB2Periph_GPIOA, ENABLE); // 2. PA8 复用推挽输出 GPIO_InitStructure.GPIO_Pin GPIO_Pin_8; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); // 3. 定时器周期 (arr1) * (psc1) / 72MHz 50us - 20kHz TIM_TimeBaseStructure.TIM_Period arr; // 999 TIM_TimeBaseStructure.TIM_Prescaler psc; // 0 TIM_TimeBaseStructure.TIM_ClockDivision 0; TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInit(TIM1, TIM_TimeBaseStructure); // 4. PWM1 模式CNT CCR 时输出有效电平高 TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse 0; // 初始占空比 0 TIM_OCInitStructure.TIM_OCPolarity TIM_OCPolarity_High; TIM_OC1Init(TIM1, TIM_OCInitStructure); // 5. 高级定时器必须开启 MOE 主输出 TIM_CtrlPWMOutputs(TIM1, ENABLE); TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); TIM_ARRPreloadConfig(TIM1, ENABLE); TIM_Cmd(TIM1, ENABLE); }这段代码的参数含义如下arr 999、psc 0时PWM 周期是(999 1) × 1 / 72MHz ≈ 13.89us对应 72kHz不是我要的 20kHz。这里故意展示一个容易被忽略的细节TIM1 挂在 APB2 上如果 APB2 分频系数是 1定时器时钟就是 72MHz想要 20kHz 应该让arr 1 3600即arr 3599或者prescaler 2且arr 1199。实际配置时务必用逻辑分析仪或示波器验证 PWM 频率不要盲信计算。2.4 ADC 采样与滤波让 PM2.5 数据可靠的三层处理ADC 采样是感知层的核心。STM32F103 的 ADC 是 12 位参考电压 3.3V采样时间可配置。对于 GP2Y1010AU 输出的模拟电压建议开启 ADC 扫描模式 DMA以 100Hz 的频率连续采样然后做滑动平均滤波。不要在中断里做滤波运算那会拖慢主循环。三层滤波是业界最稳妥的做法。第一层是硬件滤波也就是传感器电路里的 RC 低通滤波把高频噪声压掉第二层是软件滑动平均取最近 N 次采样值求平均第三层是阶跃变化限制如果前后两次滤波值之差超过阈值比如 50ug/m³则按最大变化速率限制。这样做的好处是既能滤掉随机噪声又不会像单纯低通滤波那样让真实突变响应变慢。对于 PMS5003 这类数字传感器ADC 滤波就不需要了但需要做「串口数据帧校验」和「超时处理」。如果 UART 在 500ms 内没有收到有效帧要判断传感器断线风机应切换到手动模式或安全转速避免净化器在传感器故障时停转或全速运行。3. 控制策略与代码实现用 STM32 实现多档位智能调节核心逻辑3.1 档位控制与滞回比较为什么不能只用单一浓度阈值很多学生做的净化器控制是「浓度超了开高档浓度低了关低档」听上去合理实际运行会出现抖动。问题在于传感器数据有波动当 PM2.5 浓度在阈值附近波动时档位会反复切换风机转速忽高忽低用户体验极差。解决方案是引入滞回比较设定两套阈值进入高档需要浓度超过threshold_high退出高档需要浓度低于threshold_low中间存在一个滞回区间。具体到净化器控制我一般设计四个状态待机0 档、低速1 档、中速2 档、高速3 档外加一个「自动模式」的全局开关。自动模式下根据当前 PM2.5 浓度和变化趋势决定档位。滞回参数如下表当前档位切换到更高档触发值 (ug/m³)切换到更低档触发值 (ug/m³)待机 35—低速 75 30中速 150 60高速— 120这个参数表设计的逻辑是每档的降档阈值都低于升档阈值约 5~30ug/m³形成滞回带避免临界抖动。你可以根据自己的传感器型号和环境调整核心原则是「升档激进、降档保守」——空气质量变差时快速响应变好时保持一段时间的当前档位这样既能迅速改善空气又不会因短暂波动频繁变速。档位切换还需要一个「最小驻留时间」机制也就是切到新档位后至少维持 30 秒才能再次切换。这个机制的实现很简单记录上次切换时刻的 tick每次决策前先判断是否超过最小驻留时间。它的副作用是让系统对瞬时数据变化不那么敏感但也要求你在传感器数据融合时做合理的趋势判断。3.2 定时器与 ADC 驱动代码主循环里如何优雅地调度任务在 STM32 上做多任务调度最简单的方案就是「定时器节拍 主循环轮询」。用 SysTick 产生 1ms 的 tick在主循环中基于时间片执行传感器采集、按键扫描、显示刷新和控制决策。下面是一个典型的主循环调度框架// main.c 中的调度框架 volatile uint32_t g_tick_ms 0; void SysTick_Handler(void) { g_tick_ms; // 1ms 递增 } // 时间片调度辅助宏 #define TASK_PERIOD_MS(name, period, code) \ do { \ static uint32_t last_run_##name 0; \ if (g_tick_ms - last_run_##name (period)) { \ last_run_##name g_tick_ms; \ code; \ } \ } while(0) int main(void) { // 硬件初始化…… while (1) { // 每 200ms 采集一次 PM2.5 数据 TASK_PERIOD_MS(sensor, 200, Sensor_Poll()); // 每 50ms 扫描一次按键 TASK_PERIOD_MS(key, 50, Key_Scan()); // 每 500ms 更新一次显示 TASK_PERIOD_MS(display, 500, Display_Update()); // 每 1000ms 执行一次自动模式决策 TASK_PERIOD_MS(auto_control, 1000, Auto_Control_Decision()); // 余下时间让 CPU 休眠或做低优先级任务 __WFI(); } }这个调度框架有三个值得说明的细节第一TASK_PERIOD_MS宏里的static变量让每个任务有自己的上次运行时间戳互不干扰第二g_tick_ms是无符号 32 位整数约 49 天后回绕对于净化器这种长时间运行的设备需要在比较时间差时用「差值法」而不是「绝对值比较」上面的宏已经用差值法避免了回绕问题第三__WFI()让 CPU 在无事可做时进入等待中断状态降低功耗。这个框架的局限也很明确时间片轮询不灵的场合是多个任务之间有依赖关系。比如「自动控制决策」依赖「传感器采集」的最新数据如果传感器尚未完成一次完整采样决策就会用旧数据。我的建议是在Sensor_Poll()结束时更新一个全局g_sensor_data结构体并设置g_sensor_ready 1控制决策函数先检查这个标志位再执行。3.3 PID 调速的适用边界什么时候该用闭环什么时候用档位就够档位控制足够应付大多数净化器场景但如果你要求风机转速跟随空气质量连续变化或者要稳定控制出风口风量那就要上 PID 闭环。STM32 上实现 PID 控制的典型做法是以目标 PM2.5 浓度为设定值以当前浓度为过程值PID 输出作为 PWM 占空比。但这里有个物理上的不匹配风机转速和 PM2.5 浓度之间的关系是非线性的、带大滞后而且通量受滤网阻力影响经典 PID 调不好就会震荡。因此我建议先想清楚需求再决定。如果只是「浓度高就转快点、浓度低就转慢点」用 3~4 档位的滞回控制完全足够代码简单、参数直观、答辩好讲如果要做「恒风量」或「恒 PM2.5 浓度」这类课题亮点那就在档位控制外面套一层 PID并把 PID 只作用在相邻档位之间的小范围调整避免大幅超调。增量式 PID 是净化器场景更合适的选择它输出的是 PWM 占空比的增量天然不怕积分饱和也不会在传感器读数突变时让风机转速猛冲。关键参数有三个Kp决定响应速度Ki消除稳态误差Kd抑制超调。对空气净化器这种大惯性被控对象我建议先调Kp让它有响应但微超调再加一小点Kd压超调最后加很小的Ki消除长时间偏差最好为零。3.4 自动/手动模式切换与按键消抖处理设备必须有手动模式否则用户没法在自动模式状态异常时接管控制。按键输入通常有三个模式键自动/手动切换、档位加、档位减。在自动模式下档位加减不生效在手动模式下自动决策被禁用PWM 占空比由按键直接设定。这里有一个容易疏忽的点模式切换时要把风机平滑过渡到目标占空比而不是瞬间跳变否则会有明显的「咔哒」声和电流冲击。按键消抖是经典问题。最简单可靠的软件消抖是「延时确认法」检测到引脚低电平后延时 10ms 再读一次如果还是低电平确认按下有效。但对于多按键场景更优雅的做法是状态机消抖这样不会阻塞主循环。状态机把每个按键当作一个有限状态机处理状态包括「释放态」「按下确认前」「按下确认」「释放确认前」每 1ms 扫描一次10ms 无抖动跳变则状态迁移。4. 显示模块与数据可视化在 STM32 上打造直观的人机交互界面4.1 OLED 显示布局与驱动方式I2C 还是 SPI显示模块是净化器控制系统的「门面」也往往是答辩中最先被演示的部分。常见方案是 0.96 寸 OLED驱动芯片为 SSD1306接口可选 I2C 或 SPI。I2C 版只占两个 IO 引脚接线简单但刷新速度较慢对于 128x64 分辨率全屏刷新在 400kHz 时钟下大约需要几十毫秒SPI 版刷新快得多但占用的引脚多。对于显示内容只有「当前 PM2.5 值、档位、模式、温湿度」这些数字和少量文字的净化器I2C 完全够用还能省下串口和 PWM 用的引脚。SSD1306 显示的核心操作是设置显存和写命令。常见做法是在 STM32 内部维护一个 1KB 的显存缓冲区128×64÷8所有绘制操作都在缓冲区完成然后调用OLED_Refresh()把整块缓冲区通过 I2C 写到屏幕。这么做的好处是避免逐点写屏导致的闪烁代价是多占 1KB RAM——对 F103C8T6 的 20KB RAM 来说毫无压力。值得注意的一个坑SSD1306 上电默认关闭显示需要在初始化序列里发送0xAF开启显示如果只发了显存数据而没开显示屏幕一直是黑屏。另外I2C 时序要求严格建议把 I2C 时钟设置为 400kHz 或更低并在 SCL/SDA 上接 4.7kΩ 上拉电阻。如果 STM32 内部上拉不够I2C 批量写数据的速率会显著变慢甚至出现通信错误。4.2 显示代码实现状态界面的绘制与刷新机制显示内容可以分成静态部分和动态部分。静态部分包括边框、文字标签如 PM2.5:、FAN:、MODE:和图标这些在初始化时写入显存动态部分包括浓度值、档位和模式状态定时刷新时只更新变化区域而不是整屏重绘。局部刷新的好处是减少了 I2C 通信量降低了闪烁概率也让代码层次更清晰。下面是一段基于 SSD1306 的部分刷新示例// 显示 PM2.5 数值只更新数值所在区域 void Display_Update_PM25(uint16_t pm25_value) { char buf[8]; uint8_t x 28; // 数值起始横坐标 uint8_t y 16; // 数值所在行 // 1. 先把数值区域清空为背景色 OLED_ClearArea(x, y, 48, 16); // 2. 格式化数值字符串 snprintf(buf, sizeof(buf), %3d, pm25_value); // 右对齐占 3 位 // 3. 在显存缓冲区绘制新数值 OLED_ShowString(x, y, buf, FONT_SIZE_16); // 4. 通过 I2C 仅刷新这一小块区域而不是全屏 OLED_RefreshArea(x, y, 48, 16); }这个函数的逻辑说明OLED_ClearArea在外部 RAM 缓冲区中把指定区域填充为 0x00SSD1306 中 0 表示关像素然后OLED_ShowString用 16 号字体绘制字符最后OLED_RefreshArea把缓冲区中该区域对应的字节通过 I2C 发送到屏幕。局部刷新可以让单次 I2C 数据量从 1KB 降到几十字节刷新率可以提到 10Hz 以上而不感觉闪烁。对于数值跳变还有一个显示层面的小技巧浓度值变化时不做「平滑过渡」而是直接用新值覆盖旧值因为空气净化器的数据变化本身就不快用户只关心一个明确的数字。如果要做「AQI 等级颜色变化」则需要把显示和控制逻辑分开——控制逻辑决定风机的转速显示逻辑根据浓度映射到绿色/黄色/红色等级两者使用同一份g_sensor_data避免数据不一致。4.3 指示灯与蜂鸣器反馈状态感知的补充手段除了屏幕物理指示灯和蜂鸣器也承担了重要的状态反馈功能。自动模式下指示灯可以显示当前档位低速常亮绿色中速常亮黄色高速闪烁或常亮红色故障状态下比如传感器断线指示灯以 2Hz 频率闪烁红色。这个设计在答辩演示时非常加分——评审老师一眼就能看出系统状态不需要反复看屏幕。蜂鸣器的使用要克制。只有在以下三种情况才响开机自检完成短滴一声、模式切换短滴一声、检测到故障连续长响 3 秒。不要在每次按键按下时都响那会让用户烦躁。蜂鸣器驱动用定时器 PWM 输出特定频率的方波如 2kHz或者用 GPIO 翻转加延时注意如果蜂鸣器是有源的只需高低电平控制频率不可调无源蜂鸣器则必须用 PWM 才能发声。5. 系统联调、参数整定与常见故障排查路径5.1 最小系统到整机的上电调试步骤从点灯到闭环整机调试不是把所有模块一次性接好然后上电看效果那样出了问题根本没法定位。我建议按照「电源 → 单片机最小系统 → 传感器 → 风机 → 显示 → 闭环控制」的顺序逐步验证。每一步都要有明确的通过标准比如单片机最小系统的通过标准是 LED 闪烁正常、串口能打印调试信息传感器的通过标准是串口能输出稳定的 PM2.5 数值且数值随烟雾或粉尘源有明显变化风机的通过标准是 PWM 占空比从 0 到 100% 变化时转速平滑上升且无啸叫。传感器调试时有一个很有效的实操方法在传感器附近点燃一支香注意安全远离塑料外壳观察 PM2.5 数值是否在 1~3 秒内快速上升移除烟源后数值是否在 10~30 秒内回落到初始值。如果响应时间明显更长或数值迟迟不回落大概率是传感器周围的进风结构不对——传感器需要一直接触到气泵抽进来的空气而不是密闭腔体里的静态空气。这个话题在论文里可以写成「传感器安装位置对采样响应时间的影响」。5.2 常见异常现象对照表数据乱跳、风机不转、OLED 无显示的定位思路下面的对照表整理了净化器系统调试中最常见的异常现象、原因定位和解决方向异常现象可能原因定位手段解决方案PM2.5 数值持续很高且不变化传感器无气流进入、传感器老化、粉尘附着用吹气/烟源触发看响应清理传感器风道检查风扇是否覆盖传感器采样口PM2.5 数值跳变剧烈采样时序不对、供电纹波大、传感器信号未滤波示波器测传感器输出、串口打印原始值增加 RC 滤波调整采样时序传感器供电加 100nF 去耦电容风机在低占空比下不转PWM 占空比太小无法克服静摩擦检查 PWM 波形和占空比数值设置最低启动占空比如 20%启动时用 100% 占空比促转 200ms 再降下来风机转速忽快忽慢电源功率不足、PID 参数不当用万用表测风机供电电压换更大功率适配器减小 PID 超调参数OLED 白屏或花屏I2C 地址错误、未开启显示、上拉电阻缺失用逻辑分析仪抓 I2C 通信确认设备地址为 0x3C 或 0x3D初始化序列末尾发送显示开启命令自动模式不切换档位传感器数据未更新、标志位没置位串口打印当前浓度和当前档位检查 ADC/DMA 中断是否在跑检查时间调度的周期是否过大5.3 避免 STM32 调试中的常见外设配置陷阱晶振、烧录与时钟树在做基于 STM32 的控制系统时很多学生卡在「程序烧不进」或「程序跑了但行为不对」这时候问题往往不在业务代码而在外设配置。烧录不了通常有三种原因BOOT0 引脚的电平设置不对没有进入下载模式Keil 里的 Debug 设置没选对下载器型号ST-Link 还是 J-Link或者芯片的 SWDIO/SWCLK 引脚被复用成了普通 GPIO导致调试接口被关闭。最后一种最隐蔽如果你在代码里把 SWDIO 对应的 PA13 配置成了推挽输出那下次用 ST-Link 就连不上了需要按住复位键并在下载的瞬间松开以抢占调试接口。时钟树的坑也值得多说一句。用 CubeMX 生成工程时如果选择了外部高速晶振HSE但板子上实际没有 8MHz 晶振有些最小系统板用的是 25MHz 或内部 RC 振荡器程序跑起来的串口波特率、定时器频率和 PWM 频率都会偏偏差可能达到百分之几十。遇到这种情况检查SystemCoreClock全局变量的值F103 正常应该是 72000000如果显示 64000000 或 36000000说明时钟配置和实际晶振不匹配。对于空气净化器这类对时序精度要求不高的应用直接使用内部 HSI 振荡器并倍频到 64MHz 也可以接受但要注意 ADC 的采样时间会随时钟变化需要重新校准。6. 在 STM32 上做净化器数据上传串口与本地化的可视方案6.1 通过串口打印调节实时参数手动 PID 整定的一个实用技巧系统调通之后下一步是让参数调节脱离「改代码 → 重新编译 → 烧录」的繁琐循环。我习惯的做法是把关键参数Kp、Ki、Kd、滞回阈值、风机最低占空比做成 EEPROM 或 Flash 中可修改的变量然后通过串口中断接收上位机发来的命令格式类似SET KP 0.15\r\n收到后解析并实时更新全局变量。这样在联调时不用反复烧录可以直接在串口助手里调参数直到控制效果满意为止。串口命令解析在 STM32 上建议用「逐字节接收 状态机」的方式实现不要让任务的调度周期影响按键响应。下面给出一个最简单的串口命令解析实现思路// 串口命令解析接收形如 SET KP 0.15 的命令并设置整数/浮点参数 void UART_CommandParser(uint8_t ch) { static char cmd_buf[32]; static uint8_t idx 0; if (ch \r || ch \n) { cmd_buf[idx] \0; // 结束字符串 idx 0; // 在这里用 sscanf 解析命令和值 float val 0.0f; if (sscanf(cmd_buf, SET KP %f, val) 1) { g_pid.Kp val; } else if (sscanf(cmd_buf, SET KI %f, val) 1) { g_pid.Ki val; } return; } if (idx sizeof(cmd_buf) - 1) { cmd_buf[idx] ch; } }这段代码的逻辑说明串口中断每收到一个字节就调用UART_CommandParser字符进入缓冲区遇到回车或换行时把缓冲区变成长度可变的字符串再用sscanf解析出命令和数值。注意缓冲区大小要限定防止超长字符串溢出覆盖其他全局变量——这是嵌入式开发中最容易踩的隐蔽 bug。该方案同时还能用于生产测试时的硬件自检比如上位机发TEST FAN ON验证风机回路。参数的持久化用 STM32 内部的 Flash 模拟 EEPROM 实现。F103 没有真正的 EEPROM但有 64 字节的备份寄存器和 Flash 扇区。批次把参数存到最后一个扇区利用 Flash 的「擦除后全 1」特性把修改过的参数写入新位置而不是每次擦除整个扇区。对毕设而言简化做法是仅在掉电前把当前参数写入特定 Flash 地址上电时读取并校验。我校验方式是用「参数 校验和」的结构长度为 8 的整数倍避免字节对齐问题。6.2 串口协议与 ESP8266 的边界本地上位机展示而非依赖云平台净化器的数据上传有两种层次本地可视化与云端远程控制。对于课设/毕设我建议先做成本地上位机可视化把 PM2.5 浓度、温度、湿度、风机占空比和运行模式通过串口上传到 PC 上用 Python 的pyserialmatplotlib画实时曲线。这样做的优点是技术栈简单、答辩现场演示效果直观而且不需要依赖外部网络服务不会出现「答辩时云端平台连不上」的尴尬。串口数据帧建议采用固定长度的二进制帧比如 16 字节帧头2 字节0xAA 0x55、数据长度1 字节、PM2.52 字节 uint16、温度1 字节 int8、湿度1 字节 uint8、档位1 字节、状态位1 字节、CRC162 字节、帧尾1 字节可选。帧格式越简单越好在单片机上解析不要用 JSON因为 STM32 上用 SDK 解析 JSON 的 Flash/RAM 开销不划算。Python 上位机的核心逻辑是打开串口、循环读取、按帧解析、更新曲线。虽然标题写的是 STM32 控制系统但作为扩展用 ESP8266 或 STM32 自带的以太网 MAC 做数据上传是加分项。不过要提醒一点ESP8266 本身也是 MCU它与 STM32 之间是串口透传关系STM32 只负责把数据帧发给 ESP8266ESP8266 通过 WiFi 发给手机 APP 或服务端。这意味着你需要同时调试两端的程序复杂度会明显上升。6.3 传感器校准技巧与滤网寿命提醒的实现最后落到工程实用技巧上传感器校准。PM2.5 传感器出厂时通常做过基本校准但不同的安装环境气流方向、遮挡物、温湿度会导致读数偏移。最简单的单点校准方法是把设备放到一个空气干净的环境比如室外背风处或通风良好的房间等 sensor 读数稳定后记下基准值base_reading然后把所有读数做一次平移corrected raw - base_reading。但这些数值只对当前环境有效不要做成「永久校准」最好提供一个校准模式长按按键 3 秒进入过程中自动采集 10 个数据求平均作为基准。滤网寿命提醒是很多成品净化器的标配也能给毕设加分。实现逻辑用一个 32 位变量记录风机累计运行时间以秒为单位掉电前写入 Flash 或备份寄存器。当累计运行时间超过预设值比如 300 小时可用作默认滤网寿命OLED 显示「更换滤网」提示同时风机最高档位限制到中速防止滤网堵塞时风机过载。这个功能设计上的巧劲在于它利用的是「风机开关和运转时间」这个最简单的状态量而不是用气压传感器或光传感器去检测滤网堵塞程度——后者的成本和可靠性问题在低成本方案里很难解决。提示传感器安装方向也是一个容易忽略的因素。PMS5003 的官方推荐是进风口朝下或倾斜 45 度安装避免大颗粒物在传感器内部沉积GP2Y1010AU 则要求气流通路笔直尽量不要让 PCB 面朝上积灰。把这些细节写进设计文档比贴一段漂亮的代码更能体现工程意识。本文还有配套的精品资源点击获取