蓝桥杯嵌入式备赛:如何用状态机思想重构第八届省赛电梯调度程序? 蓝桥杯嵌入式备赛状态机重构电梯调度程序的工程实践在嵌入式系统开发中状态机Finite State MachineFSM是一种强大的设计模式特别适合处理具有明确状态转换逻辑的系统。蓝桥杯嵌入式竞赛中的电梯调度问题正是状态机应用的典型场景。本文将从一个资深嵌入式工程师的角度分享如何用状态机思想重构第八届省赛的电梯调度程序提升代码的可维护性和可扩展性。1. 状态机设计基础状态机由三个核心要素构成状态集合、事件集合和转移规则。在电梯控制系统中这些要素可以直观地映射到实际业务逻辑状态集合包括空闲(IDLE)、上行(UP)、下行(DOWN)、开门(OPENING)、关门(CLOSING)、等待(WAITING)等事件集合包含楼层按键事件(FLOOR_BUTTON)、到达目标楼层事件(ARRIVAL)、定时器超时事件(TIMEOUT)等转移规则定义在特定状态下当某个事件发生时系统如何转换到新状态typedef enum { STATE_IDLE, STATE_UP, STATE_DOWN, STATE_OPENING, STATE_CLOSING, STATE_WAITING } ElevatorState; typedef enum { EVT_FLOOR_BUTTON, EVT_ARRIVAL, EVT_TIMEOUT } ElevatorEvent;与传统标志位控制相比状态机具有明显优势对比维度标志位控制状态机控制代码可读性逻辑分散难以追踪状态集中管理流程清晰可维护性修改一处可能影响全局状态独立修改影响局部扩展性新增功能需重构逻辑只需添加新状态和转移调试难度标志位组合爆炸难排查当前状态明确易追踪2. 电梯状态机建模2.1 状态定义与转换基于题目要求我们首先建立电梯的核心状态模型IDLE电梯静止在某一楼层等待用户输入WAITING已接收用户指令等待1秒后开始运行UP/DOWN电梯正在上行或下行OPENING到达目标楼层正在开门CLOSING完成开门正在关门状态转换图如下[IDLE] -- FLOOR_BUTTON -- [WAITING] -- TIMEOUT(1s) -- [UP/DOWN] [UP/DOWN] -- ARRIVAL -- [OPENING] -- TIMEOUT(2s) -- [CLOSING] [CLOSING] -- TIMEOUT(4s) -- [IDLE]2.2 事件处理机制在状态机实现中事件处理是核心逻辑。我们采用事件队列机制#define MAX_EVENTS 10 typedef struct { ElevatorEvent events[MAX_EVENTS]; uint8_t head; uint8_t tail; } EventQueue; void enqueueEvent(EventQueue *q, ElevatorEvent evt) { if ((q-tail 1) % MAX_EVENTS ! q-head) { q-events[q-tail] evt; q-tail (q-tail 1) % MAX_EVENTS; } } ElevatorEvent dequeueEvent(EventQueue *q) { ElevatorEvent evt EVT_NONE; if (q-head ! q-tail) { evt q-events[q-head]; q-head (q-head 1) % MAX_EVENTS; } return evt; }3. 状态机实现细节3.1 状态转移表实现使用状态转移表可以清晰地表达状态转换逻辑typedef struct { ElevatorState currentState; ElevatorEvent event; ElevatorState nextState; void (*action)(void); } StateTransition; const StateTransition transitionTable[] { {STATE_IDLE, EVT_FLOOR_BUTTON, STATE_WAITING, startWaitingTimer}, {STATE_WAITING, EVT_TIMEOUT, STATE_UP, startMovingUp}, {STATE_WAITING, EVT_TIMEOUT, STATE_DOWN, startMovingDown}, {STATE_UP, EVT_ARRIVAL, STATE_OPENING, stopElevator}, {STATE_DOWN, EVT_ARRIVAL, STATE_OPENING, stopElevator}, {STATE_OPENING, EVT_TIMEOUT, STATE_CLOSING, startClosingDoor}, {STATE_CLOSING, EVT_TIMEOUT, STATE_IDLE, completeCycle} };3.2 主循环与状态处理主循环负责处理事件和状态转移void elevatorMainLoop(void) { static ElevatorState currentState STATE_IDLE; EventQueue eventQueue {0}; while(1) { ElevatorEvent evt dequeueEvent(eventQueue); if (evt ! EVT_NONE) { for (int i 0; i sizeof(transitionTable)/sizeof(transitionTable[0]); i) { if (transitionTable[i].currentState currentState transitionTable[i].event evt) { if (transitionTable[i].action) { transitionTable[i].action(); } currentState transitionTable[i].nextState; break; } } } // 其他系统任务 HAL_Delay(10); } }4. 关键功能实现4.1 楼层调度算法在状态机框架下实现SCAN电梯调度算法收集所有上行和下行请求按照当前方向处理同方向请求到达端点后反向处理剩余请求void updateTargetFloors(void) { if (currentDirection DIR_UP) { // 找出所有大于当前楼层的目标 for (int i currentFloor 1; i MAX_FLOOR; i) { if (floorRequests[i]) { addTargetFloor(i); } } } else { // 找出所有小于当前楼层的目标 for (int i currentFloor - 1; i MIN_FLOOR; i--) { if (floorRequests[i]) { addTargetFloor(i); } } } }4.2 定时器管理使用硬件定时器处理各种超时事件void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim htim3) { static uint32_t counter 0; counter; // 1秒等待超时 if (currentState STATE_WAITING counter 1000/10) { enqueueEvent(eventQueue, EVT_TIMEOUT); counter 0; } // 2秒开门超时 if (currentState STATE_OPENING counter 2000/10) { enqueueEvent(eventQueue, EVT_TIMEOUT); counter 0; } } }4.3 外设控制集成将LED、按键等外设控制集成到状态机中void updateFloorIndicator(uint8_t floor) { // 关闭所有楼层LED HAL_GPIO_WritePin(F1_LED_GPIO_Port, F1_LED_Pin, GPIO_PIN_RESET); // ...其他楼层LED // 点亮当前楼层LED switch(floor) { case 1: HAL_GPIO_WritePin(F1_LED_GPIO_Port, F1_LED_Pin, GPIO_PIN_SET); break; // ...其他楼层 } // 更新LCD显示 char str[20]; sprintf(str, %d, floor); LCD_DisplayStringLine(Line4, str); }5. 调试与优化技巧5.1 状态追踪调试添加状态日志输出帮助调试const char* stateToString(ElevatorState state) { switch(state) { case STATE_IDLE: return IDLE; case STATE_WAITING: return WAITING; // ...其他状态 default: return UNKNOWN; } } void logStateTransition(ElevatorState oldState, ElevatorState newState) { char logMsg[50]; sprintf(logMsg, State change: %s - %s, stateToString(oldState), stateToString(newState)); debugUartSend(logMsg); }5.2 内存优化对于资源受限的嵌入式系统优化状态机内存使用使用位域压缩状态标志合理设计事件队列大小使用const修饰状态转移表节省RAMtypedef struct { uint8_t currentFloor : 3; // 使用3位表示1-4层 uint8_t direction : 1; // 0down, 1up uint8_t state : 3; // 主要状态编码 } CompactElevatorState;5.3 实时性保障确保状态机响应实时性关键事件使用中断触发长耗时操作分步执行状态处理函数保持简短void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin F1_Pin) { enqueueEvent(eventQueue, EVT_FLOOR_BUTTON); floorRequests[1] 1; } // ...处理其他按键 }6. 工程实践建议在实际项目中应用状态机时有几个经验值得分享状态粒度把控状态不是越多越好也不是越少越好。我通常建议每个状态对应一个明确的行为模式比如正在上行是一个状态而不需要把上行中经过每层都作为独立状态。事件设计原则事件应该代表已经发生的事情而不是应该发生的事情。例如按键按下是事件而需要去3楼是状态机内部的判断逻辑。调试技巧在LCD上实时显示当前状态和最近事件可以大幅提高调试效率。我在实际项目中会保留一个调试界面即使产品发布后也很有用。性能考量在STM32F103这类资源有限的MCU上状态机的实现要尽量轻量。避免在状态处理函数中进行复杂计算必要时可以使用查表法优化性能。