第二章:简单状态机实现

第二章:简单状态机实现

在理解了状态机的基本概念后,本章将详细介绍如何在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 状态转换表的优缺点

优点:

  1. 分离关注点:状态转换逻辑与动作代码分离
  2. 易于修改:添加或修改转换规则只需更新表格,不需要修改核心代码
  3. 可读性:转换规则以表格形式呈现,一目了然
  4. 可验证性:便于对状态转换规则进行形式化验证

缺点:

  1. 额外内存:转换表需要额外的存储空间
  2. 查找开销:需要在表中查找匹配的转换,可能影响性能
  3. 复杂条件:难以表达复杂的转换条件
  4. 调试困难:相比直接的代码逻辑,表驱动的方法可能更难调试

2.4 简单案例实现

现在,让我们通过一个实际的案例来展示如何实现一个简单的状态机。这个案例是一个LED控制器,它根据按钮输入控制LED的不同闪烁模式。

2.4.1 需求描述

设计一个LED控制器,具有以下功能:

  1. LED有四种状态:关闭、常亮、慢闪(1Hz)、快闪(5Hz)
  2. 通过按钮切换LED状态,每次按下按钮,LED状态按顺序循环切换
  3. 长按按钮(超过2秒)将LED直接切换到关闭状态
  4. 系统启动时,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方式通常足够;而对于复杂的状态机,状态转换表或其他高级实现方式可能更合适。

在下一章中,我们将探讨状态机的进阶技巧,包括状态机与事件驱动、分层状态机设计、状态机的内存管理以及状态持久化与恢复等内容。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值