Files
menu/开发文档.md
2025-12-19 09:57:41 +08:00

504 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 工业级嵌入式菜单组件开发文档
## 1. 项目概述
本项目是一个基于C语言开发的工业级嵌入式菜单组件设计用于资源受限的嵌入式系统具有高可移植性、模块化、可裁剪、低资源占用和事件驱动解耦等核心特点。
## 2. 架构设计
### 2.1 分层架构
项目采用分层架构设计,将硬件与业务逻辑彻底解耦,便于移植和扩展:
| 层级 | 主要职责 | 包含模块 | 文件位置 | 用户可定制性 |
|------|----------|----------|----------|--------------|
| **核心层** | 提供菜单组件的核心功能,包括菜单节点管理、事件处理、状态机等 | 菜单核心、事件队列、哈希表索引、栈管理 | src/core/ | 不可定制 |
| **功能扩展层** | 基于核心层扩展的功能模块,用户可根据需求裁剪 | 参数管理、多语言支持、Modbus映射、状态机扩展 | src/features/、src/param/、src/lang/ | 部分可定制 |
| **硬件端口层** | 提供硬件适配接口,对接具体硬件平台 | 硬件驱动抽象、打印接口、系统时间、中断管理 | port/ | 完全可定制 |
| **API层** | 对外提供统一的API接口屏蔽内部实现细节 | 上下文管理、菜单管理、事件处理、功能扩展接口 | 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;
```
**设计亮点**
- 灵活的状态转换规则,支持动态注册
- 状态转换动作可定制,便于扩展菜单行为
- 支持状态转换描述,便于调试
## 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. 总结
本项目是一个设计精良的工业级嵌入式菜单组件,具有高可靠性、高性能、高可移植性和高可扩展性等特点。采用分层架构、事件驱动设计、哈希表索引、可扩展状态机等先进技术,适合各种资源受限的嵌入式系统。
通过本开发文档,开发者可以全面了解菜单组件的架构设计、核心思想、数据算法和使用方法,便于快速集成和定制开发。