一、整体结构说明

嵌入式系统(基于FreeRTOS)串口命令行调试工具,采用模块化设计,核心结构分层如下:

模块层级 功能说明
1. 配置与宏定义 调试开关、缓冲区大小、密码/超时配置、硬件适配宏(UART/FreeRTOS)
2. 类型定义 命令处理函数类型、命令表结构体、全局状态枚举
3. 全局变量 命令行缓冲、参数数组、认证/超时状态、命令表
4. 基础工具函数 UART硬件收发(需用户适配)、十六/十进制解析、字符串辅助函数
5. 命令处理函数 help/read/write/tasklist/info 核心功能实现
6. 核心解析逻辑 命令行分割、命令查找、主处理流程
7. UART字符处理 行缓冲、回显、退格、密码输入、超时检查
8. 安全保护逻辑 编译开关禁用、密码认证、超时自动登出
9. 初始化与任务 CLI初始化、FreeRTOS任务入口(可选)

二、完整代码实现

/*************************************************************************
* 文件名: cli_debug_tool.c
* 功能: 嵌入式串口命令行调试工具(基于FreeRTOS)
* 适配: ARM Cortex-M系列,需用户适配UART硬件驱动
* 编译开关: ENABLE_CLI_DEBUG - 启用调试工具(生产版本注释/undef)
*************************************************************************/
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>

/* ========================== 1. 配置与宏定义 ========================== */
// 调试工具总开关:生产版本注释此行或undef
#define ENABLE_CLI_DEBUG 1

// UART硬件配置(用户根据实际硬件修改)
#define UART_BAUDRATE    115200  // 串口波特率
#define UART_TX_PIN      0       // 发送引脚(示例)
#define UART_RX_PIN      1       // 接收引脚(示例)

// 命令行缓冲区配置
#define MAX_CMD_LENGTH   128     // 最大命令行长度
#define MAX_ARGC         16      // 最大参数个数
#define CMD_PROMPT       "> "    // 命令行提示符

// 安全保护配置
#define CLI_PASSWORD     "EmbedDebug@2025"  // 认证密码
#define CLI_TIMEOUT_MS   (5 * 60 * 1000)    // 5分钟超时登出
#define ADDR_VALID_MIN   0x20000000         // 合法内存起始地址(SRAM)
#define ADDR_VALID_MAX   0x20020000         // 合法内存结束地址

/* ========================== 2. 类型定义 ========================== */
// 命令处理函数类型:argc=参数个数,argv=参数数组,返回0成功/非0失败
typedef int (*cmd_handler_t)(int argc, char* argv[]);

// 命令表项结构体:关联命令名、帮助信息、处理函数
typedef struct {
    const char* cmd_name;    // 命令名称(如"read")
    const char* help_info;   // 帮助描述
    cmd_handler_t handler;   // 处理函数指针
} cmd_entry_t;

/* ========================== 3. 全局变量 ========================== */
#ifdef ENABLE_CLI_DEBUG
// 命令行缓冲区:存储用户输入的字符(行缓冲)
static char cmd_buffer[MAX_CMD_LENGTH];
static uint8_t cmd_index = 0;  // 缓冲区当前索引

// 参数解析全局变量:argc=参数个数,argv=参数指针数组
static char* argv[MAX_ARGC];
static int argc = 0;

// 安全保护相关变量
static bool cli_authenticated = false;  // 认证状态:false=未认证,true=已认证
static char password_buffer[32];        // 密码输入缓冲区
static uint8_t password_index = 0;      // 密码缓冲区索引
static uint32_t last_activity_time = 0; // 最后操作时间(用于超时)

// FreeRTOS相关:portTICK_PERIOD_MS需在FreeRTOSConfig.h中定义
#ifndef portTICK_PERIOD_MS
#define portTICK_PERIOD_MS (1000 / configTICK_RATE_HZ)
#endif
#endif

/* ========================== 4. 基础工具函数 ========================== */
#ifdef ENABLE_CLI_DEBUG
/*************************************************************************
* 函数名: uart_send_char
* 功能: 发送单个字符到UART(需用户适配硬件驱动)
* 参数: ch - 要发送的字符
* 返回: 无
*************************************************************************/
void uart_send_char(uint8_t ch)
{
    // 【用户适配】替换为实际硬件UART发送函数
    // 示例(伪代码):
    // while(!UART_TX_BUFFER_EMPTY);
    // UART_DR_REG = ch;
}

/*************************************************************************
* 函数名: uart_send_string
* 功能: 发送字符串到UART(换行用\r\n,适配串口终端)
* 参数: str - 要发送的字符串
* 返回: 无
*************************************************************************/
void uart_send_string(const char* str)
{
    if (str == NULL) return;
    while (*str != '\0') {
        uart_send_char(*str++);
    }
}

/*************************************************************************
* 函数名: parse_hex
* 功能: 解析十六进制字符串为32位无符号整数(支持0x前缀)
* 参数: str - 输入字符串(如"0x1234"、"FF");value - 输出解析结果
* 返回: 0=成功,-1=失败(格式错误/空指针)
*************************************************************************/
static int parse_hex(const char* str, uint32_t* value)
{
    if (str == NULL || value == NULL) return -1;

    // 跳过0x/0X前缀
    if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) {
        str += 2;
    }

    *value = 0;
    while (*str != '\0') {
        char c = *str++;
        uint32_t digit = 0;

        // 解析数字/字母
        if (c >= '0' && c <= '9') {
            digit = c - '0';
        } else if (c >= 'a' && c <= 'f') {
            digit = c - 'a' + 10;
        } else if (c >= 'A' && c <= 'F') {
            digit = c - 'A' + 10;
        } else {
            return -1; // 非法字符
        }

        *value = (*value << 4) | digit; // 左移4位(16进制)+ 拼接
    }

    return 0;
}

/*************************************************************************
* 函数名: parse_dec
* 功能: 解析十进制字符串为32位有符号整数(支持负数)
* 参数: str - 输入字符串(如"123"、"-456");value - 输出解析结果
* 返回: 0=成功,-1=失败(格式错误/空指针)
*************************************************************************/
static int parse_dec(const char* str, int32_t* value)
{
    if (str == NULL || value == NULL) return -1;

    int sign = 1;
    // 处理负号
    if (*str == '-') {
        sign = -1;
        str++;
    }

    *value = 0;
    while (*str != '\0') {
        char c = *str++;
        if (c >= '0' && c <= '9') {
            *value = *value * 10 + (c - '0');
        } else {
            return -1; // 非法字符
        }
    }

    *value *= sign;
    return 0;
}
#endif

/* ========================== 5. 命令处理函数实现 ========================== */
#ifdef ENABLE_CLI_DEBUG
/*************************************************************************
* 命令: help
* 功能: 列出所有支持的命令及帮助信息
* 参数: argc - 参数个数;argv - 参数数组(无参数)
* 返回: 0=成功
*************************************************************************/
static int cmd_help(int argc, char* argv[])
{
    uart_send_string("\r\n===== Available Commands =====\r\n");
    // 提前声明命令表(避免编译报错)
    extern const cmd_entry_t cmd_table[];

    // 遍历命令表,格式化输出
    for (int i = 0; cmd_table[i].cmd_name != NULL; i++) {
        uart_send_string("  ");
        uart_send_string(cmd_table[i].cmd_name);

        // 对齐帮助信息(命令名占12个字符宽度)
        int name_len = strlen(cmd_table[i].cmd_name);
        int spaces = 12 - name_len;
        for (int j = 0; j < spaces; j++) {
            uart_send_char(' ');
        }

        uart_send_string(cmd_table[i].help_info);
        uart_send_string("\r\n");
    }
    uart_send_string("==============================\r\n");
    return 0;
}

/*************************************************************************
* 命令: read
* 功能: 读取指定内存地址的32位值(仅允许合法地址范围)
* 参数: argc=2;argv[1]=内存地址(十六进制,如0x20000000)
* 返回: 0=成功,-1=参数错误/地址非法
*************************************************************************/
static int cmd_read(int argc, char* argv[])
{
    // 参数检查:必须传入地址参数
    if (argc < 2) {
        uart_send_string("Usage: read <addr> (hex, e.g. read 0x20000000)\r\n");
        return -1;
    }

    // 解析十六进制地址
    uint32_t addr;
    if (parse_hex(argv[1], &addr) != 0) {
        uart_send_string("Error: Invalid hex address format\r\n");
        return -1;
    }

    // 地址合法性检查:防止访问非法内存导致崩溃
    if (addr < ADDR_VALID_MIN || addr > ADDR_VALID_MAX) {
        uart_send_string("Error: Address out of range (");
        uart_send_string(argv[1]);
        uart_send_string(")\r\n");
        return -1;
    }

    // 读取内存(volatile防止编译器优化,确保读取实际内存)
    volatile uint32_t* ptr = (volatile uint32_t*)addr;
    uint32_t value = *ptr;

    // 格式化输出结果
    char buffer[64];
    snprintf(buffer, sizeof(buffer), "Read: 0x%08X = 0x%08X\r\n", addr, value);
    uart_send_string(buffer);

    return 0;
}

/*************************************************************************
* 命令: write
* 功能: 写入32位值到指定内存地址(仅允许合法地址范围)
* 参数: argc=3;argv[1]=地址(十六进制);argv[2]=值(十六进制)
* 返回: 0=成功,-1=参数错误/地址非法/写入失败
*************************************************************************/
static int cmd_write(int argc, char* argv[])
{
    // 参数检查
    if (argc < 3) {
        uart_send_string("Usage: write <addr> <value> (hex, e.g. write 0x20000000 0x12345678)\r\n");
        return -1;
    }

    // 解析地址
    uint32_t addr;
    if (parse_hex(argv[1], &addr) != 0) {
        uart_send_string("Error: Invalid hex address format\r\n");
        return -1;
    }

    // 解析写入值
    uint32_t value;
    if (parse_hex(argv[2], &value) != 0) {
        uart_send_string("Error: Invalid hex value format\r\n");
        return -1;
    }

    // 地址合法性检查
    if (addr < ADDR_VALID_MIN || addr > ADDR_VALID_MAX) {
        uart_send_string("Error: Address out of range\r\n");
        return -1;
    }

    // 写入内存(volatile确保写入实际内存)
    volatile uint32_t* ptr = (volatile uint32_t*)addr;
    *ptr = value;

    // 验证写入结果
    if (*ptr == value) {
        char buffer[64];
        snprintf(buffer, sizeof(buffer), "Write OK: 0x%08X = 0x%08X\r\n", addr, value);
        uart_send_string(buffer);
        return 0;
    } else {
        uart_send_string("Error: Write failed (verify error)\r\n");
        return -1;
    }
}

/*************************************************************************
* 命令: tasklist
* 功能: 显示FreeRTOS任务列表、状态、栈使用情况
* 参数: 无
* 返回: 0=成功,-1=内存分配失败
*************************************************************************/
static int cmd_tasklist(int argc, char* argv[])
{
    uart_send_string("\r\n===== FreeRTOS Task List =====\r\n");
    uart_send_string("Name\t\tState\t\tStack High Water\r\n");
    uart_send_string("----------------------------------------\r\n");

    // 获取系统任务数量
    UBaseType_t num_tasks = uxTaskGetNumberOfTasks();
    // 分配内存存储任务状态(FreeRTOS动态内存)
    TaskStatus_t* task_status = (TaskStatus_t*)pvPortMalloc(num_tasks * sizeof(TaskStatus_t));
    if (task_status == NULL) {
        uart_send_string("Error: Memory allocation failed\r\n");
        return -1;
    }

    // 获取所有任务状态
    num_tasks = uxTaskGetSystemState(task_status, num_tasks, NULL);

    // 遍历任务列表,格式化输出
    for (UBaseType_t i = 0; i < num_tasks; i++) {
        char buffer[128];
        const char* state_str;

        // 转换任务状态为字符串
        switch (task_status[i].eCurrentState) {
            case eRunning:   state_str = "Running";   break;
            case eReady:     state_str = "Ready";     break;
            case eBlocked:   state_str = "Blocked";   break;
            case eSuspended: state_str = "Suspended"; break;
            case eDeleted:   state_str = "Deleted";   break;
            default:         state_str = "Unknown";   break;
        }

        // 栈高水位:剩余栈空间(越小越危险)
        uint32_t stack_hw = task_status[i].usStackHighWaterMark;

        // 格式化输出(任务名占12字符,状态占12字符)
        snprintf(buffer, sizeof(buffer),
                "%-12s\t%-12s\t%lu bytes\r\n",
                task_status[i].pcTaskName,
                state_str,
                stack_hw);
        uart_send_string(buffer);
    }

    // 释放内存
    vPortFree(task_status);
    uart_send_string("==============================\r\n");
    return 0;
}

/*************************************************************************
* 命令: info
* 功能: 显示系统基本信息(运行时间、任务数、堆内存)
* 参数: 无
* 返回: 0=成功
*************************************************************************/
static int cmd_info(int argc, char* argv[])
{
    uart_send_string("\r\n===== System Information =====\r\n");
    char buffer[64];

    // 系统运行时间(ms)
    uint32_t uptime = xTaskGetTickCount() * portTICK_PERIOD_MS;
    snprintf(buffer, sizeof(buffer), "Uptime: %lu ms (%lu s)\r\n", uptime, uptime/1000);
    uart_send_string(buffer);

    // 任务数量
    UBaseType_t num_tasks = uxTaskGetNumberOfTasks();
    snprintf(buffer, sizeof(buffer), "Total Tasks: %lu\r\n", num_tasks);
    uart_send_string(buffer);

    // 空闲任务栈剩余
    UBaseType_t idle_stack = uxTaskGetStackHighWaterMark(NULL);
    snprintf(buffer, sizeof(buffer), "Idle Task Stack Left: %lu bytes\r\n", idle_stack);
    uart_send_string(buffer);

    // 当前空闲堆内存
    size_t free_heap = xPortGetFreeHeapSize();
    snprintf(buffer, sizeof(buffer), "Free Heap: %lu bytes\r\n", free_heap);
    uart_send_string(buffer);

    // 历史最小空闲堆(反映内存峰值使用)
    size_t min_free_heap = xPortGetMinimumEverFreeHeapSize();
    snprintf(buffer, sizeof(buffer), "Min Free Heap: %lu bytes\r\n", min_free_heap);
    uart_send_string(buffer);

    uart_send_string("==============================\r\n");
    return 0;
}
#endif

/* ========================== 6. 核心解析逻辑 ========================== */
#ifdef ENABLE_CLI_DEBUG
/*************************************************************************
* 函数名: parse_command
* 功能: 将命令行字符串分割为参数数组(argc/argv)
* 参数: cmd_line - 输入的命令行字符串(如"read 0x20000000")
* 返回: 无
*************************************************************************/
static void parse_command(char* cmd_line)
{
    argc = 0;
    char* token = cmd_line;

    // 跳过前导空格/制表符
    while (*token == ' ' || *token == '\t') {
        token++;
    }

    // 分割字符串为参数
    while (*token != '\0' && argc < MAX_ARGC) {
        argv[argc++] = token; // 记录参数起始地址

        // 查找参数结束位置(空格/制表符)
        while (*token != ' ' && *token != '\t' && *token != '\0') {
            token++;
        }

        // 替换分隔符为字符串结束符
        if (*token != '\0') {
            *token = '\0';
            token++;

            // 跳过连续分隔符
            while (*token == ' ' || *token == '\t') {
                token++;
            }
        }
    }
}

/*************************************************************************
* 函数名: find_command
* 功能: 根据命令名查找命令表中的对应项
* 参数: cmd_name - 要查找的命令名(如"read")
* 返回: 命令表项指针(NULL=未找到)
*************************************************************************/
static const cmd_entry_t* find_command(const char* cmd_name)
{
    extern const cmd_entry_t cmd_table[];
    for (int i = 0; cmd_table[i].cmd_name != NULL; i++) {
        if (strcmp(cmd_table[i].cmd_name, cmd_name) == 0) {
            return &cmd_table[i];
        }
    }
    return NULL;
}

/*************************************************************************
* 函数名: process_command
* 功能: 命令行主处理逻辑:解析→查找→执行
* 参数: cmd_line - 输入的命令行字符串
* 返回: 无
*************************************************************************/
static void process_command(char* cmd_line)
{
    // 解析命令行为argc/argv
    parse_command(cmd_line);

    // 空命令直接返回
    if (argc == 0) {
        return;
    }

    // 查找命令
    const cmd_entry_t* cmd = find_command(argv[0]);
    if (cmd == NULL) {
        uart_send_string("Error: Unknown command - ");
        uart_send_string(argv[0]);
        uart_send_string("\r\n");
        return;
    }

    // 执行命令处理函数
    int result = cmd->handler(argc, argv);
    if (result != 0) {
        uart_send_string("Command execution failed\r\n");
    }
}
#endif

/* ========================== 7. UART字符处理 ========================== */
#ifdef ENABLE_CLI_DEBUG
/*************************************************************************
* 函数名: cli_debug_process_char
* 功能: 处理单个UART接收字符:行缓冲、回显、退格、密码认证、超时检查
* 参数: ch - 接收到的字符
* 返回: 无
*************************************************************************/
void cli_debug_process_char(uint8_t ch)
{
    // 1. 超时检查:已认证且超时则登出
    uint32_t current_time = xTaskGetTickCount();
    if (cli_authenticated && (current_time - last_activity_time) > (CLI_TIMEOUT_MS / portTICK_PERIOD_MS)) {
        cli_authenticated = false;
        uart_send_string("\r\n[Timeout] Session closed\r\nPassword: ");
        password_index = 0;
        return;
    }
    last_activity_time = current_time; // 更新最后操作时间

    // 2. 未认证:进入密码输入模式
    if (!cli_authenticated) {
        // 回车:验证密码
        if (ch == '\r' || ch == '\n') {
            password_buffer[password_index] = '\0';
            if (strcmp(password_buffer, CLI_PASSWORD) == 0) {
                cli_authenticated = true;
                uart_send_string("\r\n[Success] Access granted\r\n");
                uart_send_string(CMD_PROMPT); // 显示命令提示符
            } else {
                uart_send_string("\r\n[Failed] Wrong password\r\nPassword: ");
            }
            password_index = 0; // 重置密码缓冲区
            return;
        }

        // 退格/删除:删除最后一个字符(回显为"← 空格 ←")
        if (ch == '\b' || ch == 0x7F) {
            if (password_index > 0) {
                password_index--;
                uart_send_string("\b \b"); // 视觉上删除字符
            }
            return;
        }

        // 普通字符:存入密码缓冲区,回显为*(隐藏明文)
        if (password_index < sizeof(password_buffer) - 1) {
            password_buffer[password_index++] = ch;
            uart_send_char('*'); // 不显示明文密码
        }
        return;
    }

    // 3. 已认证:正常命令行处理
    // 回车:执行命令
    if (ch == '\r' || ch == '\n') {
        uart_send_string("\r\n"); // 换行
        cmd_buffer[cmd_index] = '\0';
        process_command(cmd_buffer); // 处理命令
        cmd_index = 0; // 重置命令缓冲区
        uart_send_string(CMD_PROMPT); // 显示提示符
        return;
    }

    // 退格/删除:删除最后一个字符
    if (ch == '\b' || ch == 0x7F) {
        if (cmd_index > 0) {
            cmd_index--;
            uart_send_string("\b \b"); // 视觉删除
        }
        return;
    }

    // 普通字符:存入缓冲区并回显
    if (cmd_index < MAX_CMD_LENGTH - 1) {
        cmd_buffer[cmd_index++] = ch;
        uart_send_char(ch); // 回显字符
    }
}
#endif

/* ========================== 8. 命令表定义 ========================== */
#ifdef ENABLE_CLI_DEBUG
// 命令表:按"命令名-帮助信息-处理函数"关联(最后一项为NULL结束)
const cmd_entry_t cmd_table[] = {
    {"help",     "Show all commands and usage",          cmd_help},
    {"read",     "Read 32-bit value from memory: read <addr>", cmd_read},
    {"write",    "Write 32-bit value to memory: write <addr> <val>", cmd_write},
    {"tasklist", "Show FreeRTOS task list and stack info", cmd_tasklist},
    {"info",     "Show system information (uptime/heap/tasks)", cmd_info},
    {NULL,       NULL,                                   NULL} // 结束标记
};
#endif

/* ========================== 9. 初始化与任务 ========================== */
/*************************************************************************
* 函数名: uart_init
* 功能: UART硬件初始化(需用户适配实际硬件)
* 参数: 无
* 返回: 无
*************************************************************************/
void uart_init(void)
{
#ifdef ENABLE_CLI_DEBUG
    // 【用户适配】添加UART初始化代码:
    // 1. 配置引脚复用(TX/RX)
    // 2. 设置波特率(UART_BAUDRATE)
    // 3. 启用接收中断(或DMA)
    // 4. 启用UART收发

    // 初始化完成后提示密码输入
    uart_send_string("\r\n===== Embedded CLI Debug Tool =====\r\n");
    uart_send_string("Please enter password: ");
    last_activity_time = xTaskGetTickCount(); // 初始化超时时间
#endif
}

/*************************************************************************
* 函数名: cli_debug_task
* 功能: FreeRTOS CLI任务(可选,若UART用中断接收则无需此任务)
* 参数: pvParameters - 任务参数(未使用)
* 返回: 无
*************************************************************************/
#ifdef ENABLE_CLI_DEBUG
void cli_debug_task(void* pvParameters)
{
    (void)pvParameters;
    uint8_t ch;

    // 任务主循环
    while (1) {
        // 【用户适配】读取UART接收字符(非阻塞)
        // 示例:if (uart_rx_available()) { ch = uart_read_char(); cli_debug_process_char(ch); }
        vTaskDelay(pdMS_TO_TICKS(10)); // 10ms延时,降低CPU占用
    }
}
#endif

/*************************************************************************
* 函数名: cli_debug_init
* 功能: CLI调试工具初始化:UART初始化 + 创建FreeRTOS任务
* 参数: 无
* 返回: 无
*************************************************************************/
void cli_debug_init(void)
{
#ifdef ENABLE_CLI_DEBUG
    // 初始化UART
    uart_init();

    // 创建CLI处理任务(优先级低于业务任务,栈大小按需调整)
    xTaskCreate(cli_debug_task, "CLI_Task", 1024, NULL, 1, NULL);
#else
    // 生产版本:无操作
#endif
}

/* ========================== 10. 生产版本兼容宏 ========================== */
#ifndef ENABLE_CLI_DEBUG
// 生产版本:空宏定义,避免编译错误
#define cli_debug_init()              ((void)0)
#define cli_debug_process_char(ch)    ((void)0)
#define cli_debug_task(pv)            ((void)0)
#endif

三、代码使用说明

1. 硬件适配
  • UART收发函数uart_send_char/uart_init 需要用户根据实际MCU(如STM32、NXP)的HAL/LL库适配;
  • UART接收方式:推荐用「中断接收」(字符到达后调用 cli_debug_process_char),也可使用FreeRTOS任务轮询(cli_debug_task);
  • 内存地址范围:修改 ADDR_VALID_MIN/ADDR_VALID_MAX 适配目标MCU的SRAM/Flash地址。
2. 编译控制
  • 开发版本:保留 #define ENABLE_CLI_DEBUG 1,编译时包含调试工具;
  • 生产版本:注释/删除 ENABLE_CLI_DEBUG,所有CLI相关函数会被空宏替换,无代码冗余。
3. 安全保护
  • 密码认证:默认密码 EmbedDebug@2025,建议根据项目修改;
  • 超时登出:5分钟无操作自动登出,可修改 CLI_TIMEOUT_MS 调整;
  • 内存保护read/write 命令仅允许访问合法地址范围,防止非法内存操作。
4. 扩展命令

添加新命令只需两步:

  1. 实现命令处理函数(如 cmd_xxx);
  2. cmd_table 中添加一行:{"xxx", "Help info", cmd_xxx}

四、典型使用流程

  1. 设备上电后,串口终端显示 Please enter password:
  2. 输入正确密码(回显为*),回车后显示提示符 >
  3. 输入命令调试:
    • help:查看所有命令;
    • read 0x20000000:读取0x20000000地址的32位值;
    • write 0x20000000 0x12345678:写入值到指定地址;
    • tasklist:查看FreeRTOS任务状态;
    • info:查看系统运行信息;
  4. 5分钟无操作自动登出,需重新输入密码。

五、关键注意事项

  1. 中断安全:若在UART中断中调用 cli_debug_process_char,需确保FreeRTOS API(如 xTaskGetTickCount)在中断中可用(启用 configUSE_TIMERS);
  2. 栈大小cli_debug_task 的栈大小(1024)需根据实际命令复杂度调整;
  3. 内存保护write 命令禁止写入程序Flash/寄存器地址,避免系统崩溃;
  4. 字符编码:仅支持ASCII字符,不支持中文;
  5. 兼容性:代码基于FreeRTOS,若使用其他RTOS,需替换 xTaskGetTickCount/uxTaskGetSystemState 等API。
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐