资讯动态

STM32智能温控风扇开源:PWM调速+PID控制,稳定控温±1℃

发布时间:2026/9/8 7:39:49 来源:尧图企业网站定制
这套代码我调试了三个晚上从PWM占空比调到PID参数中间还烧坏过一个MOS管最终总算把温度控制做到±1℃以内。今天把这个STM32智能温控风扇项目完整开源出来包含代码、原理图、仿真文件全部打包。无论是正在学STM32的学生、做嵌入式开发的工程师还是想给3D打印机或功放做主动散热的朋友这套方案都能直接参考。项目硬件上选了STM32F103C8T6做主控传感器用的DHT11后面会说明为什么没选DS18B20执行部分是PWM调速的直流无刷风扇。重点不是这些元器件有多高端而是整个系统的控制逻辑和工程化处理——怎么处理传感器噪声、怎么设计迟滞避免风扇频繁启停、怎么在失控时保证安全。1. 项目整体设计与方案选型1.1 为什么用STM32F103C8T6而不是Arduino或纯硬件方案最初确实纠结过方案。如果只用一颗555定时器加热敏电阻做滞回比较器硬件方案大概半小时就能搭完但问题在于温度阈值只能靠电位器手工调没法做精准标定没有迟滞控制的话风扇会在临界点附近来回开关噪音问题严重无法扩展显示、按键、上位机通信等功能无法记录温度变化曲线不方便调试和验证效果用Arduino的话开发门槛低但实际做产品原型时需要考虑的其实不是代码难度而是以下几点Arduino的PWM输出频率低默认490Hz左右驱动风扇会产生明显的啸叫噪音引脚驱动能力有限必须外接驱动电路那就和用STM32没有本质区别如果后续要跑简单的PID控制算法Arduino的处理能力虽然够用但在多任务处理上不如STM32灵活STM32F103C8T6的优势在几个方面72MHz主频跑PID和滤波算法绰绰有余高级定时器可以输出20kHz以上的PWM频率完全避开人耳敏感区间3.3V逻辑电平方便和各类数字传感器直连价格在10块钱以内。对于学习目的来说Cortex-M3内核的架构清晰性价比高资料也丰富遇到问题容易找到参考案例。1.2 传感器选型DHT11、DS18B20、热敏电阻三选一的取舍这个项目最初在传感器上做过对比测试整理出的结论比较直观传感器精度采样频率接口成本适用场景NTC热敏电阻约±2℃需标定高可连续采样ADC1-2元成本敏感、电路简单DS18B20约±0.5℃最高750ms/次单总线3-5元精度要求高、单点测温DHT11约±2℃最快1次/秒单总线2-3元温湿度同时监控、环境级精度最终选择DHT11的原因比较实际这个项目定位是多功能——不只是控温还想同时显示环境湿度DHT11一颗传感器就能解决两个需求。精度方面对于风扇散热这种应用场景正负2℃的误差完全可行——如果目标是维持30℃±2℃DHT11的精度波动不会导致系统失效。而且DHT11的抗干扰能力比DS18B20好一些线材长了也不太容易出现时序问题。NTC方案其实是最适合量产的选择因为成本极低且采样速度快。但精度标定是个麻烦事。B值在不同温度区间会有偏差需要查表法或者拟合公式来修正。后面在代码里我会给出一套基于查表法的NTC驱动方便大家直接改硬件。1.3 控制策略为什么选用分段PID 迟滞窗口而不是纯PID本来计划直接上位置式PID但实测后发现问题DHT11的采样周期是1秒PID控制周期也跟着是1秒这个响应速度对于温度控制来说勉强可以接受但DHT11本身的分辨率只有1℃所以PID的输入量在量化过程中会有量化噪声——误差在正负1℃之间跳动PID输出就会导致风扇转速不稳定实测效果反而不好。于是把策略调整为分段控制 迟滞窗口温度低于启动阈值TL风扇停止或最低速运转温度在TL和TH之间按比例输出PWM线性过渡温度超过TH进入PID精细调节模式目标温度TH温度下降后低于TH但不低于TL迟滞带宽保持当前调速状态不切换控制模式同时加入温度变化率判断如果1分钟内温度上升速度超过2℃直接全速运转等温度稳定后再切回自动模式。这个功能在实际中非常实用比如3D打印机热床附近的风扇加热阶段需要快速散热稳定阶段只需要维持风量。2. 硬件设计详解与原理图核心逻辑2.1 系统供电设计与电源树整个系统的电源结构是典型的双电压方案。外部输入是DC 12V 1A适配器12V直接给风扇供电同时通过LM2596降压模块转换出5V再经AMS1117-3.3稳压给STM32和传感器供电。很多人画原理图时容易忽略一个问题DHT11和风扇共用一个12V地线而STM32的地也在同一网络上会形成地环路干扰。在高频PWM驱动风扇时电机反向电动势会通过地线耦合到MCU的ADC参考地导致温度读数跳变。我的处理方法是风扇驱动回路单独走线在靠近风扇接口处放置续流二极管SS341N5819也行吸收反向电动势12V输入并联两个电容470μF电解电容稳压0.1μF陶瓷电容滤高频模拟地和数字地在MCU附近单点连接PCB上通过0Ω电阻或磁珠隔离PWM输出引脚与MOS管栅极之间串接100Ω电阻降低振铃原理图上这部分虽然看起来只是几个电容电阻但实际调试时遇到过由于省掉续流二极管导致STM32反复复位的诡异BUG。MOS管关断瞬间没有续流回路的风扇线圈会产生几十伏的尖峰电压直接击穿栅极驱动电路通过引脚反灌到MCU供电端。所以续流二极管绝不能省。2.2 STM32最小系统电路要点最小系统是老生常谈但我在这个项目里做了两个容易被忽略的处理晶振电路8MHz主晶振的两个负载电容选择22pF但你需要确认晶振的CL值通常8MHz晶振CL在18-20pF之间用22pF是最常见的选择。32.768kHz的RTC晶振在这个项目里没有使用因为没有实时时钟需求直接省掉一颗器件。如果芯片的BOOT0引脚悬空也能正常工作但为了保险10kΩ下拉到地防止飞线时不稳定。复位电路NRST引脚接了10kΩ上拉并联0.1μF电容到地。有人喜欢在复位引脚上再接一个按键我建议如果不需要手动复位的话去省略掉。因为复位按键如果布线不当反而变成天线引入干扰。调试的时候用J-Link的复位信号就够了。2.3 风扇驱动电路从GPIO直驱到N沟道MOS管风扇额定电压12V额定电流约0.2ASTM32的GPIO只能输出3.3V最大灌电流也就20mA左右直驱显然不现实。驱动方案有两种用三极管如S8050做低边驱动成本极低但饱和导通压降在0.3V左右会导致风扇两端电压偏低转速上不去用N沟道MOS管如AO3400做低边驱动导通内阻典型值在40mΩ左右压降几乎可以忽略发热极小选了AO3400方案。原理图上需要注意的逻辑是AO3400是N沟道增强型MOS管栅极电压需要高于源极电压才能导通。源极接地所以PWM高电平时栅极电压约等于3.3V这个电压对于AO3400的VGS(th)典型值约1.3V来说自然没问题。原理图连接要点STM32 PA1 (PWM输出) ---[100Ω]--- AO3400 Gate ---[10kΩ]--- GND 12V --- 风扇 风扇- --- AO3400 Drain AO3400 Source --- GND 风扇- --- SS34阳极 --- SS34阴极 --- 12V如果后续要驱动更大功率的风扇比如12V 1A的涡轮风扇AO3400就有点吃紧了。可以换成AOD4184A或IRLB3034这种大电流MOS但要注意栅极驱动电压是否足够。驱动12V风扇的话最好用一个NPN三极管将PWM电平上提到5V或10V再去驱动MOS栅极保证MOS完全导通。2.4 人机交互与显示电路显示部分选了0.96寸OLEDSSD1306I2C接口这是目前使用最为广泛、资料也最齐全的OLED屏幕。I2C只需要两根线SCL、SDA而且速率完全够用。OLED的VCC接3.3V注意别接到5V上去SSD1306的绝对最大额定电压是6V接5V虽然大多数模块板载了稳压但还是有损坏风险。按键部分用了两个模式切换按键和手动调速按键或者温度设定加减。按键电路很简单一个10kΩ上拉电阻到3.3V按键另一端接地。但是需要对GPIO做输入上拉配置这样省掉外部上拉电阻也是可以的。调试接口SWD四线接口必须引出来虽然项目代码烧录也可以走串口ISP但SWD还可以实时查看变量、做在线调试效率好很多。SWDIO和SWCLK两个引脚不需要接上拉电阻调试器内部已经有了。2.5 原理图整理的实用建议很多新手画原理图时喜欢把整个系统的连线都堆到一起信号线互相交叉看起来非常凌乱。我的习惯是分模块画电源模块一个区域MCU最小系统一个区域传感器接口一个区域风扇驱动一个区域人机交互一个区域。每个区域内部用网络标签连接不同页面之间的信号。在立创EDA嘉立创EDA上画完原理图后一定要跑一次电气规则检查ERC。常见的隐患包括悬空引脚尤其是MCU的某些IO口默认是浮空输入态容易受噪声干扰电源和地短路输出引脚连接输出引脚3. 软件架构与核心代码实现3.1 Keil5工程结构与模块划分代码采用模块化设计符合裸机开发的常用分层思路。工程目录结构如下Project ├── Core/ │ ├── Inc/ │ │ ├── main.h │ │ ├── stm32f1xx_hal_conf.h │ │ └── stm32f1xx_it.h │ └── Src/ │ ├── main.c │ ├── stm32f1xx_hal_msp.c │ └── stm32f1xx_it.c ├── Drivers/ │ ├── BSP/ │ │ ├── bsp_fan.c/h // 风扇PWM控制 │ │ ├── bsp_dht11.c/h // DHT11传感器驱动 │ │ ├── bsp_oled.c/h // OLED显示驱动 │ │ └── bsp_key.c/h // 按键输入 ├── App/ │ ├── app_control.c/h // 控制逻辑主模块 │ ├── app_display.c/h // 显示更新 │ └── app_menu.c/h // 菜单状态机 └── System/ ├── delay.c/h // 延时函数 └── usart.c/h // 串口调试输出BSP层属于硬件驱动App层属于应用逻辑。今后哪怕换了传感器或者改了显示方式只需要改BSP文件即可App层代码完全不需要动。3.2 关键实现一DHT11时序驱动的踩坑记录DHT11的驱动代码网上能找到很多版本但直接用的话十有八九会出现读取失败的问题答案就在于时序精度。DHT11对时序要求不算严苛但GPIO翻转和电平采样的时间必须卡在区间内。DHT11的通信协议是单总线一次完整的数据帧是40bit8bit湿度整数 8bit湿度小数 8bit温度整数 8bit温度小数 8bit校验和。注意这个协议和DS18B20有很大区别DS18B20的时隙更短而DHT11的时序要求不过分依赖精确的微秒级延时但需要一个可靠的微秒延时函数。我的DHT11读取代码核心片段uint8_t DHT11_ReadByte(void) { uint8_t i, data 0; for (i 0; i 8; i) { // 50us低电平表示起始位 while (HAL_GPIO_ReadPin(DHT11_GPIO_PORT, DHT11_GPIO_PIN) GPIO_PIN_RESET); delay_us(30); if (HAL_GPIO_ReadPin(DHT11_GPIO_PORT, DHT11_GPIO_PIN) GPIO_PIN_SET) { data | (0x80 i); while (HAL_GPIO_ReadPin(DHT11_GPIO_PORT, DHT11_GPIO_PIN) GPIO_PIN_SET); } } return data; }注意delay_us(30)这个30微秒判断点DHT11数据位0的高电平持续26-28μs1的高电平持续70μs所以在高电平开始后的30μs时刻采样如果还是高电平说明这是1如果已经拉低了说明是0。这个判断点选在30μs附近是最稳妥的——如果你用的是带RTOS的环境可能会发生任务调度打断这个时序导致采样失败所以最好在读取DHT11时关中断或使用互斥锁。实际调试时遇到最玄学的问题是明明代码逻辑没问题但10次读取里总有一两次失败。排查下来发现是IRQ中断优先级设置的问题SysTick中断在DHT11时序中被触发。解决办法是在读取时序期间屏蔽全局中断__disable_irq(); // 执行DHT11时序读取 __enable_irq();3.3 关键实现二PWM控制与定时器配置风扇调速用的是TIM2的CH1通道PA0引脚。选择TIM2而不是TIM1是因为TIM2是通用定时器不需要额外配置死区时间上手更快。PWM频率设定为25kHz这个频率下风扇基本静音而且处在MOS管开关损耗较小的区间。PWM初始化代码HAL库void BSP_Fan_Init(void) { TIM_HandleTypeDef htim2; TIM_OC_InitTypeDef sConfigOC; __HAL_RCC_TIM2_CLK_ENABLE(); htim2.Instance TIM2; htim2.Init.Prescaler 72 - 1; // 72MHz / 72 1MHz计数器频率1MHz htim2.Init.CounterMode TIM_COUNTERMODE_UP; htim2.Init.Period 40 - 1; // PWM频率 1MHz / 40 25kHz htim2.Init.AutoReloadPreload TIM_AUTORELOAD_PRELOAD_ENABLE; HAL_TIM_PWM_Init(htim2); sConfigOC.OCMode TIM_OCMODE_PWM1; sConfigOC.Pulse 0; sConfigOC.OCPolarity TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode TIM_OCFAST_DISABLE; HAL_TIM_PWM_ConfigChannel(htim2, sConfigOC, TIM_CHANNEL_1); HAL_TIM_PWM_Start(htim2, TIM_CHANNEL_1); } void BSP_Fan_SetSpeed(uint8_t duty) { __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, (uint32_t)duty * 40 / 100); }这里PWM周期寄存器设为40意味着占空比分辨率是2.5%粒度。如果想要更精细的控制可以把Period改大比如设成400但PWM频率就要降下来。对于风扇控制来说2.5%的粒度其实完全够用实际测试中占空比从30%到100%转速非线性的特性很明显大致从1800RPM到5200RPM的动态范围。3.4 关键实现三分段PID控制逻辑前面提到这个项目采用分段控制策略PID只在温度进入TH附近的一定区间内才生效。控制逻辑用C语言描述如下#define TEMP_TARGET 30 // 目标温度35℃以上全速 #define TEMP_L_START 15 // 温度低于15℃风扇停止 #define TEMP_DEADBAND 2 // 迟滞窗口±2℃ #define PWM_MIN 30 // 最低PWM占空比% #define PWM_MAX 100 // 最高PWM占空比% float pid_kp 2.0f, pid_ki 0.05f, pid_kd 0.5f; float pid_target 30.0f; float pid_error_last 0; float pid_integral 0; float Fan_Control_Update(float temperature) { float duty; // 分段1超高温保护全速运行 if (temperature 50.0f) { pid_integral 0; duty PWM_MAX; } // 分段2温度高于目标值进入PID精细调节 else if (temperature pid_target TEMP_DEADBAND) { float error pid_target - temperature; pid_integral error; // 积分限幅抗积分饱和 if (pid_integral 50) pid_integral 50; if (pid_integral -50) pid_integral -50; float derivative error - pid_error_last; pid_error_last error; duty pid_kp * error pid_ki * pid_integral pid_kd * derivative; // PID输出转换为PWM占空比 if (duty PWM_MIN) duty PWM_MIN; if (duty PWM_MAX) duty PWM_MAX; } // 分段3目标值附近的迟滞区间维持上次输出 else if (temperature pid_target - TEMP_DEADBAND) { // 保持当前输出不变防止风扇频繁变速 } // 分段4温度低停止风扇 else { duty 0; pid_integral 0; pid_error_last 0; } return duty; }PID参数整定过程用到了经典的Ziegler-Nichols方法。先把Ki和Kd调成0Kp从小往大加直到系统出现持续等幅振荡记录临界增益Ku和振荡周期Tu。然后按表计算控制器KpKiKdP0.5Ku--PI0.45Ku1.2Kp/Tu-PID0.6Ku2Kp/TuKp*Tu/8实测这个系统Ku大约在5.2左右Tu约12秒所以理论PID参数为Kp3.12、Ki0.52、Kd4.68。实际调试时明显发现微分项过高——因为DHT11的1℃分辨率导致微分项对量化噪声极其敏感风扇转速疯狂波动。后来把Kp降到2.0Ki降到0.05Kd降到0.5实测效果好了很多。经验结论对于低分辨率、低采样率的传感器PID的微分项要谨慎使用甚至可以直接去掉用PI控制就够了。这个项目保留了一个小型微分项主要用来抑制温度急升时的过冲。3.5 关键实现四OLED显示与菜单状态机OLED显示代码用的是经典的U8g2库移植版但因为STM32F103C8T6只有20KB的RAMU8g2全功能版在缓冲模式开会比较吃紧。实际上用的是页面缓冲模式full buffer改成page buffer一页128*8像素只有1KB的缓冲完全够用。菜单逻辑使用状态机实现代码简洁且易于扩展typedef enum { MENU_MODE_AUTO 0, MENU_MODE_MANUAL, MENU_SET_TARGET, MENU_SET_PWM, MENU_MAX } Menu_State; void App_Menu_Task(void) { static Menu_State menu_state MENU_MODE_AUTO; uint8_t key_value BSP_Key_GetValue(); switch (menu_state) { case MENU_MODE_AUTO: if (key_value KEY_OK) { // 切换自动/手动模式 if (g_control_mode MODE_AUTO) g_control_mode MODE_MANUAL; else g_control_mode MODE_AUTO; } break; case MENU_SET_TARGET: if (key_value KEY_UP) { pid_target 1.0f; if (pid_target 50) pid_target 50; } if (key_value KEY_DOWN) { pid_target - 1.0f; if (pid_target 10) pid_target 10; } break; default: break; } }UI布局也很简单第一行显示当前温度和湿度第二行显示工作模式和PWM占空比。实际使用中OLED我设置了自动休眠功能如果30秒没有按键操作清屏进入低功耗模式按任意键唤醒。这个功能对延长OLED寿命很重要OLED长期显示固定画面会烧屏这个问题在很多工控设备上很常见。3.6 看门狗与异常保护逻辑嵌入式系统必须考虑异常情况。这个项目加了两层保护独立看门狗IWDG溢出时间设定为2秒。主循环里每次循环末尾喂狗一次。如果程序跑飞或陷入死循环2秒后系统自动复位。需要注意的是在调试时如果设了断电断点看门狗会超时复位芯片导致调试中断。解决办法是在调试状态下把IWDG初始化注释掉。温度传感器断线检测如果DHT11连续读取失败超过5次系统判定传感器异常直接进入安全模式——PWM输出50%占空比让风扇以半速运转OLED上显示ERR: SENSOR。至少保证设备不会因为传感器故障导致过热。等传感器恢复后系统自动退出安全模式恢复正常控制。这个设计在工业设备里叫Fail-Safe原则是不知道环境温度时务必要保证风扇在转动而不是停转。4. 仿真环境搭建与验证过程4.1 Proteus仿真工程的配置流程Proteus仿真在这个项目里主要用来验证控制逻辑的合理性不需要实时硬件也能跑通整个系统的功能。使用Proteus 8.9以上版本可以直接找到STM32F103C8T6模型。仿真工程搭建的核心步骤在Proteus的Device Selection里面搜索STM32F103C8T6加载MCU模型添加DHT11模型Proteus库中有这个传感器模型不容易找了的话可以用Lm35代替但数据格式不同添加虚拟示波器用来观察PWM波形加载HEX文件双击MCU → Program File → 选择Keil生成的.hex文件配置MCU的晶振频率为8MHz注意要和代码里的时钟配置一致需要特别提醒的是Proteus仿真中DHT11的时序比真实硬件宽松很多代码直接在Proteus上跑得通不代表在真机上一定能跑通。仿真通过只能证明逻辑正确时序问题必须在真机上验证。4.2 仿真中的温度变化模拟与波形分析在Proteus中模拟温度变化有两种思路一是直接用滑杆控件手动改温度变量但需要写脚本二是用一个可变电阻代替NTC热敏电阻通过调整电阻值来模拟温度变化。第二种方法更贴近真实硬件行为。在仿真中注意观察PWM输出的变化当温度低于15℃时PWM输出始终为低电平风扇不转温度升高到20℃左右时PWM波形开始出现占空比随时间逐渐增加温度达到30℃后占空比稳定在PID计算值附近波形频率恒定25kHz通过调整PWM的波形参数可以在硬件上电前验证电机驱动的正确性排查硬件问题导致的MOS管发热或者烧毁隐患。4.3 仿真与实物的差异分析别把仿真太当真坦率讲Proteus仿真无法验证的部分也很明显DHT11的时序模型过于理想化没有真实的信号上升沿/下降沿延迟没有模拟电源噪声对ADC采样的影响MOSFET的开关特性栅极电荷、米勒效应在仿真里是算法模型不是真实物理特性风扇的堵转特性和反电动势无法模拟所以我个人的习惯是仿真跑通逻辑后立刻焊最小系统板跑真机。仿真验证逻辑真机验证时序和可靠性。两者各有分工不是替代关系。5. 实物调试与常见问题排查5.1 踩坑记录no stm32 target found 问题很多人在初次连接J-Link或ST-Link时会在Keil的下载界面看到error: no stm32 target found! if your product embeds debug authentication, pl...。这个问题太典型值得单独说。最常见的原因是BOOT0引脚没有接地MCU在上电时进入了ISP模式核心代码没有运行SWD接口不可用更隐蔽的原因MCU的VDD_3.3V引脚旁边缺少100nF去耦电容导致内核电压不稳SWD初始化失败我自己的经历是有一次给核心板供电后发现SWD始终连不上换了两个调试器都没用。最后查出原因是复位引脚悬空NRST被噪声干扰导致反复复位调试器刚连接上就又被复位断开。解决办法就是前面原理图里提到的NRST接10kΩ上拉到3.3V加100nF下拉电容滤波。如果遇到这个报错按照这个顺序排查确认调试器驱动是否安装J-Link需要装SEGGER驱动ST-Link需要装ST-LinkUSBDriver在Keil中检查Debug设置里的调试器型号是否选对按住复位键点击下载在下载启动时松开复位键俗称蹭复位测量MCU的3.3V供电电压是否稳定纹波是否过大用万用表测SWCLK和SWDIO引脚是否有毛刺5.2 风扇不转或转速异常的排查清单最典型的三个原因按下述优先级排查驱动电路问题概率最高用万用表测MOS管栅极电压PWM输出正常时栅极应该有跳变电压。如果栅极电压正常再测D-S之间的电压。D-S压差接近12V说明MOS没导通接近0V说明正常导通。疑似MOS管损坏时检查是否被静电击穿或者检查栅极驱动电阻是否过大导致开关速度过慢MOS管长时间处于线性区而发热烧毁。PWM频率过高或过低如果PWM频率设定和风扇本身的设计不匹配风扇可能发出明显的啸叫声。一般4线风扇用25kHz没问题但一些老式2线风扇的最佳频率在1-5kHz之间可以实际测试选出噪声最小的频率。占空比分辨率不够前面提到TIM2的Period设为40占空比步长是2.5%。如果风扇在某个占空比下抖动明显适当提高PWM分辨率。把Period设成400的话PWM频率就会降到2.5kHz需要综合权衡。5.3 DHT11读取失败的排查方向DHT11在上电后会有一个不稳定期大概1秒。首次读取前需要让总线空闲至少1秒。我在初始化代码中加了void BSP_DHT11_Init(void) { // 引脚配置为推挽输出 // 拉高总线电平保持1000ms以上 HAL_GPIO_WritePin(DHT11_GPIO_PORT, DHT11_GPIO_PIN, GPIO_PIN_SET); delay_ms(1000); }如果还是读取失败排查顺序确认上拉电阻是否接了10kΩ左右DHT11数据线需要上拉线路太长会导致波形变形建议控制在20cm以内检查是否在读取过程中开启了中断如果是则按前面说的方法关闭全局中断5.4 常见问题速查表现象可能原因解决方案SWD连接不上复位电路异常NRST接10kΩ上拉并联100nF电容PWM波形有振铃栅极驱动电阻缺失在MOS管栅极串接100Ω电阻温度读数跳变传感器电源纹波大给DHT11加0.1μF去耦电容风扇低速抖动占空比过低设置PWM_MIN低于30%时直接输出30%风扇全速停不下来PID积分饱和积分限幅上下限设为±50OLED花屏I2C时序过冲SCL/SDA加10kΩ上拉到3.3V降低速度MCU发热3.3V输出电压过高检查AMS1117-3.3输出是否超过3.6V温度超过目标值但风扇已全速PID目标值过高检查pid_target结合实际散热需求调整5.5 电源纹波对温控的影响一个被忽视的大坑最后分享一个很有价值的调试经历。系统刚开始做出来时温度读数总是有规律性地在上下跳动。TFT屏上显示的温度值每隔几秒就跳1℃左右风扇转速也跟着波动看起来非常不稳定。用示波器测了一下3.3V电源轨发现纹波超过了100mV——这个值对STM32来说虽然不至于掉电但已经足以影响到DHT11的时序判断了。根源是整个系统共用一个DC 12V电源风扇PWM调速的负载变化直接反映到了电源上然后通过线性稳压器传导到了3.3V。解决方法是加了一个二级LC滤波在AMS1117-3.3之后串了个10μH功率电感再加一只100μF钽电容。这样改完之后纹波降到30mV以下温度读数不再跳动了。6. 开源资料说明与后续扩展方向6.1 开源文件清单整理整个项目开源的内容包含四个部分Keil5完整工程源码全部C代码HAL库编写支持标准库立创EDA原理图工程可以直接打开并导出Gerber文件Proteus仿真工程可以直接运行不需要改代码配套文档引脚连接表、元器件BOM清单、调试记录BOM清单大致如下按单套计算元器件型号/规格数量单价参考MCUSTM32F103C8T618-10元温湿度传感器DHT1112-3元MOS管AO3400 SOT-2310.3元续流二极管SS3410.2元显示屏0.96寸OLED I2C18-12元风扇12V 0.2A 40mm15-10元按键轻触开关20.1元稳压芯片AMS1117-3.310.5元DC电源座5.5x2.1mm10.1元阻容无源器件若干-约3元PCB打样5cmx5cm10片1约20元整机物料成本控制在50元以内如果散热片和外壳量产材料不计的话。6.2 可能的扩展方向与技术改造项目本身已经有完整的风扇控制功能后续扩展建议按以下几个方向串口上位机联动目前只有OLED显示后续可以把温度、湿度、PWM占空比通过USART发送到PC用Python的pyqt或简单的串口助手画实时曲线。代码里已经预留了USART1的初始化只需要补一个数据打包发送的函数。WIFI远程监控在现有UART引脚上加ESP8266模块将温度信息上报到MQTT服务器手机端用MQTT客户端查看。ESP8266的AT固件操作比较简单STM32侧只需要实现几行AT指令的发收逻辑。多路风扇控制如果散热需求变大比如给多个模块独立散热可以在TIM2的其他通道输出PWM同时驱动多路风扇。注意计算总电流是否在电源适配器容量以内必要时更换更大功率的电源。无感测量环境温度DHT11只能测传感器周围的局部温度而实际需要散热的是设备核心比如MCU自身、功率管表面可以考虑加一个贴片NTC贴在发热器件上与DHT11的读数做加权融合得到更准确的受控温度。6.3 学习建议怎么从这套代码里真正学到东西如果只是把代码下载编译烧录仿真跑一下学到的很有限。有几个更有价值的实操方式先尝试独立把DHT11驱动删掉自己根据数据手册重新写一遍读取逻辑。数据手册标称的30μs、70μs等参数直接来源于代码你写的过程中会自然理解届时为什么需要延时以及为什么关中断。再试试把PID改成增量式PID对比两种算法在温度超调量和调节时间上的差异。把控制策略从分段PID改成模糊控制Fuzzy Logic不需要推导复杂公式只需要查表就能实现简单的二维模糊查询表。最后一定要做一次故障注入测试人为拔掉DHT11传感器、给风扇堵转、把12V电源降到8V看系统是怎么响应和恢复的。这些测试才是区分会写代码和会做产品的分水岭。我至今记得第一次在立创EDA上画板电源和地焊反了两根飞线整个板子烧掉了三颗AMS1117的窘境。但这套温控风扇系统从原理图设计到最终稳定运行前前后后改了三版PCB第一版因为风扇驱动电路的地线回流路径过长导致OLED在风扇转动时频繁闪烁。第二版修正了走线MOS管驱动信号又出现了振铃第三版加上了栅极串联电阻才最终定稿。有时候线和线之间那几毫米的距离差异就是稳定与不稳定之间的鸿沟。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价