第二章:简单状态机实现
在理解了状态机的基本概念后,本章将详细介绍如何在C语言中实现简单的状态机。我们将探讨几种常见的实现方法,并通过一个LED控制器的案例来展示完整的状态机实现过程。
2.1 基于枚举的状态定义
在C语言中实现状态机,首先需要定义系统的所有可能状态。使用枚举类型(enum)是最常见且直观的方式,它提供了良好的代码可读性和类型安全性。
2.1.1 状态枚举定义
typedef enum {
STATE_IDLE, // 空闲状态
STATE_ACTIVE, // 活动状态
STATE_SUSPENDED, // 挂起状态
STATE_ERROR // 错误状态
} SystemState;
2.1.2 事件枚举定义
同样,我们也需要定义可能触发状态转换的事件:
typedef enum {
EVENT_START, // 启动事件
EVENT_STOP, // 停止事件
EVENT_SUSPEND, // 挂起事件
EVENT_RESUME, // 恢复事件
EVENT_ERROR, // 错误事件
EVENT_RESET // 重置事件
} SystemEvent;
2.1.3 状态机结构体
为了更好地组织状态机相关的数据,通常会定义一个状态机结构体:
typedef struct {
SystemState current_state; // 当前状态
// 其他状态机相关的数据
uint32_t state_entry_time; // 进入当前状态的时间
uint8_t error_code; // 错误代码(如果有)
// ...
} StateMachine;
2.1.4 状态机初始化
初始化状态机,设置初始状态:
void state_machine_init(StateMachine *sm) {
if (sm == NULL) {
return;
}
sm->current_state = STATE_IDLE; // 设置初始状态
sm->state_entry_time = get_system_time();
sm->error_code = 0;
// 执行初始状态的入口动作
state_entry_actions(sm, STATE_IDLE);
}
2.2 状态转换表
switch-case是实现状态机最直观的方式,它将状态转换逻辑集中在一个函数中,便于理解和维护。虽然本节标题为状态转换表,但我们首先介绍基于switch-case的实现方式,这是最基础的状态机实现方法。
2.2.1 基本结构
void state_machine_process_event(StateMachine *sm, SystemEvent event) {
if (sm == NULL) {
return;
}
SystemState next_state = sm->current_state; // 默认保持当前状态
// 根据当前状态和事件确定下一状态
switch (sm->current_state) {
case STATE_IDLE:
switch (event) {
case EVENT_START:
next_state = STATE_ACTIVE;
break;
case EVENT_ERROR:
next_state = STATE_ERROR;
break;
// 其他事件处理...
}
break;
case STATE_ACTIVE:
switch (event) {
case EVENT_STOP:
next_state = STATE_IDLE;
break;
case EVENT_SUSPEND:
next_state = STATE_SUSPENDED;
break;
case EVENT_ERROR:
next_state = STATE_ERROR;
break;
// 其他事件处理...
}
break;
// 其他状态处理...
}
// 如果状态发生变化,执行状态转换
if (next_state != sm->current_state) {
// 执行当前状态的退出动作
state_exit_actions(sm, sm->current_state);
// 更新状态
sm->current_state = next_state;
sm->state_entry_time = get_system_time();
// 执行新状态的入口动作
state_entry_actions(sm, next_state);
}
}
2.2.2 状态入口和退出动作
void state_entry_actions(StateMachine *sm, SystemState state) {
switch (state) {
case STATE_IDLE:
// 进入空闲状态的动作
stop_all_activities();
set_low_power_mode();
break;
case STATE_ACTIVE:
// 进入活动状态的动作
start_required_activities();
set_normal_power_mode();
break;
// 其他状态的入口动作...
}
}
void state_exit_actions(StateMachine *sm, SystemState state) {
switch (state) {
case STATE_IDLE:
// 离开空闲状态的动作
prepare_for_activity();
break;
case STATE_ACTIVE:
// 离开活动状态的动作
save_activity_data();
break;
// 其他状态的退出动作...
}
}
2.2.3 状态机主循环
void state_machine_run(StateMachine *sm) {
SystemEvent event;
while (1) {
// 获取事件(可以是从队列中取出,或者通过轮询检测)
event = get_next_event();
// 处理事件
if (event != EVENT_NONE) {
state_machine_process_event(sm, event);
}
// 执行当前状态的周期性动作
state_do_actions(sm);
// 系统延时或让出CPU
system_delay_or_yield();
}
}
void state_do_actions(StateMachine *sm) {
switch (sm->current_state) {
case STATE_IDLE:
// 空闲状态的周期性动作
check_for_wake_conditions();
break;
case STATE_ACTIVE:
// 活动状态的周期性动作
update_activity_progress();
check_for_completion();
break;
// 其他状态的周期性动作...
}
}
2.3 事件处理机制
在状态机中,事件处理是核心机制之一。除了基于switch-case的直接处理方式,我们还可以使用状态转换表的方法,它将状态转换逻辑从代码中分离出来,使用表格来定义状态转换规则。这种方法更加灵活,便于修改和扩展。
2.3.1 状态转换表定义
// 状态转换结构体
typedef struct {
SystemState current_state; // 当前状态
SystemEvent event; // 触发事件
SystemState next_state; // 下一状态
void (*action)(void); // 转换动作(可选)
} StateTransition;
// 状态转换表
const StateTransition state_transitions[] = {
// 当前状态 事件 下一状态 转换动作
{STATE_IDLE, EVENT_START, STATE_ACTIVE, start_action},
{STATE_IDLE, EVENT_ERROR, STATE_ERROR, log_error},
{STATE_ACTIVE, EVENT_STOP, STATE_IDLE, stop_action},
{STATE_ACTIVE, EVENT_SUSPEND, STATE_SUSPENDED, suspend_action},
{STATE_ACTIVE, EVENT_ERROR, STATE_ERROR, log_error},
{STATE_SUSPENDED, EVENT_RESUME, STATE_ACTIVE, resume_action},
{STATE_SUSPENDED, EVENT_STOP, STATE_IDLE, stop_action},
{STATE_ERROR, EVENT_RESET, STATE_IDLE, reset_action},
// 更多状态转换...
};
#define NUM_TRANSITIONS (sizeof(state_transitions) / sizeof(state_transitions[0]))
2.3.2 基于转换表的状态机处理函数
void state_machine_process_event(StateMachine *sm, SystemEvent event) {
if (sm == NULL) {
return;
}
// 在转换表中查找匹配的转换
for (int i = 0; i < NUM_TRANSITIONS; i++) {
if (state_transitions[i].current_state == sm->current_state &&
state_transitions[i].event == event) {
// 找到匹配的转换
SystemState next_state = state_transitions[i].next_state;
// 执行当前状态的退出动作
state_exit_actions(sm, sm->current_state);
// 执行转换动作(如果有)
if (state_transitions[i].action != NULL) {
state_transitions[i].action();
}
// 更新状态
sm->current_state = next_state;
sm->state_entry_time = get_system_time();
// 执行新状态的入口动作
state_entry_actions(sm, next_state);
return; // 找到并处理了转换,退出函数
}
}
// 如果没有找到匹配的转换,可以处理默认行为或记录未处理的事件
handle_unhandled_event(sm, event);
}
2.3.3 转换动作函数
void start_action(void) {
printf("Starting activity...\n");
// 执行启动相关操作
}
void stop_action(void) {
printf("Stopping activity...\n");
// 执行停止相关操作
}
void suspend_action(void) {
printf("Suspending activity...\n");
// 执行挂起相关操作
}
void resume_action(void) {
printf("Resuming activity...\n");
// 执行恢复相关操作
}
void log_error(void) {
printf("Error detected, logging...\n");
// 记录错误信息
}
void reset_action(void) {
printf("Resetting system...\n");
// 执行重置相关操作
}
2.3.4 状态转换表的优缺点
优点:
- 分离关注点:状态转换逻辑与动作代码分离
- 易于修改:添加或修改转换规则只需更新表格,不需要修改核心代码
- 可读性:转换规则以表格形式呈现,一目了然
- 可验证性:便于对状态转换规则进行形式化验证
缺点:
- 额外内存:转换表需要额外的存储空间
- 查找开销:需要在表中查找匹配的转换,可能影响性能
- 复杂条件:难以表达复杂的转换条件
- 调试困难:相比直接的代码逻辑,表驱动的方法可能更难调试
2.4 简单案例实现
现在,让我们通过一个实际的案例来展示如何实现一个简单的状态机。这个案例是一个LED控制器,它根据按钮输入控制LED的不同闪烁模式。
2.4.1 需求描述
设计一个LED控制器,具有以下功能:
- LED有四种状态:关闭、常亮、慢闪(1Hz)、快闪(5Hz)
- 通过按钮切换LED状态,每次按下按钮,LED状态按顺序循环切换
- 长按按钮(超过2秒)将LED直接切换到关闭状态
- 系统启动时,LED默认为关闭状态
2.4.2 状态和事件定义
// LED状态定义
typedef enum {
LED_STATE_OFF, // LED关闭
LED_STATE_ON, // LED常亮
LED_STATE_SLOW_BLINK, // LED慢闪
LED_STATE_FAST_BLINK // LED快闪
} LedState;
// 按钮事件定义
typedef enum {
BUTTON_EVENT_NONE, // 无事件
BUTTON_EVENT_PRESS, // 按钮按下
BUTTON_EVENT_LONG_PRESS // 按钮长按
} ButtonEvent;
// LED控制器结构体
typedef struct {
LedState current_state; // 当前LED状态
uint32_t state_entry_time; // 进入当前状态的时间
uint32_t blink_timestamp; // 闪烁计时器
bool led_output; // LED输出状态(开/关)
} LedController;
2.4.3 状态机实现(switch-case方式)
// LED控制器初始化
void led_controller_init(LedController *controller) {
if (controller == NULL) {
return;
}
controller->current_state = LED_STATE_OFF;
controller->state_entry_time = get_system_time_ms();
controller->blink_timestamp = 0;
controller->led_output = false;
// 设置LED初始状态为关闭
set_led_output(false);
}
// 处理按钮事件
void led_controller_process_event(LedController *controller, ButtonEvent event) {
if (controller == NULL) {
return;
}
LedState next_state = controller->current_state;
switch (controller->current_state) {
case LED_STATE_OFF:
if (event == BUTTON_EVENT_PRESS) {
next_state = LED_STATE_ON;
}
// 长按在关闭状态下无效
break;
case LED_STATE_ON:
if (event == BUTTON_EVENT_PRESS) {
next_state = LED_STATE_SLOW_BLINK;
} else if (event == BUTTON_EVENT_LONG_PRESS) {
next_state = LED_STATE_OFF;
}
break;
case LED_STATE_SLOW_BLINK:
if (event == BUTTON_EVENT_PRESS) {
next_state = LED_STATE_FAST_BLINK;
} else if (event == BUTTON_EVENT_LONG_PRESS) {
next_state = LED_STATE_OFF;
}
break;
case LED_STATE_FAST_BLINK:
if (event == BUTTON_EVENT_PRESS) {
next_state = LED_STATE_OFF;
} else if (event == BUTTON_EVENT_LONG_PRESS) {
next_state = LED_STATE_OFF;
}
break;
}
// 如果状态发生变化
if (next_state != controller->current_state) {
// 执行状态转换
controller->current_state = next_state;
controller->state_entry_time = get_system_time_ms();
controller->blink_timestamp = 0;
// 根据新状态设置LED初始输出
switch (next_state) {
case LED_STATE_OFF:
controller->led_output = false;
break;
case LED_STATE_ON:
controller->led_output = true;
break;
case LED_STATE_SLOW_BLINK:
case LED_STATE_FAST_BLINK:
controller->led_output = true; // 闪烁从亮开始
break;
}
// 更新物理LED输出
set_led_output(controller->led_output);
}
}
// 更新LED状态(周期性调用)
void led_controller_update(LedController *controller) {
if (controller == NULL) {
return;
}
uint32_t current_time = get_system_time_ms();
// 根据当前状态执行相应的动作
switch (controller->current_state) {
case LED_STATE_OFF:
// LED保持关闭,无需操作
break;
case LED_STATE_ON:
// LED保持常亮,无需操作
break;
case LED_STATE_SLOW_BLINK:
// 1Hz闪烁,周期为1000ms
if (current_time - controller->blink_timestamp >= 500) {
controller->blink_timestamp = current_time;
controller->led_output = !controller->led_output;
set_led_output(controller->led_output);
}
break;
case LED_STATE_FAST_BLINK:
// 5Hz闪烁,周期为200ms
if (current_time - controller->blink_timestamp >= 100) {
controller->blink_timestamp = current_time;
controller->led_output = !controller->led_output;
set_led_output(controller->led_output);
}
break;
}
}
// 设置LED物理输出
void set_led_output(bool state) {
// 根据硬件平台实现LED控制
// 例如:GPIO设置、寄存器操作等
if (state) {
// 打开LED
GPIO_SetBits(LED_PORT, LED_PIN);
} else {
// 关闭LED
GPIO_ResetBits(LED_PORT, LED_PIN);
}
}
// 获取系统时间(毫秒)
uint32_t get_system_time_ms(void) {
// 根据硬件平台实现时间获取
// 例如:使用系统滴答计时器、RTC等
return HAL_GetTick(); // 假设使用HAL库
}
2.4.4 按钮输入处理
// 按钮状态定义
typedef enum {
BUTTON_STATE_RELEASED, // 按钮释放
BUTTON_STATE_PRESSED, // 按钮按下
BUTTON_STATE_LONG_PRESSED // 按钮长按
} ButtonState;
// 按钮控制器结构体
typedef struct {
ButtonState state; // 当前按钮状态
uint32_t press_time; // 按下时间
bool long_press_triggered; // 长按是否已触发
} ButtonController;
// 按钮控制器初始化
void button_controller_init(ButtonController *controller) {
if (controller == NULL) {
return;
}
controller->state = BUTTON_STATE_RELEASED;
controller->press_time = 0;
controller->long_press_triggered = false;
}
// 更新按钮状态并生成事件
ButtonEvent button_controller_update(ButtonController *controller) {
if (controller == NULL) {
return BUTTON_EVENT_NONE;
}
ButtonEvent event = BUTTON_EVENT_NONE;
uint32_t current_time = get_system_time_ms();
bool button_input = read_button_input(); // 读取按钮物理输入
switch (controller->state) {
case BUTTON_STATE_RELEASED:
if (button_input) { // 按钮被按下
controller->state = BUTTON_STATE_PRESSED;
controller->press_time = current_time;
controller->long_press_triggered = false;
}
break;
case BUTTON_STATE_PRESSED:
if (!button_input) { // 按钮被释放
controller->state = BUTTON_STATE_RELEASED;
// 如果没有触发长按,则触发普通按下事件
if (!controller->long_press_triggered) {
event = BUTTON_EVENT_PRESS;
}
} else if (current_time - controller->press_time >= 2000) { // 长按超过2秒
controller->state = BUTTON_STATE_LONG_PRESSED;
controller->long_press_triggered = true;
event = BUTTON_EVENT_LONG_PRESS;
}
break;
case BUTTON_STATE_LONG_PRESSED:
if (!button_input) { // 按钮被释放
controller->state = BUTTON_STATE_RELEASED;
}
break;
}
return event;
}
// 读取按钮物理输入
bool read_button_input(void) {
// 根据硬件平台实现按钮读取
// 例如:GPIO读取、寄存器操作等
// 注意:可能需要进行去抖处理
return (GPIO_ReadInputDataBit(BUTTON_PORT, BUTTON_PIN) == Bit_RESET);
}
2.4.5 主程序
int main(void) {
// 硬件初始化
system_init();
gpio_init();
// 创建控制器实例
LedController led_controller;
ButtonController button_controller;
// 初始化控制器
led_controller_init(&led_controller);
button_controller_init(&button_controller);
// 主循环
while (1) {
// 更新按钮状态并获取事件
ButtonEvent event = button_controller_update(&button_controller);
// 处理按钮事件
if (event != BUTTON_EVENT_NONE) {
led_controller_process_event(&led_controller, event);
}
// 更新LED状态
led_controller_update(&led_controller);
// 系统延时
delay_ms(10); // 10ms循环周期
}
}
2.4.6 状态转换表实现版本
为了对比,下面是使用状态转换表实现的LED控制器:
// 状态转换结构体
typedef struct {
LedState current_state; // 当前状态
ButtonEvent event; // 触发事件
LedState next_state; // 下一状态
} LedStateTransition;
// LED状态转换表
const LedStateTransition led_state_transitions[] = {
// 当前状态 事件 下一状态
{LED_STATE_OFF, BUTTON_EVENT_PRESS, LED_STATE_ON},
{LED_STATE_ON, BUTTON_EVENT_PRESS, LED_STATE_SLOW_BLINK},
{LED_STATE_ON, BUTTON_EVENT_LONG_PRESS, LED_STATE_OFF},
{LED_STATE_SLOW_BLINK, BUTTON_EVENT_PRESS, LED_STATE_FAST_BLINK},
{LED_STATE_SLOW_BLINK, BUTTON_EVENT_LONG_PRESS, LED_STATE_OFF},
{LED_STATE_FAST_BLINK, BUTTON_EVENT_PRESS, LED_STATE_OFF},
{LED_STATE_FAST_BLINK, BUTTON_EVENT_LONG_PRESS, LED_STATE_OFF}
};
#define NUM_LED_TRANSITIONS (sizeof(led_state_transitions) / sizeof(led_state_transitions[0]))
// 处理按钮事件(状态转换表版本)
void led_controller_process_event_table(LedController *controller, ButtonEvent event) {
if (controller == NULL || event == BUTTON_EVENT_NONE) {
return;
}
// 在转换表中查找匹配的转换
for (int i = 0; i < NUM_LED_TRANSITIONS; i++) {
if (led_state_transitions[i].current_state == controller->current_state &&
led_state_transitions[i].event == event) {
// 找到匹配的转换,更新状态
LedState next_state = led_state_transitions[i].next_state;
// 执行状态转换
controller->current_state = next_state;
controller->state_entry_time = get_system_time_ms();
controller->blink_timestamp = 0;
// 根据新状态设置LED初始输出
switch (next_state) {
case LED_STATE_OFF:
controller->led_output = false;
break;
case LED_STATE_ON:
controller->led_output = true;
break;
case LED_STATE_SLOW_BLINK:
case LED_STATE_FAST_BLINK:
controller->led_output = true; // 闪烁从亮开始
break;
}
// 更新物理LED输出
set_led_output(controller->led_output);
return; // 找到并处理了转换,退出函数
}
}
// 如果没有找到匹配的转换,可以处理默认行为
// 在这个简单的例子中,我们不做任何处理
}
小结
本章介绍了在C语言中实现简单状态机的几种常见方法:基于枚举的状态定义、switch-case实现方式和状态转换表实现方式。通过LED控制器的案例,我们展示了如何将状态机理论应用到实际项目中。
每种实现方法都有其优缺点:
- switch-case方式:直观、易于理解和调试,但代码可能冗长,修改不便
- 状态转换表方式:结构清晰、易于修改,但可能增加内存开销,不易表达复杂条件
在实际项目中,可以根据具体需求选择合适的实现方式,或者将不同方式结合使用。对于简单的状态机,switch-case方式通常足够;而对于复杂的状态机,状态转换表或其他高级实现方式可能更合适。
在下一章中,我们将探讨状态机的进阶技巧,包括状态机与事件驱动、分层状态机设计、状态机的内存管理以及状态持久化与恢复等内容。
1008

被折叠的 条评论
为什么被折叠?



