ARTICLE DETAIL

资讯详情

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

STM32智能晾衣架控制系统设计:从传感器采集到电机保护

STM32智能晾衣架控制系统设计:从传感器采集到电机保护 很多人在做智能家居小项目时都会把“智能晾衣架”当作首选练手课题。它既有传感器采集又有电机控制还涉及手动/自动模式切换和异常保护麻雀虽小但把嵌入式开发里最常见的几个模块都串起来了。本文就以 STM32F103 为核心从需求拆分、硬件选型、CubeMX 配置到核心代码完整梳理一套开源智能晾衣架控制系统的设计思路与实现方法。这套方案比较适合正在学 STM32 的开发者、电子设计竞赛备赛团队以及想自己 DIY 一套家用晾衣架控制器的朋友。读完你不仅能跑通一个最小可用系统还能理解“为什么晾衣架控制不能只靠一个继电器”。1. 项目背景与功能需求1.1 为什么要做智能晾衣架传统晾衣架的问题很典型人不在家时突然下雨衣服被淋湿白天出门上班衣服晒了一整天也没人收晚上忘记收衣服衣服在阳台挂一夜。这些问题本质上不是“晾衣架”的问题而是“晾衣架缺少感知和决策能力”的问题。智能晾衣架要做的就是给普通晾衣架加上三样东西感知能力检测有没有下雨、光线够不够、环境湿度怎么样。执行能力通过电机驱动晾衣杆升降或者控制晾衣架伸缩。决策能力根据传感器数据自动判断“现在应该晒出去”还是“现在应该收回来”。在实际项目中这三点对应到硬件上就是雨滴传感器、光敏电阻、温湿度传感器、直流减速电机、限位开关和主控芯片。STM32F103 作为主控承担所有输入信号的采集和输出控制逻辑。1.2 主要功能与设计指标我建议不要把功能设计得过于复杂先保证一套“最小可用系统”能跑通再逐步扩展。基础版智能晾衣架建议包含以下功能功能模块说明手动控制按键控制晾衣杆上升、下降、停止自动模式根据雨滴、光照、湿度自动收放衣架限位保护上升/下降遇到限位开关时立即停止超时保护电机连续运行超过设定时间自动断电状态提示OLED 或数码管显示当前模式、状态扩展能力预留 WiFi 模块接口可接入手机控制其中“限位保护”和“超时保护”非常重要。晾衣架属于机械运动设备如果程序只依赖按键控制而没有保护机制电机堵转、衣物缠绕、限位失效都会带来安全隐患。这部分我在后面的软件设计里会重点讲。1.3 为什么选 STM32F103STM32F103 是 ST 公司推出的一款基于 ARM Cortex-M3 内核的 32 位单片机主频最高 72MHzFlash 从 16KB 到 512KB 不等RAM 也足够跑小型 RTOS 或者裸机状态机。它的优势主要有四点资料多社区活跃遇到问题容易检索到解决方案。外设丰富ADC、定时器 PWM、I2C、SPI、USART 一应俱全。价格相对稳定开发板也很普及适合学生和 DIY 用户。可以从标准外设库平滑过渡到 HAL 库学习曲线友好。以最常见的 STM32F103C8T6 为例它有 64KB Flash、20KB RAM跑一套晾衣架控制程序绰绰有余。即使后面要加 ESP8266 联网、OLED 显示、按键扫描、多个传感器资源也完全够用。2. 系统总体方案设计2.1 硬件架构整套系统的硬件构成如下[雨滴传感器] → ADC通道 → STM32F103C8T6 → PWM输出 → [电机驱动模块] → [直流减速电机] [光敏电阻] → ADC通道 → ↑ ↑ [DHT11温湿度] → GPIO/I2C → | | [按键阵列] → GPIO输入 → | | [限位开关] → GPIO输入 → | | [OLED显示屏] ← I2C/SPI ←———————┘ | [ESP8266] ← USART ←—————————————— 串口通信主控负责周期性采集传感器数据根据当前模式手动/自动/遥控执行相应动作并通过 OLED 实时显示状态。电机驱动采用 H 桥方案通过 PWM 控制转速通过两个 GPIO 控制正反转。2.2 工作模式设计本项目采用“手动优先 自动兜底”的思路具体有三种模式手动模式MODE 1 用户按“上升”“下降”“停止”按键直接控制电机。每次按键后系统进入手动锁定状态自动判断暂停执行防止手在操作时被自动逻辑打断。自动模式MODE 2 系统根据传感器数据自动决策下雨或湿度较高 → 收回晾衣架。光线充足且无雨 → 伸出晾衣架。光线不足或夜间 → 收回晾衣架。遥控模式MODE 3可选 通过 ESP8266 接收手机 App 或上位机指令解析后执行收、放、停等命令。该模式与手动模式类似但输入来源由按键变为串口指令。三种模式通过模式按键循环切换。模式状态保存在全局变量中并同步显示在 OLED 上。2.3 软件状态机设计晾衣架控制看似简单但如果不做状态管理代码很容易变成一堆 if-else 嵌套后期很难维护。我建议用状态机来管理晾衣杆的运行状态。定义以下核心状态STATE_IDLE 空闲等待 STATE_MOVING_UP 正在上升 STATE_MOVING_DOWN 正在下降 STATE_STOPPED 手动停止 STATE_FAULT 故障保护状态切换逻辑如下空闲 → 收到上升指令 → 开始上升 空闲 → 收到下降指令 → 开始下降 上升中 → 触发上限位或超时 → 停止并回到空闲 下降中 → 触发下限位或超时 → 停止并回到空闲 上升/下降中 → 按下停止键 → 停止并回到空闲 任意状态 → 检测到电机电流异常 → 进入故障并锁定使用状态机的好处是程序逻辑清晰边界情况容易覆盖后续扩展新功能比如加一个延时晾晒只需要增加状态即可不需要大量改动原有逻辑。3. 开发环境与工程搭建3.1 开发工具清单在开始写代码之前先把开发环境准备好。以下是我推荐的组合工具用途备注STM32CubeMX图形化初始化工程生成 GPIO、ADC、定时器、USART 初始化代码STM32CubeIDE编写、编译、烧录也可以使用 Keil MDKST-Link 或 J-Link程序下载调试ST-Link V2 性价比最高串口调试助手查看日志输出用于调试串口数据万用表检测电压和接线排查硬件问题必备版本方面CubeMX 不同版本的界面可能稍有差异但只要你的 CubeMX 能生成对应芯片的工程操作方法基本一致。后面我会以 HAL 库为例来编写代码。3.2 创建 STM32CubeMX 工程打开 STM32CubeMX新建设备选择 STM32F103C8T6配置以下外设RCCHigh Speed Clock (HSE) → Crystal/Ceramic ResonatorGPIOPA0 → 按键1上升 PA1 → 按键2下降 PA2 → 按键3停止 PA3 → 按键4模式切换 PB0 → 上限位开关输入 PB1 → 下限位开关输入 PA6 → 电机方向控制 IN1 PA7 → 电机方向控制 IN2 PA8 → 使能端 EN也可以直接拉高ADCPA4 → 雨滴传感器模拟量 PA5 → 光敏电阻模拟量定时器TIM2 通道1 → 输出 PWM用于控制电机转速USARTUSART1 → 连接 ESP8266默认波特率 115200 I2C → PB6 SCL, PB7 SDA连接 OLED生成工程后在main.c中开始编写业务逻辑。3.3 工程目录规划为了让代码结构更清晰建议在工程中按功能模块划分文件Core/ └── Inc/ └── main.h └── Src/ ├── main.c // 主函数、初始化、主循环 ├── gpio.c // GPIO 初始化 ├── adc.c // ADC 初始化 ├── tim.c // PWM 初始化 ├── usart.c // 串口初始化 └── i2c.c // I2C 初始化 Bsp/ ├── bsp_motor.c // 电机控制 ├── bsp_sensor.c // 传感器采集 ├── bsp_key.c // 按键扫描 └── bsp_oled.c // OLED 显示 App/ ├── app_sm.c // 状态机控制逻辑 └── app_auto.c // 自动模式决策Bsp 层负责驱动硬件App 层负责业务逻辑。这样拆分后如果以后换主控或者换传感器只需要修改对应 Bsp 文件即可。4. 硬件模块与驱动原理4.1 直流电机驱动与 PWM 调速晾衣架升降通常使用直流减速电机配合钢丝绳或皮带实现升降。减速电机扭矩大、转速低、自锁性好比较适合晾衣架这种负载场景。驱动方案有很多种L298N经典双 H 桥驱动电流大但管压降也大容易发热。TB6612体积小、效率高适合小型电机是我比较推荐的选择。分立元件 H 桥成本最低但调试麻烦新手不建议直接上手。TB6612FNG 的接线比较简单VM → 5V 电机电源 VCC → 3.3V 逻辑电源 GND → 共地 PWMA → STM32 PWM 输出PA8 AIN1 → STM32 GPIOPA6 AIN2 → STM32 GPIOPA7 AO1/AO2 → 电机正负极电机正反转控制逻辑如下AIN1AIN2电机状态10正转上升01反转下降00停止11刹车PWM 占空比越大电机转速越快。实际调试时建议先设置 50% 左右的占空比电机平稳后再调高。晾衣架不是高速设备转速过快反而容易导致衣物堆积和机械冲击。4.2 传感器采集雨滴传感器是一个模拟量传感器输出与雨量相关的电压信号。使用时输出引脚接到 STM32 的 ADC 输入通道通过采样值判断下雨程度。为了避免传感器电极长期通电腐蚀我建议采用“周期性供电”的方式即平时关闭传感器电源采集前再打开 10ms采集完立即断电。光敏电阻配合上拉电阻组成分压电路输出的模拟电压随光照强度变化。光线越强电阻越小ADC 采样值越高。DHT11 温湿度传感器采用单总线协议数据引脚需要接上拉电阻。虽然 DHT11 精度不高但用于晾衣场景判断“空气是否潮湿”已经足够。这里要提醒一点雨滴传感器和光敏电阻的 ADC 采样值受供电电压影响较大如果使用 3.3V 供电STM32 的 ADC 参考电压也必须是 3.3V才能保证采样结果稳定。4.3 限位开关与保护限位开关是晾衣架最重要的保护器件。晾衣杆升到顶部或降到最低时机械结构会触发限位开关此时无论程序处于什么状态都必须立刻停止电机。限位开关的接线方式推荐使用常闭触点串联在电机控制回路中形成硬件层面的保护。即使单片机程序异常电机也无法越过限位继续运行。这是“硬件冗余”的思路比单纯依赖软件判断更可靠。在软件层面限位开关接入 STM32 的 GPIO启用内部上拉开关接地。读取到低电平表示触发限位。4.4 显示模块0.96 寸 OLED 是目前最常用的显示方案支持 I2C 和 SPI 两种接口。I2C 方式占用引脚少接线简单推荐优先选择。OLED 可以显示当前工作模式手动/自动/遥控晾衣杆状态上升、下降、停止当前传感器数据光照值、雨滴值报警状态如果不想用 OLED也可以用 4 位数码管成本更低但显示内容有限。本项目的核心在控制逻辑显示模块只作为辅助大家可以根据手头硬件灵活选择。4.5 ESP8266 WiFi 扩展ESP8266 可以通过串口与 STM32 通信AT 指令固件比较常用。STM32 发送 AT 指令控制 ESP8266 连接 WiFi、建立 TCP 连接、收发数据从而实现手机远程控制。需要说明的是ESP8266 模块供电要求较高不能直接接 STM32 的 3.3V 引脚建议使用独立的 3.3V LDO 稳压芯片供电且做好电容滤波。5. 核心代码实现下面进入代码部分。我将以 HAL 库为例给出一个可运行的完整框架。由于篇幅限制不会把所有外设初始化代码都贴出来CubeMX 会生成重点放在业务逻辑和驱动函数上。5.1 GPIO 与 ADC 初始化CubeMX 生成的代码会帮我们初始化 GPIO 和 ADC这里不再重复。我们在用户代码区添加一些自定义的引脚定义方便后续调用// 文件路径Core/Inc/main.h 中追加 #define KEY_UP_Pin GPIO_PIN_0 #define KEY_UP_Port GPIOA #define KEY_DOWN_Pin GPIO_PIN_1 #define KEY_DOWN_Port GPIOA #define KEY_STOP_Pin GPIO_PIN_2 #define KEY_STOP_Port GPIOA #define KEY_MODE_Pin GPIO_PIN_3 #define KEY_MODE_Port GPIOA #define LIMIT_UP_Pin GPIO_PIN_0 #define LIMIT_UP_Port GPIOB #define LIMIT_DOWN_Pin GPIO_PIN_1 #define LIMIT_DOWN_Port GPIOB #define MOTOR_IN1_Pin GPIO_PIN_6 #define MOTOR_IN1_Port GPIOA #define MOTOR_IN2_Pin GPIO_PIN_7 #define MOTOR_IN2_Port GPIOA5.2 电机控制函数电机控制是晾衣架的核心驱动我封装了一个电机控制函数统一管理方向和使能。// 文件路径Bsp/bsp_motor.c #include bsp_motor.h #include main.h #define PWM_DUTY_DEFAULT 50 // 默认占空比 50% void Motor_Stop(void) { HAL_GPIO_WritePin(MOTOR_IN1_Port, MOTOR_IN1_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(MOTOR_IN2_Port, MOTOR_IN2_Pin, GPIO_PIN_RESET); __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, 0); } void Motor_Up(uint8_t duty) { HAL_GPIO_WritePin(MOTOR_IN1_Port, MOTOR_IN1_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(MOTOR_IN2_Port, MOTOR_IN2_Pin, GPIO_PIN_RESET); __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, duty); } void Motor_Down(uint8_t duty) { HAL_GPIO_WritePin(MOTOR_IN1_Port, MOTOR_IN1_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(MOTOR_IN2_Port, MOTOR_IN2_Pin, GPIO_PIN_SET); __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, duty); }这里有一个细节停止电机时我们先把两个方向引脚都拉低再把 PWM 占空比设置为 0。这样可以避免电机因惯性继续滑行同时减少驱动芯片的导通损耗。5.3 按键扫描与消抖按键是晾衣架最基本的输入方式。机械按键在按下和释放时会产生抖动如果直接读取会导致一次按下被识别成多次所以必须做消抖。下面是一个基于状态机的按键扫描函数不需要阻塞延时适合放在主循环中周期性调用// 文件路径Bsp/bsp_key.c #include bsp_key.h #define KEY_SCAN_INTERVAL 10 // 每次扫描间隔 ms typedef struct { uint8_t key_state; // 当前稳定状态 uint8_t last_raw; // 上一次原始电平 uint8_t cnt; } Key_Handle_t; static Key_Handle_t key_up {0}; uint8_t Key_Scan(GPIO_TypeDef* port, uint16_t pin, Key_Handle_t* handle) { uint8_t raw (HAL_GPIO_ReadPin(port, pin) GPIO_PIN_RESET) ? 1 : 0; uint8_t event 0; if (raw ! handle-last_raw) { handle-cnt 0; handle-last_raw raw; } else { if (handle-cnt 5) { handle-cnt; if (handle-cnt 5) { handle-key_state raw; if (raw 1) { event KEY_EVENT_PRESSED; } } } } return event; }这段代码的思路是连续 5 次检测到相同电平才认为状态稳定。如果原始电平变化计数清零重新开始新一轮消抖。消抖时间是 5 × 10ms 50ms和常规机械按键抖动时间5~20ms相比有足够冗余。在 main 的主循环中每隔 10ms 调用一次扫描函数// 文件路径Core/Src/main.c 用户代码区 while (1) { // 10ms 时间片 if (tick_10ms_flag) { tick_10ms_flag 0; if (Key_Scan(KEY_UP_Port, KEY_UP_Pin, key_up) KEY_EVENT_PRESSED) { App_SendCommand(CMD_UP); } // 其他按键类似处理 } }5.4 传感器读取ADC 读取可以使用 HAL 库的阻塞式接口也可以使用 DMA。晾衣架对实时性要求不高使用阻塞式即可。// 文件路径Bsp/bsp_sensor.c #include bsp_sensor.h #include adc.h uint16_t Sensor_ReadRain(void) { uint16_t val 0; // 1. 打开雨滴传感器电源 HAL_GPIO_WritePin(SENSOR_PWR_Port, SENSOR_PWR_Pin, GPIO_PIN_SET); HAL_Delay(10); // 等待供电稳定 // 2. 启动 ADC 转换等待完成 HAL_ADC_Start(hadc1); if (HAL_ADC_PollForConversion(hadc1, 100) HAL_OK) { val HAL_ADC_GetValue(hadc1); } HAL_ADC_Stop(hadc1); // 3. 关闭传感器电源降低电极腐蚀速度 HAL_GPIO_WritePin(SENSOR_PWR_Port, SENSOR_PWR_Pin, GPIO_PIN_RESET); // 4. 取反使得雨量越大值越大根据传感器输出电压极性调整 return 4095 - val; } uint16_t Sensor_ReadLight(void) { uint16_t val 0; HAL_ADC_Start(hadc1); if (HAL_ADC_PollForConversion(hadc1, 100) HAL_OK) { val HAL_ADC_GetValue(hadc1); } HAL_ADC_Stop(hadc1); return val; }注意雨滴传感器的输出电压和雨量大小的关系不是线性的不同模块特性差异也比较大。工程上不建议精确依赖某一个阈值而是使用滞回比较。比如雨量阈值 3000雨量大于 3000 判定为下雨 雨量回升到 2800 以下才判定为雨停滞回区间可以避免在临界值附近反复升降衣架这个细节对用户体验影响很大。5.5 自动晾衣逻辑状态机自动模式是整个系统的核心。我先定义几个常量// 文件路径App/app_auto.h #define RAIN_THRESHOLD_ON 3000 // 进入收衣的雨量阈值 #define RAIN_THRESHOLD_OFF 2800 // 雨停恢复的阈值 #define LIGHT_THRESHOLD_NIGHT 800 // 光线低于该值判定为夜间 #define HUMIDITY_THRESHOLD 80 // 湿度高于该值判定为潮湿自动模式的决策逻辑如下// 文件路径App/app_auto.c #include app_auto.h #include bsp_sensor.h #include bsp_motor.h typedef enum { AUTO_IDLE, AUTO_EXTEND, AUTO_RETRACT, } AutoState_t; static AutoState_t auto_state AUTO_IDLE; static uint8_t is_raining 0; static uint8_t is_night 0; static uint8_t is_humid 0; void App_Auto_Task(void) { uint16_t rain_val Sensor_ReadRain(); uint16_t light_val Sensor_ReadLight(); uint8_t humidity Sensor_ReadHumidity(); // DHT11 读取函数 // 1. 更新环境判断 if (rain_val RAIN_THRESHOLD_ON) { is_raining 1; } else if (rain_val RAIN_THRESHOLD_OFF) { is_raining 0; } if (light_val LIGHT_THRESHOLD_NIGHT) { is_night 1; } else { is_night 0; } if (humidity HUMIDITY_THRESHOLD) { is_humid 1; } else { is_humid 0; } // 2. 状态机跳转 switch (auto_state) { case AUTO_IDLE: // 满足收衣条件下雨或夜间或潮湿 if (is_raining || is_night || is_humid) { auto_state AUTO_RETRACT; Motor_Down(PWM_DUTY_DEFAULT); } // 天气良好且当前处于收回状态也可以根据初始位置判断 else { auto_state AUTO_EXTEND; Motor_Up(PWM_DUTY_DEFAULT); } break; case AUTO_EXTEND: // 伸出过程中如果天气突变立即反转收回 if (is_raining || is_night || is_humid) { Motor_Stop(); auto_state AUTO_RETRACT; Motor_Down(PWM_DUTY_DEFAULT); } break; case AUTO_RETRACT: // 收回状态下如果天气恢复良好不立即伸出等用户手动确认 // 或者等一个延时避免反复伸缩 break; default: break; } }这里有一个值得讨论的设计细节收回衣架后如果天气转晴我不建议让衣架自动伸出。原因有三点衣物可能还没干透用户可能不想再晒。自动伸出后如果传感器误判会导致衣物在无人看管的情况下反复升降。从安全角度考虑自动收回属于“保护动作”自动伸出属于“主动动作”后者的风险更高。所以更稳妥的做法是自动收回后通过 OLED 提示“天气转好请手动操作”。只有用户手动按下上升键后才恢复到晒衣状态。5.6 保护逻辑限位与超时限位保护和超时保护是晾衣架控制系统中最重要的安全机制。我在状态机中单独处理// 文件路径App/app_sm.c #include app_sm.h #include bsp_motor.h #define MOTOR_TIMEOUT_MS 30000 // 电机最长运行时间 30 秒 static uint8_t run_timer_started 0; static uint32_t run_start_tick 0; void App_StateMachine_Run(void) { // 1. 限位检测硬件触发时立即停止 if (HAL_GPIO_ReadPin(LIMIT_UP_Port, LIMIT_UP_Pin) GPIO_PIN_RESET) { // 上升遇到上限位 Motor_Stop(); run_timer_started 0; // 更新 UI 状态 return; } if (HAL_GPIO_ReadPin(LIMIT_DOWN_Port, LIMIT_DOWN_Pin) GPIO_PIN_RESET) { // 下降遇到下限位 Motor_Stop(); run_timer_started 0; return; } // 2. 超时保护电机运行超过设定时间 if (is_motor_running) { if (!run_timer_started) { run_timer_started 1; run_start_tick HAL_GetTick(); } else if ((HAL_GetTick() - run_start_tick) MOTOR_TIMEOUT_MS) { Motor_Stop(); run_timer_started 0; App_SetFault(FAULT_MOTOR_TIMEOUT); } } else { run_timer_started 0; } }超时保护的意义在于如果限位开关失效或者机械结构卡住电机持续运行会烧毁驱动芯片或电机本身。设一个 30 秒的硬上限即使发生异常损失也控制在可接受范围内。从工程经验来看硬件保护和软件保护缺一不可。硬件限位开关串联断电是“最后一道防线”软件限位检测是“第一道防线”超时保护是“兜底方案”。三者同时失效的概率极低。5.7 主函数集成主函数负责初始化外设然后进入主循环。主循环中以 10ms 为时间片调度各种任务// 文件路径Core/Src/main.c int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_ADC1_Init(); MX_TIM2_Init(); MX_USART1_UART_Init(); MX_I2C1_Init(); // 初始化 BSP Bsp_Motor_Init(); Bsp_Sensor_Init(); Bsp_Key_Init(); Bsp_OLED_Init(); // 初始化 App 层 App_Init(); uint32_t tick_last HAL_GetTick(); while (1) { uint32_t tick_now HAL_GetTick(); if (tick_now - tick_last 10) { tick_last tick_now; // 1. 按键扫描 Bsp_Key_Scan(); // 2. 模式切换处理 App_HandleModeSwitch(); // 3. 根据模式执行逻辑 if (app_mode MODE_MANUAL) { // 手动模式只处理按键已经由按键中断/扫描触发 } else if (app_mode MODE_AUTO) { App_Auto_Task(); } else if (app_mode MODE_REMOTE) { App_Uart_Task(); } // 4. 保护逻辑无论什么模式都不例外 App_StateMachine_Run(); // 5. 刷新显示 Bsp_OLED_Refresh(); } } }关于主循环有一个建议不要在 while(1) 里用HAL_Delay(10)来做 10ms 延时因为 HAL_Delay 期间无法响应限位中断和按键。更好的方式是用系统 tickHAL_GetTick()做非阻塞延时或者把限位检测放到外部中断中。如果追求更高实时性可以把限位开关接到 EXTI 外部中断引脚使用下降沿触发在中断回调函数中立即停止电机。实际项目中建议这么做void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin LIMIT_UP_Pin || GPIO_Pin LIMIT_DOWN_Pin) { Motor_Stop(); } }这样即使主循环被某个阻塞操作卡住限位信号也能第一时间切断电机。6. 常见问题与排查思路在实际调试过程中智能晾衣架项目最容易出现的问题集中在硬件接线和逻辑误判两方面。下面整理一些高频问题问题现象常见原因解决思路电机不转驱动模块供电不足检查 VM 引脚是否接独立电源是否共地电机只朝一个方向转IN1/IN2 接线错误检修 H 桥输入逻辑确认正反转控制按键按下没反应GPIO 模式配置错误确认按键引脚是否启用内部上拉按下是否接 GND雨滴传感器误判供电电压不稳或电极氧化加滤波电容周期性供电使用滞回比较晾衣杆升到顶还不停限位开关未触发或接线错误确认限位开关常闭触点是否串联GPIO 能否读到低电平电机运行一段时间后发热占空比过高或 PWM 频率不合适降低占空比调整 PWM 频率到 10kHz~20kHz自动模式反复伸缩阈值太临界无滞回增加滞回区间延长判断间隔OLED 不显示I2C 地址不对或接线错误确认 I2C 地址是 0x3C 还是 0x3D串口指令收不到波特率不匹配检查 ESP8266 固件波特率先做 AT 测试关于自动模式反复伸缩的问题我再多说一点。在实际环境中雨滴传感器和光敏电阻的采样值都有噪声临界值附近轻微波动就会导致状态频繁切换。解决方案有三个加滞回比较区间。连续 N 次采样值都满足条件才判定状态变化。相同状态执行后增加 60 秒最小间隔避免短时间反复动作。这三种方案可以组合使用效果最好。7. 最佳实践与工程建议7.1 硬件设计建议电源隔离电机属于感性负载启动和停止时会产生反向电动势可能导致 STM32 复位。建议电机电源和控制电源分开共地并在电机两端并联续流二极管或 TVS 管。限位开关选择优先选择带滚轮臂的行程开关触发力度小机械寿命长。安装位置要留出缓冲余量不能刚好顶死。传感器安装位置雨滴传感器应安装在晾衣杆上方边缘且倾斜一定角度雨水能顺流而下光敏电阻要避免被晾晒衣物遮挡否则会产生误判。接线端子选型电机、限位开关、电源线建议使用可插拔端子方便后期维护和更换。7.2 软件设计建议模块化分层Bsp 层只管硬件驱动App 层只管业务逻辑不要把 GPIO 操作散落在 main.c 中。统一时间调度不要在不同函数中随意 HAL_Delay尽量采用系统 tick 的统一调度框架保证功能模块间的时序可控。日志输出调试阶段务必保留串口日志打印模式切换、传感器数值、电机动作等关键信息。很多隐蔽问题靠逻辑分析不如靠日志观察来得快。参数可配置阈值、超时时间、PWM 占空比尽量定义为宏或配置结构体不要硬编码在具体函数中。后续换传感器或调参数时只需要修改一处。配置结构体可以这样设计typedef struct { uint16_t rain_threshold_on; uint16_t rain_threshold_off; uint16_t light_threshold_night; uint8_t humidity_threshold; uint16_t motor_timeout_ms; uint8_t pwm_duty; } ClothesRack_Config_t; const ClothesRack_Config_t DefaultConfig { .rain_threshold_on 3000, .rain_threshold_off 2800, .light_threshold_night 800, .humidity_threshold 80, .motor_timeout_ms 30000, .pwm_duty 50, };这样整个系统的关键参数都在一个结构体里集中管理比散落的宏定义清晰得多。7.3 安全提醒所有硬件调试必须在断电状态下完成接线确认无误后再上电。电机驱动电路调试时建议先不接晾衣杆负载空载测试电机转向和 PWM 控制逻辑。涉及 220V 电源的晾衣架改造务必找专业电工处理不要自行带电操作。给陌生人或社区分享项目时要在文档中强调“本项目仅用于学习交流生产级产品必须通过安全认证并增加多重保护”。8. 总结围绕 STM32F103 的智能晾衣架控制系统重点不是把代码写完而是理解一套从“传感器输入”到“机械执行”再到“异常保护”的完整控制链路。文中给出的代码框架可以直接编译运行也可以作为你继续扩展的骨架。如果你准备深入做这个项目下一步可以优先尝试三个方向接入 ESP8266实现手机 App 远程控制并定时上报晾晒状态。增加步进电机或直流无刷电机方案提升升降的平顺性和定位精度。使用 FreeRTOS 将按键、传感器采集、状态机、OLED 刷新拆成独立任务体验一下嵌入式操作系统在多任务场景下的组织方式。做这种小项目最忌讳的就是拿到代码就烧录跑通了就觉得会了。真正的收获在于自己动手接线、改参数、查波形、看日志、调保护逻辑的过程。如果在调试中遇到限位不触发、传感器跳变、电机抖动等问题可以从文中的排查表格入手逐项排除也可以把报错现象和测试数据整理出来一起交流。
返回列表