
每次有朋友问我有没有适合练手又实用的STM32项目我第一个想到的都是智能除湿衣柜。这不是那种纯炫技的“点灯”工程它是一个真正能落地、能送人、能改善生活的小系统单片机采集柜内温湿度自动控制风扇、加热片或者半导体制冷片把柜内湿度压在一个安全范围内让衣服不发霉、不返潮。更关键的是这套系统包含了完整的代码、原理图和仿真文件从零开始学STM32开发或者做课程设计、毕业设计的同学照着这套资料可以走通“硬件设计→软件编码→仿真验证→实物调试”的全流程痛点很明确收获也很完整。我把整个项目从设计思路到具体实现全部拆开讲一遍包含我实际调试中踩过的坑和总结的参数选择方法。如果你正准备复刻这个项目或者想把它改造成南方的烘干鞋柜、防潮储物柜这篇文章可以直接当参考方案用。1. 项目整体设计与思路拆解1.1 这个系统到底在解决什么问题衣柜潮湿不是“感觉上不舒服”那么简单。当相对湿度长期超过70%RH衣物纤维会吸潮发黏毛料和皮具容易滋生霉菌深色衣服还会长出难看的霉斑。传统的做法是往柜子里塞除湿袋、放樟脑丸这些属于被动除湿吸收速度慢饱和后还会回吐水分。主动除湿的思路是实时测量柜内温湿度一旦湿度超标就启动除湿装置强制循环空气或加热降湿直到湿度回到安全区间再停机。所以这套STM32控制系统本质上是一个闭环反馈系统传感器负责“感知”——DHT11或DHT22采集温湿度单片机负责“决策”——比较当前湿度与设定阈值决定执行器要不要工作执行器负责“动作”——带动风扇、加热片或者半导体制冷片改变柜内环境人机交互负责“设置”——用按键调整湿度阈值用显示屏实时查看状态。对用户来说体验就是“把衣服放进去就不用管了”。1.2 为什么选STM32F103C8T6作为主控这个项目很多教程会用51单片机但我个人强烈推荐STM32F103C8T6原因有几个。第一是性能冗余充裕。51单片机跑DHT11的时序、驱动LCD1602或者OLED、再处理按键中断已经接近极限尤其做PID控制或者加定时任务时会比较吃力。而STM32F103C8T6是一颗Cortex-M3内核、最高72MHz主频的芯片跑这些任务非常轻松留出大量资源给后续扩展比如加ESP8266联网、加多个传感器。第二是生态成熟。STM32F103系列是入门领域的“万金油”无论是寄存器版还是标准外设库、HAL库网上资料数以万计。遇到问题搜一下基本都有现成答案。现在的开发板也便宜集成下载器的核心板几十块钱就能拿下。第三是外设接口丰富。这个项目需要的GPIO输入输出、定时器中断、I2C驱动OLED或USART调试打印F103C8T6全部原生支持不用外部扩展芯片原理图可以做得非常简洁。1.3 传感器选型DHT11还是DHT22/SHT30最关键的传感器选型我做过对比实验。DHT11是入门标配价格便宜、接线简单单总线协议三根线VCC、GND、DATA就能通信。但它有两个明显的短板湿度精度只有±5%RH温度精度±2℃而且采样周期必须大于1秒否则读出来的数据会漂移。DHT22精度提升到了±2%RH和±0.5℃价格大概贵三倍但测量周期同样不短。SHT30是I2C接口精度更高、响应更快但需要写I2C驱动调试门槛相对高一点。我的建议是如果目标是快速复现项目、理解控制逻辑DHT11完全够用如果想让数据更可靠、效果更接近商业产品直接换SHT30硬件上只需改接线和驱动函数。这里我把DHT11的驱动方式作为主要讲解对象因为它在Proteus仿真中也有现成模型方便跑通流程。1.4 控制策略用滞回控制别用简单阈值很多人第一次做湿度控制代码写成“如果湿度大于70%就开机小于70%就关机。”这在风扇这类执行器上会有一个致命问题执行器会围绕设定值来回切换继电器频繁吸合不仅噪音大还会缩短继电器寿命。正确做法是滞回控制。通俗讲就是“开机阈值”和“停机阈值”分开设置比如当湿度上升到75%RH开启除湿当湿度下降到65%RH停止除湿湿度在65%到75%之间时维持上一个状态。这样执行器不会在临界点来回抖动整个系统的使用寿命和体验都更好。滞回区间的大小要结合实际情况调整太小抖动太大湿度波动范围广。我的经验是5%RH到10%RH比较合适。控制策略上还可以加一个“强制开启”的手动模式方便用户在潮湿天气时直接让设备连续工作。这部分逻辑在软件架构中会体现为两种工作模式自动模式和手动模式。2. 硬件原理图设计与元器件选择2.1 STM32最小系统设计要点一个可运行的STM32F103C8T6最小系统需要四部分电源、晶振、复位电路、启动模式配置。电源部分需要特别注意滤波。VDD引脚并接100nF陶瓷电容位置尽量靠近芯片引脚器件手册上称为“去耦电容”作用是滤除高频噪声。如果是用USB的5V供电板上一定要加一颗AMS1117-3.3稳压芯片把5V降到3.3V给单片机供电。不要直接给3.3V引脚接5V会烧芯片这是新手最容易犯的错误。晶振是另一个容易被忽视的坑。STM32F103系列内部有HSI高速内部时钟频率8MHz精度对于串口通信来说勉强能用。但如果想用USART做调试输出强烈建议外部加8MHz晶振配合两个20pF负载电容这样波特率误差才足够小。注意负载电容不是随便选的要看晶振的CL值常规8MHz晶振配20pF没有大问题。复位电路就一颗10kΩ上拉电阻加一颗100nF电容到地连接NRST引脚。BOOT0和BOOT1各接10kΩ下拉电阻让芯片从Flash正常启动。逻辑上就是BOOT00、BOOT10如果BOOT0拉高会进入串口下载模式不影响正常使用。2.2 传感器接口电路设计DHT11传感器有三个或者四个引脚四脚版本中有一个脚是悬空的只需要接VCC、DATA、GND三根线。数据线要加上拉电阻典型值4.7kΩ因为DHT11的数据通信是开漏输出结构如果没有上拉电阻信号线无法可靠拉高常见故障是读出来的湿度一直是0%或者255。在实际画原理图时我用了一个4pin的接线端子把传感器的VCC、DATA、NC、GND引出来方便插拔。传感器VCC可以直接接3.3VDHT11的供电范围是3.3V到5.5V即使你后续把传感器换成长线的DHT223.3V也能稳定工作。如果选择DHT22接线完全一致只是精度不同驱动时序略有区别。如果选择SHT30数据线变成SCL和SDA两根需要另外接I2C上拉电阻这个在原理图上要提前预留。2.3 执行器驱动方式继电器、可控硅还是MOS管除湿执行器通常有三种直流风扇、加热片/半导体制冷片、交流排风扇。不同执行器对应的驱动电路是不同的。如果只驱动直流风扇比如12V涡轮风扇最简单的方式是拿一个NPN三极管或者逻辑电平MOS管做开关。这里需要看负载电流一个5V的小风扇电流通常在200mA以下常见的S8050三极管可以驱动但12V、两三瓦的大风扇建议用AO3400这类N-MOS管导通电阻低发热小。注意栅极要接一个10kΩ下拉电阻到地防止单片机复位瞬间引脚悬空导致误开启。如果是驱动220V交流风扇或者加热棒出于安全考虑建议用5V继电器隔离控制。继电器线圈需要配续流二极管型号通常选1N4007或者1N4148这个二极管很关键没有它继电器断电瞬间产生的反向电动势会击穿驱动三极管或单片机引脚。继电器驱动端还要串联一个限流电阻例如1kΩ到2kΩ。市面上也有固态继电器SSR寿命更长没有机械触点噪音但成本高、需要散热家用衣柜场景性价比不高。我实验后认为常规的5V继电器加续流二极管方案最稳妥、也最适合教学演示。2.4 显示与按键电路OLED和LCD1602怎么选显示模块我建议直接用0.96寸I2C接口的OLEDSSD1306驱动芯片蓝色或白色字体显示效果比LCD1602好太多接线只有VCC、GND、SCL、SDA四根占用单片机两个IO口。对比度不需要调也不会出现LCD1602“屏幕没字然后要调电位器”这种麻烦事。LCD1602的好处是足够传统很多教材里用得多但需要8个或6个数据引脚还要用可调电阻调对比度布线复杂不少。仿真项目中Proteus自带的LCD1602模型比较成熟用LCD1602演示也能接受但实物建议直接上OLED。按键部分至少需要两个一个是“模式切换”自动/手动一个是“湿度阈值调整”。按键电路有讲究不能直接一根线接GPIO要在按键两端加10kΩ上拉电阻这样按下时引脚读到低电平空闲时读到高电平。由于内部可以启用上拉为了省元件我可以直接用内部上拉但新手自己做板子外部上拉更直观排查问题也容易。另外为减少调试时的排查难度我还设计了一路LED指示灯接在PC13引脚除湿运行时亮起。STM32F103C8T6板上通常已经有一颗板载LED可以直接用原型阶段省一个元件。2.5 原理图布局与检查经验画原理图我习惯用嘉立创EDA专业版免费、自带元件库齐全、生成PCB方便。很多人刚画原理图时容易图省事把连线画得乱七八糟这里我提三个实际操作中有用的习惯给每个网络命名。电源网络命名为VCC_5V、VCC_3V3、GND不要全都用默认的NetLabel。命名后导出的PCB更容易检查接线错误。分块布局。最小系统放左上角传感器模块放下方执行器驱动放右侧电源部分放右上角。每块之间留出空隙后续你检查时一目了然。原理图绘制完成后用“设计规则检查”跑一遍ERC电气规则检查能查出悬空引脚、短路网络等低级错误。这个步骤不能省。3. 软件工程架构与核心代码实现3.1 软件代码架构轮询加定时器不跑RTOS这个项目不需要上RTOS实时操作系统用裸机编程实现足够。我采用的是“主循环定时器中断标志位”的经典架构Systick定时器每1ms产生一次中断在中断服务函数中对时间计数器累加主循环里检查时间标志每500ms读取一次DHT11每200ms刷新一次OLED显示每次读取完传感器后执行一次滞回控制判断。这样做的好处是逻辑清晰、实时性足够代码量小适合学习。不适合的做法是在主循环里使用delay()死等因为DHT11的读取时序本身是微妙级别的如果用阻塞式延时整个控制流程会被拖住。工程目录建议分成四个文件main.c主函数、dht11.c/h传感器驱动、oled.c/h显示驱动、control.c/h控制逻辑。分文件的好处后面扩展功能时不用把几百行代码堆在一个文件里翻来翻去。3.2 DHT11驱动时序代码关键细节DHT11的单总线时序是这个项目里最容易出bug的地方。网上能搜到很多版本但有很多是抄来抄去没实测的。我讲一下我认为最可靠的方式。读取一次DHT11的数据分为三步第一步主机发送起始信号。主机把数据线拉低至少18ms然后释放并拉高20到40us等待传感器响应。 第二步传感器响应。DHT11会将数据线拉低80us左右再拉高80us表示“准备好了我要发送数据了”。 第三步逐位读取40位数据。每位数据以50us低电平开始随后是高电平高电平持续26到28us表示逻辑0持续70us左右表示逻辑1。注意判断0和1的方法这里不能简单拿延时函数死等。我的驱动代码用“等待引脚变化计时”的方式实现uint8_t DHT11_ReadBit(void) { uint8_t i 0; while (DHT11_DATA_IN() 0); // 等待低电平结束 DHT11_Delay_us(40); // 延时40us后采样 if (DHT11_DATA_IN() 0) { return 0; } else { while (DHT11_DATA_IN() 1); return 1; } }这段代码的原理是如果高电平持续不到40us那采样时数据线已经是低电平了说明这一位是0如果持续时间超过了40us说明这一位是1。40us的判定点刚好在0和1的高电平宽度之间实测非常稳定。读取完整40位数据后数据的排列是8位湿度整数、8位湿度小数、8位温度整数、8位温度小数、8位校验和。校验方法是把前四个字节相加取低8位与校验字节比较相等则数据有效。这一步不能省因为DHT11在恶劣环境下偶尔会传错数据有校验能避免因为一次错误数据导致继电器误动作。3.3 滞回控制与阈值存储实现控制逻辑的代码不长但设计时需要考虑“上一次状态记忆”的问题。我用一个静态变量last_state保存执行器的状态判断流程如下#define HUMIDITY_ON 75 // 开启阈值单位%RH #define HUMIDITY_OFF 65 // 关闭阈值单位%RH static uint8_t heater_state 0; void Control_Update(uint8_t humidity) { if (mode MODE_AUTO) { if (humidity HUMIDITY_ON) { heater_state 1; } else if (humidity HUMIDITY_OFF) { heater_state 0; } // 如果湿度在65~75之间保持原状态不变 } else if (mode MODE_MANUAL) { // 手动模式下直接用按键控制 } HAL_GPIO_WritePin(FAN_GPIO_Port, FAN_Pin, heater_state ? GPIO_PIN_SET : GPIO_PIN_RESET); }这里有几个容易踩的点第一阈值不要写成“魔法数字”散落在代码各处。用宏定义或者const变量统一管理方便修改。我甚至建议把阈值做成可掉电保存的配置项使用STM32内部的Flash模拟EEPROM保存用户设定值这样断电后不需要重新设置。第二两个阈值不能写反。如果写成了“大于65就开小于75就关”那湿度超过75时永远无法关闭执行器会一直工作。检查这种逻辑错误最简单的方法是在调试模式下把阈值打印出来看。第三执行器状态切换时最好加一个延时去抖。刚刚设置的阈值比如从75改成74时传感器可能正在临界点附近波动虽然滞回已经避免了大部分抖动但还是建议在状态切换后加一个10秒的“最小运行时间”防止柜门被频繁打开时传感器数据剧烈波动导致执行器频繁启停。3.4 OLED显示与按键处理OLED驱动用SSD1306的I2C模式网上有现成的库但我不建议直接整个工程复制进去而是只保留需要的函数初始化、清屏、显示字符串、显示数字。显示内容规划成三行Hum: 72%RH Temp: 25C Mode: AUTO刷新频率200ms一次人眼看起来是持续更新的。这里有个细节不要每次刷新都调用全屏清屏会造成闪烁。我的做法是只在数值变化时才局部更新对应的字符区域或者用“显存模式”整个屏幕比较小一次写完显存再整体刷新效果也好。按键处理采用“扫描消抖”的方式。在定时器中断中每10ms扫描一次按键状态连续两次读到电平一致才认为按键有效。长按和短按的逻辑可以在这层实现比如短按切换模式长按进入阈值调整模式。硬件上按键接上拉按下为低电平所以判断条件是if (HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) GPIO_PIN_RESET)。3.5 完整的工程代码结构最后上一个项目文件的组织参考├── Core/ │ ├── Inc/ │ └── Src/ ├── Drivers/ │ ├── BSP/ │ │ ├── dht11.c/h │ │ ├── oled.c/h │ │ └── key.c/h │ ├── App/ │ │ ├── control.c/h │ │ └── main_app.c/h ├── MDK-ARM/ └── 说明文档.md不推荐把全部代码塞在main.c里。虽然功能少的时候也能跑但后续你加联网模块、加历史数据记录时会非常痛苦。建议一开始就习惯分模块开发。4. Proteus仿真验证方法4.1 仿真模型搭建与元件清单Proteus 8 Professional是学习单片机常用的仿真工具。搭建这个工程的仿真图需要的元件清单如下STM32F103C8Proteus库里直接搜STM32F103C8DHT11Proteus库里有DHT11模型可以直接调用LCD1602或者LM016L如果驱动的是LCD电阻若干上拉电阻、限流电阻LED指示灯按键电源端子Proteus里的STM32模型和真实芯片行为不完全一样最典型的问题是如果你用HAL库生成的代码在Proteus里跑不起来。因为Proteus对HAL库底层的时钟配置支持不够好。如果你坚持用Proteus验证建议工程基于标准外设库或者寄存器编写或者直接在CubeMX里配置好时钟后生成标准库代码。我实测下来标准库代码在Proteus里运行成功率高得多。4.2 仿真调试技巧虚拟终端与数据观察在仿真阶段最有价值的调试工具是Proteus的Virtual Terminal虚拟终端。把STM32的USART1引脚接到虚拟终端上程序里用printf把温湿度和控制状态实时打印出来你就能直观地看到控制逻辑是否按照预期运行。DHT11模型在Proteus里是模拟信号源它的输出是固定变化的。如果你需要模拟湿度升高的过程可以在DHT11模型属性里调整它的参数或者在仿真运行过程中手动改变模型参数。不过更快的验证方式是做“开环测试”用一组预先写好的湿度数组比如从55逐步递增到80喂给控制函数观察执行器状态是否在75时开启、在65时关闭。这能验证核心控制逻辑不依赖传感器。4.3 仿真与实物的差异注意事项一定要记住仿真通过不代表实物就能直接跑。仿真最大的局限是信号都是理想的DHT11的时序在Proteus里基本不会出问题但实物中走线电容、传感器供电质量、GPIO拉电流能力都会影响时序精度。其次Proteus里的继电器、风扇模型是简化的不体现线圈反电动势、触点电弧这些真实情况。你在仿真里不加续流二极管没问题实物不加可能直接炸三极管。所以我的建议是把仿真当成“逻辑验证工具”而不是“电路可靠性验证工具”。真正要装机还是要焊一块板子实测。5. 常见问题排查与避坑实录5.1 常见问题速查表我在调试这个项目时碰到过不少问题也帮朋友排查过各种奇葩现象整理了一张表格几乎覆盖最容易翻车的位置问题现象可能原因排查与解决方法DHT11读取湿度一直是0%或255上拉电阻缺失、接线错误、GPIO配置为开漏且没使能内部上拉检查原理图有没有4.7kΩ上拉检查数据线接的GPIO引脚号是否与代码一致DHT11数据读取不稳定偶发乱码读取频率过高、延时时序不准、电源纹波大每两次读取间隔至少1.5秒用逻辑分析仪抓时序确认每一位宽度继电器频繁吸合断开未做滞回控制或滞回区间过小参考滞回算法把开启和关闭阈值拉开至少5%RHSTM32核心板不启动BOOT0被拉高、供电不足、晶振虚焊确认BOOT0接GND万用表量3.3V电压示波器或万用表确认晶振引脚有波形OLED不显示或显示花屏I2C地址错误、连线接反、复位时序不满足SSD1306默认地址是0x78或0x7A检查驱动里的地址宏按手册正确上电复位风扇/加热片不工作驱动管基极/栅极电阻选错、MOS管Vgs不足、继电器线圈没供电用万用表测控制引脚电压确认驱动电路电源与负载电源共地Proteus仿真程序跑不起来使用了HAL库、时钟配置与仿真模型不匹配改用标准库或寄存器版本在CubeMX中把HCLK设为72MHz后再生成代码柜内湿度反而升高除湿原理选错加热加湿而不是除湿半导体制冷片会产生冷凝水要设计接水盘加热片在密闭空间可能先升温后升温降湿需要配合通风5.2 独家经验传感器放在哪个位置最有代表性这是一个容易被忽略的细节。很多人把DHT11直接粘在柜门内侧或者角落读取到的湿度分布严重不均衡导致控制逻辑误判。我实测下来的建议是把传感器放在衣柜中部偏上的位置因为湿热空气向上走上部湿度最先变化能最快反映柜内真实湿度。同时传感器不要靠近除湿风扇的出风口否则风扇一开局部湿度瞬间降低单片机会误以为除湿已经完成而停机造成“机器在努力工作但柜内水分没排出去”的假象。还有一点衣柜除湿如果用的是半导体制冷片冷凝器旁边会有水珠传感器应该远离冷凝器安装避免水珠滴到传感器上导致短路或读数失真。5.3 继电器与驱动电路的实际教训关于继电器驱动我说一个真实的教训。第一次画板子时我没加续流二极管继电器是5V的驱动管用的是S8050驱动引脚是STM32的PB12。功能测试时一切正常大约反复吸合几十次之后S8050烧了STM32的PB12引脚也坏掉了。原因就是继电器的感性负载在断电瞬间产生了反向电动势电压尖峰轻松超过几十伏直接击穿了三极管。后来我在继电器线圈两端并联一个1N4148二极管阴极接正极、阳极接负极这个问题彻底消失。打那以后凡是驱动继电器或者电机我都养成了先画续流二极管的习惯。另外继电器的线圈额定电压和触点额定电压是两码事。5V继电器是指线圈是5V驱动的但触点可以承受250VAC或30VDC选型时一定要看清楚负载电流是否在触点容量范围内。比如驱动220V交流风扇选10A触点容量的继电器才够稳健。5.4 半导体制冷还是加热片哪种除湿方案效果好这个问题我对比过很久。半导体制冷片的原理是帕尔贴效应一面制冷一面发热空气中的水汽在冷端凝结成水滴从而实现除湿。它的优点是除湿效果明显尤其环境温度在20℃以上时冷凝速度很快但缺点是功耗高、需要设计冷凝水收集否则柜子里面会积水。加热片比如PTC加热片的除湿原理是升温降湿。空气温度升高后相对湿度会下降同时空气流动性增强湿气会被挤出柜外。优点是成本低、结构简单缺点是在密闭空间里只是“暂时把湿度藏起来”一旦温度降下来湿度还会回升必须结合通风孔使用。如果你在家里自己用我更推荐“风扇通风孔”的物理除湿方式把柜内湿空气吹出去配合干燥剂辅助吸收这是成本最低、也最不容易翻车的方案。把控制逻辑稍加改动把执行器从加热片换成风扇系统整体框架不需要动效果就非常明显。6. 项目扩展方向与软硬件升级建议6.1 加装Wi-Fi模块实现远程监控做完了基础版本后如果你觉得它不够“智能”下一步我建议加一个ESP8266模块。它通过串口和STM32通信STM32把温湿度数据和控制状态发给ESP8266ESP8266再通过Wi-Fi把数据推送到手机APP实现远程查看。这样衣柜里哪个时段湿度最高、除湿器运行了多长时间都能在手机上看到历史曲线。这个扩展不复杂STM32的USART2和ESP8266连接代码里用AT指令初始化和配置TCP连接即可。如果想再进一步可以用ESP32直接替代STM32但学习曲线会更陡不如先做串口通信。6.2 增加加热除湿和紫外杀菌模块南方回南天的时候除湿可能还不够衣服会有霉味单纯靠风扇很难解决。这时候可以加一组紫外杀菌灯用另一个继电器独立控制当湿度连续超过阈值或者设定时间到点时紫外灯自动开启杀菌。注意紫外线对人有伤害要加门磁开关做保护衣柜门打开时强制断电这是安全底线。6.3 用内部Flash保存用户配置这个功能是后期优化最有性价比的一项。STM32F103C8T6有64KB Flash用户配置区可以放在最后一页每次写入前先擦除整页。配合“断电保存”功能用户调整过的湿度阈值、手动模式状态下次开机时自动恢复不用重新设置体验会好很多。我建议把配置数据结构定义成一个结构体包含启动标志、湿度阈值、模式等写入和读取都基于这个结构体处理避免零散地址管理带来折腾。6.4 从课程设计到量产需要考虑的优化如果你的目标是把这个项目当作毕业设计展示或者更进一步做成商品原型有四个方向值得优化低功耗设计。用STM32的待机模式湿度没有超标时进入低功耗每10分钟醒来采样一次可以大幅延长电池供电方案的续航。PCB合理布局。把继电器放在板子边缘远离传感器和单片机避免继电器吸合瞬间的电磁干扰影响DHT11读数。传感器冗余。在关键位置放两路传感器程序通过对比判断谁的数据更合理防止单点传感器故障导致整个系统失效。安全认证。如果控制的是220V执行器板卡设计时要注意爬电距离和绝缘间距建议完全隔离强弱电。写在最后做这个项目的最大心得是嵌入式开发不是把代码写完就结束硬件、软件、环境三者要反复配合调优才会有好的体验。如果你之前只写过一些简单的单片机流水灯这套智能除湿衣柜控制系统会很适合作为你的第二个或者第三个完整项目。它不复杂但麻雀虽小、五脏俱全有传感器采集有逻辑控制有执行器驱动有人机交互有仿真验证。复刻的时候我建议你按“先仿真、后硬件、再调试”的顺序来。先把工程编译下载到开发板用一个USB转串口模块把DHT11的数据打印到电脑上确认传感器读数是稳定的然后再接继电器和风扇最后再整个装到衣柜里。每一步都走稳其实这个项目一两天就能跑通。希望这套开源资料和这篇拆解笔记能帮你少走一些我走过的弯路。