有限狀態機 (Finite State Machine, FSM) 是軟體工程中處理複雜邏輯流程的利器。在嵌入式系統中,受限於記憶體與即時性要求,C 語言實作 FSM 通常採用 跳轉表 驅動,達到 O(1) 狀態轉換、無分支預測失誤、程式碼結構清晰的效果。本文從核心要素分析、資料結構設計、完整程式碼實作三個層面,帶你打造可擴充的嵌入式級 FSM 框架。
一個 FSM 本質上包含三個核心概念:
狀態集合: {S1, S2, S3, STRAP}
條件集合: {C1, C2}
S1 --C1/A1--> S2
S2 --C2/A2--> S3
S3 --C1/A3--> S2
其他輸入 --> STRAP (陷阱狀態,不可逃脫)
對應真理表:
| 當前狀態 | 輸入 | 下一狀態 | 動作 |
|---|---|---|---|
| S1 | C1 | S2 | A1 |
| S2 | C2 | S3 | A2 |
| S3 | C1 | S2 | A3 |
| 任意 | 其他 | STRAP | ATRAP |
typedef int State;
typedef int Condition;
#define STATES 4
#define STATE1 0
#define STATE2 1
#define STATE3 2
#define STATETRAP 3
#define CONDITIONS 2
#define CONDITION1 0
#define CONDITION2 1
typedef void (*ActionFunc)(State current, Condition cond);
typedef struct {
State next; // 下一狀態
ActionFunc action; // 執行動作
} Transition, *PTransition;
// 動作函式實作 (此處僅列印)
void action1(State s, Condition c) { printf("Action 1 triggered\n"); }
void action2(State s, Condition c) { printf("Action 2 triggered\n"); }
void action3(State s, Condition c) { printf("Action 3 triggered\n"); }
void actionTrap(State s, Condition c) { printf("Trap action triggered\n"); }
// 轉移實例
Transition t1 = { STATE2, action1 }; // S1 + C1 -> S2, A1
Transition t2 = { STATE3, action2 }; // S2 + C2 -> S3, A2
Transition t3 = { STATE2, action3 }; // S3 + C1 -> S2, A3
Transition tt = { STATETRAP, actionTrap }; // 陷阱轉移
// 索引: [當前狀態][輸入條件]
PTransition transition_table[STATES][CONDITIONS] = {
/* C1 C2 */
/* S1 */ &t1, &tt, // S1: C1->t1, C2->trap
/* S2 */ &tt, &t2, // S2: C1->trap, C2->t2
/* S3 */ &t3, &tt, // S3: C1->t3, C2->trap
/* TR */ &tt, &tt // TRAP: 任何輸入留在陷阱
};
typedef struct {
State current;
} StateMachine, *PStateMachine;
State step(PStateMachine machine, Condition cond) {
PTransition t = transition_table[machine->current][cond];
(t->action)(machine->current, cond); // 執行動作
machine->current = t->next; // 更新狀態
printf("Current state: %d\n", machine->current);
return machine->current;
}
#include <stdio.h>
#include <stdlib.h>
typedef int State;
typedef int Condition;
#define STATENUM 4
#define STATE1 0
#define STATE2 1
#define STATE3 2
#define STATETRAP 3
#define CONDITIONS 2
#define CONDITION1 0
#define CONDITION2 1
typedef void (*ActionFunc)(State, Condition);
typedef struct {
State next;
ActionFunc action;
} Transition, *PTransition;
// 動作函式宣告
void action1(State, Condition);
void action2(State, Condition);
void action3(State, Condition);
void actionTrap(State, Condition);
// 轉移定義
Transition t1 = { STATE2, action1 };
Transition t2 = { STATE3, action2 };
Transition t3 = { STATE2, action3 };
Transition tt = { STATETRAP, actionTrap };
// 跳轉表
PTransition transition_table[STATENUM][CONDITIONS] = {
{ &t1, &tt },
{ &tt, &t2 },
{ &t3, &tt },
{ &tt, &tt }
};
typedef struct { State current; } StateMachine, *PStateMachine;
State step(PStateMachine machine, Condition cond) {
PTransition t = transition_table[machine->current][cond];
(t->action)(machine->current, cond);
machine->current = t->next;
printf("Current state: %d\n", machine->current);
return machine->current;
}
// 動作實作
void action1(State s, Condition c) { printf("Action 1 triggered\n"); }
void action2(State s, Condition c) { printf("Action 2 triggered\n"); }
void action3(State s, Condition c) { printf("Action 3 triggered\n"); }
void actionTrap(State s, Condition c) { printf("Trap action triggered\n"); }
int main() {
StateMachine machine = { .current = STATE1 };
int input;
printf("FSM Demo: Enter 0 (C1) or 1 (C2), other to quit\n");
while (scanf("%d", &input) == 1) {
if (input < 0 || input >= CONDITIONS) break;
step(&machine, input);
}
return 0;
}
FSM Demo: Enter 0 (C1) or 1 (C2), other to quit
0
Action 1 triggered
Current state: 1
1
Action 2 triggered
Current state: 2
0
Action 3 triggered
Current state: 1
1
Trap action triggered
Current state: 3
0
Trap action triggered
Current state: 3
| 需求 | 擴充方案 |
|---|---|
| 多任務/重入支援 | 狀態機結構體加入 mutex 或採用無鎖環狀緩衝區佇列事件 |
| 動態增減狀態 | 跳轉表改為動態配置的二維陣列或雜湊表 |
| 狀態進入/離開動作 | 擴充 Transition 結構加入 on_entry, on_exit 函式指標 |
| 層狀狀態機 (HSM) | 狀態結構體加入 parent 指標,步進函式遞迴處理 |
| 事件帶參數 | Condition 改為 void* 或聯合體,動作函式簽名調整 |
| 自動化生成 | 用 Python/Excel 定義狀態表,腳本生成 C 碼,避免手寫錯誤 |
總結:跳轉表驅動的 FSM 是嵌入式系統的「黃金模式」——確定性執行時間、極低記憶體佔用、結構與邏輯分離、易於測試與維護。掌握此模式,即掌握了複雜控制邏輯的工程化鑰匙。