
在实际嵌入式开发项目中温度控制是一个经典且实用的应用场景。使用STM32单片机结合温度传感器和风扇驱动模块可以构建一个能够根据环境温度自动调节风速并在温度过高时发出警报的智能风扇系统。这不仅是对单片机GPIO、ADC、定时器、中断等基础外设的综合运用也涉及简单的控制逻辑和状态机设计非常适合作为从理论学习转向项目实践的练手项目。本文将基于江科大STM32开发套件通常指正点原子或野火等国内常见开发板带你从零开始搭建一个具备温度报警和手动/自动双模式切换功能的智能风扇。你将学习到如何读取DS18B20温度传感器数据、通过PWM控制风扇转速、设计按键交互逻辑以及使用蜂鸣器实现报警功能。最终你将得到一个可以实际运行、代码结构清晰、便于扩展的完整项目。1. 理解智能风扇系统的核心组件与工作原理一个完整的智能风扇控制系统其硬件核心是微控制器MCU它负责采集输入信号、执行控制算法、并驱动输出设备。软件部分则需要实现数据采集、逻辑判断和信号生成。理解每个组件的角色和它们之间的数据流是进行后续硬件连接和软件编程的基础。1.1 系统硬件架构与数据流系统的硬件通常由以下几部分构成主控单元STM32F103系列作为大脑负责处理所有逻辑。我们选择STM32F103C8T6即常见的“蓝色药丸”核心板或最小系统板因其资源丰富且性价比高。温度采集模块DS18B20一款单总线数字温度传感器精度可达±0.5°C直接将温度值以数字信号形式传给MCU省去了MCU内部ADC和复杂校准的过程。执行机构直流风扇驱动电路风扇通常为5V或12V直流电机。STM32的GPIO引脚驱动能力有限通常为3.3V电流几mA无法直接驱动电机因此需要驱动电路。最常用的是三极管如S8050放大电路或电机驱动芯片如L9110S、TB6612。人机交互模块按键用于模式切换自动/手动、手动模式下调节风速档位。OLED显示屏可选但推荐用于实时显示当前温度、风扇模式、风速档位、报警阈值等信息极大提升调试和用户体验。蜂鸣器用于温度超限时的声光报警。电源模块为STM323.3V、传感器3.3V-5V和风扇5V/12V提供稳定电压。开发板通常自带USB供电和稳压电路。数据流如下图所示概念性描述环境温度被DS18B20感知并转换为数字信号通过单总线协议发送给STM32。STM32解析出温度值后根据当前工作模式自动或手动和设定的报警阈值进行逻辑判断。在自动模式下STM32依据温度值计算并输出对应占空比的PWM信号到风扇驱动电路从而控制风扇转速在手动模式下则根据按键设定的固定档位输出PWM。同时STM32将状态信息输出到OLED显示并在温度超过阈值时驱动蜂鸣器报警。1.2 核心软件概念PWM与单总线协议PWM脉冲宽度调制是控制风扇转速的关键。STM32的通用定时器如TIM2、TIM3、TIM4可以很方便地生成PWM信号。PWM信号是一种周期固定、但高电平导通时间占整个周期比例可调的方波。这个比例称为“占空比”。对于直流风扇占空比越大平均电压越高转速就越快。我们需要配置一个定时器通道为PWM输出模式并通过修改“捕获/比较寄存器”CCR的值来动态改变占空比。DS18B20的单总线协议是软件难点之一。单总线意味着只用一根数据线DQ同时完成供电和数据传输。协议要求严格的时序包括初始化、写一位、读一位、写一个字节、读一个字节等操作。这些操作都需要微秒级的延时必须通过STM32的系统滴答定时器SysTick或普通定时器实现精确延时。通常我们会将底层时序操作封装成独立的驱动文件ds18b20.c/.h。2. 环境准备与项目工程搭建在开始编码前需要准备好软件开发环境并建立一个基础工程框架。清晰的工程结构能有效管理代码避免后续混乱。2.1 硬件清单与连接示意图组件型号/说明连接至STM32引脚备注STM32核心板STM32F103C8T6-使用江科大/正点原子套餐内板子即可温度传感器DS18B20DQ - PA1需接4.7K上拉电阻至3.3V风扇模块5V直流风扇 L9110S驱动IN1 - PB6, IN2 - PB7PB6/PB7可复用为TIM4_CH1/CH2输出PWM按键1模式切换键KEY1 - PA0配置为上拉输入按下接地按键2风速加/档位加KEY2 - PA2同上按键3风速减/档位减KEY3 - PA3同上蜂鸣器有源蜂鸣器BEEP - PB8高电平触发需串联限流电阻OLED显示屏I2C接口SSD1306驱动SCL - PB10, SDA - PB11可选但强烈建议添加注意引脚分配并非唯一可根据开发板实际布局和代码配置灵活调整但需注意引脚是否支持所需功能如定时器通道、I2C等。2.2 软件环境与工程创建IDE与编译器使用Keil MDK-ARMKeil uVision 5。确保已安装STM32F1系列的设备支持包DFP。固件库使用标准外设库Standard Peripheral Library或HAL库。本文示例基于标准库因其更贴近硬件便于理解原理。江科大的教程也多基于此。创建工程在Keil中新建Project选择设备STM32F103C8。在工程中创建以下文件夹结构并添加对应的源文件和头文件SmartFan/ ├── USER/ │ ├── main.c │ ├── stm32f10x_it.c (中断服务函数) │ └── system_stm32f10x.c ├── CORE/ │ ├── core_cm3.c │ └── startup_stm32f10x_md.s (启动文件) ├── FWLIB/ │ ├── inc/ (标准库头文件) │ └── src/ (标准库源文件) ├── HARDWARE/ │ ├── bsp_ds18b20.c/.h │ ├── bsp_pwm_fan.c/.h │ ├── bsp_key.c/.h │ ├── bsp_beep.c/.h │ └── bsp_oled.c/.h (可选) └── SYSLIB/ └── delay.c/.h (基于SysTick的延时函数)* 在Keil的Manage Project Items中建立相同的分组并将文件添加到对应分组。 * 在Options for Target - C/C - Include Paths中添加所有头文件目录.\CORE, .\USER, .\FWLIB\inc, .\HARDWARE, .\SYSLIB。 * 在Options for Target - Debug中选择你的调试器如ST-Link并勾选Run to main()。3. 底层驱动模块编写在编写主业务逻辑前需要先完成各个硬件模块的底层驱动。这些驱动应提供简洁的API供上层调用。3.1 精确延时函数SYSLIB/delay.cDS18B20的时序要求精确到微秒级别因此需要一个可靠的延时函数。通常利用SysTick定时器实现。// delay.h #ifndef __DELAY_H #define __DELAY_H #include stm32f10x.h void Delay_Init(void); // 初始化SysTick void Delay_us(uint32_t nus); // 微秒延时 void Delay_ms(uint32_t nms); // 毫秒延时 #endif// delay.c #include delay.h static uint8_t fac_us 0; // 微秒延时倍乘数 static uint16_t fac_ms 0; // 毫秒延时倍乘数 void Delay_Init() { // 选择外部时钟源 HCLK/8 SysTick-CTRL 0xFFFFFFFB; // SYSCLK为72MHzHCLK/89MHz fac_us SystemCoreClock / 8000000; fac_ms (uint16_t)fac_us * 1000; } void Delay_us(uint32_t nus) { uint32_t temp; SysTick-LOAD nus * fac_us; // 设置重装值 SysTick-VAL 0x00; // 清空计数器 SysTick-CTRL 0x01; // 启动使能计数器 do { temp SysTick-CTRL; } while((temp 0x01) !(temp (1 16))); // 等待时间到达 SysTick-CTRL 0x00; // 关闭计数器 SysTick-VAL 0x00; // 清空计数器 } void Delay_ms(uint32_t nms) { uint32_t temp; // 注意nms最大值SysTick-LOAD是24位寄存器 SysTick-LOAD (uint32_t)nms * fac_ms; SysTick-VAL 0x00; SysTick-CTRL 0x01; do { temp SysTick-CTRL; } while((temp 0x01) !(temp (1 16))); SysTick-CTRL 0x00; SysTick-VAL 0x00; }3.2 DS18B20温度传感器驱动HARDWARE/bsp_ds18b20.c此驱动封装了单总线协议的所有底层操作最终提供一个读取温度值的函数。// bsp_ds18b20.h #ifndef __BSP_DS18B20_H #define __BSP_DS18B20_H #include stm32f10x.h #define DS18B20_DQ_GPIO_PORT GPIOA #define DS18B20_DQ_GPIO_PIN GPIO_Pin_1 #define DS18B20_DQ_GPIO_CLK RCC_APB2Periph_GPIOA void DS18B20_Init(void); float DS18B20_GetTemp(void); #endif// bsp_ds18b20.c #include bsp_ds18b20.h #include delay.h // 宏定义设置DQ引脚为输出/输入模式以及高低电平 #define DS18B20_DQ_OUT_HIGH() GPIO_SetBits(DS18B20_DQ_GPIO_PORT, DS18B20_DQ_GPIO_PIN) #define DS18B20_DQ_OUT_LOW() GPIO_ResetBits(DS18B20_DQ_GPIO_PORT, DS18B20_DQ_GPIO_PIN) #define DS18B20_DQ_IN() GPIO_ReadInputDataBit(DS18B20_DQ_GPIO_PORT, DS18B20_DQ_GPIO_PIN) static void DS18B20_Mode_IPU(void); // 配置为上拉输入 static void DS18B20_Mode_Out_PP(void); // 配置为推挽输出 static uint8_t DS18B20_Init_Sequence(void); static void DS18B20_Write_Byte(uint8_t dat); static uint8_t DS18B20_Read_Byte(void); void DS18B20_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(DS18B20_DQ_GPIO_CLK, ENABLE); // 初始化为推挽输出并释放总线置高 GPIO_InitStructure.GPIO_Pin DS18B20_DQ_GPIO_PIN; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(DS18B20_DQ_GPIO_PORT, GPIO_InitStructure); DS18B20_DQ_OUT_HIGH(); } // 读取温度值返回浮点数 float DS18B20_GetTemp(void) { uint8_t tempL, tempH; uint16_t temp; float temperature; DS18B20_Init_Sequence(); // 初始化序列 DS18B20_Write_Byte(0xCC); // 跳过ROM命令 DS18B20_Write_Byte(0x44); // 开始温度转换 Delay_ms(750); // 等待转换完成12位精度需750ms DS18B20_Init_Sequence(); DS18B20_Write_Byte(0xCC); DS18B20_Write_Byte(0xBE); // 读取暂存器 tempL DS18B20_Read_Byte(); // 低字节 tempH DS18B20_Read_Byte(); // 高字节 temp (tempH 8) | tempL; // 判断正负温度DS18B20以补码形式存储 if((temp 0xF800) 0xF800) { // 温度为负 temp ~temp 1; temperature -(temp * 0.0625); } else { temperature temp * 0.0625; // 12位精度最小分辨率0.0625°C } return temperature; } // ... 此处省略具体的时序函数Mode_IPU, Mode_Out_PP, Init_Sequence, Write_Byte, Read_Byte // 这些函数需要严格按照DS18B20数据手册的时序图用Delay_us()实现。3.3 PWM风扇驱动HARDWARE/bsp_pwm_fan.c此驱动初始化一个定时器用于产生PWM并提供一个函数用于设置占空比即风速。// bsp_pwm_fan.h #ifndef __BSP_PWM_FAN_H #define __BSP_PWM_FAN_H #include stm32f10x.h #define FAN_PWM_TIM TIM4 #define FAN_PWM_TIM_CLK RCC_APB1Periph_TIM4 #define FAN_PWM_GPIO_CLK RCC_APB2Periph_GPIOB #define FAN_PWM_GPIO_PORT GPIOB #define FAN_PWM_GPIO_PIN GPIO_Pin_6 // TIM4_CH1 #define FAN_PWM_TIM_CHANNEL TIM_Channel_1 void Fan_PWM_Init(uint16_t arr, uint16_t psc); void Fan_Set_Speed(uint16_t speed); // speed: 0-arr, 0为停止arr为全速 #endif// bsp_pwm_fan.c #include bsp_pwm_fan.h void Fan_PWM_Init(uint16_t arr, uint16_t psc) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; // 开启时钟 RCC_APB2PeriphClockCmd(FAN_PWM_GPIO_CLK, ENABLE); RCC_APB1PeriphClockCmd(FAN_PWM_TIM_CLK, ENABLE); // 配置GPIO为复用推挽输出 GPIO_InitStructure.GPIO_Pin FAN_PWM_GPIO_PIN; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(FAN_PWM_GPIO_PORT, GPIO_InitStructure); // 初始化定时器时基单元 // 定时器时钟为72MHz (APB1 prescaler1) // PWM频率 72MHz / (psc1) / (arr1) // 例如psc71, arr999则频率72M/72/10001000Hz TIM_TimeBaseStructure.TIM_Period arr; // 自动重装载值 TIM_TimeBaseStructure.TIM_Prescaler psc; // 预分频系数 TIM_TimeBaseStructure.TIM_ClockDivision 0; TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInit(FAN_PWM_TIM, TIM_TimeBaseStructure); // 初始化PWM输出通道 TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM1; // PWM模式1 TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OCPolarity TIM_OCPolarity_High; // 高电平有效 TIM_OCInitStructure.TIM_Pulse 0; // 初始占空比为0风扇停止 TIM_OC1Init(FAN_PWM_TIM, TIM_OCInitStructure); TIM_OC1PreloadConfig(FAN_PWM_TIM, TIM_OCPreload_Enable); // 使能预装载 TIM_ARRPreloadConfig(FAN_PWM_TIM, ENABLE); // 使能ARR预装载 TIM_Cmd(FAN_PWM_TIM, ENABLE); // 使能定时器 } void Fan_Set_Speed(uint16_t speed) { // 限制speed在0到ARR之间 if(speed FAN_PWM_TIM-ARR) { speed FAN_PWM_TIM-ARR; } TIM_SetCompare1(FAN_PWM_TIM, speed); // 修改CCR1的值改变占空比 }3.4 按键与蜂鸣器驱动按键驱动需实现消抖和状态检测蜂鸣器驱动则简单控制GPIO高低电平。代码较为常规此处仅给出关键思路。按键配置为上拉输入外部中断或轮询检测下降沿。采用状态机进行消抖如检测到按下后延时10-20ms再确认状态。蜂鸣器配置为推挽输出。报警时可以简单置高电平或使用另一个PWM产生“滴滴”声。4. 应用层逻辑整合与主程序实现底层驱动准备好后就可以在应用层将它们组合起来实现智能风扇的业务逻辑。4.1 系统状态与参数定义在main.c或独立的app_fan_ctrl.c中定义系统运行所需的状态变量和参数。// 系统工作模式枚举 typedef enum { MODE_AUTO 0, MODE_MANUAL } FanMode_t; // 全局状态结构体简化示例 typedef struct { FanMode_t mode; // 当前模式 float current_temp; // 当前温度 float alarm_threshold; // 报警温度阈值例如 35.0 uint8_t manual_level; // 手动档位例如 0-4 uint16_t pwm_arr; // PWM定时器的ARR值用于计算占空比 } SystemState_t; SystemState_t g_sys_state { .mode MODE_AUTO, .current_temp 0.0, .alarm_threshold 35.0, .manual_level 0, .pwm_arr 999 // 与初始化一致 }; // 温度-PWM映射表自动模式用 // 假设温度低于25度风扇停转25-30度低速30-35度中速35度以上全速 const uint16_t temp_to_pwm_map[][2] { {250, 0}, // 温度*10, PWM Compare值 {300, 300}, {350, 600}, {400, 999} // 40度以上全速 }; #define TEMP_MAP_SIZE (sizeof(temp_to_pwm_map)/sizeof(temp_to_pwm_map[0]))4.2 主循环与模式控制逻辑主程序在初始化所有硬件后进入一个无限循环周期性执行温度采集、逻辑判断、输出控制和人机交互更新。// main.c #include stm32f10x.h #include delay.h #include bsp_ds18b20.h #include bsp_pwm_fan.h #include bsp_key.h #include bsp_beep.h #include bsp_oled.h // 如果使用OLED // ... 其他头文件 int main(void) { uint32_t last_check_time 0; uint32_t last_display_time 0; // 系统初始化 Delay_Init(); DS18B20_Init(); // PWM频率设为100Hz定时器时钟72M预分频psc719周期arr999 Fan_PWM_Init(999, 719); Key_Init(); Beep_Init(); OLED_Init(); // 如果使用OLED OLED_Clear(); OLED_ShowString(0, 0, Smart Fan Demo); OLED_ShowString(0, 2, Temp: . C); OLED_ShowString(0, 4, Mode: AUTO); OLED_ShowString(0, 6, PWM: /999); while(1) { uint32_t now Get_Tick_ms(); // 需要实现一个获取系统毫秒计数的函数 // 1. 按键扫描与处理每10ms if(now - last_check_time 10) { last_check_time now; Key_Scan(); // 扫描按键更新状态 // 处理模式切换键 if(Key_GetState(KEY_MODE) KEY_PRESS) { g_sys_state.mode (g_sys_state.mode MODE_AUTO) ? MODE_MANUAL : MODE_AUTO; // 模式切换时可以重置手动档位或做其他处理 if(g_sys_state.mode MODE_MANUAL) { g_sys_state.manual_level 2; // 切换到手动模式时默认2档 } } // 处理手动模式下的档位加减 if(g_sys_state.mode MODE_MANUAL) { if(Key_GetState(KEY_UP) KEY_PRESS) { if(g_sys_state.manual_level 4) g_sys_state.manual_level; } if(Key_GetState(KEY_DOWN) KEY_PRESS) { if(g_sys_state.manual_level 0) g_sys_state.manual_level--; } } } // 2. 温度采集与风扇控制每500ms if(now - last_display_time 500) { last_display_time now; // 读取温度 g_sys_state.current_temp DS18B20_GetTemp(); // 根据模式控制风扇 uint16_t target_pwm 0; if(g_sys_state.mode MODE_AUTO) { // 自动模式根据温度查表计算PWM int temp_scaled (int)(g_sys_state.current_temp * 10); for(int i 0; i TEMP_MAP_SIZE; i) { if(temp_scaled temp_to_pwm_map[i][0]) { target_pwm temp_to_pwm_map[i][1]; break; } } } else { // 手动模式根据档位计算PWM (0档0, 1档250, 2档500, 3档750, 4档999) target_pwm g_sys_state.manual_level * 250; if(target_pwm g_sys_state.pwm_arr) target_pwm g_sys_state.pwm_arr; } Fan_Set_Speed(target_pwm); // 温度报警判断 if(g_sys_state.current_temp g_sys_state.alarm_threshold) { Beep_On(); } else { Beep_Off(); } // 3. 更新显示如果使用OLED OLED_ShowNum(40, 2, (int)g_sys_state.current_temp, 2); // 整数部分 OLED_ShowNum(64, 2, (int)(g_sys_state.current_temp * 10) % 10, 1); // 小数部分 OLED_ShowString(0, 4, (g_sys_state.mode MODE_AUTO) ? Mode: AUTO : Mode: MANUAL); OLED_ShowNum(40, 6, target_pwm, 4); // 显示档位或报警状态 if(g_sys_state.current_temp g_sys_state.alarm_threshold) { OLED_ShowString(80, 0, ALARM!); } else { OLED_ShowString(80, 0, ); } } } }5. 系统调试、验证与常见问题排查代码编写完成后需要下载到开发板进行实际测试。这个阶段往往会遇到各种问题需要系统地排查。5.1 上电前检查清单电源确认所有模块供电电压正确STM32为3.3VDS18B20可3.3V/5V风扇驱动模块注意逻辑电压与电机电压。连接对照原理图或连接表逐一检查杜邦线是否连接牢固特别是电源、地线和信号线。上拉电阻DS18B20的DQ线必须接一个4.7KΩ上拉电阻到3.3V否则无法正常通信。驱动电路确认风扇驱动模块如L9110S的接线正确STM32的PWM信号接到输入引脚电机接输出端。5.2 分模块调试方法不要一次性写完所有代码再调试。建议按以下顺序逐个验证延时函数写一个简单的LED闪烁程序500ms间隔验证Delay_ms函数是否准确。DS18B20单独编写测试程序循环读取温度并通过串口打印需先实现串口驱动。用手触摸传感器观察温度值是否变化。如果读回85°C或-127°C通常是时序不准或硬件连接问题。PWM输出将驱动风扇的PWM引脚暂时接一个LED。在程序中循环改变Fan_Set_Speed的值观察LED亮度是否平滑变化。也可以用示波器或逻辑分析仪测量引脚波形确认频率和占空比是否符合预期。按键编写测试程序按下不同按键通过串口或LED输出不同信息验证按键扫描和消抖逻辑。OLED显示单独测试OLED显示固定字符和变量确保I2C通信正常。5.3 常见问题与解决方案问题现象可能原因检查与解决思路DS18B20读回固定值85°C或-127°C1. 时序不准确。2. 上拉电阻未接或接触不良。3. 电源不稳定。4. 传感器损坏。1. 用逻辑分析仪抓取DQ线波形对照数据手册检查复位脉冲、读写时序的宽度。2. 确认4.7K上拉电阻已正确连接至3.3V。3. 给DS18B20的VDD单独供电仍共地。4. 更换传感器。风扇不转或一直全速转动1. PWM未输出或频率不对。2. 驱动电路故障。3. 风扇供电不足。1. 用万用表电压档测PWM引脚改变占空比时平均电压应变化。或用示波器看波形。2. 检查驱动芯片使能端、逻辑电压。直接给驱动芯片输入高/低电平看风扇是否响应。3. 检查风扇额定电压确保电源能提供足够电流。按键反应不灵或连击1. 消抖时间设置不当。2. 上拉电阻未启用或损坏。3. 按键扫描频率太高或太低。1. 调整消抖延时通常10-20ms为宜。2. 确认GPIO配置为上拉输入模式或硬件有上拉电阻。3. 确保按键扫描周期稳定如10ms一次。OLED白屏或不显示1. I2C地址错误。2. 初始化序列不对。3. 电源或接线问题。1. 常见SSD1306地址为0x78或0x7A用I2C扫描程序确认。2. 检查OLED初始化代码特别是对比度、显示开关等命令。3. 确认VCC、GND、SCL、SDA连接正确。系统运行一段时间后死机1. 堆栈溢出。2. 中断冲突。3. 电源干扰。1. 在启动文件(startup_stm32f10x_md.s)中适当调大堆栈大小。2. 检查是否有中断服务函数未定义或逻辑错误。3. 电机启停可能引起电源波动在电机电源端并联大电容如1000uF稳压。5.4 功能验证步骤自动模式验证用打火机或吹风机轻微加热DS18B20注意不要过热观察OLED显示温度上升风扇转速应随之加快。当温度超过设定的报警阈值如35°C时蜂鸣器应响起OLED显示报警提示。停止加热后温度下降风扇减速蜂鸣器停止。手动模式验证按下模式切换键OLED模式显示应变为“MANUAL”。此时按加/减键应能切换档位如0-4档风扇转速应随之阶跃变化且不受温度影响。边界条件测试测试温度在映射表边界如25°C30°C时风扇PWM值是否按预期跳变。测试手动模式档位在最小和最大时风扇是否停止和全速。6. 优化扩展与生产环境考量基础功能实现后可以从稳定性、用户体验和功能扩展角度进行优化并思考如何将其转化为一个更可靠的产品原型。6.1 软件优化建议状态机设计将主循环中的按键处理、温度控制等逻辑用状态机重构使程序结构更清晰易于维护和扩展。例如可以定义IDLE,AUTO_CTRL,MANUAL_CTRL,ALARM等状态。参数可配置将报警阈值、温度-PWM映射表、手动档位对应的PWM值等参数存储在STM32的Flash中利用内部EEPROM模拟或外部Flash并通过按键和OLED设计一个配置菜单实现运行时修改和掉电保存。滤波算法DS18B20的读数可能存在微小波动。可以引入软件滤波如滑动平均滤波或中值滤波使显示和控制更平滑。#define TEMP_FILTER_SIZE 5 float temp_history[TEMP_FILTER_SIZE]; uint8_t temp_index 0; float Get_Filtered_Temp(float new_temp) { temp_history[temp_index] new_temp; temp_index (temp_index 1) % TEMP_FILTER_SIZE; float sum 0; for(int i0; iTEMP_FILTER_SIZE; i) { sum temp_history[i]; } return sum / TEMP_FILTER_SIZE; }非阻塞式延时将Delay_ms替换为基于系统滴答定时器的非阻塞延时避免在延时期间CPU空转可以同时处理其他任务。6.2 硬件与生产环境考量电源隔离与滤波电机是感性负载启停和PWM调速会产生较大的噪声和反电动势。应在电机两端并联续流二极管在电源输入端加入π型滤波电路电感电容并将MCU的数字地与电机驱动电源地单点连接以减少干扰。过热与过流保护实际产品中需要考虑风扇堵转或短路的情况。可以在风扇电源回路串联一个采样电阻通过MCU的ADC监测电流超过阈值则关闭PWM输出并报警。多种传感器融合除了温度还可以增加湿度传感器如DHT11、人体红外传感器HC-SR501实现“恒温恒湿”或“人来即开人走即关”的智能模式。通信接口添加Wi-Fi如ESP-01S或蓝牙模块使风扇能够接入手机APP或智能家居平台实现远程控制和状态监控。结构设计与安全设计合适的外壳确保通风散热同时隔离高压部分防止用户触电。风扇叶片需加装防护网。通过这个项目你不仅掌握了STM32多个外设的协同工作方式更实践了从传感器数据采集、控制算法到执行器驱动的完整闭环开发流程。下一步可以尝试用FreeRTOS等实时操作系统来管理多个任务或者探索更先进的控制算法如PID控制让风扇转速变化更加平滑和精准。