简介:面向嵌入式初学者和STM32开发者的除湿器项目工程包,覆盖从硬件搭建到软件联调全过程,以STM32为主控实现湿度采集、压缩机/风扇控制、人机交互与故障检测。工程包含DHT11传感器驱动、控制算法、中断服务、错误处理等源码模块,以及硬件设计、软件架构、系统流程图、测试验证方案等设计文档,适合课程设计、毕业设计或产品原型参考。压缩包共295个文件,以C源码(80个c、126个h)和编译中间文件为主,另含Keil工程文件、CubeMX配置、PDF说明文档、链接脚本与调试文件,完整呈现从工程生成到编译烧录的目录结构,整体约7.52MB。该工程可帮助理解STM32G0系列定时器、I2C、UART、ADC等外设的实际用法,掌握传感器数据读取、控制策略和功率器件驱动思路。目前已有485人学习下载,是快速上手完整MCU项目的实用资料。
1. 一块STM32小板子怎样把除湿器从“手办”做成“产品”
梅雨季的墙皮、地下室角落的霉味、衣柜里的潮气,这些痛点不会等你学会PID再去处理。基于STM32的除湿器项目,实际是在回答一个问题:怎样用一颗主流MCU把湿度传感器、压缩机或半导体制冷片、显示面板和按键组织成一个能自动运行、出问题会自己停的小系统。源码与设计文档,就是把这个系统从“能亮灯”推进到“能交付”的那两层东西。它适合正在做STM32毕业设计的人,也适合刚接手小家电固件开发的初级工程师,前者要完整方案,后者要边界和参数。记住一个反直觉结论:除湿器的控制核心不是把湿度压到某个点,而是维持在一个舒适区间,过度除湿浪费电不说,人也干得难受。
2. STM32除湿器的硬件骨架:传感器选型、驱动电路与引脚分配
2.1 湿度传感器:DHT22与SHT30两种接法怎么选
除湿器需要一个湿度传感器作为控制依据,常见方案两端:DHT22(AM2302)和SHT30。DHT22便宜,单总线协议,读一次耗时约20ms,精度±2%RH,足够做区间控制;SHT30走I2C,精度±1.5%RH,有CRC校验,适合对读数稳定性要求更高的环境。对除湿器这个场景,我一般推荐DHT22起步,因为湿度控制本身是慢变量,传感器响应时间并不影响系统实时性。
接线也很直接:
// DHT22 单总线连接 // PA0 —— 传感器DATA引脚,需要外部4.7k上拉到3.3V // PA1 —— 传感器VCC,直接接3.3V或5V(注意DHT22供电范围3.3~5.5V) // GND —— 共地DHT22单总线对时序要求严,读引脚配置为开漏加上拉,代码里操作引脚时要注意切换方向。SHT30则简单得多,两个引脚SCL、SDA接I2C1对应引脚,外加10k上拉即可。选型时还要看MCU引脚余量:SHT30占I2C总线,后续如果要接OLED、EEPROM,可以共用;DHT22占用独立GPIO,反而更省总线资源。
2.2 负载驱动:继电器、双向可控硅与NMOS的取舍
除湿器负载分两类:一类是压缩机型除湿器,功率大,用继电器或可控硅控制压缩机启停;另一类是半导体制冷片型,功率几十瓦,可以用NMOS做PWM调速或开关控制。两者在电路设计上差异明显。
最常见的做法是继电器方案。继电器适合工频交流负载,但继电器本身有机械寿命和吸合弹跳问题,控制引脚需要三极管或ULN2003驱动,不能直接用GPIO灌电流。典型电路如下:
// 继电器驱动电路(以PNP三极管为例) // PB0 —— 限流电阻1k —— PNP基极 // PNP射极 —— 3.3V // PNP集电极 —— 继电器线圈一端 // 线圈另一端 —— GND(低电平驱动时要注意逻辑方向) // 线圈两端并联1N4148二极管,方向:阳极接线圈低端,阴极接3.3V // 继电器触点 —— 串在负载火线上这里最容易踩的坑是续流二极管接反,接反会导致三极管被反峰电压击穿。代码层面,继电器控制逻辑要加一个“最小开关周期”约束,比如每次切换后至少保持3秒,否则频繁启停会缩短压缩机寿命,同时产生振荡。
半导体制冷片方案则用NMOS做低边开关,或者用PWM控制功率,散热风扇和制冷片分开供电更稳妥。
2.3 显示与交互:I2C OLED加独立按键的最小交互方案
一个除湿器至少要能显示当前湿度和目标湿度,最好还有工作状态。OLED 0.96寸(SSD1306)是性价比最高的选择,占两个I2C引脚,显示信息量对除湿器足够。按键方面,独立按键比矩阵更实用,因为除湿器操作面本身就不需要太多按钮。
我一般保留两个按键:模式键(切换自动/手动模式)和设定键(调整目标湿度阈值)。按键要配置内部上拉输入,并通过检测下降沿触发事件:
// 按键扫描逻辑(非阻塞,每10ms调用一次) GPIO_InitStructure.Mode = GPIO_Mode_IPU; // 内部上拉输入 GPIO_InitStructure.Pin = GPIO_Pin_8; // 模式键接PB8 GPIO_InitStructure.Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); // 读取时直接检查电平,消抖后再触发事件 if (Key_Read(GPIOB, GPIO_Pin_8) == KEY_PRESSED && debounce_cnt > 3) { mode = !mode; }GPIO配置时内部上拉有两个作用:减少外部上拉电阻,同时在按键悬空时保证电平稳定。OLED显示刷新放到主循环的时间片里,不要和传感器读取放在同一个中断里,否则刷新显示会干扰单总线时序。
2.4 原理图与设计文档:交付时要画到什么程度
设计文档里必须包含原理图、接线表和BOM表。原理图不要求画到PCB级,但至少要能让人照着接线复现,接线表比原理图更直观:
| 外设 | MCU引脚 | 接口类型 | 电平要求 | 备注 |
|---|---|---|---|---|
| DHT22 DATA | PA0 | GPIO_OD | 3.3V,需上拉 | 传感器电源单独接 |
| OLED SCL | PB6 | I2C1_SCL | 3.3V | 与SHT30共用I2C时用地址区分 |
| OLED SDA | PB7 | I2C1_SDA | 3.3V | 上拉电阻10k |
| 继电器控制 | PB0 | GPIO_PP | 3.3V高低电平 | 经三极管驱动,续流二极管并联 |
| 模式键 | PB8 | GPIO_IPU | 3.3V | 按下为低电平 |
| 设定键 | PB9 | GPIO_IPU | 3.3V | 按下为低电平 |
| 风扇控制 | PA8 | TIM1_CH1 | PWM 20kHz | 低端NMOS驱动 |
接线表能让拿到文档的人在半天内把硬件搭起来,MCU引脚分配要避免把I2C引脚和晶体振荡器引脚相邻,否则高频信号耦合会影响通信稳定性。
3. STM32除湿器控制逻辑:滞回控制、四态状态机与ADC采样
3.1 湿度闭环:为什么用滞回控制而不用PID
除湿器目标湿度设定通常是一个值(比如55%RH),但现实中压缩机和半导体制冷片都只有“开”和“关”两个状态,没有中间功率档位。这种情况下PID很难用,因为执行器是离散的,需要改成PWM的占空比输出才能用PID,会导致压缩机频繁启停,寿命大跌。
滞回控制才是常见做法。设定目标上阈值(自动停机点)和下阈值(自动启动点),上下阈值之间形成一个滞回带:
// 滞回控制核心逻辑(伪代码) #define HUMIDITY_TARGET 55.0f // 目标湿度 #define HYSTERESIS 5.0f // 滞回区间 ±5%RH #define HUMIDITY_START (HUMIDITY_TARGET + HYSTERESIS) // 60%RH 启动 #define HUMIDITY_STOP (HUMIDITY_TARGET - HYSTERESIS) // 50%RH 停止 if (humidity >= HUMIDITY_START && dehumidifier_state == STATE_IDLE) { dehumidifier_state = STATE_RUNNING; } else if (humidity <= HUMIDITY_STOP && dehumidifier_state == STATE_RUNNING) { dehumidifier_state = STATE_IDLE; }启动条件与停止条件分别用了不同阈值,这个滞回区间避免了“湿度在目标值附近抖动导致继电器频繁切换”的问题。区间大小影响控制效果:过大则湿度波动大,过小则继电器启动频繁。我一般设置±5%RH,在密封性一般的房间内实测湿度波动约±7%RH,如果希望更稳,可以在软件里加一阶低通滤波,等效于对读数做平滑。
3.2 状态机:待机、抽湿、化霜、故障
除湿器的运行状态远比“开/关”复杂。压缩机启动后需要延时保护,蒸发器可能结霜需要停机化霜,传感器异常时需要进入故障保护。用状态机管理这些状态,比散落的if判断清晰得多。
typedef enum { STATE_IDLE, // 待机:湿度低于启动阈值或手动关闭 STATE_RUNNING, // 抽湿:压缩机或制冷片满载运行 STATE_DEFROST, // 化霜:蒸发器温度过低,停机化霜 STATE_FAULT // 故障:传感器异常或负载过流,停止运行 } DehumidifierState; DehumidifierState state = STATE_IDLE; uint32_t compressor_on_time = 0; // 压缩机累计运行时间,用于化霜判断状态机的状态转移条件写在单独函数里,每个状态入口执行对应操作,避免在中断里做复杂逻辑。最常见的错误是漏掉“故障恢复”路径:传感器恢复后如何回到待机?我建议状态机在故障状态做3次重试,若连续3次采样均正常才自动恢复。
3.3 ADC采样:用模拟湿度传感器做连续采集
如果选用的是模拟湿度传感器(如HIH-5030、HS1101),需要走ADC通路。这类传感器输出随湿度变化的电压信号,STM32内部12位ADC足够用。ADC采集要注意参考电压的稳定,在传感器电源和参考电压之间加电容滤波。
// ADC1 通道0 采样配置(PA0 复用为ADC_IN0) ADC_InitTypeDef ADC_InitStructure; ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; // 连续转换 ADC_InitStructure.ADC_NumberOfChannel = 1; ADC_InitStructure.ADC_ScanConvMode = DISABLE; ADC_Init(ADC1, &ADC_InitStructure); ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5); ADC_SoftwareStartConvCmd(ADC1, ENABLE); // 采集后换算电压值: ADC值 / 4096 * 3.3V float voltage = (float)adc_value / 4096.0f * 3.3f; // 按传感器说明书标定公式换算湿度,例如 HIH-5030: // RH = (voltage / supply_voltage - 0.16) / 0.0062ADC采样不能用单次读数直接做控制,我习惯连续采8次取平均,再丢最大最小值,这个滑动滤波能有效去除工频干扰。SampleTime_239Cycles5是慢速采样,适合湿度这种缓变信号,采样时间短反而会引入噪声。
3.4 定时时间片:1秒调度、200毫秒按键扫描、100毫秒刷新显示
裸机程序要稳定运行,推荐用SysTick做时基,在中断里置标志位,主循环做轮询。将20ms分成一个时间片,按需调用不同任务。
volatile uint32_t systick_ms = 0; void SysTick_Handler(void) { systick_ms++; } // 在主循环中的非阻塞调度 if (systick_ms - last_sensor_time >= 1000) // 传感器每秒读一次 read_humidity(); if (systick_ms - last_key_time >= 10) // 按键每10ms扫一次 scan_key(); if (systick_ms - last_display_time >= 100) // 显示每100ms刷新一次 update_display();注意所有任务都不要在中断里执行,中断里只做“置标志位”和“计数”。读DHT22的20ms延时如果放在中断里,会直接影响其他中断响应,这是裸机开发最常见的稳定性杀手。
4. 源码组织与设计文档配套:从例程到可交付的STM32项目
4.1 工程目录:驱动层、逻辑层、应用层怎么划分
拿到别人的STM32工程最怕什么?全部源码堆在一个main.c里。真正的STM32除湿器项目,源码至少要按三层划分:驱动层(HAL)、逻辑层(控制策略)、应用层(外部交互)。这个结构让设计文档好写,也更接近工业项目的组织形式。
Dehumidifier/ ├── Core/ │ ├── Inc/ │ └── Src/ main.c、中断处理 ├── Drivers/ │ ├── BSP/ bsp_dht22.c、bsp_oled.c、bsp_key.c、bsp_relay.c │ └── Device/ stm32f1xx_hal_conf.h ├── Middlewares/ │ └── Control/ humid_control.c、state_machine.c、filter.c ├── Docs/ │ ├── 硬件接线表.md │ ├── 控制逻辑说明.md │ └── 测试报告模板.md └── 工程文件(.uvprojx 或其他IDE工程)驱动层每个外设对应一个文件,对外暴露初始化函数和读/写/控制接口。逻辑层不直接操作寄存器,只调用驱动接口,这样换MCU型号时逻辑层代码基本不用动。应用层只管按键事件到逻辑层命令的映射,以及显示内容的拼接。
4.2 除湿器核心框架代码骨架:轮询与状态切换的边界
中间层控制逻辑要能独立测试,不能和硬件耦合太深。下面给一个精简但完整的逻辑骨架:
// humid_control.c 核心控制逻辑(省略硬件操作,只保留策略) #include "humid_control.h" static void state_machine_step(HumidityData_t *data) { switch (current_state) { case STATE_IDLE: if (data->humidity >= CONFIG_START_RH) { transition_to(STATE_RUNNING); relay_on(); } break; case STATE_RUNNING: if (data->humidity <= CONFIG_STOP_RH) { transition_to(STATE_IDLE); relay_off(); } else if (compressor_runtime > DEFROST_INTERVAL) { transition_to(STATE_DEFROST); relay_off(); // 停止制冷,开始化霜 } break; case STATE_DEFROST: if (defrost_timer >= DEFROST_DURATION) { transition_to(STATE_RUNNING); relay_on(); } break; case STATE_FAULT: if (fault_retry < 3) { fault_retry++; transition_to(STATE_IDLE); } break; } }状态转移全部集中在一个函数里,修改控制逻辑时只动这一个文件。transition_to函数里可以插入状态切换时的日志输出或统计计数,对调试和后期产品优化都有帮助。
4.3 设计文档的四个必写模块
设计文档不是给评审看的,是给别人拿去维护的,至少要有四个部分。第一是系统框图,用文字叙述数据流向即可:传感器→MCU→驱动→负载,反馈回来后处理器再做决策。第二是硬件设计说明,包括传感器选型理由、每个引脚的电气规格、电源树(供电从哪里来、稳压到多少伏)。第三是软件架构说明,画状态转移关系图并说明每个状态的进入和退出条件。第四是测试验收标准,写出“湿度60%时启动、50%时停机”这类可验证的指标,项目的完成度才有据可依。
4.4 源码与文档的边界:注释只写意图不写过程
源码注释同样要克制。驱动层注释写硬件约束(“DHT22两次读取间隔不得小于1s”),逻辑层注释写策略意图(“使用滞回避免继电器频繁切换”),不要写“这是个判断语句”这种废话。设计文档则补足源码里无法表达的背景,比如“为什么目标湿度默认设为55%而非45%”,这主要考虑了体感舒适度和能耗平衡,45%以下持续运行可能过度除湿,人会觉得口干舌燥,同时耗电量也大。
5. 把参数调对的三个技巧与两个坑
第一,湿度传感器标定不能只信说明书。DHT22的温度补偿公式是基于25℃标定的,实际环境偏离越大误差越明显。我习惯在同一环境里和标准温湿度计对读,做一个单点偏移校正,在代码里保存一个校准常量:
#define HUMIDITY_CAL_OFFSET -2.0f // 待标定,按实测定 float humidity_real = dht22_humidity + HUMIDITY_CAL_OFFSET;第二,继电器的最小开停周期要写进代码。即便有了滞回控制,偶尔仍会有临界抖动,我一般用软件定时器做锁存:继电器状态切换后30秒内不允许再次切换,这个时长足够避开压缩机的启动电流尖峰,也能保护继电器触点。
第三,化霜逻辑不要只看运行时长,最好结合温度判断。如果除湿器带温度传感器,在蒸发器温度低于3℃、且压缩机持续运行超过45分钟这两个条件同时满足时再进入化霜,比单纯计时更合理。没有温度传感器时,就用运行时长结合环境湿度做粗略推断。
这两个坑最常见。第一是DHT22的读取失败处理不当,单总线通信一旦被中断,读回来的数据全为0xFF,如果直接拿去做控制会把湿度算成100%,导致除湿机永远不停。所以每次读取要校验,连续两次失败就进入故障状态,不要做最后一次值的保持。第二是I2C总线没有上拉或上拉电阻过大,OLED显示花屏或偶尔不响应,排查时先用示波器看SCL/SDA波形,再检查代码里的I2C时钟速度,SSD1306建议把时钟降到400kHz以下,不要直接开最大1MHz。最后,把除湿器的目标湿度默认值固定在55%RH,把用户可调范围限定在40%~70%,这样既能适应多数家庭环境,又避免参数被调得太极端导致实际使用的投诉率上升。
本文还有配套的精品资源,点击获取