/** * @file menu_example.c * @brief 菜单组件完整使用示例(包含Modbus映射功能演示) * @note 工业级嵌入式菜单组件 - 使用示例 */ #include "menu.h" #include "menu_port.h" #include "menu_def.h" /************************** 示例配置 **************************/ // 菜单节点ID定义 #define MENU_ROOT_ID 1 // 根节点 #define MENU_PARAM_ID 2 // 参数设置节点 #define MENU_MODBUS_ID 3 // Modbus设置节点 #define MENU_TEMP_ID 4 // 温度参数节点 #define MENU_HUMID_ID 5 // 湿度参数节点 #define MENU_PRESS_ID 6 // 压力参数节点 // 参数ID定义 #define PARAM_TEMP_ID 1 // 温度参数ID #define PARAM_HUMID_ID 2 // 湿度参数ID #define PARAM_PRESS_ID 3 // 压力参数ID // Modbus寄存器地址定义 #define REG_TEMP_ADDR 40001 // 温度寄存器地址 #define REG_HUMID_ADDR 40003 // 湿度寄存器地址 #define REG_PRESS_ADDR 40005 // 压力寄存器地址 /************************** 全局变量 **************************/ // 模拟Modbus寄存器缓冲区 uint8_t g_modbus_reg_buf[256] = {0}; /************************** 菜单回调函数 **************************/ /** * @brief 进入参数设置菜单回调 * @param node_id 菜单节点ID * @return 错误码 */ static MenuErrCode menu_cb_enter_param(MenuNodeId node_id) { MENU_DEBUG("Enter param menu: %d", node_id); return MENU_OK; } /** * @brief 退出参数设置菜单回调 * @param node_id 菜单节点ID * @return 错误码 */ static MenuErrCode menu_cb_exit_param(MenuNodeId node_id) { MENU_DEBUG("Exit param menu: %d", node_id); return MENU_OK; } /** * @brief 进入Modbus设置菜单回调 * @param node_id 菜单节点ID * @return 错误码 */ static MenuErrCode menu_cb_enter_modbus(MenuNodeId node_id) { MENU_DEBUG("Enter Modbus menu: %d", node_id); return MENU_OK; } /** * @brief 模拟Modbus通信流程 */ static void modbus_communication_demo(void) { #if MENU_CONFIG_ENABLE_MODBUS_MAP MENU_DEBUG("=== Modbus Communication Demo ==="); // 1. 模拟参数到寄存器的转换(参数修改后更新寄存器) { MENU_DEBUG("1. Param to Reg Demo:"); // 设置温度参数为28.5 menu_param_set_value(PARAM_TEMP_ID, 28.5f); // 将温度参数转换到Modbus寄存器 uint8_t buf_len = 4; uint8_t reg_buf[4] = {0}; MenuErrCode err = menu_modbus_map_param_to_reg(PARAM_TEMP_ID, reg_buf, &buf_len); if (err == MENU_OK) { MENU_DEBUG(" Temp param (28.5f) to reg: [0x%02X, 0x%02X, 0x%02X, 0x%02X]", reg_buf[0], reg_buf[1], reg_buf[2], reg_buf[3]); } else { MENU_DEBUG(" Param to reg failed: %d", err); } } // 2. 模拟寄存器到参数的转换(接收到Modbus写入请求后更新参数) { MENU_DEBUG("2. Reg to Param Demo:"); // 模拟Modbus写入湿度参数(65.0%) // 65.0f的二进制表示(大端):0x42820000,对应Modbus标准字节序为0x8242 0x0000 uint8_t reg_buf[4] = {0x82, 0x42, 0x00, 0x00}; // 将Modbus寄存器数据转换为参数值 MenuErrCode err = menu_modbus_map_reg_to_param(PARAM_HUMID_ID, reg_buf, sizeof(reg_buf)); if (err == MENU_OK) { // 读取转换后的参数值 float humid_val; menu_param_get_value(PARAM_HUMID_ID, &humid_val); MENU_DEBUG(" Reg data [0x82, 0x42, 0x00, 0x00] to humid param: %.1f%%", humid_val); } else { MENU_DEBUG(" Reg to param failed: %d", err); } } // 3. 模拟Modbus读取请求处理 { MENU_DEBUG("3. Modbus Read Request Demo:"); // 模拟Modbus主机读取压力参数 uint8_t buf_len = 4; uint8_t reg_buf[4] = {0}; // 这里应该先调用menu_modbus_map_param_to_reg将参数转换到寄存器缓冲区 MenuErrCode err = menu_modbus_map_param_to_reg(PARAM_PRESS_ID, reg_buf, &buf_len); if (err == MENU_OK) { // 然后返回给Modbus主机 MENU_DEBUG(" Read pressure reg: [0x%02X, 0x%02X, 0x%02X, 0x%02X]", reg_buf[0], reg_buf[1], reg_buf[2], reg_buf[3]); } else { MENU_DEBUG(" Read pressure failed: %d", err); } } // 4. 模拟Modbus写入请求处理 { MENU_DEBUG("4. Modbus Write Request Demo:"); // 模拟Modbus主机写入温度参数(30.0°C) // 30.0f的二进制表示(大端):0x41F00000,对应Modbus标准字节序为0xF041 0x0000 uint8_t reg_buf[4] = {0xF0, 0x41, 0x00, 0x00}; // 调用menu_modbus_map_reg_to_param将寄存器数据转换为参数值 MenuErrCode err = menu_modbus_map_reg_to_param(PARAM_TEMP_ID, reg_buf, sizeof(reg_buf)); if (err == MENU_OK) { // 读取转换后的参数值 float temp_val; menu_param_get_value(PARAM_TEMP_ID, &temp_val); MENU_DEBUG(" Write temp reg: [0xF0, 0x41, 0x00, 0x00] -> temp param: %.1f°C", temp_val); } else { MENU_DEBUG(" Write temp failed: %d", err); } } MENU_DEBUG("=== Modbus Communication Demo End ==="); #endif } /************************** 主函数 **************************/ int main(void) { MENU_DEBUG("=== Menu Component Example ==="); /************************** 1. 初始化硬件 **************************/ MENU_DEBUG("1. Initialize hardware..."); // 这里应该初始化硬件,如UART、LCD、SysTick等 // hal_init(); /************************** 2. 初始化菜单组件 **************************/ MENU_DEBUG("2. Initialize menu component..."); MenuErrCode err = menu_init(); if (err != MENU_OK) { MENU_DEBUG(" Menu init failed: %d", err); return -1; } /************************** 3. 注册菜单节点 **************************/ MENU_DEBUG("3. Register menu nodes..."); // 注册根节点 err = menu_register_node(0, MENU_ROOT_ID, "主菜单", NULL, NULL); if (err != MENU_OK) { MENU_DEBUG(" Register root node failed: %d", err); return -1; } // 注册参数设置节点 err = menu_register_node(MENU_ROOT_ID, MENU_PARAM_ID, "参数设置", menu_cb_enter_param, menu_cb_exit_param); if (err != MENU_OK) { MENU_DEBUG(" Register param node failed: %d", err); return -1; } // 注册Modbus设置节点 err = menu_register_node(MENU_ROOT_ID, MENU_MODBUS_ID, "Modbus设置", menu_cb_enter_modbus, NULL); if (err != MENU_OK) { MENU_DEBUG(" Register modbus node failed: %d", err); return -1; } // 注册温度参数节点 err = menu_register_node(MENU_PARAM_ID, MENU_TEMP_ID, "温度设置", NULL, NULL); if (err != MENU_OK) { MENU_DEBUG(" Register temp node failed: %d", err); return -1; } // 注册湿度参数节点 err = menu_register_node(MENU_PARAM_ID, MENU_HUMID_ID, "湿度设置", NULL, NULL); if (err != MENU_OK) { MENU_DEBUG(" Register humid node failed: %d", err); return -1; } // 注册压力参数节点 err = menu_register_node(MENU_PARAM_ID, MENU_PRESS_ID, "压力设置", NULL, NULL); if (err != MENU_OK) { MENU_DEBUG(" Register press node failed: %d", err); return -1; } /************************** 4. 注册参数 **************************/ MENU_DEBUG("4. Register parameters..."); #if MENU_CONFIG_ENABLE_PARAM // 注册温度参数(范围:-40.0°C ~ 120.0°C,默认25.0°C,精度0.1°C) err = menu_param_register(MENU_TEMP_ID, PARAM_TEMP_ID, MENU_PARAM_TYPE_FLOAT, -40.0f, 120.0f, 25.0f, 0.1f); if (err != MENU_OK) { MENU_DEBUG(" Register temp param failed: %d", err); return -1; } // 注册湿度参数(范围:0.0% ~ 100.0%,默认50.0%,精度0.1%) err = menu_param_register(MENU_HUMID_ID, PARAM_HUMID_ID, MENU_PARAM_TYPE_FLOAT, 0.0f, 100.0f, 50.0f, 0.1f); if (err != MENU_OK) { MENU_DEBUG(" Register humid param failed: %d", err); return -1; } // 注册压力参数(范围:0.0kPa ~ 1000.0kPa,默认101.3kPa,精度0.1kPa) err = menu_param_register(MENU_PRESS_ID, PARAM_PRESS_ID, MENU_PARAM_TYPE_FLOAT, 0.0f, 1000.0f, 101.3f, 0.1f); if (err != MENU_OK) { MENU_DEBUG(" Register press param failed: %d", err); return -1; } #endif /************************** 5. 注册Modbus映射 **************************/ MENU_DEBUG("5. Register Modbus mappings..."); // 注册温度参数到Modbus寄存器映射 err = menu_modbus_map_register( PARAM_TEMP_ID, // 参数ID MODBUS_REG_TYPE_HOLDING_REG, // 寄存器类型 REG_TEMP_ADDR, // 起始地址 2, // 寄存器数量(浮点型占2个16位寄存器) MODBUS_PERM_READ_WRITE, // 读写权限 2 // 字节序(Modbus标准) ); if (err != MENU_OK) { MENU_DEBUG(" Register temp modbus map failed: %d", err); return -1; } // 注册湿度参数到Modbus寄存器映射 err = menu_modbus_map_register( PARAM_HUMID_ID, // 参数ID MODBUS_REG_TYPE_HOLDING_REG, // 寄存器类型 REG_HUMID_ADDR, // 起始地址 2, // 寄存器数量 MODBUS_PERM_READ_WRITE, // 读写权限 2 // 字节序(Modbus标准) ); if (err != MENU_OK) { MENU_DEBUG(" Register humid modbus map failed: %d", err); return -1; } // 注册压力参数到Modbus寄存器映射 err = menu_modbus_map_register( PARAM_PRESS_ID, // 参数ID MODBUS_REG_TYPE_HOLDING_REG, // 寄存器类型 REG_PRESS_ADDR, // 起始地址 2, // 寄存器数量 MODBUS_PERM_READ_WRITE, // 读写权限 2 // 字节序(Modbus标准) ); if (err != MENU_OK) { MENU_DEBUG(" Register press modbus map failed: %d", err); return -1; } /************************** 6. 运行Modbus通信演示 **************************/ MENU_DEBUG("6. Run Modbus communication demo..."); modbus_communication_demo(); /************************** 7. 进入主循环 **************************/ MENU_DEBUG("7. Enter main loop..."); while (1) { // 处理菜单事件和刷新显示 menu_main_loop(); // 其他业务逻辑 // ... // 模拟延时 menu_port_delay_ms(10); } return 0; }