# 状态机+事件总线通用模板 **Repository Path**: zzhau1/State_Event_template ## Basic Information - **Project Name**: 状态机+事件总线通用模板 - **Description**: 状态机+事件总线通用模板 - **Primary Language**: C - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 1 - **Created**: 2026-07-09 - **Last Updated**: 2026-09-05 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 嵌入式事件总线与状态机通用模板 一个基于 C 语言的轻量级嵌入式事件总线和状态机框架,专为资源受限的嵌入式设备设计,具有良好的可复用性和稳定性。 ## 项目结构 ``` 状态机通用模板/ ├── include/ │ ├── event_bus.h # 事件总线模块头文件 │ └── state_machine.h # 状态机核心框架头文件 ├── src/ │ ├── event_bus.c # 事件总线实现 │ ├── state_machine.c # 状态机核心实现 │ └── main.c # 测试程序 ├── bin/ │ └── test_sm.exe # 编译后的可执行文件 └── README.md # 使用文档 ``` ## 核心模块 ### 1. 事件总线模块 (event_bus.c/h) 轻量级的事件发布/订阅机制,实现各模块间的解耦通信。 #### 核心特性 - **优先级响应**:高优先级订阅者优先收到事件通知 - **重复订阅检测**:防止同一回调函数重复注册 - **安全发布机制**:发布前验证事件数据有效性 - **无动态内存分配**:使用静态数组存储订阅者信息 - **异步事件支持**:支持中断和高优先级任务中发布事件 - **ISR安全版本**:提供中断安全的事件发布接口,参数验证在临界区内完成 #### 配置参数 | 参数 | 默认值 | 说明 | |------|--------|------| | `EVENT_BUS_MAX_SUBSCRIBERS` | 16 | 最大订阅者数量 | | `EVENT_BUS_MAX_DATA_SIZE` | 32 | 最大事件数据长度(字节) | | `EVENT_BUS_QUEUE_SIZE` | 16 | 异步事件队列大小 | #### 事件优先级 | 优先级 | 值 | 说明 | |--------|---|------| | `EVENT_PRIORITY_HIGHEST` | 0 | 最高优先级 - 系统关键模块 | | `EVENT_PRIORITY_HIGH` | 1 | 高优先级 - 核心业务模块 | | `EVENT_PRIORITY_NORMAL` | 2 | 普通优先级 - 一般业务模块 | | `EVENT_PRIORITY_LOW` | 3 | 低优先级 - 辅助功能模块 | | `EVENT_PRIORITY_LOWEST` | 4 | 最低优先级 - 日志/调试模块 | #### 预定义事件类型 系统预定义了丰富的事件类型,涵盖常见的嵌入式应用场景: - **系统事件**:`EVENT_BUS_SYSTEM_STARTUP`, `EVENT_BUS_SYSTEM_SHUTDOWN`, `EVENT_BUS_SYSTEM_SLEEP`, `EVENT_BUS_SYSTEM_WAKEUP` - **电源事件**:`EVENT_BUS_POWER_ON`, `EVENT_BUS_POWER_OFF` - **按键事件**:`EVENT_BUS_POWER_PRESS_LONG`, `EVENT_BUS_POWER_SINGLE_CLICK`, `EVENT_BUS_MENU_SINGLE_CLICK`, `EVENT_BUS_TRIGGER_PRESS_DOWN` - **电量事件**:`EVENT_BUS_BATTERY_LOW`, `EVENT_BUS_BATTERY_CRITICAL`, `EVENT_BUS_BATTERY_NORMAL`, `EVENT_BUS_BATTERY_CHARGING`, `EVENT_BUS_BATTERY_FULL` - **UI事件**:`EVENT_BUS_UI_UPDATE_DISPLAY`, `EVENT_BUS_UI_SHOW_ALERT`, `EVENT_BUS_UI_SCREEN_ON`, `EVENT_BUS_UI_SCREEN_OFF` - **通信事件**:`EVENT_BUS_COMM_RX_DATA`, `EVENT_BUS_COMM_TX_COMPLETE`, `EVENT_BUS_COMM_ERROR` - **定时器事件**:`EVENT_BUS_1MS_TIMEOUT`, `EVENT_BUS_1000MS_TIMEOUT` - **ADC事件**:`EVENT_BUS_ADC_DMA_HT_SET_BIT`, `EVENT_BUS_ADC_DMA_TC_SET_BIT`, `EVENT_BUS_ADC_DATA_READY` #### API 函数 | 函数名 | 功能 | 参数 | 返回值 | |--------|------|------|--------| | `event_bus_init` | 初始化事件总线 | `bus`: 事件总线指针 | 无 | | `event_bus_subscribe` | 订阅事件(带优先级) | `bus`: 事件总线指针;`event_type`: 事件类型;`priority`: 优先级;`callback`: 回调函数;`context`: 上下文 | `true`成功 / `false`失败 | | `event_bus_subscribe_default` | 订阅事件(默认优先级) | `bus`: 事件总线指针;`event_type`: 事件类型;`callback`: 回调函数;`context`: 上下文 | `true`成功 / `false`失败 | | `event_bus_unsubscribe` | 取消订阅事件 | `bus`: 事件总线指针;`event_type`: 事件类型;`callback`: 回调函数;`context`: 上下文 | `true`成功 / `false`失败 | | `event_bus_unsubscribe_all` | 取消所有订阅 | `bus`: 事件总线指针;`callback`: 回调函数;`context`: 上下文 | 取消数量 | | `event_bus_publish` | 发布事件 | `bus`: 事件总线指针;`event`: 事件指针 | 通知数量 | | `event_bus_publish_safe` | 安全发布事件 | `bus`: 事件总线指针;`event`: 事件指针 | 通知数量(-1表示无效) | | `event_bus_publish_async` | 异步发布事件 | `bus`: 事件总线指针;`event`: 事件指针 | `true`成功 / `false`失败 | | `event_bus_publish_from_isr` | ISR安全发布(参数验证在临界区内) | `bus`: 事件总线指针;`event`: 事件指针 | `true`成功 / `false`失败 | | `event_bus_process_queue` | 处理异步队列 | `bus`: 事件总线指针 | 处理数量 | | `event_bus_get_subscriber_count` | 获取订阅者数量 | `bus`: 事件总线指针;`event_type`: 事件类型 | 订阅者数量 | | `event_bus_get_queue_count` | 获取队列数量 | `bus`: 事件总线指针 | 队列数量 | | `event_bus_is_valid_event` | 检查事件有效性 | `event`: 事件指针 | `true`有效 / `false`无效 | | `event_bus_deinit` | 清理事件总线 | `bus`: 事件总线指针 | 无 | #### 使用示例 ```c #include "event_bus.h" // 1. 定义事件总线实例 EventBus bus; // 2. 初始化事件总线 event_bus_init(&bus); // 3. 定义回调函数 void on_button_pressed(const Event* event, void* context) { printf("Button pressed!\n"); } // 4. 订阅事件(高优先级) event_bus_subscribe(&bus, EVENT_BUS_POWER_SINGLE_CLICK, EVENT_PRIORITY_HIGH, on_button_pressed, NULL); // 5. 发布事件 Event event = { .type = EVENT_BUS_POWER_SINGLE_CLICK, .data_len = 0 }; event_bus_publish(&bus, &event); // 6. 异步发布事件(适用于中断) event_bus_publish_async(&bus, &event); // 7. 处理异步队列(在主循环中) event_bus_process_queue(&bus); ``` --- ### 2. 状态机模块 (state_machine.c/h) 轻量级的有限状态机(FSM)实现,支持事件驱动的状态转换,使用静态表格实现 O(1) 查找。 #### 核心特性 - **无动态内存分配**:所有数据结构使用静态数组,存储在 Flash 中 - **静态转换表**:使用二维数组实现状态转换,查找时间 O(1) - **状态配置表**:使用状态ID作为索引,查找时间 O(1) - **状态回调机制**:支持进入/退出状态回调函数 - **递归深度限制**:防止进入回调返回新状态导致的无限递归 - **异步事件队列**:支持中断安全的事件触发 - **状态回退**:记录上一个状态,支持状态回滚 - **ISR安全版本**:提供中断安全的事件触发接口 #### 配置参数 | 参数 | 默认值 | 说明 | |------|--------|------| | `SM_MAX_STATES` | 16 | 最大状态数量 | | `SM_MAX_EVENTS` | 256 | 最大事件数量(转换表列数) | | `SM_EVENT_QUEUE_SIZE` | 16 | 异步事件队列大小 | | `SM_MAX_RECURSION_DEPTH` | 4 | 状态转换最大递归深度 | #### API 函数 | 函数名 | 功能 | 参数 | 返回值 | |--------|------|------|--------| | `sm_init` | 初始化状态机(使用静态配置) | `sm`: 状态机指针;`config`: 配置指针;`initial_state`: 初始状态;`context`: 上下文 | `true`成功 / `false`失败 | | `sm_trigger_event` | 触发状态转换(O(1)查找,带递归限制) | `sm`: 状态机指针;`event`: 触发事件 | `true`成功 / `false`失败 | | `sm_trigger_event_async` | 异步触发转换 | `sm`: 状态机指针;`event`: 触发事件 | `true`成功 / `false`失败 | | `sm_trigger_event_from_isr` | ISR安全触发 | `sm`: 状态机指针;`event`: 触发事件 | `true`成功 / `false`失败 | | `sm_process_queue` | 处理异步队列 | `sm`: 状态机指针 | 处理数量 | | `sm_get_current_state` | 获取当前状态 | `sm`: 状态机指针 | 当前状态ID | | `sm_get_previous_state` | 获取上一个状态 | `sm`: 状态机指针 | 上一个状态ID | | `sm_set_state` | 手动设置状态 | `sm`: 状态机指针;`new_state`: 目标状态 | `true`成功 / `false`失败 | | `sm_reset` | 重置状态机 | `sm`: 状态机指针;`initial_state`: 初始状态 | `true`成功 / `false`失败 | | `sm_set_context` | 设置上下文 | `sm`: 状态机指针;`context`: 上下文 | 无 | | `sm_get_context` | 获取上下文 | `sm`: 状态机指针 | 上下文指针 | | `sm_get_queue_count` | 获取队列数量 | `sm`: 状态机指针 | 队列数量 | #### 使用示例(静态表格方式) ```c #include "state_machine.h" // 1. 定义状态枚举(必须从0开始连续编号) typedef enum { STATE_IDLE = 0, STATE_ACTIVE, STATE_ERROR, STATE_MAX } MyState; // 2. 定义状态处理函数 StateId on_enter_idle(StateId state, void* context) { printf("Entering IDLE state\n"); return STATE_NONE; } StateId on_exit_idle(StateId state, void* context) { printf("Exiting IDLE state\n"); return STATE_NONE; } // 3. 定义静态状态表(使用状态ID作为索引) static const StateConfig my_states[STATE_MAX] = { [STATE_IDLE] = {STATE_IDLE, on_enter_idle, on_exit_idle}, [STATE_ACTIVE] = {STATE_ACTIVE, on_enter_active, on_exit_active}, [STATE_ERROR] = {STATE_ERROR, on_enter_error, on_exit_error}, }; // 4. 定义静态转换表(二维数组,O(1)查找) // 使用标准C99指定初始化语法,花括号明确分组每一行 static const StateId my_transition_table[STATE_MAX][SM_MAX_EVENTS] = { [STATE_IDLE] = { [EVENT_BUS_POWER_SINGLE_CLICK] = STATE_ACTIVE, }, [STATE_ACTIVE] = { [EVENT_BUS_POWER_SINGLE_CLICK] = STATE_ERROR, }, [STATE_ERROR] = { [EVENT_BUS_SYSTEM_STARTUP] = STATE_IDLE, }, }; // 5. 定义状态机配置 // transition_table 使用 (const StateId*) 强制转换,兼容MDK/Keil编译器 static const StateMachineConfig my_sm_config = { .states = my_states, .state_count = sizeof(my_states) / sizeof(my_states[0]), .transition_table = (const StateId*)my_transition_table, .max_event = SM_MAX_EVENTS, .max_state = STATE_MAX, }; // 6. 创建并初始化状态机(使用静态配置) StateMachine sm; sm_init(&sm, &my_sm_config, STATE_IDLE, NULL); // 7. 触发事件转换状态(O(1)查找) sm_trigger_event(&sm, EVENT_BUS_POWER_SINGLE_CLICK); ``` #### 性能特点 | 操作 | 时间复杂度 | 说明 | |------|-----------|------| | 状态查找 | O(1) | 直接数组索引 | | 转换规则查找 | O(1) | 直接二维数组索引 | | 状态转换 | O(1) | 包含回调执行 | --- ## 模块间事件通信架构 事件总线作为模块间通信的桥梁,实现了发布/订阅模式: ``` ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ 按键扫描模块 │ │ 电量检测模块 │ │ UI显示模块 │ │ (发布者) │ │ (发布者) │ │ (订阅者) │ └────────┬─────────┘ └────────┬─────────┘ └────────┬─────────┘ │ │ │ │ 发布 EVENT_BUS_POWER_SINGLE_CLICK │ └────────────────────────►┐ │ │ │ │ 发布 EVENT_BUS_BATTERY_LOW └────────────────────────►│ ``` ### 事件发布流程 ``` 事件创建 → 验证事件有效性 → 按优先级遍历订阅者 → 调用订阅者回调 ``` ### 状态机与事件总线集成 状态机可以订阅事件总线的事件,实现事件驱动的状态转换: ```c // 状态机订阅事件总线的低电量事件 event_bus_subscribe(&bus, EVENT_BUS_BATTERY_LOW, EVENT_PRIORITY_HIGHEST, state_machine_callback, &sm); // 当事件发布时,状态机自动处理并转换状态 void state_machine_callback(const Event* event, void* context) { StateMachine* sm = (StateMachine*)context; sm_trigger_event(sm, event->type); } ``` --- ## 最佳实践 ### 1. 事件优先级设计 | 优先级 | 适用场景 | 示例 | |--------|----------|------| | HIGHEST | 系统关键操作 | 电量严重不足、系统错误 | | HIGH | 核心业务逻辑 | 电源按钮、模式切换 | | NORMAL | 一般业务操作 | 普通按键、数据更新 | | LOW | 辅助功能 | 显示更新、日志记录 | | LOWEST | 调试信息 | 调试输出、性能统计 | ### 2. 异步事件使用 在中断服务程序或高优先级任务中,应使用异步事件发布: ```c // 在中断中发布事件 void button_isr(void) { Event event = {.type = EVENT_BUS_POWER_SINGLE_CLICK, .data_len = 0}; event_bus_publish_from_isr(&bus, &event); } // 在主循环中处理异步事件 void main_loop(void) { event_bus_process_queue(&bus); sm_process_queue(&sm); } ``` ### 3. 状态机设计原则 - **单一职责**:每个状态只负责一个明确的功能 - **最小转换**:状态转换应简单直接 - **回调分离**:进入回调处理初始化,退出回调处理清理 - **上下文传递**:使用 context 参数传递共享数据 - **静态配置**:使用静态表格方式,编译时确定规则,存储在 Flash 中 ### 4. 状态ID设计 状态ID必须从0开始连续编号,且不超过 `SM_MAX_STATES`: ```c typedef enum { STATE_FIRST = 0, // 必须从0开始 STATE_SECOND, STATE_THIRD, STATE_MAX // 必须是最后一个,用于max_state } MyState; ``` ### 5. 内存管理 - 使用静态分配的缓冲区存储事件数据 - 事件数据长度不超过 `EVENT_BUS_MAX_DATA_SIZE` - 转换表大小:`STATE_MAX x SM_MAX_EVENTS`,未使用的位置初始化为 STATE_NONE ### 6. ISR安全性 - `event_bus_publish_from_isr()`:参数验证和入队操作在临界区内完成 - `sm_trigger_event_from_isr()`:队列操作使用临界区保护 - 主循环中处理队列,避免在ISR中执行复杂操作 --- ## 编译和运行 ```bash # 编译 gcc -Iinclude src/event_bus.c src/state_machine.c src/main.c -o bin/test_sm.exe # 运行 bin/test_sm.exe ``` ### 测试输出示例 测试程序验证以下功能: 1. 事件总线基本功能(订阅、发布、取消订阅) 2. 事件优先级机制 3. 异步事件发布和队列处理 4. 状态机基本功能(静态配置、状态转换) 5. 状态机事件触发(O(1)查找) 6. 状态机异步队列 7. 事件总线与状态机集成 --- ## 许可证 MIT License --- ## 版本历史 - **v2.1.0**:可靠性改进版本 - 状态机改为静态表格方式(O(1)查找) - 添加状态转换递归深度限制(防止栈溢出) - 改进ISR安全性(参数验证移到临界区内) - 状态查找优化为O(1)直接索引 - 移除动态注册API(sm_register_state/transition) - 新增SM_MAX_EVENTS和SM_MAX_RECURSION_DEPTH配置参数 - **v2.0.0**:重构版本 - 新增事件总线模块(`event_bus.c/h`) - 支持事件优先级机制 - 支持事件发布/订阅模式 - 支持异步事件队列 - 支持ISR安全操作 - 状态机模块重构(`state_machine.c/h`) - 无动态内存分配设计 - 预定义丰富的事件类型 - **v1.0.0**:初始版本,包含事件系统和状态机框架