/* Includes ----------------------------------------------------------------- */ #include "device/mrobot.h" #include "device/device.h" #include "device/motor.h" #include "component/freertos_cli.h" #include "bsp/uart.h" #include #include #include #include #include #include /* Private variables -------------------------------------------------------- */ static MRobot_Device_t devices[MROBOT_MAX_DEVICES]; static uint8_t device_count = 0; static char current_path[64] = "/"; static char output_buffer[MROBOT_MAX_OUTPUT_LEN]; /* UART 相关变量 */ static uint8_t uart_rx_char; static uint8_t cmd_buffer[128]; static volatile uint8_t cmd_index = 0; static volatile bool cmd_ready = false; static volatile bool htop_mode = false; static volatile bool htop_exit = false; static volatile bool tx_complete = true; /* Private function prototypes ---------------------------------------------- */ static BaseType_t cmd_help(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString); static BaseType_t cmd_htop(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString); static BaseType_t cmd_cd(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString); static BaseType_t cmd_ls(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString); static BaseType_t cmd_show(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString); static BaseType_t cmd_pwd(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString); /* CLI 命令定义 */ static const CLI_Command_Definition_t cmd_def_help = { "help", "help: 显示所有可用命令\r\n", cmd_help, 0 }; static const CLI_Command_Definition_t cmd_def_htop = { "htop", "htop: 动态显示 FreeRTOS 任务状态 (按 'q' 退出)\r\n", cmd_htop, 0 }; static const CLI_Command_Definition_t cmd_def_cd = { "cd", "cd : 切换目录\r\n", cmd_cd, 1 }; static const CLI_Command_Definition_t cmd_def_ls = { "ls", "ls: 列出当前目录内容\r\n", cmd_ls, 0 }; static const CLI_Command_Definition_t cmd_def_show = { "show", "show [device] [count]: 显示设备信息,count 默认为 1\r\n", cmd_show, -1 /* 可变参数 */ }; static const CLI_Command_Definition_t cmd_def_pwd = { "pwd", "pwd: 显示当前目录\r\n", cmd_pwd, 0 }; /* Private functions -------------------------------------------------------- */ /* 通用 IMU 设备打印函数 */ static void mrobot_print_imu(void *device_data, char *buffer, uint16_t buffer_size) { DEVICE_IMU_t *imu = (DEVICE_IMU_t *)device_data; /* 将浮点数转换为整数和小数部分,避免 printf 浮点数支持问题 */ int accl_x_int = (int)(imu->accl.x); int accl_x_frac = (int)((imu->accl.x - accl_x_int) * 1000); int accl_y_int = (int)(imu->accl.y); int accl_y_frac = (int)((imu->accl.y - accl_y_int) * 1000); int accl_z_int = (int)(imu->accl.z); int accl_z_frac = (int)((imu->accl.z - accl_z_int) * 1000); int gyro_x_int = (int)(imu->gyro.x); int gyro_x_frac = (int)((imu->gyro.x - gyro_x_int) * 1000); int gyro_y_int = (int)(imu->gyro.y); int gyro_y_frac = (int)((imu->gyro.y - gyro_y_int) * 1000); int gyro_z_int = (int)(imu->gyro.z); int gyro_z_frac = (int)((imu->gyro.z - gyro_z_int) * 1000); int temp_int = (int)(imu->temp); int temp_frac = (int)((imu->temp - temp_int) * 100); float roll_deg = imu->euler.rol * 57.2958f; float pitch_deg = imu->euler.pit * 57.2958f; float yaw_deg = imu->euler.yaw * 57.2958f; int roll_int = (int)roll_deg; int roll_frac = (int)((roll_deg - roll_int) * 100); int pitch_int = (int)pitch_deg; int pitch_frac = (int)((pitch_deg - pitch_int) * 100); int yaw_int = (int)yaw_deg; int yaw_frac = (int)((yaw_deg - yaw_int) * 100); snprintf(buffer, buffer_size, "状态: %s\r\n" "加速度计: X=%d.%03d Y=%d.%03d Z=%d.%03d m/s²\r\n" "陀螺仪: X=%d.%03d Y=%d.%03d Z=%d.%03d rad/s\r\n" "温度: %d.%02d °C\r\n" "欧拉角: Roll=%d.%02d Pitch=%d.%02d Yaw=%d.%02d °\r\n", imu->header.online ? "在线" : "离线", accl_x_int, abs(accl_x_frac), accl_y_int, abs(accl_y_frac), accl_z_int, abs(accl_z_frac), gyro_x_int, abs(gyro_x_frac), gyro_y_int, abs(gyro_y_frac), gyro_z_int, abs(gyro_z_frac), temp_int, abs(temp_frac), roll_int, abs(roll_frac), pitch_int, abs(pitch_frac), yaw_int, abs(yaw_frac)); } /* 通用电机设备打印函数 */ static void mrobot_print_motor(void *device_data, char *buffer, uint16_t buffer_size) { MOTOR_t *motor = (MOTOR_t *)device_data; int angle_int = (int)(motor->feedback.rotor_abs_angle); int angle_frac = (int)((motor->feedback.rotor_abs_angle - angle_int) * 100); int speed_int = (int)(motor->feedback.rotor_speed); int speed_frac = (int)((motor->feedback.rotor_speed - speed_int) * 100); int current_int = (int)(motor->feedback.torque_current); int current_frac = (int)((motor->feedback.torque_current - current_int) * 100); int temp_int = (int)(motor->feedback.temp); int temp_frac = (int)((motor->feedback.temp - temp_int) * 10); snprintf(buffer, buffer_size, "状态: %s\r\n" "反装: %s\r\n" "角度: %d.%02d °\r\n" "转速: %d.%02d RPM\r\n" "电流: %d.%02d A\r\n" "温度: %d.%01d °C\r\n", motor->header.online ? "在线" : "离线", motor->reverse ? "是" : "否", angle_int, abs(angle_frac), speed_int, abs(speed_frac), current_int, abs(current_frac), temp_int, abs(temp_frac)); } /* help 命令实现 */ static BaseType_t cmd_help(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString) { (void)pcCommandString; snprintf(pcWriteBuffer, xWriteBufferLen, "MRobot CLI v1.0\r\n" "可用命令:\r\n" " help - 显示帮助信息\r\n" " htop - 动态显示任务状态 (按 'q' 退出)\r\n" " cd - 切换目录\r\n" " ls - 列出目录内容\r\n" " pwd - 显示当前路径\r\n" " show - 显示设备信息\r\n" "\r\n"); return pdFALSE; } /* htop 命令实现 - 由 cli.c 处理动态显示 */ static BaseType_t cmd_htop(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString) { (void)pcCommandString; (void)pcWriteBuffer; (void)xWriteBufferLen; /* htop 命令在 cli.c 中被特殊处理,这里返回空 */ return pdFALSE; } /* pwd 命令实现 */ static BaseType_t cmd_pwd(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString) { (void)pcCommandString; snprintf(pcWriteBuffer, xWriteBufferLen, "%s\r\n", current_path); return pdFALSE; } /* cd 命令实现 */ static BaseType_t cmd_cd(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString) { const char *param; BaseType_t param_len; param = FreeRTOS_CLIGetParameter(pcCommandString, 1, ¶m_len); if (param != NULL) { char path[64]; strncpy(path, param, param_len); path[param_len] = '\0'; if (strcmp(path, "/") == 0 || strcmp(path, "..") == 0) { strcpy(current_path, "/"); } else if (strcmp(path, "dev") == 0 || strcmp(path, "/dev") == 0) { strcpy(current_path, "/dev"); } else if (strcmp(path, "modules") == 0 || strcmp(path, "/modules") == 0) { strcpy(current_path, "/modules"); } else { snprintf(pcWriteBuffer, xWriteBufferLen, "错误: 目录不存在\r\n"); return pdFALSE; } snprintf(pcWriteBuffer, xWriteBufferLen, "切换到: %s\r\n", current_path); } else { snprintf(pcWriteBuffer, xWriteBufferLen, "错误: 缺少路径参数\r\n"); } return pdFALSE; } /* ls 命令实现 */ static BaseType_t cmd_ls(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString) { (void)pcCommandString; int offset = 0; if (strcmp(current_path, "/") == 0) { offset = snprintf(pcWriteBuffer, xWriteBufferLen, "dev/\r\nmodules/\r\n"); } else if (strcmp(current_path, "/dev") == 0) { offset = snprintf(pcWriteBuffer, xWriteBufferLen, "设备列表:\r\n"); for (uint8_t i = 0; i < device_count && offset < (int)xWriteBufferLen; i++) { offset += snprintf(pcWriteBuffer + offset, xWriteBufferLen - offset, " %s\r\n", devices[i].name); } if (device_count == 0) { offset += snprintf(pcWriteBuffer + offset, xWriteBufferLen - offset, " (无设备)\r\n"); } } else if (strcmp(current_path, "/modules") == 0) { offset = snprintf(pcWriteBuffer, xWriteBufferLen, "(模块功能暂未实现)\r\n"); } return pdFALSE; } /* show 命令实现 */ static BaseType_t cmd_show(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString) { const char *param; const char *count_param; BaseType_t param_len, count_param_len; static uint32_t print_count = 0; static uint32_t current_iteration = 0; static char saved_device_name[32] = {0}; /* 首次调用时解析参数 */ if (current_iteration == 0) { param = FreeRTOS_CLIGetParameter(pcCommandString, 1, ¶m_len); count_param = FreeRTOS_CLIGetParameter(pcCommandString, 2, &count_param_len); if (strcmp(current_path, "/dev") != 0) { snprintf(pcWriteBuffer, xWriteBufferLen, "错误: show 命令仅在 /dev 目录下可用\r\n"); return pdFALSE; } /* 解析打印次数,默认为 1 */ print_count = 1; if (count_param != NULL) { char count_str[16]; strncpy(count_str, count_param, count_param_len < 15 ? count_param_len : 15); count_str[count_param_len < 15 ? count_param_len : 15] = '\0'; int parsed_count = atoi(count_str); if (parsed_count > 0 && parsed_count <= 100) { print_count = parsed_count; } } /* 保存设备名称 */ if (param != NULL) { strncpy(saved_device_name, param, param_len); saved_device_name[param_len] = '\0'; } else { saved_device_name[0] = '\0'; } } /* 执行打印 */ int offset = 0; /* 多次打印时清屏(像htop一样) */ if (print_count > 1) { const char *clear = "\033[2J\033[H"; /* ANSI清屏 + 光标归位 */ offset = snprintf(pcWriteBuffer, xWriteBufferLen, "%s", clear); } if (saved_device_name[0] == '\0') { /* 显示所有设备 */ if (print_count > 1) { offset += snprintf(pcWriteBuffer + offset, xWriteBufferLen - offset, "[第 %u/%u 次]\r\n", current_iteration + 1, print_count); } offset += snprintf(pcWriteBuffer + offset, xWriteBufferLen - offset, "所有设备信息:\r\n"); for (uint8_t i = 0; i < device_count && offset < (int)xWriteBufferLen; i++) { offset += snprintf(pcWriteBuffer + offset, xWriteBufferLen - offset, "\r\n=== %s ===\r\n", devices[i].name); if (devices[i].print_callback != NULL) { devices[i].print_callback(devices[i].data, pcWriteBuffer + offset, xWriteBufferLen - offset); offset = strlen(pcWriteBuffer); } } } else { /* 显示特定设备 */ bool found = false; for (uint8_t i = 0; i < device_count; i++) { if (strcmp(devices[i].name, saved_device_name) == 0) { found = true; int offset = 0; if (print_count > 1) { offset = snprintf(pcWriteBuffer, xWriteBufferLen, "[第 %u/%u 次] === %s ===\r\n", current_iteration + 1, print_count, devices[i].name); } else { offset = snprintf(pcWriteBuffer, xWriteBufferLen, "=== %s ===\r\n", devices[i].name); } if (devices[i].print_callback != NULL) { devices[i].print_callback(devices[i].data, pcWriteBuffer + offset, xWriteBufferLen - offset); } else { snprintf(pcWriteBuffer + offset, xWriteBufferLen - offset, "无打印函数\r\n"); } break; } } if (!found && current_iteration == 0) { snprintf(pcWriteBuffer, xWriteBufferLen, "错误: 设备 '%s' 未找到\r\n", saved_device_name); current_iteration = 0; return pdFALSE; } } /* 判断是否需要继续打印 */ current_iteration++; if (current_iteration < print_count) { osDelay(200); /* 延时 200ms 再打印下一次 */ return pdTRUE; /* 返回 pdTRUE 表示还有更多输出 */ } else { current_iteration = 0; /* 重置计数器 */ return pdFALSE; } } /* UART 发送完成回调 */ static void mrobot_uart_tx_callback(void) { tx_complete = true; } /* UART 接收中断回调 */ static void mrobot_uart_rx_callback(void) { uint8_t ch = uart_rx_char; /* 如果在 htop 模式,检查退出键 */ if (htop_mode) { if (ch == 'q' || ch == 'Q' || ch == 27) { /* q 或 ESC */ htop_exit = true; } /* 重新启动接收 */ BSP_UART_Receive(BSP_UART_VOFA, &uart_rx_char, 1, false); return; } if (ch == '\r' || ch == '\n') { if (cmd_index > 0) { cmd_buffer[cmd_index] = '\0'; cmd_ready = true; /* 换行使用中断方式发送(数据量小) */ BSP_UART_Transmit(BSP_UART_VOFA, (uint8_t *)"\r\n", 2, false); } } else if (ch == 127 || ch == 8) { /* 退格键 */ if (cmd_index > 0) { cmd_index--; /* 回显退格(中断方式) */ const char *backspace = "\b \b"; BSP_UART_Transmit(BSP_UART_VOFA, (uint8_t *)backspace, 3, false); } } else if (ch >= 32 && ch < 127 && cmd_index < sizeof(cmd_buffer) - 1) { /* 只接受可打印字符 */ cmd_buffer[cmd_index++] = ch; /* 回显字符(中断方式) */ BSP_UART_Transmit(BSP_UART_VOFA, &ch, 1, false); } /* 重新启动接收 */ BSP_UART_Receive(BSP_UART_VOFA, &uart_rx_char, 1, false); } /* Exported functions ------------------------------------------------------- */ void MRobot_Init(void) { /* 初始化设备数组 */ memset(devices, 0, sizeof(devices)); device_count = 0; /* 注册 CLI 命令 */ FreeRTOS_CLIRegisterCommand(&cmd_def_help); FreeRTOS_CLIRegisterCommand(&cmd_def_htop); FreeRTOS_CLIRegisterCommand(&cmd_def_cd); FreeRTOS_CLIRegisterCommand(&cmd_def_ls); FreeRTOS_CLIRegisterCommand(&cmd_def_show); FreeRTOS_CLIRegisterCommand(&cmd_def_pwd); /* 注册 UART 回调 */ BSP_UART_RegisterCallback(BSP_UART_VOFA, BSP_UART_RX_CPLT_CB, mrobot_uart_rx_callback); BSP_UART_RegisterCallback(BSP_UART_VOFA, BSP_UART_TX_CPLT_CB, mrobot_uart_tx_callback); /* 启动 UART 接收 */ BSP_UART_Receive(BSP_UART_VOFA, &uart_rx_char, 1, false); /* 发送欢迎消息 */ const char *welcome = "\r\n\r\n" "================================\r\n" " MRobot CLI v1.0\r\n" " 输入 'help' 获取帮助\r\n" "================================\r\n" "root@mrobot:~$ "; tx_complete = false; BSP_UART_Transmit(BSP_UART_VOFA, (uint8_t *)welcome, strlen(welcome), true); while (!tx_complete) { osDelay(1); } /* 等待发送完成 */ } int8_t MRobot_RegisterDevice(const char *name, MRobot_DeviceType_t type, void *data, MRobot_PrintCallback_t print_callback) { if (device_count >= MROBOT_MAX_DEVICES) { return -1; } if (name == NULL || data == NULL) { return -1; } strncpy(devices[device_count].name, name, sizeof(devices[device_count].name) - 1); devices[device_count].type = type; devices[device_count].data = data; devices[device_count].print_callback = print_callback; device_count++; return 0; } int8_t MRobot_RegisterIMU(const char *name, void *imu_device) { return MRobot_RegisterDevice(name, MROBOT_DEVICE_TYPE_IMU, imu_device, mrobot_print_imu); } int8_t MRobot_RegisterMotor(const char *name, void *motor) { return MRobot_RegisterDevice(name, MROBOT_DEVICE_TYPE_MOTOR, motor, mrobot_print_motor); } void MRobot_Run(void) { /* 动态 htop 模式 */ if (htop_mode) { /* 清屏并重置光标 */ const char *clear = "\033[2J\033[H"; tx_complete = false; BSP_UART_Transmit(BSP_UART_VOFA, (uint8_t *)clear, strlen(clear), true); while (!tx_complete) { osDelay(1); } /* 等待发送完成 */ /* 显示 htop 头部 */ const char *header = "MRobot Task Monitor (按 'q' 退出)\r\n" "================================================================================\r\n"; tx_complete = false; BSP_UART_Transmit(BSP_UART_VOFA, (uint8_t *)header, strlen(header), true); while (!tx_complete) { osDelay(1); } /* 等待发送完成 */ /* 获取任务列表 */ char task_buffer[1024]; vTaskList(task_buffer); /* 格式化输出 */ char display_buffer[1536]; int offset = snprintf(display_buffer, sizeof(display_buffer), "%-16s %-8s %-4s %-8s %-4s\r\n" "--------------------------------------------------------------------------------\r\n", "Task Name", "State", "Prio", "Stack", "Num"); /* 解析并美化任务列表 */ char *line = strtok(task_buffer, "\r\n"); while (line != NULL && offset < (int)sizeof(display_buffer) - 100) { char name[17] = {0}; char state[9] = {0}; int prio = 0; int stack = 0; int num = 0; if (sscanf(line, "%16s %c %d %d %d", name, &state[0], &prio, &stack, &num) == 5) { /* 状态字符转换 */ const char *state_str = "?"; switch(state[0]) { case 'R': state_str = "Running"; break; case 'B': state_str = "Blocked"; break; case 'S': state_str = "Suspend"; break; case 'D': state_str = "Deleted"; break; default: state_str = "Unknown"; break; } offset += snprintf(display_buffer + offset, sizeof(display_buffer) - offset, "%-16s %-8s %-4d %-8d %-4d\r\n", name, state_str, prio, stack, num); } line = strtok(NULL, "\r\n"); } /* 添加运行时统计 */ offset += snprintf(display_buffer + offset, sizeof(display_buffer) - offset, "--------------------------------------------------------------------------------\r\n" "System Tick: %lu\r\n", (unsigned long)xTaskGetTickCount()); tx_complete = false; BSP_UART_Transmit(BSP_UART_VOFA, (uint8_t *)display_buffer, strlen(display_buffer), true); while (!tx_complete) { osDelay(1); } /* 等待发送完成 */ /* 检查退出标志 */ if (htop_exit) { htop_mode = false; htop_exit = false; /* 清屏 */ tx_complete = false; BSP_UART_Transmit(BSP_UART_VOFA, (uint8_t *)clear, strlen(clear), true); while (!tx_complete) { osDelay(1); } /* 等待发送完成 */ /* 显示提示符 */ char prompt[128]; snprintf(prompt, sizeof(prompt), "root@mrobot:%s$ ", current_path); tx_complete = false; BSP_UART_Transmit(BSP_UART_VOFA, (uint8_t *)prompt, strlen(prompt), true); while (!tx_complete) { osDelay(1); } /* 等待发送完成 */ } osDelay(200); /* 每 200ms 刷新一次,5fps */ return; } /* 检查是否有命令需要处理 */ if (cmd_ready) { /* 检查是否是 htop 命令 */ if (strcmp((char *)cmd_buffer, "htop") == 0) { htop_mode = true; htop_exit = false; } else { /* 处理其他命令 */ BaseType_t result; output_buffer[0] = '\0'; /* 清空输出缓冲区 */ do { result = FreeRTOS_CLIProcessCommand((char *)cmd_buffer, output_buffer, sizeof(output_buffer)); /* 立即发送输出 */ if (strlen(output_buffer) > 0) { tx_complete = false; BSP_UART_Transmit(BSP_UART_VOFA, (uint8_t *)output_buffer, strlen(output_buffer), true); while (!tx_complete) { osDelay(1); } output_buffer[0] = '\0'; /* 清空缓冲区准备下一次 */ } } while (result != pdFALSE); /* 发送提示符 */ char prompt[128]; snprintf(prompt, sizeof(prompt), "root@mrobot:%s$ ", current_path); tx_complete = false; BSP_UART_Transmit(BSP_UART_VOFA, (uint8_t *)prompt, strlen(prompt), true); while (!tx_complete) { osDelay(1); } /* 等待发送完成 */ } /* 重置状态 */ cmd_index = 0; cmd_ready = false; } osDelay(10); }