智能晾衣架嵌入式编程:实现电机控制、限位检测与遇阻停止的安全逻辑
智能晾衣架最怕三件事:升到顶不停烧电机、降到底继续拽断钢丝、中途卡住还硬推。固件不能把“按键→拉高GPIO”当控制逻辑,得做成状态机:方向指令只改目标,电机执行、限位消抖、电流判堵转全在定时任务里跑。
一、电机驱动:H桥+PWM,别直接IO推
小功率机型用TB6612/L298N驱动直流减速电机,正反转靠AIN1/AIN2电平,速度靠PWM:
void motor_up(void){
gpio_write(AIN1,1); gpio_write(AIN2,0);
pwm_set(DUTY_UP); // 软启动从30%爬升
}
void motor_down(void){
gpio_write(AIN1,0); gpio_write(AIN2,1);
pwm_set(DUTY_DOWN);
}
void motor_stop(void){
pwm_set(0);
gpio_write(AIN1,0); gpio_write(AIN2,0);
}
要点:
启动占空比从低往上调,别一上来100%,减少启动电流冲击
停止先清零PWM再关方向脚,避免H桥直通
上升速度可比下降快一档,下降要慢,防夹人
方向切换前必须先stop,至少几ms死区
二、限位检测:硬件冗余+软件消抖
上/下限位用微动开关或霍尔开关,位置在卷线机构两端。微动稳、霍尔无机械磨损,量产常两者并存:霍尔做主定位,微动做硬件兜底。
硬件层:限位开关常闭触点串进电机控制回路,MCU死机也能断。
软件层:GPIO读限位必须消抖,机械开关抖动5~20ms,连续采样确认:
#define LIMIT_OK_CNT 4 // 40ms@10ms
static uint8_t up_cnt,dn_cnt;
uint8_t limit_up,limit_down;
void limit_task_10ms(void){
if(gpio_read(UP_LIMIT)==0){ if(++up_cnt>=LIMIT_OK_CNT) limit_up=1; }
else { up_cnt=0; limit_up=0; }
if(gpio_read(DN_LIMIT)==0){ if(++dn_cnt>=LIMIT_OK_CNT) limit_down=1; }
else { dn_cnt=0; limit_down=0; }
}
判定优先级最高,任何状态都先查限位:
void motor_guard(void){
if(dir==UP && limit_up){
motor_stop(); dir=STOP; err=ERR_TOP;
}
if(dir==DOWN && limit_down){
motor_stop(); dir=STOP; err=ERR_BOTTOM;
}
}
别在按键中断里直接停电机,也别只判一次电平——抖动会误触发。
三、遇阻停止:电流采样判堵转
下降途中衣架碰人头、钢丝绳卡导轨,电机没到限位但已经堵转。反电动势消失,电流会持续拉高。
电机电源串采样电阻或用ACS712/INA180接ADC:
#define I_RUN_MAX 1200 // mA,按机型标定
static uint16_t cur_buf[8];
static uint8_t ci;
void stall_task_10ms(void){
uint16_t cur = adc_current_ma();
cur_buf[ci]=cur; ci=(ci+1)%8;
uint32_t sum=0; for(int i=0;i<8;i++) sum+=cur_buf[i];
uint16_t avg = sum/8;
if(dir!=STOP && avg>I_RUN_MAX){
motor_stop();
dir=STOP;
err=ERR_STALL;
buzz_alarm();
// 下降遇阻回退一小段
if(last_dir==DOWN) motor_up(); delay_ms(300); motor_stop();
}
}
关键细节:
避开启动电流:起步300ms内不判堵转,否则正常启动被误断
用滑动平均不用单次采样,尖峰干扰不敏感
阈值按实测定:正常带载0.3~0.8A,堵转1.2A以上
遇阻后别继续同方向重试,下降场景回退一段更安全
有编码器可加“有电流无脉冲”判据,比纯电流更稳
四、主状态机
别用while(down){...}阻塞,主循环只跑任务:
typedef enum{STOP,UP,DOWN}dir_t;
static dir_t dir=STOP,last_dir=STOP;
void ctrl_set(dir_t d){ dir=d; last_dir=d; }
void sys_10ms(void){
limit_task_10ms();
motor_guard(); // 限位最高优先级
stall_task_10ms(); // 遇阻
switch(dir){
case UP: motor_up(); break;
case DOWN: motor_down(); break;
case STOP: motor_stop(); break;
}
}
按键/APP/遥控都只调ctrl_set(),不碰GPIO;电机动作统一出口,避免多处写AIN脚状态撕裂。
五、落坑清单
按键中断里直接拉GPIO→方向脚竞争
限位读一次就信→机械抖动误停
只做软件限位无限位开关→MCU死机冲顶
启动瞬间判电流→正常起步被断
堵转后继续同方向推→电机烧毁
PWM不软启动→启动电流冲击继电器
下降速度和上升一样快→夹人没反应时间
霍尔失效没微动兜底→到顶不停
遇阻不回退→衣架卡死在障碍物上
方向切换不停机→H桥瞬间直通
六、落地顺序
TB6612接电机,先调正反转+PW
上/下限位接GPIO,常闭硬件串回路
10ms任务里连续采样消抖
限位触发无条件停电机
ADC接电流采样,标正常/堵转阈值
起步300ms避让,滑动平均判堵转
下降遇阻停+回退
状态机统一出口,按键只改dir
过载/超重可再加称重阈值
晾衣架安全逻辑的本质不是“能升降”,是任何异常都能在几十毫秒内断电。限位开关是最后一道墙,电流堵转是过程保护,状态机是防止多处控制互相打架。固件里最稳的写法永远是:指令只改目标方向、电机动作只有一个出口、限位优先于方向、遇阻先停再回退——别让“再走一点点”的侥幸逻辑留在下降分支里。





