FreeRTOS vs RT-Thread 函数对比查阅手册

版本说明:

  • FreeRTOS Native: FreeRTOS 原生 API
  • CMSIS-RTOS v2: ARM 定义的通用 RTOS 接口标准 (常用于 STM32 等)
  • RT-Thread: RT-Thread 标准版/Nano 版 API

目录 (Table of Contents)

  1. 快速迁移 Checklist
  2. 核心差异概览 (Critical Differences)
  3. 线程/任务管理 (Thread Management)
  4. 消息队列 (Message Queue)
  5. 信号量与互斥量 (Semaphore & Mutex)
  6. 事件组 (Event Group)
  7. 软件定时器 (Software Timer)
  8. 中断管理 (Interrupt Management)
  9. 内存管理 (Memory Management)
  10. 邮箱 (Mailbox)
  11. 调试与诊断参考

前言

  • 相比裸机的大循环状态机,RTOS 使用调度器自动切换任务,实现准并行处理
  • RTOS 下的队列、信号量等,在轮询等待是会阻塞程序进入挂起状态,不占用,而裸机的标志位、数组,是一直在占用 CPU 轮询。

1. 核心差异概览

在开始 API 对比前,必须注意以下核心机制的区别,否则极易导致 Bug。FreeRTOS 基于“优先级就绪链表 + 抢占式调度”,同优先级是否轮转由 configUSE_TIME_SLICING 控制;RT-Thread 则内建时间片轮转且优先级数值相反,两者的调度时序及饥饿行为会因此不同。

特性 FreeRTOS CMSIS-RTOS v2 RT-Thread 注意
优先级定义 数值越大,优先级越高 数值越大,优先级越高 数值越小,优先级越高 (0 最高) 这是最大的坑! 移植时务必转换逻辑。
栈大小单位 通常为字 (Word, 4 Bytes) 通常为字节 (Byte) 通常为字节 (Byte) xTaskCreate 传入 128 代表 512 字节;rt_thread_create 传入 512 代表 512 字节。
中断调用 必须使用 FromISR 后缀的专用 API 统一 API (内部自动检测) 统一 API (内部自动检测) FreeRTOS 在中断中调用普通 API 会导致崩溃。
时间片轮转 支持 (需配置 configUSE_TIME_SLICING) 支持 支持 (同优先级任务按时间片轮转)

2. 线程/任务管理 (Thread Management)

2.1 API 对比表

功能 FreeRTOS Native (task.h) CMSIS-RTOS v2 (cmsis_os2.h) RT-Thread (rtthread.h)
创建(动态) xTaskCreate osThreadNew rt_thread_create
创建(静态) xTaskCreateStatic osThreadNew (传入 buffer) rt_thread_init
启动 vTaskStartScheduler (全局启动) osKernelStart rt_thread_startup (单个启动)
删除 vTaskDelete osThreadTerminate rt_thread_delete / detach
延时(相对) vTaskDelay osDelay rt_thread_delay / mdelay
延时(绝对) vTaskDelayUntil osDelayUntil rt_thread_delay_until
挂起/恢复 vTaskSuspend / Resume osThreadSuspend / Resume rt_thread_suspend / resume
让权 taskYIELD osThreadYield rt_thread_yield

2.2 详细说明与示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
graph LR
subgraph 控制API
SUSP["挂起态\n(手动暂停)"]
end

subgraph 调度流程
READY["就绪列表\n(等待CPU)"]
RUN["运行态\n(占用CPU)"]
BLOCK["阻塞队列\n(等待事件/延时)"]
SCHED["调度器/时间片\n(根据优先级选择任务)"]
ISR["事件/中断\n(FromISR/rt_event_send)"]
end

SUSP -- vTaskResume()/rt_thread_resume --> READY
READY -- 获得CPU --> SCHED
SCHED -- 选择最高优先级 --> RUN
RUN -- 更高优先级到来/时间片到期 --> READY
RUN -- 阻塞型API(Delay/Queue/Sem) --> BLOCK
BLOCK -- 事件满足/超时 --> READY
RUN -- vTaskSuspend()/rt_thread_suspend --> SUSP
BLOCK -- vTaskSuspend() --> SUSP
ISR -- 唤醒API(FromISR/rt_event_send) --> READY

FreeRTOS 创建任务

1
2
3
4
5
6
7
8
9
10
11
12
// 原型: BaseType_t xTaskCreate(TaskFunction_t pxTaskCode, const char *pcName, configSTACK_DEPTH_TYPE usStackDepth, void *pvParameters, UBaseType_t uxPriority, TaskHandle_t *pxCreatedTask);
// 注意: usStackDepth 是 StackType_t 的个数 (通常是 4字节 Word)

void vTaskCode(void * pvParameters) {
for(;;) {
// 任务代码
vTaskDelay(pdMS_TO_TICKS(1000)); // 延时 1000ms
}
}

// 创建
xTaskCreate(vTaskCode, "NAME", 128, NULL, 1, NULL); // 栈大小 128*4=512字节, 优先级 1

RT-Thread 创建任务

1
2
3
4
5
6
7
8
9
10
11
12
13
// 原型: rt_thread_t rt_thread_create(const char *name, void (*entry)(void *parameter), void *parameter, rt_uint32_t stack_size, rt_uint8_t priority, rt_uint32_t tick);
// 注意: stack_size 是 字节 (Byte)

void thread_entry(void *parameter) {
while(1) {
// 线程代码
rt_thread_mdelay(1000); // 延时 1000ms
}
}

// 创建并启动
rt_thread_t tid = rt_thread_create("NAME", thread_entry, RT_NULL, 512, 25, 10); // 栈 512字节, 优先级 25 (0 最高)
if (tid != RT_NULL) rt_thread_startup(tid);

2.3 栈单位换算与诊断

FreeRTOS 以 StackType_t (通常 4 字节) 为单位计量栈深;RT-Thread 直接使用字节数。因此在移植阶段推荐加入以下辅助宏与断言:

1
2
3
4
5
6
7
#define FREERTOS_STACK_WORDS(bytes) ((bytes) / sizeof(StackType_t))
#define RTTHREAD_STACK_BYTES(words) ((words) * sizeof(StackType_t))

/* 迁移期断言,避免配置错误 */
static inline void assert_stack_compat(size_t freertos_words, size_t rt_bytes) {
configASSERT(RTTHREAD_STACK_BYTES(freertos_words) == rt_bytes);
}

同时打开 uxTaskGetStackHighWaterMarkrt_thread_control(..., RT_THREAD_CTRL_INFO, ...),在调试阶段记录“最低余量”,可提前发现栈配置不匹配的问题。


3. 消息队列 (Message Queue)

3.1 API 对比表

功能 FreeRTOS Native (queue.h) CMSIS-RTOS v2 RT-Thread
创建 xQueueCreate osMessageQueueNew rt_mq_create
发送 xQueueSend / xQueueSendToFront osMessageQueuePut rt_mq_send / rt_mq_urgent
接收 xQueueReceive osMessageQueueGet rt_mq_recv
ISR 发送 xQueueSendFromISR osMessageQueuePut rt_mq_send
ISR 接收 xQueueReceiveFromISR osMessageQueueGet rt_mq_recv

3.2 关键差异与实现原理

  • FreeRTOS: 队列是“循环 buffer + 读写索引”,节点大小恒定且发送/接收都走 memcpy;xQueueSendFromISR 内部会根据 uxMessagesWaiting 判断是否需要唤醒高优先级任务。
  • RT-Thread: rt_mq 同样是循环 buffer,并利用 suspend_entry 链表管理阻塞线程;为了减小延迟,通常只传 4 字节或指针。
  • 大数据传输策略: 两个系统都推荐“队列只传句柄,真实数据放独立内存池”。RT-Thread 还提供了 **邮箱 (Mailbox)**,结构上仅存储一个 rt_ubase_t,因此写入/读取无需 memcpy,延迟更可控。

4. 信号量与互斥量 (Semaphore & Mutex)

4.1 API 对比表

功能 FreeRTOS Native (semphr.h) CMSIS-RTOS v2 RT-Thread
二值信号量 xSemaphoreCreateBinary osSemaphoreNew(1, 1, ...) rt_sem_create
计数信号量 xSemaphoreCreateCounting osSemaphoreNew(max, init, ...) rt_sem_create
互斥量 xSemaphoreCreateMutex osMutexNew rt_mutex_create
递归互斥量 xSemaphoreCreateRecursiveMutex osMutexNew(attr) (默认支持递归)
获取 (P 操作) xSemaphoreTake osSemaphoreAcquire / osMutexAcquire rt_sem_take / rt_mutex_take
释放 (V 操作) xSemaphoreGive osSemaphoreRelease / osMutexRelease rt_sem_release / rt_mutex_release

4.2 详细说明

  • 优先级翻转: 两者都实现了优先级继承算法——当低优先级任务持有互斥量阻塞高优先级任务时,调度器临时提升低优先级任务的优先级,直到释放互斥量;信号量不具备该特性。
  • 递归锁: FreeRTOS 需要显式创建 RecursiveMutex 并使用 xSemaphoreTakeRecursive;RT-Thread 的 Mutex 结构带 hold 计数与 owner 标记,天然支持同线程多次获取/释放。
  • ISR 限制: FreeRTOS 互斥量禁止在中断中操作;RT-Thread 会在编译期 RT_DEBUG 下报警告,但依旧不建议这么做。

5. 事件组 (Event Group)

用于多对多同步,例如“等待 任务 A 和 任务 B 都完成”。

5.1 API 对比表

功能 FreeRTOS Native (event_groups.h) CMSIS-RTOS v2 RT-Thread
创建 xEventGroupCreate osEventFlagsNew rt_event_create
发送事件 xEventGroupSetBits osEventFlagsSet rt_event_send
等待事件 xEventGroupWaitBits osEventFlagsWait rt_event_recv
清除事件 xEventGroupClearBits osEventFlagsClear (接收时可选自动清除)

5.2 逻辑标志与自动清除

  • FreeRTOS: xEventGroupWaitBits 参数 xWaitForAllBits 决定是 AND (所有位都置 1) 还是 OR (任意位置 1);xClearOnExit 可在任务唤醒时自动清零。
  • RT-Thread: rt_event_recv 参数 option 使用 RT_EVENT_FLAG_ANDRT_EVENT_FLAG_OR,并可组合 RT_EVENT_FLAG_CLEAR 自动清除;底层实现基于 32 位整型的按位操作,与 FreeRTOS 类似。
  • 最佳实践: 事件组适合作为“状态同步”,若要传递数据仍应通过消息队列或邮箱完成功能分离。

6. 软件定时器 (Software Timer)

6.1 API 对比表

功能 FreeRTOS Native (timers.h) CMSIS-RTOS v2 RT-Thread
创建 xTimerCreate osTimerNew rt_timer_create
启动 xTimerStart osTimerStart rt_timer_start
停止 xTimerStop osTimerStop rt_timer_stop
模式 One-shot / Auto-reload osTimerOnce / osTimerPeriodic RT_TIMER_FLAG_ONE_SHOT / PERIODIC

6.2 执行上下文与推荐模板

  • FreeRTOS: 定时器回调函数在 定时器服务任务 (Daemon Task) 上下文中执行。绝对不能在回调中调用阻塞 API (如 vTaskDelay),否则会阻塞所有定时器。推荐模板:在回调里向队列发送命令或设置事件位,由普通任务消费。
  • RT-Thread:
    • HARD_TIMER 模式: 回调在 中断上下文 执行 (严禁阻塞),适合做 GPIO 翻转、唤醒等硬实时动作。
    • SOFT_TIMER 模式: 回调在 timer 线程 执行 (类似 FreeRTOS),同样避免阻塞操作;可调用 rt_event_send 向业务线程播报。
  • 调试建议: 对 FreeRTOS 可开启 configUSE_TRACE_FACILITY 结合 vTaskGetInfo 观察定时器任务负载;RT-Thread 则可调用 list_timer 查看 Timer 队列状态。

7. 中断管理 (Interrupt Management)

7.1 关键 API

功能 FreeRTOS Native CMSIS-RTOS v2 RT-Thread
进入临界区 taskENTER_CRITICAL() osKernelLock() rt_enter_critical()
退出临界区 taskEXIT_CRITICAL() osKernelUnlock() rt_exit_critical()
关中断 portDISABLE_INTERRUPTS() - rt_hw_interrupt_disable()
开中断 portENABLE_INTERRUPTS() - rt_hw_interrupt_enable()

7.2 ISR 调用规则与优先级门限

  • FreeRTOS:
    • 中断中必须使用 FromISR 结尾的 API (例如 xQueueSendFromISR)。
    • 这些 API 通常需要一个 BaseType_t *pxHigherPriorityTaskWoken 参数,用于判断是否需要触发上下文切换。
    • 退出中断时需调用 portYIELD_FROM_ISR(xHigherPriorityTaskWoken)
    • configMAX_SYSCALL_INTERRUPT_PRIORITY: 仅低于(数值更大)该门限的中断才能调用 FromISR API。要将 NVIC 的 0-15 级映射到 FreeRTOS,需要结合芯片实现的优先级位数,例如 STM32F1 仅实现 4 bit,可使用 NVIC_SetPriority(IRQn, <value << (8 - __NVIC_PRIO_BITS)>) 方式保持一致。
  • RT-Thread:
    • API 内部自动检测当前是线程环境还是中断环境。
    • 无需特殊后缀,无需手动处理上下文切换请求。
    • RT-Thread 仍限制“中断中不可阻塞”,如 rt_sem_take 在 ISR 环境会立即返回 -RT_EFULL 并记录日志。

8. 内存管理 (Memory Management)

功能 FreeRTOS Native CMSIS-RTOS v2 RT-Thread
动态分配 pvPortMalloc malloc (需配置) rt_malloc
动态释放 vPortFree free rt_free
获取剩余内存 xPortGetFreeHeapSize - rt_memory_info (需开启 mem info)
  • FreeRTOS: 内存管理策略由 heap_x.c 文件决定 (heap_1 到 heap_5)。最常用的是 heap_4.c (支持碎片合并);若需要多区域或静态分区,可使用 heap_5.c 并在 vPortDefineHeapRegions 中注册多块 RAM。
  • RT-Thread: 默认使用小内存管理算法 (small mem),以链表方式维护空闲块;在需要 determinism 的平台可以切换到 slab/finsh 相关组件。可通过 rt_memory_infomemtrace 插件实时查看使用情况。
  • 迁移提示: FreeRTOS 的 pvPortMalloc 默认是线程安全的 (因为加锁), RT-Thread 的 rt_malloc 亦如此,但若在 ISR 中分配/释放都会失败,应提前安排对象池或静态缓存。

9. 邮箱 (Mailbox) - RT-Thread 特有

RT-Thread 提供了一种比消息队列更高效的通信机制:邮箱

  • 特点: 固定 4 字节容量 (32 位系统),开销极低。
  • 用途: 适合传递指针、状态码。
  • FreeRTOS 对应: FreeRTOS 没有专门的 Mailbox,通常创建一个 ItemSize = sizeof(void*) 的 Queue 来模拟。
功能 FreeRTOS (模拟) RT-Thread (rt_mb) 说明
创建 xQueueCreate(len, sizeof(void *)) rt_mb_create(name, len, flag) FreeRTOS 仍是循环 buffer;RT-Thread 固定 4 字节。
发送指针 xQueueSend(queue, &ptr, timeout) rt_mb_send(mb, (rt_uint32_t)ptr) RT-Thread 直接写入 rt_uint32_t,无 memcpy。
接收指针 xQueueReceive(queue, &ptr, timeout) rt_mb_recv(mb, &ptr, timeout) 两者都支持阻塞/超时。
ISR 支持 xQueueSendFromISR rt_mb_send (自动判 ISR) 依旧要遵守 configMAX_SYSCALL_INTERRUPT_PRIORITY
典型用途 线程间传对象指针、IO 完成通知 GUI 事件、网络栈包指针、驱动状态机
1
2
3
// RT-Thread Mailbox 示例
rt_mailbox_t mb = rt_mb_create("mb", 32, RT_IPC_FLAG_FIFO);
rt_mb_send(mb, (rt_uint32_t)&my_data); // 发送指针

在 FreeRTOS 中若确需类似功能,可将 Queue 的 uxItemSize 设为 sizeof(void *) 并搭配 xQueueOverwrite 等 API,但由于依旧存在 memcpy,极限性能不及 RT-Thread 的 Mailbox。


10. 调试参考

  • FreeRTOS: 推荐开启 configCHECK_FOR_STACK_OVERFLOW (mode 2) 与 configGENERATE_RUN_TIME_STATS,配合 vTaskGetRunTimeStats 输出任务占比,快速确认调度行为是否符合预期。
  • RT-Thread: 可在 FinSH 中使用 thread, list_timer, list_mem 等命令,或在代码里调用 rt_thread_dumprt_timer_dump 了解运行态。