FreeRTOS阅读源码之编程风格

FreeRTOS 阅读源码之编程风格


一、编程风格

FreeRTOS 严格遵循匈牙利命名法——看到名字就能推断出返回值类型、所属模块和参数类型。

变量看前缀,类型一目了然  → u/c/s/l/x/p
函数三段式,返回+模块+动作 → vTaskDelay / xQueueSend
宏看开头词,就知在哪定义    → config/pd/port/task
💬 匈牙利命名法已不是现代必用规范(IDE 弱化了其价值),但读 FreeRTOS 源码时必须掌握

1.1 数据类型

FreeRTOS 对标准 C 类型进行了重定义(定义在 portmacro.h 中),加 port 前缀——port 表示接口,将 FreeRTOS 移植到处理器上时,需要用接口文件把它们连接在一起。

在 STM32CubeIDE 中,该文件位于:Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM3/portmacro.h
CubeIDE 中 portmacro.h 的位置

/* portmacro.h 中的数据类型重定义 */
#define portCHAR        char
#define portFLOAT       float
#define portDOUBLE      double
#define portLONG        long
#define portSHORT       short
#define portSTACK_TYPE  uint32_t
#define portBASE_TYPE   long

typedef portSTACK_TYPE  StackType_t;
typedef long            BaseType_t;
typedef unsigned long   UBaseType_t;

#if( configUSE_16_BIT_TICKS == 1 )
    typedef uint16_t TickType_t;
    #define portMAX_DELAY ( TickType_t ) 0xffff
#else
    typedef uint32_t TickType_t;
    #define portMAX_DELAY ( TickType_t ) 0xffffffffUL
#endif
💡 自己写代码时不一定要用 portXXX 类型,直接用 uint32_t 等标准类型完全可以。
🚫 int 从不使用!

FreeRTOS 只用 short(16 位)和 long(32 位),因为 int 在不同架构上大小不同。
⚠️ char 的符号问题:必须明确指定 char 是有符号还是无符号。KEIL 默认 char 为无符号,可通过 Options → C/C++ → Plain Char is Signed 勾选改为有符号。

1.2 变量名

变量名的前缀表示数据类型。无符号加 u,指针加 p,可组合(如 uc = unsigned char,pc = char 指针)。

前缀 含义 举例
c char cCharToSend
s short sTaskPriority
l long lBytesReceived
x BaseType_t / 结构体 / 句柄 xQueue, xSemaphore
u unsigned(组合用) ucMotorSpeed, ulTimerCount
p pointer(组合用) pcTaskName, pvParameters, pxTopOfStack

1.3 函数名

格式:返回值前缀 + 所属模块(= 所在文件名) + 功能描述

私有函数额外加 prv(private)前缀。

返回值前缀 类型 举例
v void vTaskDelay()
x BaseType_t / 句柄 xTaskCreate()
pv void* pvPortMalloc()
ux UBaseType_t uxTaskPriorityGet()
prv 私有函数 prvTaskIsTaskSuspended()
模块名 定义在 说明
Task tasks.c 任务管理
Queue queue.c 队列操作
Semaphore semphr.h(宏封装) 信号量 / 互斥量
Timer timers.c 软件定时器
EventGroup event_groups.c 事件组
Port port.c 硬件移植层
List list.c 链表操作
🔍 拆解练习

```
vTaskPrioritySet() → v + Task + PrioritySet
返回 void,在 task.c 中定义,功能:设置优先级

xQueueReceive() → x + Queue + Receive
返回 BaseType_t,在 queue.c 中定义,功能:接收队列

uxTaskGetStackHighWaterMark() → ux + Task + GetStackHighWaterMark
返回 UBaseType_t,在 tasks.c 中定义,功能:获取栈高水位线
```

1.4 宏

宏用大写字母,小写前缀表示定义文件

前缀 定义文件 举例
config FreeRTOSConfig.h configUSE_PREEMPTION
pd projdefs.h pdTRUE, pdFALSE
port portmacro.h portMAX_DELAY
task task.h taskENTER_CRITICAL()
err projdefs.h errQUEUE_FULL
ℹ️ 信号量函数虽然是宏定义实现的,但命名遵循函数命名规范而非宏规范。

通用宏定义(贯穿整个代码):

说明
pdTRUE / pdPASS 1 成功
pdFALSE / pdFAIL 0 失败

1.5 格式

  1. 缩进:1 Tab = 4 空格,建议用空格不用 Tab(避免跨编辑器格式错乱)
  2. 大括号:Allman 风格(独占一行)
  3. 注释/* */ 风格(兼容 C89),不用 //

二、版权与发展史

  • 2003 年:由美国的 Richard Barry 发布,是 FreeRTOS 的拥有者和维护者
  • 经历 9 个大版本迭代,与众多半导体厂商合作,拥有数百万开发者,市场占有率最高的 RTOS
  • 2018 年:被亚马逊(Amazon)收购,改名为 AWS FreeRTOS
    • 版本号升级为 V10,开源协议从 GPLv2+ 改为 MIT(完全免费)
    • V10 相比 V9 主要加入了物联网相关组件,内核基本不变

三、收费问题

3.1 FreeRTOS(免费开源)

遵循 GPLv2+ 许可协议:

  • 未修改内核源码 → 产品全部代码可以闭源
  • 修改了内核源码 → 修改部分必须开源,其他应用部分不用
  • 个人和公司均可免费使用

3.2 FreeRTOS vs OpenRTOS

代码完全一样,区别在于可获取的服务:

比较项目 FreeRTOS OpenRTOS
是否免费
可否商业使用
是否提供技术支持
是否被法律保护
是否需要开源修改的内核源码

3.3 SaveRTOS(安全认证版,收费)

基于 FreeRTOS,针对特定安全领域做了认证(工控 IEC 61508、医疗 IEC 62304、汽车 ISO 26262、航空 DO178B 等)。


四、资料获取

资源 获取方式
最新源码 freertos.org → Download Source
历史版本 SourceForge
官方电子书 官网 → PDF Books
快速入门 官网 → Quick Start(网页版)
💡 官方两本必读书

1. FreeRTOS V10.0.0 Reference Manual.pdf — API 参考手册

2. Mastering the FreeRTOS Real Time Kernel - A Hands-On Tutorial Guide.pdf — 入门教程

整理自《FreeRTOS 内核实现与应用开发实战指南:基于 STM32》第 1 章(刘火良、杨森 著)


五、自测题

Q1:看到变量名 pxCurrentTCB,能推断出什么信息?

- p → 指针
- x → 指向 BaseType_t 或结构体
- 组合起来:这是一个指向结构体的指针,名为 CurrentTCB(当前任务控制块)

Q2:函数 xTimerStart() 的返回值类型、所属模块、定义文件分别是什么?

- x → 返回 BaseType_t
- Timer → 所属模块:软件定时器
- 定义在 timers.c

Q3:宏 configUSE_PREEMPTION 定义在哪个文件?

前缀 config → 定义在 FreeRTOSConfig.h

Q4:FreeRTOS 为什么不用 int 类型?

因为 int 在不同处理器架构上大小不同(16 位平台是 16 bit,32 位平台是 32 bit),FreeRTOS 只用 short(16 位)和 long(32 位)来保证跨平台一致性。

Q5:FreeRTOS、OpenRTOS、SaveRTOS 三者的核心区别是什么?

- FreeRTOS:免费开源(GPLv2+),无技术支持,修改内核须开源
- OpenRTOS:代码相同但收费,提供技术支持和法律保护,无需开源
- SaveRTOS:收费,针对安全关键领域做了行业认证(IEC 61508、ISO 26262 等)

Q6:看到函数 prvListTasksWithinSingleList(),拆解它的命名。

- prv私有函数(private,仅文件内部调用)
- List → 所属模块:链表,定义在 list.c
- TasksWithinSingleList → 功能:列出单个链表中的任务