用STM32G431状态机实现蓝桥杯升降控制器:从理论到实战的深度解析
在嵌入式系统开发中,状态机是一种极其重要的编程范式,尤其适合处理那些具有明确状态转换逻辑的应用场景。蓝桥杯嵌入式组比赛经常考察选手对状态机的理解和应用能力,而升降控制器正是检验这一能力的经典案例。本文将带你深入理解如何基于STM32G431,用状态机思维构建一个高效、可维护的升降控制系统。
1. 状态机设计基础与升降控制器需求分析
状态机(State Machine)是一种数学模型,由一组状态、转移条件和动作组成。在嵌入式系统中,状态机特别适合用来描述那些具有离散状态和明确转换条件的系统行为。
升降控制器的典型需求包括:
- 楼层选择:用户可以通过按键选择目标楼层
- 运动控制:电梯需要根据当前楼层和目标楼层决定上行或下行
- 门控制:到达目标楼层后需要开门,停留一段时间后自动关门
- 状态指示:通过LED显示当前楼层和运行方向
传统实现可能会使用大量if-else嵌套,而状态机则将这些逻辑分解为清晰的状态和转换:
c复制typedef enum {
IDLE, // 空闲状态,等待用户输入
DOOR_CLOSING, // 正在关门
MOVING, // 正在移动
DOOR_OPENING // 正在开门
} LiftState;
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. STM32G431状态机实现框架
2.1 硬件资源配置
在STM32G431上实现升降控制器需要合理分配硬件资源:
| 外设 | 引脚 | 功能描述 |
|---|---|---|
| TIM2_CH1 | PA5 | 电梯门控制(高电平开门) |
| TIM3_CH2 | PA4 | 升降方向控制(高电平上行) |
| TIM3_CH1 | PA6 | 升降电机PWM控制 |
| TIM17_CH1 | PA7 | 门电机PWM控制 |
| GPIO | PB0-3 | 楼层选择按键 |
| GPIO | PC0-7 | 楼层指示LED |
2.2 核心状态定义
基于题目要求,我们定义了8个状态:
- 状态0:空闲等待,接收用户输入
- 状态1:确认用户选择,开始关门
- 状态2:执行关门动作
- 状态3:判断移动方向
- 状态4:执行升降动作
- 状态5:到达目标楼层,准备开门
- 状态6:执行开门动作
- 状态7:检查是否有其他目标楼层
状态转换的核心逻辑体现在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;
// 其他状态转换...
}
}
3. 关键模块实现细节
3.1 按键处理与楼层选择
按键处理需要防抖和状态检测,核心代码如下:
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;
// 其他楼层处理...
}
}
}
3.2 PWM控制实现
电梯门和升降电机都采用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);
3.3 状态迁移中的时间控制
每个状态的时间控制是升降控制器流畅运行的关键:
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;
4. 调试技巧与常见问题解决
4.1 状态机调试方法
调试状态机时,可以采用以下策略:
- 状态可视化:在LCD上显示当前状态值
- 时间参数调整:适当缩短各状态时间便于快速测试
- 断点设置:在状态转换处设置断点观察变量变化
示例调试代码:
c复制// 在LCD上显示状态机当前状态
sprintf((char*)LCD_String_Disp, " Status Test:%01d",Status_Ctrl);
LCD_DisplayStringLine(Line9, LCD_String_Disp);
4.2 常见问题与解决方案
-
状态卡死:
- 检查所有状态是否都有退出条件
- 确保时间判断逻辑正确
-
PWM输出异常:
- 验证定时器配置参数
- 检查GPIO复用功能是否使能
-
按键响应不灵敏:
- 调整按键扫描频率
- 优化防抖算法
在实现过程中,特别要注意状态转换的条件是否完备,避免出现状态"漏网"的情况。例如,在状态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检查是否有其他目标楼层需要服务。
