
做嵌入式这几年陆陆续续收到很多同学的私信问得最多的就是“手里有个STM32板子但不知道做个什么项目有意思”。今天干脆把之前开源过的一个项目完整地整理出来就是标题里写的这套多功能智能温控风扇系统。这个项目我把代码、原理图和仿真工程都打包放在一起了硬件上用STM32F103C8T6做主控外设包括温湿度传感器、OLED屏幕、按键、蜂鸣器和直流风扇软件上实现了自动温控、手动调速、温度报警几个核心功能并且能在Proteus里直接跑仿真。如果你正在做课程设计、准备电子竞赛或者单纯想找一套能改能学的嵌入式练手项目这套东西会非常对胃口。老规矩先把项目的整体情况说清楚代码是用标准外设库写的工程结构比较简单没有用RTOS逻辑清晰到哪怕你只学过一点儿C语言也能跟上原理图是Altium Designer画的PDF和源工程都放出仿真用的是Proteus 8.10以上版本已经调好了打开就能看到风扇转速随温度变化。接下来我把从硬件选型到代码逐模块的实现以及仿真验证的过程全部拆开讲最后附上我实际做下来遇到的一堆问题和排查方法希望能帮大家少走弯路。1. 项目到底要做什么需求拆解与整体方案1.1 一堆散装需求背后我到底想要一个什么样的系统先讲一下这个项目最初的起点。市面上常见的温控风扇电路很多但绝大多数都是“温度一到阈值风扇全速转”要么“超过温度就开低于就关”这种开关量控制有两个明显的痛点一是风扇突然启动噪音很大二是温度在阈值附近波动时风扇会频繁启停非常影响寿命。我设计这个系统的时候给自己列了几个硬性需求温度能实时显示、风扇转速可以平滑调节而不是粗暴地开关、支持自动和手动两种模式、温度超过上限要报警。把这些需求翻译成嵌入式语言就是需要一路温度传感器采集环境温度需要PWM输出驱动风扇实现调速需要OLED屏幕做本地显示需要按键实现模式切换和参数设置需要蜂鸣器在超温时报警还要留一个串口用来调试。这套需求在STM32F103C8T6上实现属于标准的中低难度项目但它卡在一个很关键的交叉点上既涉及传感器数据的解析又涉及PWM电机控制的时序还有显示和输入的人机交互再加一个Proteus仿真验证环节正好把嵌入式开发里的“采集-处理-输出-交互”全链路走了一遍。这也是为什么我推荐大家拿它练手而不是去做一个纯点灯或者纯串口打印的简单例程。1.2 为什么选中STM32F103C8T6这颗芯片而不是51或者Arduino我在群里经常被问“用STC89C52不行吗”“用Arduino Nano不是更快吗”这里统一回答一下。STC89C52确实便宜51内核的资料也铺天盖地但它的定时器资源太紧张了。我的需求里至少要同时用到一个定时器做PWM、一个定时器做按键消抖和超时判断再加一个延时函数51的Timer 0和Timer 1就得来回切换代码写起来非常憋屈后期想加一个I2C的OLED还得软件模拟时序压力很大。Arduino的问题是反过来它太“舒服”了各种库一封装你最后只会在setup和loop里调函数硬件底层发生什么完全没感觉。做作品可以但如果你想靠这个项目去理解寄存器、中断、定时器这些嵌入式基本功还是得回到标准库甚至寄存器开发。STM32F103C8T6刚好卡在中间它的运算性能比51强了好几个量级72MHz主频足以跑PID算法和显示刷新外设资源丰富一个TIM2就能输出四路PWMI2C、USART、ADC全是硬件外设最关键是Proteus对它的模拟支持已经非常成熟这直接决定了仿真环节能不能顺利跑起来。我这个项目用的是标准外设库Standard Peripheral Library虽然ST官方现在已经主推HAL库了但标准库在中小项目里依然值得学原因就一个它离寄存器近每一个外设函数你都能猜到它大概率在读哪个寄存器、改哪个位这对建立单片机硬件思维帮助特别大。等你能用标准库把一套项目跑顺了再去切HAL或者LL就是语法层面的迁移不费劲。1.3 整体架构与控制链路整个系统的控制链路可以用一句话串起来温度传感器DHT11采集环境温度主控STM32F103C8T6读取数据后一方面把温度显示到OLED上另一方面根据当前的运行模式计算PWM占空比然后通过GPIO引脚输出对应频率和占空比的方波驱动三极管或MOS管电路来改变风扇两端的平均电压从而控制风扇转速。这里有一个很多人容易忽略的点MCU的GPIO输出电流只有几毫安根本不能直接驱动直流电机哪怕是个5V的小风扇也不行。所以中间必须要加功率驱动电路我用的方案是NPN三极管SS8050加续流二极管属于最经典的低边驱动方式。如果风扇电流超过500mA就得换成MOS管或者电机驱动芯片这一点我在第2节硬件部分会细讲。系统的工作模式我设计了两种也是项目“智能”二字的体现。自动模式下用户设定一个目标温度和一个回差温度比如目标温度30℃、回差2℃那么当环境温度小于28℃30减2时风扇停止当环境温度大于32℃30加2时风扇全速在28℃到32℃之间则按照比例关系线性调速。这个回差的机制非常重要它能有效避免风扇在目标温度附近反复启停。手动模式下用户直接用按键在20%到100%占空比之间调风扇转速适合夏天固定吹风或者冬天强制通风的场景。除了温度采集和风扇控制系统还加了一个超温报警功能当环境温度超过用户在设置界面里配置的报警上限时蜂鸣器会以一定频率鸣叫并在OLED上显示“OVER TEMP”警告。整个逻辑我用一个简单的状态机来管理分成正常、设置、报警三个状态第3节会详细讲状态机的实现。2. 硬件设计与原理图解析2.1 主控最小系统与电源设计先看主控最小系统。STM32F103C8T6在常见的蓝色开发板上已经把最小系统画好了但如果你要自己画原理图画PCB板这个时候最小系统就需要自己搭了。最小系统由四部分组成3.3V供电、8MHz外部晶振加两个20pF负载电容、复位电路、BOOT0启动模式选择。这里稍微展开讲一下晶振电容的计算这个在很多原理图里都被一笔带过但确实是调试板上很常见的坑。STM32F103的8MHz晶振通常要求负载电容CL等于18到20pFPCB上还有寄生电容CS一般在2到6pF两个外接电容C1和C2串联后就是CL (C1 × C2)/(C1 C2) CS。如果目标是CL20pF取CS5pF那么C1和C2串联后的电容应该是15pF所以每颗电容取30pF。市面上一半的板子会直接焊20pF电容也能正常工作因为晶振起振条件余量很大但如果你想认真算一遍上面的公式就是这个项目的标准答案。复位电路简单得多一个10kΩ上拉电阻加一个100nF电容到地再把NRST引脚引出一个按键即可。电源部分是这个项目里我最想强调的。STM32的MCU供电是3.3V而风扇的额定电压通常是5V或者12V这两个电源不能混用。我的板子上用了两种方案如果是5V风扇直接从USB的5V取电经过一个AMS1117-3.3给MCU供电风扇电源和逻辑电源共地如果是12V风扇常用的做法是外接一个12V适配器经过降压模块得到5V再经AMS1117得到3.3V。关键点是MCU的3.3V电源和风扇的12V电源必须共地否则PWM信号无法形成回流路径风扇驱动电路会完全无法工作。这个坑我见过无数次了放在仿真里还不容易暴露但实际搭板子时不共地风扇根本不会转。2.2 温度采集DHT11还是DS18B20温湿度传感器我最后选了DHT11而不是DS18B20理由有三点第一DHT11同时输出温度和湿度项目功能上多个湿度数据可以做扩展第二Proteus里DHT11的直接仿真模型非常成熟拖出来就能用DS18B20的时序模型在仿真时容易因为时序问题卡住第三DHT11的数据格式是40位单总线读一次数据的过程包含起始信号、响应信号、40个数据位比DS18B20的ROM指令流程更容易对初学者讲清楚。当然DHT11的缺点也很明显温度精度只有正负2℃湿度精度正负5%RH采样周期最短1秒这两个指标意味着它不适合高精度测量场合。如果你后续想做一个精确控温的孵化器或者恒温箱建议把DHT11换成DS18B20测温精度能到0.5℃甚至更高。我在这套代码里把传感器抽象成了一个dht11_read()函数返回温度和湿度结构体后续换成DS18B20时只需要改动这一个文件其他逻辑不用动。原理图里DHT11的接法很简单一根数据线接MCU的PB0加上一个4.7kΩ上拉电阻到3.3V因为单总线协议要求总线空闲时保持高电平。供电是5V还是3.3V取决于具体模块我画的原理图里用的3.3V数据线直连即可不需要电平转换。2.3 风扇驱动PWM调速的低边驱动电路风扇电机属于直流有刷电机调速最常用的方式是调整PWM波形的占空比等效改变电机两端的平均电压。PWM的频率选择有个原则要高于人耳能听到的20kHz否则电机绕组会发出尖锐的啸叫。我在代码里把PWM频率设在25kHz实测听不到任何噪声。驱动电路方面小功率风扇5V/200mA以内可以用三极管SS8050做低边驱动电机一端接5V正极另一端接三极管的集电极三极管发射极接地基极串联一个1kΩ电阻接MCU的PA0引脚。当PA0输出高电平时三极管导通电机通电输出PWM波时电机以对应占空比的平均电压运行。三极管的基极电阻是根据驱动电流算的STM32GPIO输出高电平约3.3V三极管Vbe约0.7V如果想让基极电流在3mA左右电阻取(3.3-0.7)/0.003约等于860Ω取整用1kΩ没问题。这里要注意的是MCU引脚输出PWM高电平的时候基极电流由MCU的引脚承受所以驱动中功率风扇时基极电阻不能太小不能让GPIO过载。电机是感性负载在PWM关断瞬间会产生反向感应电动势如果不加保护冲击电压很可能打坏三极管。解决办法是在电机两端反向并联一个续流二极管1N4148阴极接电源正极阳极接三极管集电极。PWM关断时电机电流通过二极管续流保护了三极管。如果是大功率风扇比如12V/1A以上建议换成N沟道MOS管AO3400它的导通电阻只有几十毫欧发热更小驱动电流也更大价格也就几毛钱一片。2.4 显示与交互模块显示方案我选的是0.96寸OLEDI2C接口SSD1306控制器分辨率128×64。I2C只需要两根线SCL接PB6SDA接PB7配上4.7kΩ上拉电阻。如果不想用OLED也可以用LCD1602加I2C转接板不过LCD1602需要占用更多IO口且显示内容有限我在仿真里也备了一套基于LCD1602的显示方案方便没有OLED仿真模型的Proteus版本。按键部分我用了4个独立按键分别接PA4、PA5、PA6、PA7按下时接地空闲时通过内部上拉电阻保持高电平。这里特别注意要用STM32内部的GPIO_PuPd_UP上拉省掉外部电阻。按键的消抖不靠硬件电容我是在代码里用定时器做10ms轮询加软件消抖具体逻辑在第3节讲。蜂鸣器我选的是低电平触发的有源蜂鸣器接在PB8上用一个NPN三极管驱动用法和风扇的低边驱动一致。选有源蜂鸣器是因为内部自带振荡源给个低电平就能响不需要MCU输出PWM去驱动无源蜂鸣器代码上省事很多。整个原理图的引脚分配我整理成了一张表方便大家对照着看功能引脚说明PWM输出风扇PA0TIM2_CH125kHz频率DHT11数据PB0单总线协议4.7kΩ上拉OLED-SCLPB6I2C1时钟OLED-SDAPB7I2C1数据蜂鸣器PB8低电平触发三极管驱动按键K1PA4模式切换自动/手动按键K2PA5进入设置/确认按键K3PA6数值增加按键K4PA7数值减少3. 软件设计与核心代码实现3.1 工程架构与代码分层很多初次接触这个项目的朋友拿到代码后会第一时间打开main.c然后被一长串while(1)里的逻辑绕晕。所以这次开源我特意做了分层设计虽然用的是标准库但文件组织向工程化靠拢总共分成四层驱动层、中间层、应用层和主循环。驱动层包含bsp_dht11.c、bsp_oled.c、bsp_pwm.c、bsp_key.c这些文件每个文件只负责操作一个具体外设。比如bsp_pwm.c里就是一个PWM_Init(void)函数把TIM2的初始化、GPIO复用配置、PWM模式设置全部封装在里面对外只保留一个Set_Fan_PWM(uint8_t duty)函数应用层根本不用关心底层寄存器到底怎么改。中间层是本项目控制逻辑的核心文件是control.c里面有PID相关实现、温度过滤、模式管理。应用层则对应状态机的各个状态和转移逻辑。main.c里做的事情非常少就是初始化外设后进入while(1)然后调用状态机处理函数。这样的好处是你拿到代码后想改显示逻辑就去application_display.c想换温度传感器就改bsp_dht11.c不需要理解全局修修补补的难度很低。3.2 DHT11的数据读取与解析DHT11的单总线时序是这套代码里最容易出错的点我详细说一下。MCU先拉低数据线至少18ms然后释放并拉高20到40us这叫起始信号DHT11收到后会先拉低80us响应再拉高80us然后开始输出40位数据顺序是8位湿度整数、8位湿度小数、8位温度整数、8位温度小数、8位校验和。每一位数据的解析关键在于高电平持续的时间。DHT11的数据位编码方式很独特先是50us低电平然后如果是“0”高电平持续26到28us如果是“1”高电平持续70us左右。所以读一个数据位的流程是等待引脚变低确认低电平过去后等到引脚变高然后延时40us再读引脚电平。如果此时引脚为高说明这一位是“1”如果为低说明高电平已经结束这一位是“0”。这个“延时40us再读电平”的写法非常巧妙比测量高电平宽度的方式稳定得多因为DHT11的时序容差较大延时40us的判断窗口在26us到70us之间留出了足够的中间区域。我用标准库实现的核心代码如下uint8_t DHT11_ReadByte(void) { uint8_t i, data 0; for (i 0; i 8; i) { while (DHT11_DQ_IN() 0); // 等待50us低电平结束 delay_us(40); // 延时40us后采样 if (DHT11_DQ_IN() 1) // 如果还是高说明是1 { data | (0x80 i); } while (DHT11_DQ_IN() 1); // 等待高电平结束 } return data; }校验处理是DHT11接收端的兜底逻辑。读完40位后把前4个字节相加判断低8位是否等于校验和。如果不相等说明本次数据读取异常直接丢弃保留上一次的有效值。这个处理在实际使用中非常必要因为DHT11在某些电磁环境较差的时候确实会偶发错误数据不加校验的话风扇可能突然抽风转一下。3.3 温控策略从分段线性调速到PID闭环自动模式下的调速策略是这个项目最核心的控制算法。我给用户提供了两套可切换的策略默认是分段线性调速进阶是增量式PID调速通过长按K2键进入设置菜单切换。分段线性调速的逻辑不复杂但设计好“回差”机制是关键。目标温度设为TempSet30℃回差TempHys2℃。当实时温度T小于TempSet-TempHys即28℃时占空比输出0%风扇停转当T大于TempSetTempHys即32℃时输出100%当T在28到32℃之间时占空比按比例插值duty (T - 28) / (32 - 28) × 100。为了防止温度在小范围内波动时占空比抖得太厉害我在计算后还会做一次一阶低通滤波把本次的占空比和上一次的占空比按权重合并相当于给转速变化加了“软启动”。PID策略则适合对温度稳定性要求更高的场景。因为控制对象是风扇这家伙本身就是一个大惯性系统所以用增量式PID而不是位置式PID。增量式的输出是PWM占空比的增量delta叠加到上一次的占空比上公式是delta Kp × (err[k] - err[k-1]) Ki × err[k] Kd × (err[k] - 2err[k-1] err[k-2])增量式的好处是不会有积分饱和因为PID只计算增量不会累计出一个非常大的输出量。整定参数我实测下来用Kp30Ki2Kd1单位为百分比效果相对平稳。如果你要用在自己的环境里先把Ki和Kd置0只调Kp直到温度能快速靠近目标但出现小幅振荡然后慢慢加Ki消除稳态误差最后加一点Kd抑制振荡。这个调参顺序适用于绝大多数增量式PID场景。去抖滤波和PID计算我写在control.c里关键代码如下static float temp_filtered 0; void Control_Update(float temp) { float err, delta, target_duty; if (sys.mode MODE_AUTO) { if (sys.ctrl_mode CTRL_LINEAR) { if (temp sys.temp_set - sys.temp_hys) target_duty 0; else if (temp sys.temp_set sys.temp_hys) target_duty 100; else target_duty (temp - (sys.temp_set - sys.temp_hys)) / (2 * sys.temp_hys) * 100; } else // CTRL_PID { err sys.temp_set - temp; delta Kp * (err - pid.err_pre1) Ki * err Kd * (err - 2 * pid.err_pre1 pid.err_pre2); pid.err_pre2 pid.err_pre1; pid.err_pre1 err; target_duty pid.duty_last delta; if (target_duty 0) target_duty 0; if (target_duty 100) target_duty 100; pid.duty_last target_duty; } // 一阶低通滤波平滑转速变化 temp_filtered 0.2f * temp_filtered 0.8f * target_duty; Set_Fan_PWM((uint8_t)temp_filtered); } else { Set_Fan_PWM(sys.manual_duty); } }3.4 状态机与前台的显示刷新逻辑前面的代码基本解决了“采集”和“控制”的问题但一个多功能系统还得有舒服的交互体验这里就需要一个轻量级的状态机。系统状态我定义为STATE_NORMAL正常运行界面显示实时温度、湿度、风扇占空比和当前模式。STATE_MENU设置界面可以切换“目标温度”、“回差温度”、“报警温度”、“控制策略”这几个参数K3和K4负责加减K2确认并返回。STATE_ALARM超温报警状态蜂鸣器响OLED显示警告图标和当前温度。每次按键触发后在状态机里判断当前状态和按键编号决定状态迁移还是数值调整。因为按键采用定时器10ms扫描一次并处理消抖所以状态机的响应实时性足够好用。OLED的显示刷新我做了一个1Hz的定时器标志位每次刷新读取最新的DHT11数据和控制器的PID输出。如果每帧都在while循环里刷新OLEDI2C通信会占用不少CPU时间而且OLED的SSD1306控制器写整屏数据需要接近几十毫秒太高频率刷新意义不大100ms到200ms刷新一次完全够用。温度显示保留一位小数湿度显示整数。状态机和显示刷新是这套系统里“多功能”的主要表现如果只做温度控制不看人机交互那这套系统只能算半个成品。我把按键扫描、状态迁移、界面绘制分成独立模块后以后想加一个“定时开关风扇”功能就只需要在状态机里添一个时间判断分支代码可扩展性很好。4. 仿真搭建与验证从Proteus建工程到温度变化实测4.1 Proteus仿真工程搭建的核心要点先说版本我用的是Proteus 8.10下载安装之后需要在“Pick Devices”里搜索STM32F103C8T6注意不同版本的中文翻译可能不太一样英文界面是“STM32F103C8T6”中文界面可能是“意法半导体”分类下。放置芯片到原理图后双击芯片选择Program File指向我们编译生成的HEX文件。这里有个非常常见的坑Proteus识别不了MDK默认生成的axf文件必须要在MDK里勾选“Create HEX File”或者在Output选项卡里设置“Create Batch File”。MDK5默认情况下是不生成HEX文件的很多人仿真卡在黑屏或者报错就是这一步没做。在Proteus里画仿真电路时有几个地方需要特别注意。首先是DHT11模型Proteus 8.10以上版本自带DHT11库直接搜索“DHT11”即可放置如果你的Proteus版本找不到这个模型可以用信号发生器模拟温度变化或者干脆换成DS18B20模型但这需要重写bsp_dht11.c为单总线协议改动量不小建议直接用高版本Proteus。OLED显示屏在Proteus里的模型支持不太统一我在仿真演示时改用LCD1602加I2C转接模块的方式这种方式在Proteus里仿真I2C设备更稳定。如果你一定想在实物里用OLED仿真阶段先用LCD1602验证逻辑等实物焊接了再换成OLED代码里只需要把显示驱动函数替换掉控制核心完全不用改。仿真电路里还需要放置一个直流电机代表风扇用“MOTOR-DC”元件即可把控制端接到PWM输出引脚。为了能看到转速随温度变化的效果我给电机并联了一个“VOLTMETER”电压表观察平均电压的变化。4.2 仿真验证的实际操作流程仿真的完整操作流程我整理成了步骤照着做一遍基本能跑通在Proteus中新建工程选择“Schematic Capture”模式命名保存。从库中取出STM32F103C8T6、DHT11、LCD1602、I2C转接模块、直流电机、按键、电阻、NPN三极管等元件。按第2节的原理图连接电路注意PWM输出引脚PA0要接到三极管的基极电阻上DHT11数据线要加上拉电阻。双击STM32芯片上传编译好的.hex文件设置晶振频率为8MHz。点击运行LCD1602上会显示初始温度和湿度。修改DHT11的“Temperature”属性从25℃逐步调到35℃观察风扇转速变化和LCD上占空比的联动。第一次跑仿真的时候最容易出现的问题就是DHT11的数据线在上电后一直保持低电平导致MCU在读取时卡死在“等待引脚变高”的循环里。这个通常是DHT11模型的上拉电阻没有放置或者放置位置不对加上一个4.7kΩ上拉电阻即可解决。我把交变温度下的仿真结果记录了一下表格如下设定温度℃环境温度℃占空比%现象30250风扇停止3028.512风扇极慢速旋转303050风扇中速旋转3032100风扇全速旋转3035100全速蜂鸣器报警这个结果和理论计算的线性曲线完全吻合说明PWM输出、ADC采集如果有、控制算法的链路是通的。4.3 仿真和实物的差异提醒要提醒一句仿真跑通了只代表逻辑没问题离实物能跑还有一段距离。Proteus的仿真模型是理想化的它不会模拟电源纹波、不会模拟电磁干扰、也不会模拟电机启动时的浪涌电流。我在实物调试时遇到过这样一个差异仿真中PWM占空比30%时风扇能转但实物里同样占空比下风扇直接不转原因是电机启动需要克服静摩擦力而PWM平均电压低于启动电压时电机就是纹丝不动。解决方案是在软件里做一点“启动补偿”也就是当占空比低于某个阈值比如15%时先输出100%持续100ms再回落到设定占空比。这个小技巧能让风扇在极低占空比下也能可靠启动代码加在Set_Fan_PWM函数里就行。5. 常见问题与排查技巧实录5.1 仿真阶段的高频问题速查现象可能原因排查与解决MDK编译后找不到HEX文件MDK默认未生成Options for Target → Output → 勾选Create HEX FileProteus里芯片运行但LCD无显示HEX未加载或芯片选型不对双击芯片重新选择HEX检查是否选的是F103C8T6而非别的型号DHT11读回的数据恒为0上拉电阻缺失或引脚接错检查数据线是否加了4.7kΩ上拉到3.3V风扇不转但温度显示正常三极管模型选择错误或PWM引脚未配置确认PA0连接三极管基极检查PWM初始化是否成功仿真速度极慢时钟频率设置过高或者波形记录开启把晶振设为8MHz关闭无关的调试图表按键按下没反应内部上拉未启用或消抖参数太短确认GPIO配置为输入上拉模式消抖时间建议10ms这套速查表是我把试验过程中记录的问题收敛出来的覆盖了大家实际提交问题里90%的疑惑。如果你仿真时遇到“error: no stm32 target found”这种弹窗十有八九是Proteus版本里没正确关联ST-Link调试器但仿真情况下根本不需要调试器直接用HEX文件加载即可。5.2 实物调试的三个独家避坑点第一个避坑点在上电顺序。如果你的风扇是12V供电而MCU是5V转3.3V供电务必先给MCU上电再给风扇供电或者同时上电但确认两者共地。如果先给风扇上电电机启动瞬间的大电流可能在共地线上产生地弹干扰3.3V的复位电路导致MCU反复重启。我实际遇到过一次最后是给复位芯片加了100ms的上电延时才解决。第二个避坑点关于PWM频率的选择。我在前面建议用25kHz但实物测试时发现某些风扇在25kHz时反而会出现“呜呜”声这是因为电机内部结构存在谐振点。遇到这种问题不要慌尝试把PWM频率降低到20kHz或者提高到30kHz避开谐振频率段即可。注意最低不要低于18kHz否则人耳就能听到明显的啸叫。第三个避坑点是PID参数放到实物上要重新整定。仿真里的Kp30Ki2Kd1在实物上往往偏大因为真实风扇的转动惯量和温度传感器的延时都比仿真模型大直接用仿真参数会导致转速震荡。我建议从Kp10开始实测观察转速是否稳定再逐次调整通常实物参数比仿真参数小一个量级也是正常的。5.3 代码扩展与移植建议这套项目本身的定位是“多功能基础版”代码和原理图都留出了扩展空间。如果你想基于它做更多文章这里有三个方向可以参考。方向一把DHT11换成SHT30。SHT30是I2C接口的温湿度传感器精度比DHT11高一个数量级替换时只需要改bsp_dht11.c为i2c读取逻辑上层应用不用动。你还能顺便学习一下I2C寄存器读写。方向二增加WIFI远程监控。在串口引脚上接一个ESP8266模块把温度和风扇状态通过MQTT协议上报到本地服务器手机端就能远程查看和控制。这一步需要增加AT指令解析模块工作量大一点但整个系统就从本地控制升级成了物联网节点。方向三把系统移植到HAL库或者FreeRTOS上。标准库版本逻辑完全可读之后用CubeMX重新生成HAL工程移植只要按外设接口对号入座。如果想上RTOS则可以把DHT11读取、按键扫描、显示刷新分别做成三个独立任务优先级的分配刚好对应实时性的不同要求。我个人做了这个项目之后最大的感受是嵌入式入门阶段最怕的就是“被库牵着走”知其然不知其所以然。这套从原理图到代码再到仿真的完整开源项目最大的价值在于给了你一条可以顺着拆解的链路每一个模块的选型、每一段代码的逻辑都能落到具体的硬件时序和控制策略上而不是一个又一个封装好的黑盒。这个项目后续我还会再出一个进阶版加入热风枪加热模拟环境温度、风速检测闭环和上位机曲线显示预计也会一并开源出来。如果大家在复现过程中遇到问题欢迎在评论区贴出截图和现象我看到会第一时间回复。