# 工业级嵌入式菜单组件开发文档 ## 1. 项目概述 本项目是一个基于C语言开发的工业级嵌入式菜单组件,设计用于资源受限的嵌入式系统,具有高可移植性、模块化、可裁剪、低资源占用和事件驱动解耦等核心特点。 ## 2. 架构设计 ### 2.1 分层架构 项目采用分层架构设计,将硬件与业务逻辑彻底解耦,便于移植和扩展: | 层级 | 主要职责 | 包含模块 | 文件位置 | 用户可定制性 | |------|----------|----------|----------|--------------| | **核心层** | 提供菜单组件的核心功能,包括菜单节点管理、事件处理、状态机等 | 菜单核心(menu_core.c)、事件队列(menu_event.c)、哈希表索引(menu_hash.c)、栈管理(menu_stack.c) | src/core/ | 不可定制 | | **功能扩展层** | 基于核心层扩展的功能模块,用户可根据需求裁剪 | 参数管理(menu_param.c)、多语言支持(menu_lang.c) | src/param/、src/lang/ | 部分可定制 | | **硬件端口层** | 提供硬件适配接口,对接具体硬件平台 | 硬件驱动抽象(menu_port.c) | port/ | 完全可定制 | | **API层** | 对外提供统一的API接口,屏蔽内部实现细节 | 统一API接口(menu_api.c) | api/ | 不可定制 | ### 2.2 模块间依赖关系 ``` +------------------+ | 用户应用层 | +------------------+ ↓ +------------------+ | API层 | +------------------+ ↓ +------------------+ | 功能扩展层 | +------------------+ ↓ +------------------+ | 核心层 | +------------------+ ↓ +------------------+ | 硬件端口层 | +------------------+ ↓ +------------------+ | 硬件平台 | +------------------+ ``` ## 3. 核心思想 ### 3.1 无动态内存分配 为了保证系统的稳定性和可靠性,避免内存碎片问题,项目采用了完全静态的内存管理方式: - 所有菜单节点、事件队列、哈希表等均使用静态数组分配 - 提供内存大小查询接口,便于用户根据实际需求调整内存配置 - 每个模块都有明确的内存占用上限,便于资源规划 ### 3.2 事件驱动解耦 采用事件驱动设计,将硬件输入与软件逻辑解耦,提高系统响应性: - 硬件事件通过事件队列异步处理,避免阻塞主程序 - 支持多优先级事件处理,保证关键事件优先处理 - 事件超时机制,避免无效事件占用系统资源 ### 3.3 高效节点查找 为了提高菜单节点的查找效率,项目采用了哈希表索引技术: - 将节点查找时间复杂度从O(n)优化到O(1) - 采用优化的哈希函数,减少哈希冲突 - 支持动态注册和注销菜单节点,哈希表自动更新 ### 3.4 可扩展状态机 设计了灵活的状态机框架,支持动态扩展状态转换规则: - 内置基础状态转换规则,覆盖常见菜单操作 - 支持动态注册和注销自定义状态转换规则 - 状态转换动作可定制,便于扩展菜单行为 - 支持状态切换和状态查询,便于外部系统集成 ### 3.5 模块化设计 采用模块化设计,各功能模块独立,便于维护和扩展: - 每个模块都有清晰的接口定义,模块间低耦合 - 支持通过宏开关裁剪不需要的功能,减少资源占用 - 模块化的硬件适配层,便于移植到不同硬件平台 ## 4. 核心数据结构 ### 4.1 菜单节点结构体 ```c typedef struct MenuNode { MenuNodeId id; ///< 节点ID(唯一) MenuNodeId parent_id; ///< 父节点ID(根节点为0) const char* name; ///< 菜单名称(或多语言索引) MenuCallback enter_cb; ///< 进入回调 MenuCallback exit_cb; ///< 退出回调 MenuNodeId first_child_id; ///< 第一个子节点ID(替代指针) MenuNodeId next_sibling_id; ///< 下一个兄弟节点ID(替代指针) MenuNodeId prev_sibling_id; ///< 上一个兄弟节点ID(替代指针) MenuNodeId hash_next_id; ///< 哈希表下一个节点ID(替代指针) // 位域:减少内存占用 struct { bool is_registered : 1; ///< 是否已注册 bool is_selected : 1; ///< 是否被选中 unsigned int reserved : 6; ///< 保留位 } flags; #if MENU_CONFIG_ENABLE_PARAM uint16_t param_id; ///< 绑定的参数ID(启用参数时有效) #endif } MenuNode; ``` **设计亮点**: - 使用索引代替指针,减少内存占用(32位系统节省12字节/节点,64位系统节省28字节/节点) - 采用双向链表结构,便于兄弟节点间遍历 - 使用哈希表索引,提高节点查找效率 - 位域设计,减少内存占用 ### 4.2 菜单核心上下文 ```c typedef struct { MenuNode nodes[MENU_CONFIG_MAX_NODES]; ///< 静态菜单节点池(无动态分配) MenuNode* hash_table[MENU_CONFIG_HASH_TABLE_SIZE]; ///< 哈希表,用于快速查找节点 MenuStack stack; ///< 菜单导航栈 MenuNavPath nav_path; ///< 菜单导航路径 MenuEventQueue event_queue; ///< 事件队列 MenuNodeId current_node_id; ///< 当前选中的节点ID MenuState current_state; ///< 当前菜单状态 uint32_t last_refresh_tick; ///< 上次刷新时间(ms) bool is_initialized; ///< 是否已初始化 MenuErrCode error_code; ///< 当前错误码 const char* error_msg; ///< 当前错误信息 uint16_t node_count; ///< 已注册节点数量 uint16_t free_node_count; ///< 空闲节点数量 // 状态机扩展 #if MENU_CONFIG_ENABLE_STATE_MACHINE_EXT MenuStateTransition custom_transitions[MENU_CONFIG_MAX_STATE_TRANSITIONS]; ///< 自定义状态转换规则 uint16_t custom_transition_count; ///< 自定义状态转换规则数量 #endif } MenuCoreCtx; ``` **设计亮点**: - 集中管理菜单组件的所有状态和资源 - 静态数组设计,无动态内存分配 - 包含所有核心数据结构,便于统一管理 - 支持状态机扩展,便于功能扩展 ### 4.3 事件队列结构体 ```c typedef struct { MenuEvent buffer[MENU_CONFIG_EVENT_QUEUE_LEN]; ///< 队列缓冲区 uint8_t head; ///< 入队指针 uint8_t tail; ///< 出队指针 uint8_t count; ///< 队列元素数量 uint8_t reserved; ///< 保留字节,用于对齐 } MenuEventQueue; ``` **设计亮点**: - 环形队列设计,避免内存碎片 - 支持多优先级事件处理 - 队列满时自动替换低优先级事件,保证高优先级事件优先处理 - 事件超时机制,避免无效事件占用资源 ### 4.4 状态转换结构体 ```c typedef struct { MenuState current_state; ///< 当前状态 MenuEventType event; ///< 触发事件 MenuState next_state; ///< 下一个状态 MenuErrCode (*action)(MenuGlobalCtx* global_ctx); ///< 状态转换动作 const char* desc; ///< 状态转换描述(用于调试) } MenuStateTransition; ``` **设计亮点**: - 灵活的状态转换规则,支持动态注册 - 状态转换动作可定制,便于扩展菜单行为 - 支持状态转换描述,便于调试 ### 4.5 参数结构体 ```c typedef struct { uint16_t id; ///< 参数ID const char* name; ///< 参数名称 ParamType type; ///< 参数类型 void* value; ///< 参数值指针 void* default_value; ///< 默认值指针 void* min_value; ///< 最小值指针 void* max_value; ///< 最大值指针 bool is_read_only; ///< 是否只读 bool is_modified; ///< 是否已修改 } MenuParam; ``` **设计亮点**: - 支持多种参数类型(int8/uint8/int16/uint16/int32/uint32/float) - 自动范围检查和边界处理 - 参数与菜单节点绑定,支持菜单直接调整参数 - 支持默认值恢复功能 ### 4.6 语言包结构体 ```c typedef struct { LangId id; ///< 语言ID const char* name; ///< 语言名称(如"English"、"中文") const LangStrItem* strings; ///< 字符串数组 uint16_t string_count; ///< 字符串数量 } LangPack; ``` **设计亮点**: - 支持多种语言切换 - 字符串ID映射机制,便于管理和维护 - 自动回退到默认语言,提高系统容错性 - 优化的语言字符串查找算法 ## 5. 核心算法 ### 5.1 哈希表算法 **功能**:用于快速查找菜单节点,将节点查找时间复杂度从O(n)优化到O(1) **实现原理**: ```c static uint16_t menu_core_hash_func(MenuNodeId node_id) { // 优化后的哈希函数:使用混合哈希算法,减少冲突,提高分布均匀性 // 结合了旋转和异或操作,适合嵌入式系统的高效计算 uint32_t hash = node_id; // 旋转和异或操作,提高哈希分布的均匀性 hash = (hash << 15) | (hash >> 17); // 旋转15位 hash ^= 0x61C88647; // 黄金比例常数,提高随机性 hash *= 0x85EBca6B; // 另一个黄金比例常数 hash = (hash << 13) | (hash >> 19); // 旋转13位 hash ^= 0xC2B2AE35; // 再一次异或黄金比例常数 return (uint16_t)(hash % MENU_CONFIG_HASH_TABLE_SIZE); } ``` **设计亮点**: - 采用优化的混合哈希算法,减少冲突 - 结合旋转和异或操作,提高哈希分布的均匀性 - 适合嵌入式系统的高效计算,执行速度快 - 支持动态添加和删除节点,哈希表自动更新 ### 5.2 事件队列算法 **功能**:实现基于优先级的事件处理机制,支持多优先级事件,队列满时自动替换低优先级事件 **实现原理**: - 采用环形队列设计,避免内存碎片 - 支持4级事件优先级(低、普通、高、紧急) - 同优先级事件采用FIFO顺序处理 - 队列满时,低优先级事件会被新的高优先级事件替换 - 事件包含时间戳,支持超时处理 **关键函数**: - `menu_post_event`:发送事件到事件队列 - `menu_core_get_event`:从事件队列获取下一个待处理事件 - `menu_main_loop`:菜单主循环,处理事件和刷新显示 ### 5.3 状态机算法 **功能**:实现菜单的状态管理和状态转换,支持动态注册自定义状态转换规则 **实现原理**: - 基于有限状态机(FSM)设计,定义了菜单的基本状态和状态转换规则 - 支持动态注册和注销自定义状态转换规则 - 状态转换时执行相应的动作函数,便于扩展菜单行为 - 支持状态查询和状态切换 **内置状态**: - `MENU_STATE_INIT`:初始化状态 - `MENU_STATE_NORMAL`:正常导航状态 - `MENU_STATE_PARAM_EDIT`:参数编辑状态 - `MENU_STATE_CONFIRM`:确认状态 - `MENU_STATE_ERROR`:错误状态 **关键函数**: - `menu_state_register_transition`:注册自定义状态转换规则 - `menu_state_unregister_transition`:注销自定义状态转换规则 - `menu_state_get_current`:获取当前状态 - `menu_state_switch`:切换到指定状态 - `menu_state_transition`:处理状态转换 ### 5.4 菜单导航算法 **功能**:实现菜单的上下左右导航,支持多级菜单和栈管理 **实现原理**: - 使用栈数据结构管理导航层级 - 支持环形导航(到达边界自动循环) - 记录完整的导航路径,便于回溯 - 支持导航深度查询和路径获取 **关键函数**: - `menu_core_navigate_enter`:进入子菜单 - `menu_core_navigate_back`:返回上一级菜单 - `menu_core_navigate_up`:上移菜单项 - `menu_core_navigate_down`:下移菜单项 - `menu_get_nav_path`:获取导航路径 - `menu_get_nav_depth`:获取导航深度 ## 6. 核心功能模块 ### 6.1 菜单节点管理 **功能**:提供菜单节点的注册、删除、更新和查找功能 **实现原理**: - 使用静态数组存储菜单节点,无动态内存分配 - 采用哈希表索引,提高节点查找效率 - 支持批量注册菜单节点,提高初始化效率 - 支持动态更新和删除菜单节点 **关键函数**: - `menu_register_node`:注册单个菜单节点 - `menu_register_nodes`:批量注册菜单节点 - `menu_unregister_node`:删除菜单节点 - `menu_update_node`:更新菜单节点 - `menu_core_find_node`:查找菜单节点 ### 6.2 参数管理 **功能**:提供参数的注册、读写、范围检查和默认值恢复功能 **实现原理**: - 支持多种参数类型(int8/uint8/int16/uint16/int32/uint32/float) - 自动范围检查和边界处理 - 参数与菜单节点绑定,支持菜单直接调整参数 - 支持默认值恢复功能 **关键函数**: - `menu_param_register`:注册参数 - `menu_param_set_value`:设置参数值 - `menu_param_get_value`:获取参数值 - `menu_param_get_type`:获取参数类型 ### 6.3 多语言支持 **功能**:提供多语言切换和字符串管理功能 **实现原理**: - 支持多种语言切换 - 字符串ID映射机制,便于管理和维护 - 自动回退到默认语言,提高系统容错性 - 优化的语言字符串查找算法 **关键函数**: - `menu_lang_set_current`:设置当前语言 - `menu_lang_get_current`:获取当前语言 - `menu_lang_register_str`:注册语言字符串 - `menu_lang_load_pack`:加载语言包 ### 6.4 Modbus映射 **功能**:实现参数与Modbus寄存器的双向映射 **实现原理**: - 支持多种Modbus寄存器类型(线圈、离散输入、保持寄存器、输入寄存器) - 支持多种字节序(小端、大端、Modbus标准) - 自动类型转换和边界检查 - 灵活的读写权限控制 **关键函数**: - `menu_modbus_map_register`:注册参数与Modbus寄存器的映射关系 - `menu_modbus_map_param_to_reg`:参数值转换为Modbus寄存器数据 - `menu_modbus_map_reg_to_param`:Modbus寄存器数据转换为参数值 ## 7. 硬件适配层 **功能**:提供硬件适配接口,对接具体硬件平台 **实现原理**: - 定义统一的硬件驱动结构体,包含各种硬件接口 - 支持运行时配置硬件驱动,便于动态切换 - 屏蔽不同硬件平台的差异,提高组件的可移植性 **硬件接口**: ```c typedef struct { void (*printf)(const char* fmt, va_list args); ///< 硬件打印接口 uint32_t (*get_tick)(void); ///< 获取系统滴答时间 void (*delay_ms)(uint32_t ms); ///< 硬件延迟函数 void (*display)(const char* menu_name, uint16_t menu_id); ///< 菜单显示接口 MenuEventType (*key_scan)(void); ///< 按键扫描接口(可选) void (*irq_ctrl)(bool enable); ///< 中断管理接口(可选) void (*error_handler)(MenuErrCode err_code); ///< 错误处理接口(可选) MenuErrCode (*modbus_send)(ModbusRegType reg_type, uint16_t reg_addr, const uint8_t* reg_buf, uint8_t buf_len); ///< Modbus发送接口(可选) MenuErrCode (*modbus_receive)(ModbusRegType reg_type, uint16_t reg_addr, uint8_t* reg_buf, uint8_t buf_len); ///< Modbus接收接口(可选) } MenuPortDriver; ``` **关键函数**: - `menu_port_init`:硬件端口初始化 - `menu_port_deinit`:硬件端口反初始化 ## 8. 配置管理 **功能**:提供灵活的配置选项,便于用户根据需求定制功能 **实现原理**: - 所有配置项集中在`menu_config.h`文件中 - 支持通过宏开关裁剪不需要的功能 - 支持调整资源大小,如最大节点数、栈深度、事件队列长度等 - 支持调试开关,便于开发和调试 **核心配置项**: | 配置项 | 说明 | 默认值 | |-------|------|-------| | MENU_CONFIG_MAX_NODES | 最大菜单节点数 | 32 | | MENU_CONFIG_HASH_TABLE_SIZE | 菜单哈希表大小(必须为质数) | 31 | | MENU_CONFIG_STACK_DEPTH | 菜单栈深度 | 8 | | MENU_CONFIG_EVENT_QUEUE_LEN | 事件队列长度 | 16 | | MENU_CONFIG_ENABLE_ASSERT | 是否启用断言 | 1 | | MENU_CONFIG_ENABLE_DEBUG | 是否启用调试打印 | 1 | | MENU_CONFIG_ENABLE_MEM_MONITOR | 是否启用内存监控 | 1 | | MENU_CONFIG_ENABLE_PARAM | 是否启用参数管理 | 1 | | MENU_CONFIG_ENABLE_LANG | 是否启用多语言支持 | 1 | | MENU_CONFIG_ENABLE_MODBUS_MAP | 是否启用Modbus映射 | 1 | ## 9. 工业级设计考量 ### 9.1 可靠性设计 - **无动态内存分配**:完全使用静态数组,避免内存碎片和内存泄漏 - **完善的错误处理**:每个函数都有明确的错误码返回,便于调试和错误定位 - **边界检查**:所有数组访问都有边界检查,防止越界访问 - **断言机制**:关键函数入口有断言检查,便于调试 - **事件超时处理**:避免无效事件占用系统资源 ### 9.2 性能优化 - **高效的哈希表算法**:将节点查找时间复杂度从O(n)优化到O(1) - **位域设计**:减少内存占用,提高缓存命中率 - **索引代替指针**:减少内存占用,提高系统兼容性 - **事件驱动设计**:提高系统响应性,减少CPU占用 - **模块化设计**:支持功能裁剪,减少资源占用 ### 9.3 可移植性设计 - **硬件抽象层**:屏蔽不同硬件平台的差异,提高组件的可移植性 - **标准C语言实现**:不依赖特定的编译器或操作系统 - **灵活的配置选项**:支持根据不同硬件平台调整资源大小 - **模块化设计**:各功能模块独立,便于移植和扩展 ### 9.4 可维护性设计 - **清晰的代码结构**:采用分层架构和模块化设计,代码结构清晰 - **完善的文档**:提供详细的API文档和开发文档 - **统一的代码风格**:采用清晰的命名规范和注释风格 - **调试支持**:提供丰富的调试打印和日志功能 - **错误定位机制**:每个错误都有明确的错误码和错误信息 ## 10. 应用场景 本菜单组件适用于各种资源受限的嵌入式系统,特别是工业控制领域,如: - 工业控制器 - 智能家居设备 - 仪器仪表 - 医疗设备 - 嵌入式人机界面 - Modbus从站设备 ## 11. 开发流程 ### 11.1 硬件适配 1. 根据目标硬件平台,实现`menu_port.c`中的硬件接口 2. 配置`menu_config.h`中的资源大小和功能开关 3. 编译和测试硬件适配层 ### 11.2 菜单设计 1. 设计菜单结构,确定菜单节点的层级关系 2. 注册菜单节点,构建菜单树 3. 注册参数(可选) 4. 注册Modbus映射(可选) 5. 注册自定义状态转换规则(可选) ### 11.3 应用集成 1. 在应用程序中初始化菜单组件 2. 在主循环中调用`menu_main_loop`处理事件 3. 调用`menu_post_event`发送硬件事件(如按键事件) 4. 根据需要调用其他API接口,实现自定义功能 ## 12. 测试与验证 ### 12.1 单元测试 项目提供了单元测试框架,位于`test/menu_test.c`,包含以下测试用例: - 菜单初始化和反初始化测试 - 菜单节点注册和删除测试 - 菜单导航测试 - 事件队列测试 - 参数管理测试 - 多语言支持测试 - Modbus映射测试 ### 12.2 集成测试 - 硬件适配测试:验证硬件接口的正确性 - 功能测试:验证各个功能模块的正确性 - 性能测试:测试菜单组件的执行效率和资源占用 - 可靠性测试:测试菜单组件在各种异常情况下的表现 ### 12.3 现场测试 - 在目标硬件平台上进行测试,验证菜单组件的实际运行效果 - 测试各种用户操作场景,确保菜单组件的易用性和稳定性 - 测试长时间运行情况下的稳定性 ## 13. 版本管理 | 版本 | 发布日期 | 主要变更 | |------|----------|----------| | v1.0.0 | 2025-12-19 | 初始版本,包含核心功能 | ## 14. 总结 本项目是一个设计精良的工业级嵌入式菜单组件,具有高可靠性、高性能、高可移植性和高可扩展性等特点。采用分层架构、事件驱动设计、哈希表索引、可扩展状态机等先进技术,适合各种资源受限的嵌入式系统。 通过本开发文档,开发者可以全面了解菜单组件的架构设计、核心思想、数据算法和使用方法,便于快速集成和定制开发。