在嵌入式系统开发中,状态机是一种极其重要的编程范式,尤其适合处理那些具有明确状态转换逻辑的应用场景。蓝桥杯嵌入式组比赛经常考察选手对状态机的理解和应用能力,而升降控制器正是检验这一能力的经典案例。本文将带你深入理解如何基于STM32G431,用状态机思维构建一个高效、可维护的升降控制系统。
状态机(State Machine)是一种数学模型,由一组状态、转移条件和动作组成。在嵌入式系统中,状态机特别适合用来描述那些具有离散状态和明确转换条件的系统行为。
升降控制器的典型需求包括:
传统实现可能会使用大量if-else嵌套,而状态机则将这些逻辑分解为清晰的状态和转换:
c复制typedef enum {
IDLE, // 空闲状态,等待用户输入
DOOR_CLOSING, // 正在关门
MOVING, // 正在移动
DOOR_OPENING // 正在开门
} LiftState;
在STM32G431上实现升降控制器需要合理分配硬件资源:
| 外设 | 引脚 | 功能描述 |
|---|---|---|
| TIM2_CH1 | PA5 | 电梯门控制(高电平开门) |
| TIM3_CH2 | PA4 | 升降方向控制(高电平上行) |
| TIM3_CH1 | PA6 | 升降电机PWM控制 |
| TIM17_CH1 | PA7 | 门电机PWM控制 |
| GPIO | PB0-3 | 楼层选择按键 |
| GPIO | PC0-7 | 楼层指示LED |
基于题目要求,我们定义了8个状态:
状态转换的核心逻辑体现在Lift_Ctrl()函数中:
c复制void Lift_Ctrl(void) {
switch(Status_Ctrl) {
case 0: // 空闲状态
if(key_press_flag) Status_Ctrl = 1;
break;
case 1: // 确认选择
if((uwTick - uwTick_Time_Count) >= 1000)
Status_Ctrl = 2;
break;
// 其他状态转换...
}
}
按键处理需要防抖和状态检测,核心代码如下:
c复制void KEY_Proc(void) {
if((uwTick - uwTick_KEY_Speed_Ctrl) < 50) return;
uwTick_KEY_Speed_Ctrl = uwTick;
key_value = KEY_Scan();
key_down = key_value & (key_value ^ key_old);
key_old = key_value;
if(Status_Ctrl == 0 || Status_Ctrl == 1) {
switch(key_down) {
case 1: // 1楼按键
if(Current_floor != 1) {
Set_floor |= 0x01;
ucLED |= 0x01;
key_press_flag = 1;
uwTick_Time_Count = uwTick;
}
break;
// 其他楼层处理...
}
}
}
电梯门和升降电机都采用PWM控制,配置如下:
c复制// TIM2初始化 - 门控制
htim2.Instance = TIM2;
htim2.Init.Prescaler = 83;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 999;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_PWM_Init(&htim2);
// TIM3初始化 - 升降控制
htim3.Instance = TIM3;
htim3.Init.Prescaler = 83;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 999;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_PWM_Init(&htim3);
每个状态的时间控制是升降控制器流畅运行的关键:
c复制case 2: // 关门状态
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, 0); // 关门
__HAL_TIM_SET_COMPARE(&htim17, TIM_CHANNEL_1, 250); // 50%占空比
uwTick_Time_Count = uwTick;
Status_Ctrl = 3;
break;
case 3: // 判断关门是否完成
if((uwTick - uwTick_Time_Count) >= 4000) { // 4秒关门时间
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_1);
HAL_TIM_PWM_Stop(&htim17, TIM_CHANNEL_1);
Status_Ctrl = 4;
}
break;
调试状态机时,可以采用以下策略:
示例调试代码:
c复制// 在LCD上显示状态机当前状态
sprintf((char*)LCD_String_Disp, " Status Test:%01d",Status_Ctrl);
LCD_DisplayStringLine(Line9, LCD_String_Disp);
状态卡死:
PWM输出异常:
按键响应不灵敏:
在实现过程中,特别要注意状态转换的条件是否完备,避免出现状态"漏网"的情况。例如,在状态7中:
c复制case 7: // 开门完成
if((uwTick - uwTick_Time_Count) >= 4000) {
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_1);
HAL_TIM_PWM_Stop(&htim17, TIM_CHANNEL_1);
uwTick_Time_Count = uwTick;
Status_Ctrl = 8;
}
break;
这段代码确保了开门动作完成后,系统会进入状态8检查是否有其他目标楼层需要服务。