ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

STM32除湿器实战:从传感器选型到滞回控制与状态机设计

STM32除湿器实战:从传感器选型到滞回控制与状态机设计 简介面向嵌入式初学者和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两种接法怎么选除湿器需要一个湿度传感器作为控制依据常见方案两端DHT22AM2302和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 DATAPA0GPIO_OD3.3V需上拉传感器电源单独接OLED SCLPB6I2C1_SCL3.3V与SHT30共用I2C时用地址区分OLED SDAPB7I2C1_SDA3.3V上拉电阻10k继电器控制PB0GPIO_PP3.3V高低电平经三极管驱动续流二极管并联模式键PB8GPIO_IPU3.3V按下为低电平设定键PB9GPIO_IPU3.3V按下为低电平风扇控制PA8TIM1_CH1PWM 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%这样既能适应多数家庭环境又避免参数被调得太极端导致实际使用的投诉率上升。本文还有配套的精品资源点击获取
返回列表