多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

基于STM32的智能除湿衣柜控制系统:从硬件选型到状态机实现

基于STM32的智能除湿衣柜控制系统:从硬件选型到状态机实现 1. 项目缘起与整体设计思路南方回南天有多离谱住过一楼或者地下室的朋友应该都懂。墙面冒水珠、衣柜里的衣服摸起来潮乎乎的放半个月就能闻到一股霉味皮衣和真丝衬衫更是重灾区。市面上带除湿功能的衣柜动辄两三千本质上就是加了个半导体除湿模块加个湿度开关溢价高得离谱。我去年帮朋友改造老房子的时候顺手做了这个STM32智能除湿衣柜控制系统整套硬件成本压到一百出头代码、原理图、仿真全部开源前后迭代了三个版本现在稳定跑了小半年。这个项目解决的核心问题很明确在封闭衣柜空间内根据实时温湿度自动启停除湿执行机构同时兼顾防凝露、防过热和低功耗待机。它适合有STM32基础、想找一个完整闭环项目练手的嵌入式学习者也适合想低成本改造家里衣柜的动手派。整套方案基于STM32F103C8T6最小系统板外挂DHT11温湿度传感器、继电器驱动半导体制冷片、OLED本地显示预留了ESP8266接口做数据上报。代码层面用标准库开发Keil5工程直接编译Proteus仿真文件同步提供没有硬件也能先把逻辑跑通。为什么选STM32F103C8T6而不是更便宜的STC89C52这里有个很实际的考量。除湿控制不是简单的“湿度高于阈值就开继电器”它涉及滞回比较、延时保护、传感器滤波、状态机切换这几件事。51单片机跑这些逻辑虽然也能跑但RAM和Flash余量太小后期想加个OLED菜单或者串口协议就很吃力。F103C8T6有64KB Flash、20KB RAM资源充裕而且ADC、定时器、USART外设齐全价格也就十块钱左右性价比在这个场景下是最优解。整体方案的设计思路可以概括为“感知—决策—执行—反馈”四层。感知层用DHT11采集温湿度单总线协议虽然精度一般湿度±5%RH温度±2℃但对于衣柜除湿这种场景完全够用毕竟我们关心的是“潮不潮”而不是“精确到小数点后两位”。决策层在STM32内部实现核心是一个带滞回区间的状态机避免湿度在阈值附近反复跳动导致继电器频繁吸合。执行层通过继电器控制半导体制冷片的通断制冷片冷面凝露、热面散热配合小风扇把干燥空气循环起来。反馈层用0.96寸OLED显示当前温湿度和系统状态同时预留串口输出调试信息。注意半导体制冷片工作时热面温度很高必须配散热片和风扇否则几分钟就能把自己烧了。这一点在原理图里体现为风扇和制冷片并联供电但实际布线时建议风扇单独走一路避免制冷片启动瞬间把风扇电压拉低。方案选型上还有一个容易被忽略的点为什么用继电器而不是MOS管直接驱动制冷片。制冷片额定电流普遍在3A到6A之间MOS管方案需要选低导通电阻的型号并加散热而继电器模块现成、隔离性好、驱动简单虽然寿命有次数限制但配合滞回控制后每天吸合次数不超过二十次用个三五年没问题。成本上继电器模块也就两三块钱比搭MOS驱动电路省事得多。2. 硬件核心细节与原理图拆解2.1 主控最小系统与供电设计原理图的主控部分就是标准的STM32F103C8T6最小系统8MHz晶振提供HSE时钟经过PLL倍频到72MHz作为系统主频32.768kHz晶振留给RTC虽然这个项目暂时没用到但预留出来方便后期加定时记录功能。复位电路用10k上拉加100nF电容BOOT0和BOOT1各接10k下拉电阻保证从Flash启动。SWD接口引出四根线3.3V、GND、SWDIO、SWCLK用ST-Link下载调试比串口下载稳定得多。供电部分是整个硬件里最需要仔细处理的地方。系统有三路电压需求12V给制冷片和风扇5V给继电器模块和OLED部分OLED支持5V3.3V给STM32和DHT11。我的做法是12V适配器输入经过LM2596降压到5V再经过AMS1117-3.3降到3.3V。这里有个坑LM2596的输出电容不能省我第一版为了省空间只放了100uF结果继电器吸合瞬间5V轨跌到4.2VSTM32直接复位。后来换成470uF电解并联100nF陶瓷问题消失。DHT11的供电也要注意。它虽然标称3.3V到5.5V都能工作但3.3V供电时数据线高电平只有3.3V如果STM32也是3.3V供电就没问题。数据线需要接一个4.7k到10k的上拉电阻到3.3V我用的4.7k实测通信稳定。DHT11的采样周期不能低于1秒 datasheet写的是1Hz实际使用中我设成2秒读一次给传感器足够的恢复时间。2.2 传感器与执行机构的接口电路DHT11的接口很简单VCC接3.3VGND接地DATA接PA0并配上拉电阻。但布线时有个细节DATA线尽量远离继电器和制冷片的电源线否则继电器吸合瞬间的电磁干扰可能导致DHT11读取出错。我在PCB上把DHT11的走线放在板子另一侧中间用地线隔离误码率从原来的百分之几降到几乎为零。继电器模块我用的是现成的5V低电平触发模块IN脚接PB0VCC接5VGND共地。模块内部自带光耦隔离和续流二极管所以STM32的IO口直接驱动没问题。但要注意有些继电器模块是高电平触发买的时候看清楚否则上电瞬间继电器就吸合了。如果不确定可以在代码里把初始化电平设成高电平对应低电平触发模块的断开状态上电后再根据逻辑控制。OLED用的是0.96寸I2C接口的SSD1306SCL接PB6SDA接PB7走的是STM32的硬件I2C1。这里我踩过一个坑STM32F103的硬件I2C有已知的锁死问题在某些干扰下会卡在等待ACK的状态。解决办法有两个一是用软件模拟I2C二是加超时检测并在超时后重新初始化I2C外设。我选了后者在代码里加了一个I2C超时复位函数跑了半年没再出现过死机。风扇和制冷片的驱动电路在原理图上就是继电器常开触点串联12V电源。但实际接线时制冷片和风扇建议并联但分别走线因为制冷片启动电流大如果和风扇共用细线风扇转速会明显下降。我用的是0.5平方的线单独给制冷片风扇用0.2平方的线两者在电源端汇合。2.3 原理图绘制与PCB布局要点原理图我用的是立创EDA免费且元件库全DHT11、SSD1306、STM32F103C8T6都有现成符号。绘制时注意几个点电源网络要标注清楚12V、5V、3.3V用不同颜色区分网络标签要统一命名比如DHT11_DATA、OLED_SCL避免自动生成的N$1这种名字后期查线很痛苦每个元件的封装要提前确认特别是DHT11有两种封装一种是四针直插一种是三针模块买之前看清楚。PCB布局我遵循“强弱电分离、模拟数字分离”的原则。12V和继电器部分放在板子左侧STM32和传感器放在右侧中间用地线隔离带隔开。DHT11如果外接的话接口放在板子边缘远离发热元件。晶振尽量靠近STM32的OSC引脚走线短而粗下面不要走其他信号线。SWD接口放在板子边缘方便插拔。实操心得第一版PCB我把继电器放在了DHT11旁边结果每次继电器吸合DHT11就报一次校验错误。后来把继电器挪到板子另一头中间加了地线隔离问题解决。如果你也遇到类似情况先检查传感器和干扰源的物理距离。3. 软件架构与核心代码实现3.1 系统状态机与滞回控制逻辑软件的核心是一个四状态的状态机待机态、除湿态、延时保护态、故障态。待机态下系统每2秒读一次DHT11湿度低于55%RH就保持待机高于60%RH进入除湿态。除湿态下继电器吸合制冷片和风扇工作同时启动一个定时器记录除湿时长。如果湿度降到50%RH以下退出除湿态回到待机如果连续除湿超过30分钟湿度还没降下来进入故障态关闭继电器并在OLED上显示错误码。这里的关键是滞回区间的设计。如果只用单一阈值比如湿度高于60%开、低于60%关那么湿度在60%附近波动时继电器会疯狂吸合断开一天下来几百次继电器寿命急剧缩短。我设的滞回区间是10%RH高于60%才开低于50%才关。这样即使湿度在55%到65%之间波动继电器也不会频繁动作。实测在回南天环境下继电器每天吸合次数在15到20次之间完全在安全范围内。延时保护态是干什么的制冷片停机后不能立刻重启因为热面温度还没降下来立刻重启会导致制冷效率极低甚至损坏。我在代码里设了3分钟的最小停机时间继电器断开后启动一个软件定时器3分钟内即使湿度超标也不允许再次吸合。这个时间是根据制冷片的熱惯性估算的实际用红外测温枪测过停机3分钟后热面温度从60度降到35度左右可以安全重启。故障态的处理逻辑是连续除湿超过30分钟湿度下降不足5%RH判定为“除湿无效”可能是制冷片坏了、风扇停了或者衣柜门没关。这时候关闭继电器OLED显示“ERR”同时串口输出错误信息。用户看到后可以检查硬件。这个逻辑帮我朋友发现过一次风扇被衣服挡住的问题。3.2 DHT11驱动与数据滤波DHT11的驱动代码网上很多但质量参差不齐。我参考了正点原子的例程后自己重写了一版核心是微秒级延时和超时检测。DHT11的单总线协议是主机拉低至少18ms然后拉高20到40us释放总线DHT11响应时拉低80us再拉高80us然后开始传输40位数据。每一位以50us低电平开始高电平持续26到28us表示0持续70us表示1。代码里我用TIM4做微秒延时因为SysTick在中断里调用有风险。具体实现是TIM4预分频到1MHz即每计数一次是1us然后写一个delay_us函数。读取一位数据的逻辑是等待低电平结束然后延时40us再读引脚电平如果是高就是1低就是0。这个40us的采样点很关键太早可能读到上升沿太晚可能读到下一位的开始。uint8_t DHT11_ReadBit(void) { uint8_t retry 0; while(DHT11_PIN_READ() retry 100) { retry; delay_us(1); } retry 0; while(!DHT11_PIN_READ() retry 100) { retry; delay_us(1); } delay_us(40); if(DHT11_PIN_READ()) return 1; else return 0; }数据滤波方面我用了滑动平均加中值滤波的组合。每次读取DHT11得到温湿度后存入一个长度为5的数组然后去掉最大值和最小值剩下三个取平均。这样既能滤掉偶发的野值又不会引入太大延迟。实测下来原始数据偶尔会跳变1到2%RH滤波后曲线平滑很多。注意DHT11上电后需要1秒的稳定时间代码里在初始化后加了1.5秒延时。如果跳过这个延时第一次读取大概率失败。3.3 OLED显示与串口调试信息输出OLED显示我写了一个简单的多级菜单主界面显示当前温湿度、系统状态和除湿累计时长。状态用图标表示待机是一个月亮除湿是一个水滴故障是一个感叹号。图标用取模软件生成存在代码的常量数组里。显示刷新率设成2Hz和DHT11采样率同步避免频繁刷屏导致OLED闪烁。串口部分用USART1波特率115200每2秒输出一行调试信息格式是“Humi:xx.x Temp:xx.x State:x Relay:x”。这个输出在调试时非常有用可以直观看到状态机的切换过程。比如你发现继电器频繁吸合看串口输出就能知道是湿度波动导致的还是逻辑写错了。代码结构上我分了五个文件main.c、dht11.c、oled.c、relay.c、uart.c。main.c里是主循环和状态机dht11.c负责传感器读取和滤波oled.c负责显示relay.c负责继电器控制和延时保护uart.c负责串口输出。这种模块化写法方便后期移植比如你想把DHT11换成SHT30只需要改dht11.c里的读取函数其他文件不用动。4. 仿真验证与实物调试对比4.1 Proteus仿真环境搭建没有硬件的朋友可以用Proteus先把逻辑跑通。仿真工程里我放了STM32F103C8T6、DHT11、SSD1306、继电器和LED模拟制冷片。DHT11在Proteus里没有现成模型我用一个电位器模拟湿度输出通过ADC读取然后在代码里做映射。具体做法是电位器接PA1ADC采样值0到4095映射到湿度0到100%RH。这样转动电位器就能模拟湿度变化观察继电器和OLED的响应。仿真里要注意时钟配置。Proteus的STM32模型默认用的是内部8MHz RC振荡器如果你代码里配置了外部晶振仿真会跑不起来。解决办法是在Proteus的STM32属性里把Crystal Frequency设成8MHz然后在代码里用HSE。我试过用内部RC延时函数会偏慢DHT11通信会失败。OLED在Proteus里用I2C调试器代替可以实时看到写入的显存数据。虽然不是图形化显示但能验证I2C通信是否正常。继电器用LED代替吸合时LED亮。整个仿真跑下来状态机切换、滞回控制、延时保护这些逻辑都能验证唯一不能验证的是DHT11的实际时序因为Proteus的DHT11模型是理想化的。4.2 实物调试中的典型问题与解决实物调试遇到的问题比仿真多得多。第一个问题是DHT11读取失败率高十次里有三次返回错误。排查后发现是延时函数不准TIM4的预分频值算错了。重新计算后延时误差从±5us降到±1us读取成功率提到99%以上。这里提醒一句微秒延时函数一定要用示波器或者逻辑分析仪校准光靠眼睛看代码是看不出来的。第二个问题是继电器吸合导致OLED花屏。原因是继电器线圈断电时产生反向电动势通过电源线耦合到了OLED。解决办法是在继电器线圈两端并联一个续流二极管1N4148同时在OLED的VCC和GND之间加一个100uF电解电容。改完之后花屏再没出现过。第三个问题是制冷片效率低。实测除湿半小时衣柜湿度只降了3%RH。排查发现是风扇方向装反了把冷面的冷气吹到了外面热面的热气留在了衣柜里。把风扇反过来装让风先经过冷面再吹向衣柜内部效率立刻提升半小时降了12%RH。这个坑很隐蔽因为风扇转起来看起来都差不多但方向错了效果天差地别。问题现象可能原因排查方法解决方案DHT11读取失败延时不准、上拉电阻缺失示波器看时序校准延时、加4.7k上拉OLED花屏继电器干扰、电源纹波示波器看5V轨加续流二极管、加滤波电容除湿效率低风扇方向反、衣柜不密封手感测风温调整风扇方向、密封衣柜继电器频繁吸合滞回区间太小看串口日志加大滞回区间到10%RHSTM32复位电源跌落示波器看3.3V轨加大滤波电容、单独供电4.3 代码诊断与OTA升级的预留设计虽然这个项目目前没上OTA但我在代码里预留了Bootloader接口。具体做法是把Flash分成两部分0x08000000到0x08003FFF给Bootloader0x08004000往后给应用程序。Bootloader里实现一个简单的串口协议收到特定命令后跳转到应用程序区。这样后期想加OTA功能只需要写一个上位机通过串口发送bin文件Bootloader负责写入Flash并跳转。代码诊断方面我在每个模块里加了错误计数和状态标志。比如DHT11连续读取失败5次置位一个错误标志OLED显示“SENSOR ERR”。继电器驱动电路如果检测到反馈信号异常预留了一个IO口做继电器状态回读也会置位错误标志。这些标志可以通过串口查询方便远程诊断。虽然现在没做远程但接口留好了后期加个ESP8266就能上报。实操心得Bootloader的跳转地址一定要和应用程序的链接地址一致。我第一版Bootloader跳转到0x08004000但应用程序的Keil工程里IROM1起始地址还是0x08000000结果跳过去就HardFault。改完链接地址后正常。这个坑很典型做OTA的时候一定要注意。5. 常见问题排查与避坑经验实录5.1 传感器与执行机构的典型故障DHT11最常见的问题是上电后第一次读取失败。这不是代码bug而是传感器内部需要稳定时间。我的做法是在初始化后延时1.5秒再读并且第一次读取的结果直接丢弃从第二次开始才纳入滤波。另外DHT11的响应速度慢如果衣柜门频繁开关湿度变化快DHT11可能跟不上。这种场景建议换SHT30I2C接口响应快很多价格也就贵几块钱。继电器的问题是触点粘连。虽然继电器标称寿命10万次但那是理想条件下的。如果控制的是制冷片这种感性负载触点断开时会产生电弧加速氧化。我的做法是在继电器触点两端并联一个RC吸收电路100欧姆串联0.1uF实测触点温度明显降低。另外继电器的驱动电流也要注意有些模块标称5V驱动实际吸合电流要70mA以上如果STM32的IO口直接驱动最大20mA根本带不动。必须用三极管或者现成的驱动模块。制冷片的问题是冷面结冰。如果湿度很高且温度很低冷面可能结冰冰层反而阻碍热交换。我的代码里加了温度判断如果温度低于5℃即使湿度超标也不开除湿避免结冰。这个逻辑在北方冬天特别有用南方回南天一般不会触发。5.2 电源与干扰问题的排查思路电源问题占了我调试时间的一半以上。最典型的是继电器吸合导致STM32复位。排查方法是用示波器看3.3V轨在继电器吸合瞬间有没有跌落。如果有说明电源功率不够或者滤波不足。解决办法是加大5V和3.3V的滤波电容或者给继电器单独供电。我最后用的是12V转5V的LM2596给继电器12V转3.3V的AMS1117给STM32两路分开问题彻底解决。干扰问题的排查比较麻烦。我的经验是先怀疑电源再怀疑地线最后怀疑信号线。电源问题看纹波地线问题看地环路信号线问题看走线。DHT11的DATA线如果和继电器控制线平行走线超过5厘米误码率会明显上升。解决办法是垂直交叉走线或者中间加地线隔离。PCB布局时把传感器接口放在板子边缘远离继电器和电源部分。还有一个隐蔽的问题是晶振不起振。STM32的HSE晶振如果负载电容不匹配可能起振很慢甚至不起振。我的做法是负载电容用20pF晶振规格书推荐值并在PCB上把晶振尽量靠近芯片走线短而粗。如果还是不起振可以尝试降低驱动能力或者换晶振。实测某宝上买的便宜晶振起振时间要几秒换品牌晶振后瞬间起振。5.3 代码调试与工具链的踩坑记录Keil5的安装有个坑Keil5和Keil4的器件包不兼容。如果你之前装过Keil4再装Keil5器件包可能会冲突。解决办法是卸载Keil4清理注册表再装Keil5。另外STM32F1的器件包要单独下载装完Keil5后默认只有ARM的包需要去官网下载STM32F1xx_DFP。ST-Link的驱动问题也很常见。STM32无法识别USB设备多半是ST-Link的驱动没装好。解决办法是去官网下载最新的ST-Link Utility安装时会自动装驱动。如果还是不行换一根USB线试试有些线只有充电功能没有数据功能。另外ST-Link的固件也要更新老固件可能不支持新的芯片。代码提示方面VSCode写C没有代码提示需要装C/C插件并配置includePath。我的做法是用Keil写代码用VSCode看代码和搜索。Keil的代码提示虽然弱但编译调试方便VSCode的代码提示强但配置编译环境麻烦。两者结合用效率最高。工具用途常见问题解决Keil5编译调试器件包缺失下载STM32F1xx_DFPST-Link Utility下载程序驱动未装官网下载最新版VSCode代码阅读无提示装C/C插件配includePath立创EDA原理图PCB封装错误买元件前确认封装Proteus仿真时钟配置错误设Crystal为8MHz6. 项目扩展与个人实操体会这个项目的基础功能已经稳定但可扩展的方向很多。最直接的是加ESP8266做数据上报把温湿度数据传到手机或者云端实现远程监控。我在代码里预留了USART2给ESP8266用AT指令连接WiFi每5分钟上报一次数据。这样即使不在家也能知道衣柜有没有受潮。另一个方向是加人体感应用HC-SR501检测衣柜门开关开门时暂停除湿关门后恢复避免冷气外泄。还有一个有意思的扩展是用A*算法做除湿策略优化。听起来有点杀鸡用牛刀但如果你有多个衣柜或者多个除湿点可以用A*算法规划最优的除湿顺序比如先除湿湿度最高的柜子再除湿次高的整体能耗最低。这个思路来自我做智能家居项目时的经验单点控制用阈值就够了多点控制就需要算法了。我个人在实际操作中的体会是硬件项目最花时间的不是写代码而是调试硬件。代码逻辑半天就能写完但硬件问题可能调一个星期。所以建议新手先用Proteus仿真把逻辑跑通再打板做实物。打板的时候一定要留测试点比如3.3V、5V、GND、DHT11_DATA、RELAY_IN方便用示波器和万用表测量。另外元件不要一次性全焊上去先焊电源部分测好电压再焊主控最后焊外设这样出问题容易定位。最后分享一个小技巧DHT11的读取间隔不要低于2秒。我试过1秒读一次连续跑一天后传感器发热湿度读数偏高3%到5%。改成2秒后传感器温度降下来读数恢复正常。如果你需要更快的响应建议换SHT30或者HTU21D这些数字传感器自带加热补偿可以高频读取。这个项目后续还可以这样扩展把除湿逻辑做成可配置的通过OLED菜单调整阈值和延时时间不用重新烧录程序。或者加一个SD卡模块把温湿度数据记录到CSV文件方便后期分析衣柜的湿度变化规律。再或者用蓝牙模块做近场配置手机APP调参数。这些扩展都不难核心的状态机不用动只需要加外设驱动和菜单逻辑。
返回列表