跳转到内容
新建笔记

FreeRTOS 数据类型、Tick 与命名约定

FreeRTOS 的公共类型把应用代码与处理器移植层隔开。先查当前内核版本和 portable/.../portmacro.h,再判断宽度、符号和栈单位。本文以 FreeRTOS-Kernel V11.1.0、GCC ARM_CM3、32 位 Cortex-M3 为具体例子;其他移植不能只凭类型名推断。

类型用途本文移植中的含义
BaseType_tAPI 返回值、布尔式判断等有符号 long,该 ARM ABI 下为 32 位
UBaseType_t任务优先级、队列长度等无符号 long,该 ARM ABI 下为 32 位
TickType_tTick 计数和等待时间按配置使用无符号 16 位或 32 位整数
StackType_t任务栈的一个元素uint32_t,一个元素为 4 字节
TaskHandle_t由内核返回的任务句柄指向不透明任务结构的指针,不应解引用私有 TCB
TaskHandle_t *接收任务句柄的输出参数句柄变量的地址,例如 &worker_handle

BaseType_t 并不是无符号类型。UBaseType_t 才表达无符号的对应类型。桌面宿主移植的 long 和栈元素可能与 STM32 不同,因此调试输出也不能直接假定所有类型都适合 %lu。

#include "FreeRTOS.h"
#include "task.h"
/* 这些断言特意限定本文的 GCC ARM_CM3 构建。 */
_Static_assert(sizeof(StackType_t) == 4, "CM3 stack element is 4 bytes");
_Static_assert(sizeof(BaseType_t) == 4, "CM3 BaseType_t is 32 bits");
_Static_assert((BaseType_t)-1 < 0, "BaseType_t must be signed");
_Static_assert((UBaseType_t)-1 > 0, "UBaseType_t must be unsigned");
size_t stack_bytes_for_words(configSTACK_DEPTH_TYPE words)
{
return (size_t)words * sizeof(StackType_t);
}

此转换只适用于结果可由 size_t 表示的栈深度。原生 xTaskCreate() 的栈深度是 StackType_t 元素数;512 在此移植中为 2048 字节。CMSIS-RTOS2 的 osThreadAttr_t.stack_size 则直接使用字节,不应再次误乘或漏乘 4。

任务控制块保存状态、优先级、栈指针等内核信息;任务栈保存调用现场和局部变量。句柄用于调用公共 API,既不是整块任务内存,也不是可以随意读写的栈地址。

旧配置使用 configUSE_16_BIT_TICKS 的值:值为 1 选择 16 位,值为 0 选择 32 位。仅检查该宏有没有定义,会把两种情况混淆。

V11.1.0 还支持 configTICK_TYPE_WIDTH_IN_BITS;它与旧配置二选一。内核公共定义包含 16、32、64 位选项,但 V11.1.0 的 GCC ARM_CM3 移植只接受 16 或 32 位。不能因为公共头文件出现 TICK_TYPE_WIDTH_64_BITS 就认为这个端口能直接使用 64 位 Tick。

若 Tick 频率为 ftf_t,一个 Tick 的名义长度为 1/ft1/f_t。100 Hz 对应 10 ms,1000 Hz 对应 1 ms,这些是可选配置,不是不同操作系统的固定规律。

pdMS_TO_TICKS(ms) 把毫秒换算成 Tick。V11.1.0 默认实现用 64 位中间乘法后做整数除法,因此会向下截断,最终结果仍须能由 TickType_t 表示。例如 100 Hz 下 1 ms 得到 0 Tick;不能把它当成至少等待 1 ms 的保证。应用可按需要检查零值、拆分超长等待,或提供经过范围验证的换算策略。

16 位 Tick 在 1000 Hz 下约每 65.536 秒回绕;32 位约每 49.71 天回绕。内核阻塞 API 会处理其支持范围内的 Tick 溢出,应用自行比较绝对时间时仍需采用无符号差值和明确的最大间隔。低功耗 tickless 模式可能暂时抑制周期中断并补偿经过的时间,逻辑 Tick 不等于始终存在一一对应的物理 SysTick 中断。

FreeRTOS 历史命名约定表,包含变量前缀、函数前缀、宏前缀和布尔返回值

图保留了原学习资料的完整表格。表中 task.c、timer.c 应对应实际文件 tasks.c、timers.c;short、long 的字节数还取决于 ABI,不能把历史表格当成所有平台的类型定义。

常见前缀阅读含义
c、s、l历史约定中的 char、short、long
x移植定义类型,如 BaseType_t,也常用于句柄
u无符号修饰,与基础类型前缀组合
p指针修饰,与基础类型前缀组合
uc、pc常见的 unsigned char、char 指针变量
函数 v...通常返回 void
函数 x...通常返回移植定义类型或句柄,须看声明
函数 pv...通常返回 void *
宏 port...、task...移植层、任务层宏
宏 pd...、config...、err...公共定义、配置、错误码

名字不能代替 C 类型检查。pdTRUE、pdPASS 通常为 1,pdFALSE、pdFAIL 通常为 0,但具体 API 的返回约定须单独阅读:队列发送成功只说明数据进入队列,不代表消费者已经完成业务处理。

常见 API 按任务、队列、信号量/互斥量、软件定时器、事件组、流/消息缓冲区和任务通知组织。通知属于任务 API,信号量和互斥量建立在队列实现之上;不能据此认为它们的使用约束相同。ISR 可用性、阻塞行为和所有权均以对应 API 文档为准。