目录
- 状态图的本质理解
- 什么时候必须用状态图
- 状态图的核心元素详解
- 从需求到代码的完整流程
- 7个完整实战案例
- 高级特性:层次状态与并发
- 状态表与代码生成
- 调试与优化技巧
- PlantUML 绘制指南
一、状态图的本质理
核心定义
状态图 = switch-case 状态机的"上帝视角设计图"
你现在写的代码
c
// 你可能这样写代码
switch(state) {
case IDLE:
// ...
break;
case RUN:
// ...
break;
case ERROR:
// ...
break;
}
在 UML 里应该怎么做
1. 先画状态图 → 理清状态和转换
2. 再写代码 → 一一对应,不会乱
3. 边调试 → 对照状态图检查
状态图的价值
| 维度 | 没有状态图 | 有状态图 |
|---|---|---|
| 设计阶段 | 边写边想,容易遗漏 | 全局视角,提前发现问题 |
| 开发阶段 | if-else 嵌套地狱 | 清晰的 switch-case |
| 调试阶段 | 不知道当前在哪个状态 | 一眼看出状态转换逻辑 |
| 维护阶段 | 要读完整代码才能理解 | 看图就知道系统行为 |
| 交接阶段 | 难以理解前人代码 | 图文结合,快速上手 |
二、什么时候"必须"用状态图?
判断标准
只要满足下面任意一条,就该画状态图:
✅ 系统有多种运行模式
例如:待机、运行、睡眠
✅ 行为依赖于当前状态
例如:只有在"空闲"状态才能接收新任务
✅ 同一个事件,在不同状态下行为不同
例如:按下按钮,在不同模式下响应不同
✅ 用 if/else 已经开始乱了
例如:多层嵌套,看不清楚逻辑
✅ 需要记录"系统在干什么"
例如:设备状态监控、日志记录
嵌入式中的典型场景
| 应用场景 | 为什么需要状态图 |
|---|---|
| 设备控制 | 初始化 → 就绪 → 运行 → 故障 |
| 通信协议 | 连接 → 握手 → 传输 → 断开 |
| 电源管理 | 正常 → 低功耗 → 睡眠 → 唤醒 |
| 用户界面 | 主菜单 → 子菜单 → 设置 → 返回 |
| 传感器采集 | 空闲 → 采样 → 滤波 → 上报 |
| 电机控制 | 停止 → 启动 → 加速 → 匀速 → 减速 |
👉 嵌入式项目 90% 都满足这些条件
三、状态图的核心元素详解
元素对照表
| UML 元素 | UML 含义 | C 语言对应 | 图形表示 |
|---|---|---|---|
| State | 系统所处状态 | enum |
圆角矩形 |
| Event | 触发条件 | 标志位/消息 | 箭头上的标签 |
| Transition | 状态切换 | state = XXX |
箭头 |
| Action | 进入/退出动作 | 函数调用 | entry: / exit: |
| Guard | 转换条件 | if (condition) |
[condition] |
| Initial State | 初始状态 | 初始值 | 实心圆 |
| Final State | 终止状态 | 结束标记 | 圆圈内有实心圆 |
详细解释
1. State(状态)
定义:系统在某个时间段内的稳定行为模式
图形表示:
┌──────────────┐
│ 状态名称 │
├──────────────┤
│ entry: 动作 │
│ do: 活动 │
│ exit: 动作 │
└──────────────┘
C 语言实现:
c
typedef enum {
STATE_IDLE,
STATE_RUN,
STATE_ERROR
} SystemState_t;
SystemState_t currentState = STATE_IDLE;
判断标准:
- ✅ 是一段稳定的行为区间
- ✅ 有明确的进入条件和退出条件
- ❌ 不是瞬时的动作(如"发送数据"不是状态)
2. Event(事件)
定义:触发状态转换的外部或内部条件
常见形式:
| 事件类型 | 示例 | C 语言实现 |
|---|---|---|
| 外部信号 | 按键、传感器 | if (key_pressed) |
| 定时器 | 超时 | if (timer_expired) |
| 消息 | RTOS队列 | xQueueReceive() |
| 中断 | UART接收 | HAL_UART_RxCpltCallback() |
| 条件 | 阈值判断 | if (temp > 50) |
命名规范:
c
// 好的命名(动词形式)
btn_pressed
timeout_occurred
data_received
error_detected
// 不好的命名
btn // 不清楚是什么事件
data // 太抽象
error // 是什么错误?
3. Transition(转换)
定义:从一个状态到另一个状态的变化
完整语法:
事件[守卫条件] / 动作
示例:
btn_pressed[count < 10] / count++
C 语言实现:
c
// 完整的转换逻辑
if (btn_pressed && count < 10) { // 事件 + 守卫
count++; // 动作
currentState = STATE_RUN; // 状态转换
}
4. Action(动作)
分类:
| 动作类型 | 触发时机 | UML 写法 | C 实现 |
|---|---|---|---|
| Entry | 进入状态时执行一次 | entry: Motor_Start() |
首次进入时调用 |
| Exit | 离开状态时执行一次 | exit: Motor_Stop() |
转换前调用 |
| Do | 在状态内持续执行 | do: ReadSensor() |
每次循环都执行 |
| Transition | 状态转换时执行 | event / Action() |
转换时调用一次 |
实现模式:
c
// Entry/Exit 动作的标准实现
typedef enum {
STATE_IDLE,
STATE_RUN,
STATE_ERROR
} State_t;
State_t currentState = STATE_IDLE;
State_t lastState = STATE_IDLE;
void StateMachine_Update(void) {
// 检测状态变化
if (currentState != lastState) {
// Exit 动作(离开旧状态)
switch (lastState) {
case STATE_RUN:
Motor_Stop(); // exit action
break;
}
// Entry 动作(进入新状态)
switch (currentState) {
case STATE_RUN:
Motor_Start(); // entry action
break;
}
lastState = currentState;
}
// Do 动作(状态内活动)
switch (currentState) {
case STATE_RUN:
ReadSensor(); // do activity
break;
}
}
5. Guard(守卫条件)
定义 :在方括号 [] 中的布尔条件,决定转换是否发生
示例:
event[temp > 100] → STATE_OVERHEAT
event[battery < 10%] → STATE_LOW_POWER
C 实现:
c
if (event_occurred && temp > 100) {
currentState = STATE_OVERHEAT;
}
四、从需求到代码的完整流程
流程图
需求分析 → 列出状态 → 确定事件 → 画状态图 → 编写代码 → 测试验证
完整示例:STM32 设备控制
🎯 需求描述
设计一个设备控制系统,要求:
- 上电后自动初始化
- 初始化完成后进入空闲状态
- 收到启动命令后开始运行
- 运行过程中如果检测到错误,进入错误状态
- 错误状态下收到复位命令,返回空闲状态
- 任何状态下收到关机命令,进入关机流程
步骤 1:列出所有状态
c
typedef enum {
STATE_INIT, // 初始化状态
STATE_IDLE, // 空闲状态
STATE_RUN, // 运行状态
STATE_ERROR, // 错误状态
STATE_SHUTDOWN // 关机状态
} DeviceState_t;
状态划分原则:
- ✅ 每个状态都是一个稳定的行为区间
- ✅ 状态之间有明确的边界
- ✅ 状态数量合理(建议 3-7 个)
步骤 2:确定所有事件
c
typedef enum {
EVENT_NONE,
EVENT_INIT_DONE, // 初始化完成
EVENT_START_CMD, // 启动命令
EVENT_STOP_CMD, // 停止命令
EVENT_ERROR, // 错误检测
EVENT_RESET_CMD, // 复位命令
EVENT_SHUTDOWN_CMD // 关机命令
} DeviceEvent_t;
步骤 3:画 UML 状态图
● (开始)
│
▼
┌─────────┐
│ INIT │
│─────────│
│entry: │
│ HW_Init()│
└────┬────┘
│ init_done
▼
┌─────────┐
┌─────│ IDLE │◄─────────┐
│ │─────────│ │
│ │entry: │ │ reset_cmd
│ │ LED_Off()│ │
│ └────┬────┘ │
│ │ start_cmd │
│ ▼ │
│ ┌─────────┐ │
│ │ RUN │──────────┘
│ │─────────│
│ │entry: │
│ │ Motor_On()│
│ │exit: │
│ │Motor_Off()│
│ └────┬────┘
│ │ error_detected
│ ▼
│ ┌─────────┐
└────>│ ERROR │
│─────────│
│entry: │
│ LED_Red()│
└────┬────┘
│ shutdown_cmd
▼
┌─────────┐
│SHUTDOWN │
│─────────│
│entry: │
│ Cleanup()│
└────┬────┘
│
▼
◉ (结束)
步骤 4:状态转换表(设计文档)
| 当前状态 | 事件 | 守卫条件 | 动作 | 下一状态 |
|---|---|---|---|---|
| INIT | init_done | - | - | IDLE |
| IDLE | start_cmd | - | - | RUN |
| RUN | stop_cmd | - | - | IDLE |
| RUN | error_detected | - | - | ERROR |
| ERROR | reset_cmd | - | - | IDLE |
| * | shutdown_cmd | - | Cleanup() | SHUTDOWN |
说明 :* 表示任何状态
步骤 5:编写 C 代码
5.1 状态机结构体
c
typedef struct {
DeviceState_t currentState;
DeviceState_t lastState;
DeviceEvent_t event;
bool stateChanged;
} StateMachine_t;
static StateMachine_t sm = {
.currentState = STATE_INIT,
.lastState = STATE_INIT,
.event = EVENT_NONE,
.stateChanged = false
};
5.2 状态机核心函数
c
void StateMachine_Update(void)
{
// 1. 检测状态变化,执行 Entry/Exit 动作
if (sm.currentState != sm.lastState) {
// Exit 动作
StateMachine_ExitAction(sm.lastState);
// Entry 动作
StateMachine_EntryAction(sm.currentState);
sm.lastState = sm.currentState;
sm.stateChanged = true;
} else {
sm.stateChanged = false;
}
// 2. 状态转换逻辑
switch (sm.currentState)
{
case STATE_INIT:
if (sm.event == EVENT_INIT_DONE) {
sm.currentState = STATE_IDLE;
}
break;
case STATE_IDLE:
if (sm.event == EVENT_START_CMD) {
sm.currentState = STATE_RUN;
}
break;
case STATE_RUN:
if (sm.event == EVENT_STOP_CMD) {
sm.currentState = STATE_IDLE;
}
else if (sm.event == EVENT_ERROR) {
sm.currentState = STATE_ERROR;
}
break;
case STATE_ERROR:
if (sm.event == EVENT_RESET_CMD) {
sm.currentState = STATE_IDLE;
}
break;
case STATE_SHUTDOWN:
// 终止状态,不再转换
break;
}
// 3. 全局转换(任何状态都响应)
if (sm.event == EVENT_SHUTDOWN_CMD) {
sm.currentState = STATE_SHUTDOWN;
}
// 4. 清除事件
sm.event = EVENT_NONE;
// 5. Do 动作(状态内活动)
StateMachine_DoAction(sm.currentState);
}
5.3 Entry/Exit/Do 动作
c
void StateMachine_EntryAction(DeviceState_t state)
{
switch (state)
{
case STATE_INIT:
printf("Entering INIT state\n");
HW_Init();
// 初始化完成后自动触发事件
StateMachine_SendEvent(EVENT_INIT_DONE);
break;
case STATE_IDLE:
printf("Entering IDLE state\n");
LED_Off();
break;
case STATE_RUN:
printf("Entering RUN state\n");
Motor_Start();
LED_Green();
break;
case STATE_ERROR:
printf("Entering ERROR state\n");
LED_Red();
Buzzer_On();
break;
case STATE_SHUTDOWN:
printf("Entering SHUTDOWN state\n");
Cleanup();
break;
}
}
void StateMachine_ExitAction(DeviceState_t state)
{
switch (state)
{
case STATE_RUN:
printf("Exiting RUN state\n");
Motor_Stop();
break;
case STATE_ERROR:
printf("Exiting ERROR state\n");
Buzzer_Off();
break;
default:
// 其他状态没有特殊的退出动作
break;
}
}
void StateMachine_DoAction(DeviceState_t state)
{
switch (state)
{
case STATE_RUN:
// 运行状态下持续监测
if (CheckError()) {
StateMachine_SendEvent(EVENT_ERROR);
}
UpdateSensors();
break;
case STATE_ERROR:
// 错误状态下闪烁LED
LED_Toggle();
break;
default:
break;
}
}
5.4 事件发送接口
c
void StateMachine_SendEvent(DeviceEvent_t event)
{
sm.event = event;
}
DeviceState_t StateMachine_GetCurrentState(void)
{
return sm.currentState;
}
const char* StateMachine_GetStateName(DeviceState_t state)
{
switch (state) {
case STATE_INIT: return "INIT";
case STATE_IDLE: return "IDLE";
case STATE_RUN: return "RUN";
case STATE_ERROR: return "ERROR";
case STATE_SHUTDOWN: return "SHUTDOWN";
default: return "UNKNOWN";
}
}
5.5 主循环使用
c
int main(void)
{
HAL_Init();
while (1)
{
// 1. 更新状态机
StateMachine_Update();
// 2. 处理外部输入
if (Button_Pressed()) {
StateMachine_SendEvent(EVENT_START_CMD);
}
// 3. 延时
HAL_Delay(10);
}
}
PlantUML 代码
plantuml
@startuml DeviceStateMachine
[*] --> INIT
state INIT {
INIT : entry: HW_Init()
}
state IDLE {
IDLE : entry: LED_Off()
}
state RUN {
RUN : entry: Motor_Start()
RUN : do: UpdateSensors()
RUN : exit: Motor_Stop()
}
state ERROR {
ERROR : entry: LED_Red()
ERROR : entry: Buzzer_On()
ERROR : exit: Buzzer_Off()
}
state SHUTDOWN {
SHUTDOWN : entry: Cleanup()
}
INIT --> IDLE : init_done
IDLE --> RUN : start_cmd
RUN --> IDLE : stop_cmd
RUN --> ERROR : error_detected
ERROR --> IDLE : reset_cmd
INIT --> SHUTDOWN : shutdown_cmd
IDLE --> SHUTDOWN : shutdown_cmd
RUN --> SHUTDOWN : shutdown_cmd
ERROR --> SHUTDOWN : shutdown_cmd
SHUTDOWN --> [*]
@enduml
五、完整实战案例
案例 1:电机控制器状态机
需求描述
- 电机有停止、启动、加速、匀速、减速五个状态
- 启动后经过加速阶段进入匀速运行
- 收到停止命令后经过减速阶段才停止
- 任何状态下检测到过流立即停止
状态图
●
│
▼
┌────────┐
│ STOP │◄──────────┐
│────────│ │
│speed=0 │ │ overcurrent
└───┬────┘ │
│ start_cmd │
▼ │
┌────────┐ │
│ START │ │
│────────│ │
│speed++ │ │
└───┬────┘ │
│ speed>=min │
▼ │
┌────────┐ │
│ ACCEL │ │
│────────│ │
│speed++ │ │
└───┬────┘ │
│ speed>=target │
▼ │
┌────────┐ │
│ STEADY│───────────┤
│────────│ │
│speed=target │
└───┬────┘ │
│ stop_cmd │
▼ │
┌────────┐ │
│ DECEL │ │
│────────│ │
│speed-- │ │
└───┬────┘ │
│ speed<=0 │
└─────────────────┘
代码实现
c
typedef enum {
MOTOR_STOP,
MOTOR_START,
MOTOR_ACCEL,
MOTOR_STEADY,
MOTOR_DECEL
} MotorState_t;
typedef struct {
MotorState_t state;
uint16_t speed;
uint16_t targetSpeed;
bool startCmd;
bool stopCmd;
bool overcurrent;
} MotorController_t;
static MotorController_t motor = {
.state = MOTOR_STOP,
.speed = 0,
.targetSpeed = 1000,
.startCmd = false,
.stopCmd = false,
.overcurrent = false
};
void MotorController_Update(void)
{
// 全局紧急停止
if (motor.overcurrent) {
motor.speed = 0;
motor.state = MOTOR_STOP;
Motor_SetSpeed(0);
return;
}
switch (motor.state)
{
case MOTOR_STOP:
motor.speed = 0;
Motor_SetSpeed(0);
if (motor.startCmd) {
motor.startCmd = false;
motor.state = MOTOR_START;
}
break;
case MOTOR_START:
motor.speed += 10; // 启动加速度
Motor_SetSpeed(motor.speed);
if (motor.speed >= 100) { // 最小稳定速度
motor.state = MOTOR_ACCEL;
}
break;
case MOTOR_ACCEL:
if (motor.speed < motor.targetSpeed) {
motor.speed += 5; // 加速度
Motor_SetSpeed(motor.speed);
}
if (motor.speed >= motor.targetSpeed) {
motor.state = MOTOR_STEADY;
}
if (motor.stopCmd) {
motor.stopCmd = false;
motor.state = MOTOR_DECEL;
}
break;
case MOTOR_STEADY:
motor.speed = motor.targetSpeed;
Motor_SetSpeed(motor.speed);
if (motor.stopCmd) {
motor.stopCmd = false;
motor.state = MOTOR_DECEL;
}
break;
case MOTOR_DECEL:
if (motor.speed > 0) {
motor.speed -= 10; // 减速度
if (motor.speed < 10) {
motor.speed = 0;
}
Motor_SetSpeed(motor.speed);
}
if (motor.speed == 0) {
motor.state = MOTOR_STOP;
}
break;
}
}
案例 2:TCP 连接状态机(协议栈)
状态图
●
│
▼
┌────────┐
│ CLOSED │
└───┬────┘
│ open
▼
┌────────┐
│ LISTEN │
└───┬────┘
│ syn_received
▼
┌────────┐
│SYN_RCVD│
└───┬────┘
│ ack_received
▼
┌────────┐
│ESTABLISH│
└───┬────┘
│ close / fin_received
▼
┌────────┐
│FIN_WAIT│
└───┬────┘
│ ack_received
▼
┌────────┐
│ CLOSED │
└────────┘
代码实现
c
typedef enum {
TCP_CLOSED,
TCP_LISTEN,
TCP_SYN_RCVD,
TCP_ESTABLISHED,
TCP_FIN_WAIT,
TCP_CLOSING,
TCP_TIME_WAIT
} TcpState_t;
typedef struct {
TcpState_t state;
uint32_t timeout;
} TcpConnection_t;
void TCP_StateMachine(TcpConnection_t *conn, TcpEvent_t event)
{
switch (conn->state)
{
case TCP_CLOSED:
if (event == TCP_EVENT_OPEN) {
conn->state = TCP_LISTEN;
}
break;
case TCP_LISTEN:
if (event == TCP_EVENT_SYN_RECEIVED) {
TCP_SendSynAck();
conn->state = TCP_SYN_RCVD;
}
break;
case TCP_SYN_RCVD:
if (event == TCP_EVENT_ACK_RECEIVED) {
conn->state = TCP_ESTABLISHED;
}
else if (event == TCP_EVENT_TIMEOUT) {
conn->state = TCP_CLOSED;
}
break;
case TCP_ESTABLISHED:
if (event == TCP_EVENT_CLOSE) {
TCP_SendFin();
conn->state = TCP_FIN_WAIT;
}
else if (event == TCP_EVENT_FIN_RECEIVED) {
TCP_SendAck();
conn->state = TCP_CLOSING;
}
break;
case TCP_FIN_WAIT:
if (event == TCP_EVENT_ACK_RECEIVED) {
conn->state = TCP_CLOSED;
}
break;
case TCP_CLOSING:
if (event == TCP_EVENT_ACK_RECEIVED) {
conn->state = TCP_TIME_WAIT;
conn->timeout = GetTick() + 2000; // 2秒超时
}
break;
case TCP_TIME_WAIT:
if (GetTick() >= conn->timeout) {
conn->state = TCP_CLOSED;
}
break;
}
}
案例 3:按键状态机(防抖 + 长按检测)
需求描述
- 检测按键按下、释放
- 防抖处理
- 区分短按和长按
状态图
┌────────┐
┌──>│ IDLE │
│ └───┬────┘
│ │ key_down
│ ▼
│ ┌────────┐
│ │DEBOUNCE│
│ └───┬────┘
│ │ timer > 50ms
│ ▼
│ ┌────────┐
│ │PRESSED │
│ └───┬────┘
│ │ timer > 1000ms
│ ▼
│ ┌────────┐
│ │LONGPRESS
│ └───┬────┘
│ │ key_up
│ ▼
│ ┌────────┐
└───│RELEASED│
└────────┘
代码实现
c
typedef enum {
KEY_IDLE,
KEY_DEBOUNCE,
KEY_PRESSED,
KEY_LONG_PRESS,
KEY_RELEASED
} KeyState_t;
typedef struct {
KeyState_t state;
uint32_t pressTime;
bool shortPressDetected;
bool longPressDetected;
} KeyStateMachine_t;
static KeyStateMachine_t key = {
.state = KEY_IDLE,
.pressTime = 0,
.shortPressDetected = false,
.longPressDetected = false
};
void Key_StateMachine_Update(void)
{
bool keyDown = HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_RESET;
uint32_t currentTime = HAL_GetTick();
switch (key.state)
{
case KEY_IDLE:
if (keyDown) {
key.pressTime = currentTime;
key.state = KEY_DEBOUNCE;
}
break;
case KEY_DEBOUNCE:
if (!keyDown) {
// 抖动,回到空闲
key.state = KEY_IDLE;
}
else if (currentTime - key.pressTime > 50) {
// 防抖时间到,确认按下
key.state = KEY_PRESSED;
}
break;
case KEY_PRESSED:
if (!keyDown) {
// 短按
key.shortPressDetected = true;
key.state = KEY_RELEASED;
}
else if (currentTime - key.pressTime > 1000) {
// 长按阈值
key.state = KEY_LONG_PRESS;
}
break;
case KEY_LONG_PRESS:
if (!keyDown) {
// 长按释放
key.longPressDetected = true;
key.state = KEY_RELEASED;
}
// 可以在这里持续触发长按事件
break;
case KEY_RELEASED:
// 等待按键完全释放
if (!keyDown) {
key.state = KEY_IDLE;
}
break;
}
}
bool Key_IsShortPressed(void)
{
if (key.shortPressDetected) {
key.shortPressDetected = false;
return true;
}
return false;
}
bool Key_IsLongPressed(void)
{
if (key.longPressDetected) {
key.longPressDetected = false;
return true;
}
return false;
}
案例 4:菜单系统状态机(层次状态)
状态图
┌──────────────────┐
│ MENU_SYSTEM │
└────────┬─────────┘
│
┌────────────────┼────────────────┐
│ │ │
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐
│ MAIN │ │SETTINGS│ │ INFO │
│ │ │ │ │ │
└────┬───┘ └────┬───┘ └────────┘
│ │
│ └──────┬─────────┐
▼ ▼ ▼
┌────────┐ ┌──────────┐ ┌──────────┐
│ RUN │ │ DATETIME │ │ ALARM │
└────────┘ └──────────┘ └──────────┘
代码实现(层次状态机)
c
typedef enum {
MENU_MAIN,
MENU_RUN,
MENU_SETTINGS,
MENU_DATETIME,
MENU_ALARM,
MENU_INFO
} MenuState_t;
typedef struct {
MenuState_t current;
MenuState_t parent;
uint8_t selectedItem;
} MenuStateMachine_t;
static MenuStateMachine_t menu = {
.current = MENU_MAIN,
.parent = MENU_MAIN,
.selectedItem = 0
};
void Menu_StateMachine(MenuEvent_t event)
{
switch (menu.current)
{
case MENU_MAIN:
if (event == EVENT_SELECT) {
switch (menu.selectedItem) {
case 0:
menu.current = MENU_RUN;
menu.parent = MENU_MAIN;
break;
case 1:
menu.current = MENU_SETTINGS;
menu.parent = MENU_MAIN;
break;
case 2:
menu.current = MENU_INFO;
menu.parent = MENU_MAIN;
break;
}
}
else if (event == EVENT_UP) {
menu.selectedItem = (menu.selectedItem > 0) ?
menu.selectedItem - 1 : 2;
}
else if (event == EVENT_DOWN) {
menu.selectedItem = (menu.selectedItem < 2) ?
menu.selectedItem + 1 : 0;
}
break;
case MENU_SETTINGS:
if (event == EVENT_SELECT) {
switch (menu.selectedItem) {
case 0:
menu.current = MENU_DATETIME;
menu.parent = MENU_SETTINGS;
break;
case 1:
menu.current = MENU_ALARM;
menu.parent = MENU_SETTINGS;
break;
}
}
else if (event == EVENT_BACK) {
menu.current = menu.parent;
}
break;
case MENU_DATETIME:
case MENU_ALARM:
case MENU_INFO:
if (event == EVENT_BACK) {
menu.current = menu.parent;
}
break;
case MENU_RUN:
if (event == EVENT_BACK) {
menu.current = MENU_MAIN;
}
break;
}
}
案例 5:洗衣机控制器
需求描述
- 开机 → 选择模式 → 启动 → 洗涤 → 漂洗 → 脱水 → 完成
- 任何阶段可以暂停/恢复
- 开门时强制暂停
状态图
●
│
▼
┌────────┐
│ READY │
└───┬────┘
│ start
▼
┌────────┐
│ WASH │◄─────┐
└───┬────┘ │
│ timeout │ resume
▼ │
┌────────┐ │
│ RINSE │ │
└───┬────┘ │
│ timeout │
▼ │
┌────────┐ │
│ SPIN │ │
└───┬────┘ │
│ timeout │
▼ │
┌────────┐ │
│ DONE │ │
└────────┘ │
│ │
│ pause / door_open
▼ │
┌────────┐ │
│ PAUSE │──────┘
└────────┘
代码实现
c
typedef enum {
WM_READY,
WM_WASH,
WM_RINSE,
WM_SPIN,
WM_PAUSE,
WM_DONE
} WashingMachineState_t;
typedef struct {
WashingMachineState_t state;
WashingMachineState_t stateBeforePause;
uint32_t timer;
bool doorOpen;
} WashingMachine_t;
static WashingMachine_t wm = {
.state = WM_READY,
.stateBeforePause = WM_READY,
.timer = 0,
.doorOpen = false
};
void WashingMachine_Update(void)
{
uint32_t currentTime = HAL_GetTick();
// 全局条件:门打开时强制暂停
if (wm.doorOpen && wm.state != WM_PAUSE && wm.state != WM_READY) {
wm.stateBeforePause = wm.state;
wm.state = WM_PAUSE;
Motor_Stop();
return;
}
switch (wm.state)
{
case WM_READY:
if (GetStartButton() && !wm.doorOpen) {
wm.state = WM_WASH;
wm.timer = currentTime;
Motor_Start(MOTOR_WASH_SPEED);
Valve_Open();
}
break;
case WM_WASH:
if (currentTime - wm.timer > 10000) { // 10秒洗涤
wm.state = WM_RINSE;
wm.timer = currentTime;
Valve_Close();
Pump_On(); // 排水
}
break;
case WM_RINSE:
if (currentTime - wm.timer > 5000) { // 5秒漂洗
wm.state = WM_SPIN;
wm.timer = currentTime;
Pump_Off();
Motor_Start(MOTOR_SPIN_SPEED);
}
break;
case WM_SPIN:
if (currentTime - wm.timer > 5000) { // 5秒脱水
wm.state = WM_DONE;
Motor_Stop();
Buzzer_Beep();
}
break;
case WM_PAUSE:
Motor_Stop();
if (GetResumeButton() && !wm.doorOpen) {
wm.state = wm.stateBeforePause;
wm.timer = currentTime; // 重置定时器
// 恢复对应状态的硬件
switch (wm.state) {
case WM_WASH:
Motor_Start(MOTOR_WASH_SPEED);
break;
case WM_SPIN:
Motor_Start(MOTOR_SPIN_SPEED);
break;
default:
break;
}
}
break;
case WM_DONE:
if (GetResetButton()) {
wm.state = WM_READY;
}
break;
}
}
案例 6:UART 接收状态机(协议解析)
场景描述
接收格式:[STX][LEN][DATA...][CHK][ETX]
- STX = 0x02 (起始符)
- LEN = 数据长度
- DATA = 实际数据
- CHK = 校验和
- ETX = 0x03 (结束符)
状态图
┌────────┐
│ IDLE │←────────────┐
└───┬────┘ │
│ rx == STX │ error / timeout
▼ │
┌────────┐ │
│ LEN │─────────────┤
└───┬────┘ │
│ rx byte │
▼ │
┌────────┐ │
│ DATA │─────────────┤
└───┬────┘ │
│ count == len │
▼ │
┌────────┐ │
│ CHK │─────────────┤
└───┬────┘ │
│ rx byte │
▼ │
┌────────┐ │
│ ETX │─────────────┤
└───┬────┘ │
│ rx == ETX │
▼ │
┌────────┐ │
│ DONE │─────────────┘
└────────┘
代码实现
c
typedef enum {
UART_RX_IDLE,
UART_RX_LEN,
UART_RX_DATA,
UART_RX_CHK,
UART_RX_ETX,
UART_RX_DONE,
UART_RX_ERROR
} UartRxState_t;
#define STX 0x02
#define ETX 0x03
#define MAX_DATA_LEN 128
typedef struct {
UartRxState_t state;
uint8_t buffer[MAX_DATA_LEN];
uint8_t length;
uint8_t index;
uint8_t checksum;
uint32_t timeout;
} UartRxStateMachine_t;
static UartRxStateMachine_t uart = {
.state = UART_RX_IDLE,
.length = 0,
.index = 0,
.checksum = 0,
.timeout = 0
};
void UART_RxStateMachine(uint8_t rxByte)
{
uint32_t currentTime = HAL_GetTick();
// 超时检测
if (uart.state != UART_RX_IDLE &&
currentTime - uart.timeout > 1000) {
uart.state = UART_RX_ERROR;
return;
}
switch (uart.state)
{
case UART_RX_IDLE:
if (rxByte == STX) {
uart.state = UART_RX_LEN;
uart.index = 0;
uart.checksum = 0;
uart.timeout = currentTime;
}
break;
case UART_RX_LEN:
uart.length = rxByte;
if (uart.length > 0 && uart.length <= MAX_DATA_LEN) {
uart.state = UART_RX_DATA;
uart.checksum += rxByte;
} else {
uart.state = UART_RX_ERROR;
}
break;
case UART_RX_DATA:
uart.buffer[uart.index++] = rxByte;
uart.checksum += rxByte;
if (uart.index >= uart.length) {
uart.state = UART_RX_CHK;
}
break;
case UART_RX_CHK:
if (rxByte == uart.checksum) {
uart.state = UART_RX_ETX;
} else {
uart.state = UART_RX_ERROR;
}
break;
case UART_RX_ETX:
if (rxByte == ETX) {
uart.state = UART_RX_DONE;
// 处理接收到的完整数据包
ProcessReceivedData(uart.buffer, uart.length);
} else {
uart.state = UART_RX_ERROR;
}
break;
case UART_RX_DONE:
case UART_RX_ERROR:
// 重置状态机
uart.state = UART_RX_IDLE;
break;
}
}
案例 7:电源管理状态机(低功耗设计)
需求描述
- 正常运行
- 空闲一段时间后进入低功耗模式
- 有活动时唤醒
- 电池低电量时进入深度睡眠
状态图
┌────────┐
│ NORMAL │◄───────────┐
└───┬────┘ │
│ idle_timeout │ activity
▼ │
┌────────┐ │
│ IDLE │────────────┘
└───┬────┘
│ sleep_timeout
▼
┌────────┐
│ SLEEP │◄───────────┐
└───┬────┘ │
│ wakeup │
└─────────────────┘
│ battery_low
▼
┌────────┐
│DEEP_SLEEP
└────────┘
代码实现
c
typedef enum {
PWR_NORMAL,
PWR_IDLE,
PWR_SLEEP,
PWR_DEEP_SLEEP
} PowerState_t;
typedef struct {
PowerState_t state;
uint32_t lastActivity;
uint16_t batteryLevel;
} PowerManagement_t;
static PowerManagement_t pwr = {
.state = PWR_NORMAL,
.lastActivity = 0,
.batteryLevel = 100
};
#define IDLE_TIMEOUT 10000 // 10秒无活动进入IDLE
#define SLEEP_TIMEOUT 30000 // 30秒无活动进入SLEEP
#define LOW_BATTERY_THRESHOLD 10
void PowerManagement_Update(void)
{
uint32_t currentTime = HAL_GetTick();
uint32_t idleTime = currentTime - pwr.lastActivity;
// 读取电池电量
pwr.batteryLevel = ReadBatteryLevel();
// 电池低电量强制深度睡眠
if (pwr.batteryLevel < LOW_BATTERY_THRESHOLD) {
if (pwr.state != PWR_DEEP_SLEEP) {
PowerManagement_EnterDeepSleep();
pwr.state = PWR_DEEP_SLEEP;
}
return;
}
switch (pwr.state)
{
case PWR_NORMAL:
// 正常运行
UpdateDisplay();
ProcessSensors();
if (idleTime > IDLE_TIMEOUT) {
pwr.state = PWR_IDLE;
// Entry action
Display_Dim();
ReduceCPU_Frequency();
}
break;
case PWR_IDLE:
// 降低功耗模式
if (idleTime > SLEEP_TIMEOUT) {
pwr.state = PWR_SLEEP;
// Entry action
Display_Off();
DisablePeripherals();
EnterSleepMode();
}
// 有活动时唤醒
if (DetectActivity()) {
pwr.state = PWR_NORMAL;
pwr.lastActivity = currentTime;
// Entry action
Display_Bright();
RestoreCPU_Frequency();
}
break;
case PWR_SLEEP:
// 睡眠模式(等待中断唤醒)
// 这里通常由中断唤醒
break;
case PWR_DEEP_SLEEP:
// 深度睡眠模式
// 只能通过复位或特定外部事件唤醒
break;
}
}
void PowerManagement_OnActivity(void)
{
pwr.lastActivity = HAL_GetTick();
if (pwr.state == PWR_SLEEP) {
// 从睡眠唤醒
ExitSleepMode();
EnablePeripherals();
Display_On();
pwr.state = PWR_NORMAL;
}
else if (pwr.state == PWR_IDLE) {
pwr.state = PWR_NORMAL;
}
}
// 中断回调中调用
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == WAKEUP_PIN) {
PowerManagement_OnActivity();
}
}
六、高级特性:层次状态与并发
层次状态(Hierarchical State)
概念
定义:一个状态可以包含子状态,形成状态的层次结构。
优势:
- 减少状态转换数量
- 复用公共转换
- 提高可维护性
示例:RUN 状态的层次结构
┌────────── RUN ─────────────┐
│ │
│ ┌──────┐ ┌──────┐ │
│ │IDLE │───>│WORK │ │
│ └──────┘ └──┬───┘ │
│ │ │
│ ▼ │
│ ┌──────┐ │
│ │PAUSE │ │
│ └──────┘ │
│ │
└────────────────────────────┘
│ stop
▼
┌──────┐
│STOP │
└──────┘
特点:
- 在 RUN 的任何子状态下,收到 stop 事件都会退出到 STOP
- 不需要为每个子状态都画到 STOP 的转换
代码实现
c
typedef enum {
STATE_STOP,
STATE_RUN,
STATE_RUN_IDLE,
STATE_RUN_WORK,
STATE_RUN_PAUSE
} HierarchicalState_t;
typedef struct {
HierarchicalState_t state;
HierarchicalState_t parent;
} StateMachine_t;
void StateMachine_Update(StateMachine_t *sm, Event_t event)
{
// 先检查父状态的转换
if (sm->parent == STATE_RUN) {
if (event == EVENT_STOP) {
sm->state = STATE_STOP;
sm->parent = STATE_STOP;
return;
}
}
// 再检查当前状态的转换
switch (sm->state)
{
case STATE_RUN_IDLE:
if (event == EVENT_START) {
sm->state = STATE_RUN_WORK;
}
break;
case STATE_RUN_WORK:
if (event == EVENT_PAUSE) {
sm->state = STATE_RUN_PAUSE;
}
break;
case STATE_RUN_PAUSE:
if (event == EVENT_RESUME) {
sm->state = STATE_RUN_WORK;
}
break;
}
}
并发状态(Concurrent States)
概念
定义:系统同时处于多个正交的状态。
适用场景:
- 多任务系统
- 独立的子系统
- 并行的行为
示例:智能手机状态
┌─────────────────────────────┐
│ Smartphone System │
├─────────────┬───────────────┤
│ │ │
│ Screen │ Network │
│ ┌────┐ │ ┌────┐ │
│ │On │ │ │WiFi│ │
│ └────┘ │ └────┘ │
│ ┌────┐ │ ┌────┐ │
│ │Off │ │ │4G │ │
│ └────┘ │ └────┘ │
│ │ ┌────┐ │
│ │ │Off │ │
│ │ └────┘ │
└─────────────┴───────────────┘
特点:
- 屏幕状态和网络状态独立
- 屏幕可以是 On,同时网络是 WiFi
- 两个维度互不影响
PlantUML 代码
plantuml
@startuml ConcurrentStates
state SmartphoneSystem {
state Screen {
state On
state Off
On --> Off : timeout
Off --> On : touch
}
--
state Network {
state WiFi
state FourG
state Offline
WiFi --> FourG : weak_signal
FourG --> Offline : no_signal
Offline --> WiFi : signal_restored
}
}
@enduml
七、状态表与代码生成
状态转换表
| 当前状态 | 事件 | 守卫条件 | 动作 | 下一状态 |
|---|---|---|---|---|
| IDLE | start | - | Motor_On() | RUN |
| RUN | stop | - | Motor_Off() | IDLE |
| RUN | error | temp > 80 | Alarm_On() | ERROR |
| ERROR | reset | - | Alarm_Off() | IDLE |
| * | emergency | - | EmergencyStop() | ERROR |
基于表驱动的状态机
c
typedef void (*ActionFunc)(void);
typedef bool (*GuardFunc)(void);
typedef struct {
State_t currentState;
Event_t event;
GuardFunc guard;
ActionFunc action;
State_t nextState;
} StateTransition_t;
// 状态转换表
const StateTransition_t transitionTable[] = {
{STATE_IDLE, EVENT_START, NULL, Motor_On, STATE_RUN},
{STATE_RUN, EVENT_STOP, NULL, Motor_Off, STATE_IDLE},
{STATE_RUN, EVENT_ERROR, CheckOverTemp, Alarm_On, STATE_ERROR},
{STATE_ERROR, EVENT_RESET, NULL, Alarm_Off, STATE_IDLE},
// 通配符状态
{STATE_ANY, EVENT_EMERGENCY, NULL, EmergencyStop, STATE_ERROR},
};
#define TRANSITION_COUNT (sizeof(transitionTable) / sizeof(StateTransition_t))
void StateMachine_Process(State_t *state, Event_t event)
{
for (int i = 0; i < TRANSITION_COUNT; i++)
{
const StateTransition_t *trans = &transitionTable[i];
// 检查状态匹配
if (trans->currentState != *state && trans->currentState != STATE_ANY) {
continue;
}
// 检查事件匹配
if (trans->event != event) {
continue;
}
// 检查守卫条件
if (trans->guard != NULL && !trans->guard()) {
continue;
}
// 执行动作
if (trans->action != NULL) {
trans->action();
}
// 状态转换
*state = trans->nextState;
break;
}
}
八、状态机的常见6个坑(避坑指南)
❌ 错误 1:把"动作"当"状态"
c
// 错误示例
typedef enum {
STATE_IDLE,
STATE_SENDING_DATA, // ❌ 这是动作,不是状态
STATE_RECEIVING_DATA // ❌ 这是动作,不是状态
} State_t;
// 正确示例
typedef enum {
STATE_IDLE,
STATE_CONNECTED, // ✅ 这是状态
STATE_DISCONNECTED // ✅ 这是状态
} State_t;
// 发送和接收是 CONNECTED 状态下的活动
判断标准:
- 状态 = 稳定的行为区间
- 动作 = 瞬时的操作
❌ 错误 2:状态过多(>7 个)
c
// 错误示例:状态太多
typedef enum {
STATE_INIT,
STATE_CHECK_CONFIG,
STATE_LOAD_DATA,
STATE_VERIFY_DATA,
STATE_PROCESS_STEP1,
STATE_PROCESS_STEP2,
STATE_PROCESS_STEP3,
STATE_SAVE_RESULT,
STATE_CLEANUP,
STATE_DONE,
STATE_ERROR1,
STATE_ERROR2,
STATE_ERROR3
} State_t; // ❌ 太多了!
// 正确示例:合并相似状态
typedef enum {
STATE_INIT,
STATE_PROCESS, // ✅ 合并多个处理步骤
STATE_DONE,
STATE_ERROR
} State_t;
// 使用子状态或步骤变量
typedef enum {
PROCESS_STEP1,
PROCESS_STEP2,
PROCESS_STEP3
} ProcessStep_t;
原则:
- 主状态不超过 7 个
- 使用层次状态分解复杂逻辑
- 使用步骤变量而不是状态
❌ 错误 3:状态中写阻塞代码
c
// 错误示例
case STATE_RUN:
HAL_Delay(1000); // ❌ 阻塞延时
ProcessData();
break;
// 正确示例
case STATE_RUN:
if (currentTime - lastTime > 1000) { // ✅ 非阻塞
ProcessData();
lastTime = currentTime;
}
break;
原则:
- 状态机应该快速执行
- 使用定时器而不是延时
- 异步处理长时间操作
❌ 错误 4:事件不清晰
c
// 错误示例:用标志位判断
if (flag1 && flag2 && !flag3) { // ❌ 逻辑混乱
state = STATE_RUN;
}
// 正确示例:明确的事件
if (event == EVENT_START && !errorFlag) { // ✅ 清晰
state = STATE_RUN;
}
原则:
- 定义明确的事件类型
- 事件名称具有描述性
- 避免复杂的条件判断
❌ 错误 5:没有 ERROR 状态
c
// 错误示例:没有错误处理
switch (state) {
case STATE_IDLE:
case STATE_RUN:
}
// ❌ 出错了怎么办?
// 正确示例:有错误状态
switch (state) {
case STATE_IDLE:
case STATE_RUN:
case STATE_ERROR: // ✅ 错误处理
if (resetCmd) {
state = STATE_IDLE;
}
break;
}
原则:
- 总是有错误/异常状态
- 错误状态有恢复机制
- 记录错误日志
❌ 错误 6:状态图和代码不一致
c
// 设计阶段画了状态图
// 开发阶段随意修改代码
// 两者完全不一致
// ❌ 文档失去意义
// 正确做法
// 1. 修改代码时同步更新状态图
// 2. 代码注释中引用状态图
// 3. 代码审查时检查一致性
原则:
- 状态图是活文档
- 代码变动时更新图
- 定期检查一致性
九、调试与优化技巧
1. 状态日志
c
void StateMachine_SetState(State_t newState)
{
State_t oldState = currentState;
currentState = newState;
// 状态变化日志
if (oldState != newState) {
printf("[%lu] State: %s -> %s\n",
HAL_GetTick(),
GetStateName(oldState),
GetStateName(newState));
}
}
const char* GetStateName(State_t state)
{
switch (state) {
case STATE_IDLE: return "IDLE";
case STATE_RUN: return "RUN";
case STATE_ERROR: return "ERROR";
default: return "UNKNOWN";
}
}
2. 状态可视化(LED 指示)
c
void UpdateStatusLED(void)
{
switch (currentState)
{
case STATE_IDLE:
LED_Green();
break;
case STATE_RUN:
LED_Blue_Blink();
break;
case STATE_ERROR:
LED_Red();
break;
}
}
3. 状态持续时间监测
c
typedef struct {
State_t state;
uint32_t enterTime;
uint32_t duration;
} StateInfo_t;
StateInfo_t stateInfo;
void OnStateEnter(State_t state)
{
stateInfo.state = state;
stateInfo.enterTime = HAL_GetTick();
}
void UpdateStateDuration(void)
{
stateInfo.duration = HAL_GetTick() - stateInfo.enterTime;
// 检测异常长时间停留
if (stateInfo.duration > 10000) {
printf("Warning: State %s timeout\n", GetStateName(stateInfo.state));
}
}
4. 状态转换计数
c
uint32_t transitionCount[STATE_MAX][STATE_MAX];
void RecordTransition(State_t from, State_t to)
{
transitionCount[from][to]++;
}
void PrintTransitionStatistics(void)
{
printf("State Transition Statistics:\n");
for (int i = 0; i < STATE_MAX; i++) {
for (int j = 0; j < STATE_MAX; j++) {
if (transitionCount[i][j] > 0) {
printf("%s -> %s: %lu\n",
GetStateName(i),
GetStateName(j),
transitionCount[i][j]);
}
}
}
}
十、PlantUML 绘制指南
基本语法
plantuml
@startuml
[*] --> State1
state State1 {
State1 : entry: Action1()
State1 : do: Activity()
State1 : exit: Action2()
}
State1 --> State2 : event[condition] / action
State2 --> [*]
@enduml
常用元素
| 元素 | 语法 | 说明 |
|---|---|---|
| 初始状态 | [*] |
黑色实心圆 |
| 终止状态 | [*] |
圆圈内有实心圆 |
| 状态 | state Name |
圆角矩形 |
| 转换 | S1 --> S2 : event |
箭头 |
| 守卫条件 | event[guard] |
方括号 |
| 动作 | event / action |
斜杠 |
| Entry | entry: Action() |
冒号 |
| Exit | exit: Action() |
冒号 |
| Do | do: Activity() |
冒号 |
完整示例
plantuml
@startuml DeviceStateMachine
title 设备控制状态机
[*] --> INIT
state INIT {
INIT : entry: HW_Init()
}
state IDLE {
IDLE : entry: LED_Off()
}
state RUN {
RUN : entry: Motor_Start()
RUN : do: ReadSensors()
RUN : exit: Motor_Stop()
}
state ERROR {
ERROR : entry: LED_Red()
ERROR : do: LED_Blink()
}
INIT --> IDLE : init_done
IDLE --> RUN : start_cmd
RUN --> IDLE : stop_cmd
RUN --> ERROR : error[temp > 80] / Alarm()
ERROR --> IDLE : reset_cmd
note right of RUN
运行状态下持续监测温度
超过80度自动进入错误状态
end note
@enduml
十一、总结与最佳实践
状态图的核心价值
1. 设计工具:先画图再写代码
2. 沟通工具:统一团队理解
3. 调试工具:对照检查逻辑
4. 文档工具:维护和交接
使用建议
✅ 写代码前先画状态图
✅ 调试时对照状态图
✅ 代码变动时更新状态图
✅ 状态数量控制在 3-7 个
✅ 总是有错误处理状态
✅ 使用非阻塞代码
✅ 明确的事件定义
附录:参考资源
- UML 2.5 State Machine Diagram Specification
- "Practical UML Statecharts in C/C++" - Miro Samek
- PlantUML 官网:https://plantuml.com
- 状态机设计模式