从蓝桥杯电梯赛题到真实项目:如何用状态机思想重构你的嵌入式程序

在嵌入式开发领域,蓝桥杯等竞赛题目往往能反映实际工程中的典型问题。第八届蓝桥杯嵌入式省赛的电梯控制题目就是一个绝佳案例——它表面上考察基础外设操作,实则暗含了软件架构设计的深层考验。许多参赛者采用"面条式代码"(即大量嵌套的条件判断和全局标志位)完成了功能,却在代码可维护性、扩展性上留下了隐患。本文将带你用**有限状态机(FSM)**重新解构这个案例,展示如何将竞赛代码升级为工业级设计。

1. 为什么状态机是嵌入式开发的利器

1.1 典型问题:标志位泛滥的"面条代码"

原始解决方案中充斥着这样的代码片段:

int flag_up = 0;  //上行标志位
int flag_down = 0; //下行标志位 
int push_flag = 0; //按键操作完成标志位
int arrive_tar = 0; //到达目标楼层标志位

这种设计存在三个致命缺陷:

  1. 状态覆盖风险 :当 flag_up flag_down 同时为1时,系统行为不可预测
  2. 逻辑耦合度高 :修改一个功能可能影响多个无关模块
  3. 调试困难 :需要同时跟踪多个标志位的变化时序

1.2 状态机的工程价值

有限状态机通过明确定义:

  • 状态集合 (如IDLE、MOVING_UP等)
  • 事件触发器 (如按键按下、定时器超时)
  • 状态转移规则

使得复杂系统的行为变得可建模、可验证。在电梯控制场景中,状态机尤其适合处理:

  • 异步事件(如随机楼层请求)
  • 时序敏感操作(如开门保持时间)
  • 安全约束(如运行中禁止开门)

提示:状态机不是万能的,但对于中等复杂度的控制逻辑(3-15个状态),它能显著降低认知负荷。

2. 电梯系统的状态建模

2.1 状态枚举与事件定义

首先提取系统的核心状态(建议使用枚举增强可读性):

typedef enum {
    IDLE,              // 待机状态
    DOOR_OPENING,      // 开门中
    DOOR_OPEN,         // 门已开
    DOOR_CLOSING,      // 关门中
    MOVING_UP,         // 上升中
    MOVING_DOWN,       // 下降中
    EMERGENCY_STOP     // 紧急停止
} ElevatorState;

关键事件定义示例:

// 事件类型定义
typedef enum {
    EVT_FLOOR_BTN,    // 楼层按钮按下
    EVT_ARRIVE_FLOOR, // 到达目标楼层
    EVT_DOOR_OPENED,  // 门完全打开
    EVT_DOOR_CLOSED,  // 门完全关闭
    EVT_TIMEOUT       // 超时事件
} ElevatorEvent;

2.2 状态转移表设计

用表格明确状态转换规则:

当前状态 事件 条件判断 下一状态 执行动作
IDLE EVT_FLOOR_BTN 目标楼层>当前楼层 MOVING_UP 启动电机,点亮上行灯
IDLE EVT_FLOOR_BTN 目标楼层<当前楼层 MOVING_DOWN 启动电机,点亮下行灯
MOVING_UP EVT_ARRIVE_FLOOR 到达最高目标楼层 DOOR_OPENING 停止电机,启动开门机构
DOOR_OPEN EVT_TIMEOUT 开门保持时间≥3秒 DOOR_CLOSING 启动关门机构

这种表达方式比原始代码中的多重 if-else 更直观,也更容易验证逻辑完整性。

3. 状态机实现模式对比

3.1 嵌套switch方案

适合资源受限的MCU,无需额外库支持:

void Elevator_RunStateMachine(ElevatorEvent evt) {
    static ElevatorState state = IDLE;
    
    switch(state) {
        case IDLE:
            if(evt == EVT_FLOOR_BTN) {
                if(target_floor > current_floor) {
                    state = MOVING_UP;
                    Motor_Start(UP);
                }
                // 其他条件分支...
            }
            break;
        case MOVING_UP:
            if(evt == EVT_ARRIVE_FLOOR) {
                state = DOOR_OPENING;
                Motor_Stop();
                Door_StartOpen();
            }
            break;
        // 其他状态处理...
    }
}

3.2 表驱动方案

更易于维护和扩展,适合复杂系统:

// 状态转移表项
typedef struct {
    ElevatorState nextState;
    void (*action)(void);
} StateTransition;

// 状态转移表
StateTransition fsmTable[NUM_STATES][NUM_EVENTS] = {
    [IDLE][EVT_FLOOR_BTN] = {MOVING_UP, Motor_StartUp},
    [MOVING_UP][EVT_ARRIVE_FLOOR] = {DOOR_OPENING, Motor_Stop},
    // 其他表项...
};

// 统一处理函数
void Elevator_HandleEvent(ElevatorEvent evt) {
    StateTransition trans = fsmTable[currentState][evt];
    if(trans.action) trans.action();
    currentState = trans.nextState;
}

3.3 两种方案的实测对比

指标 嵌套switch方案 表驱动方案
代码体积 较小 较大(约+15%)
执行效率 略高 略低(约5%)
可维护性 较差 优秀
新增状态成本
适合场景 简单状态机 复杂状态机

在STM32F103这类资源有限的平台上,嵌套switch可能是更务实的选择。

4. 从竞赛到工程的进阶技巧

4.1 时间管理重构

原始代码中存在大量 HAL_Delay() 阻塞调用:

while(1) {
    // ... 
    HAL_Delay(5);  // 低效的忙等待
}

改进方案应采用 非阻塞式定时器

// 在定时器中断中更新时间标记
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
    if(htim == &htim3) {
        system_ticks++;
    }
}

// 主循环中检查超时
if((system_ticks - last_tick) >= DOOR_HOLD_TICKS) {
    PostEvent(EVT_TIMEOUT);
    last_tick = system_ticks;
}

4.2 分层架构设计

将系统划分为三个层次:

  1. 硬件抽象层 :封装GPIO、PWM等底层操作
    void Door_SetPosition(uint8_t percent) {
        __HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_1, percent);
    }
    
  2. 状态机核心层 :纯逻辑处理,不直接操作硬件
  3. 应用层 :处理业务逻辑如调度算法

4.3 调试支持增强

添加状态跟踪机制:

const char *StateToString(ElevatorState s) {
    static const char *names[] = {
        [IDLE] = "IDLE",
        [MOVING_UP] = "MOVING_UP",
        // 其他状态...
    };
    return names[s];
}

void Debug_PrintStateChange(ElevatorState old, ElevatorState new) {
    printf("[FSM] %s -> %s\n", StateToString(old), StateToString(new));
}

5. 真实项目中的状态机实践

在工业电梯控制器中,我们进一步扩展了状态机模型:

5.1 状态持久化

添加EEPROM状态保存,应对意外断电:

void State_SavePersistent(ElevatorState s) {
    uint8_t data = (uint8_t)s;
    HAL_FLASH_Unlock();
    FLASH_Erase_Sector(FLASH_SECTOR_6, VOLTAGE_RANGE_3);
    HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, 0x08080000, data);
    HAL_FLASH_Lock();
}

5.2 安全状态设计

引入安全状态机并行运行:

typedef enum {
    SAFETY_NORMAL,
    SAFETY_OVERLOAD,
    SAFETY_DOOR_OBSTACLE
} SafetyState;

5.3 测试用例设计

基于状态转移表自动生成测试用例:

测试ID 初始状态 输入事件 预期新状态 预期动作
TC-01 IDLE EVT_FLOOR_BTN(3) MOVING_UP 电机启动,LED亮
TC-02 MOVING_UP EVT_ARRIVE_FLOOR DOOR_OPENING 电机制动

这种从竞赛到工程的思维转变,正是嵌入式开发者专业能力的分水岭。当你在下次项目评审中展示清晰的状态图而非杂乱的标志位时,团队协作效率将获得质的提升。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐