這篇博客介紹 狀態機(State Machine),適合用于嵌入式開發、驅動開發、協議解析、按鍵識別等多種場景。
一、什么是狀態機(State Machine)?
狀態機(State Machine)是一種用于描述系統行為的抽象模型,它能夠根據當前狀態和輸入條件,轉換到下一個狀態,并執行相應操作。
使用場景常見有:
- 按鍵消抖與識別
- 通信協議(如 UART、Modbus、CAN)
- 嵌入式流程控制(菜單系統、流程調度等)(我們此前的文章為貪吃蛇小游戲設計菜單系統 和 貪吃蛇的狀態改變 便用到了狀態機)
- Linux 驅動狀態切換
- UI 界面狀態設計
- …等
二、狀態機的基本組成
元素 | 說明 |
---|---|
狀態(State) | 系統可能處于的狀態集合,例如:IDLE、WAIT、RUN |
事件/輸入(Event) | 觸發狀態轉移的條件,例如:按鍵按下、超時、數據接收 |
動作(Action) | 狀態轉移時執行的操作,例如:發送數據、點亮LED |
轉移(Transition) | 狀態 + 輸入 → 新狀態 + 動作 |
狀態機分類
類型 | 說明 | 示例 |
---|---|---|
有限狀態機(FSM) | 狀態數量有限,事件驅動型 | 按鍵識別 |
Mealy 狀態機 | 輸出依賴于狀態 + 輸入 | 通信協議 |
Moore 狀態機 | 輸出只依賴當前狀態 | 電梯狀態控制 |
層級狀態機 | 狀態嵌套,適合復雜系統 | UI 菜單系統 |
三、狀態機圖示(例:按鍵識別)
+---------+ 按下 +---------+
| 空閑態 | -------------> | 按下態 |
+---------+ +---------+^ || | 松開| v
+---------+ <------------- +---------+
| 單擊處理 | | 松開態 |
+---------+ 超時 +---------+
四、代碼模版說明(結構體 + 函數指針實現)
? 1. 狀態定義
typedef enum {STATE_IDLE,STATE_PRESS,STATE_RELEASE,STATE_CLICK,STATE_MAX
} State_t;
? 2. 事件定義
typedef enum {EVENT_NONE,EVENT_KEY_DOWN,EVENT_KEY_UP,EVENT_TIMEOUT
} Event_t;
? 3. 狀態處理函數指針表
typedef void (*ActionFunc)(void);typedef struct {State_t current_state;Event_t event;State_t next_state;ActionFunc action;
} StateTable_t;
? 4. 狀態轉移表
void do_nothing(void) {}
void handle_click(void) { printf("Click!\n"); }StateTable_t state_table[] = {{STATE_IDLE, EVENT_KEY_DOWN, STATE_PRESS, do_nothing},{STATE_PRESS, EVENT_KEY_UP, STATE_CLICK, handle_click},{STATE_CLICK, EVENT_TIMEOUT, STATE_IDLE, do_nothing},{STATE_PRESS, EVENT_TIMEOUT, STATE_IDLE, do_nothing},
};
? 5. 狀態機執行函數
State_t current_state = STATE_IDLE;void state_machine_run(Event_t evt) {for (int i = 0; i < sizeof(state_table)/sizeof(StateTable_t); ++i) {if (state_table[i].current_state == current_state &&state_table[i].event == evt) {if (state_table[i].action) {state_table[i].action();}current_state = state_table[i].next_state;break;}}
}
狀態機的實際作用:
場景 狀態說明
按鍵處理 IDLE → PRESS → RELEASE → CLICK
通信協議 WAIT_RX → RECEIVING → PARSE → REPLY
UI 界面 主界面 → 設置界面 → 子菜單 → 返回
電機控制 停止 → 加速 → 穩速 → 減速
使用狀態機時需要注意:① 狀態機邏輯清晰,避免 if/else
嵌套過深;② 使用結構體+函數指針提高可擴展性;③ 可配合定時器、事件隊列使用;④ 狀態機應保持單一職責,避免過度復雜。
狀態機調試方法:
狀態打印 每次狀態切換打印當前狀態
圖示輔助 畫狀態圖幫助理清邏輯
加日志 用 log 函數記錄狀態流轉
使用斷點 在 state_machine_run() 中下斷點觀察轉移
狀態機是嵌入式開發中最實用的控制邏輯模型之一,掌握它能幫助在程序邏輯中有效管理復雜流程、提高代碼可讀性與可維護性。
以上,歡迎有從事同行業的電子信息工程、互聯網通信、嵌入式開發的朋友共同探討與提問,我可以提供實戰演示或模板庫。希望內容能夠對你產生幫助!