FreeRTOS基础篇(二)——FreeRTOS工程介绍
FreeRTOS基础篇(二)——FreeRTOS工程介绍
前言:
上一篇文章介绍了 FreeRTOS 的概念和核心功能,这一篇从源码工程的角度出发,介绍 FreeRTOS 的仓库组成、目录结构、内核文件、移植层和配置文件,帮助读者建立一个完整的工程视图。具体的移植操作放在进阶篇。
1 获取源码
FreeRTOS 官网 支持中文,提供丰富的包括文档、教程和 API 参考等。
可以从官网下载源码,也可以从 GitHub 仓库 clone 源码。FreeRTOS 在 GitHub 上按用途拆成了几个仓库,常见的有内核版、完整版、长期支持版和实验版:
| 仓库 | 定位 | 包含内容 | 更新策略 | 适用场景 | 是否适合生产环境 |
|---|---|---|---|---|---|
| FreeRTOS-Kernel | 内核版 | 仅内核源码和移植层(portable),不含协议栈、文件系统等中间件和 Demo |
持续更新并打版本标签(如 V11.3.1),新功能先合入这里 |
只需要 RTOS 内核、其他组件自选的项目;它也是完整版和 LTS 版的子模块 | 是 |
| FreeRTOS | 完整版(Classic) | 内核 + FreeRTOS-Plus 组件(TCP/IP、FAT、POSIX 等)+ 大量官方 Demo | 跟随内核/LTS 更新,正式发行版更新较慢 | 学习、评估、参考官方 Demo | 是 |
| FreeRTOS-LTS | 长期支持版 | 内核 + 常用 IoT 库的固定版本组合 | 只修复安全漏洞和关键 Bug,不新增功能;每个版本支持 2 年 | 工业级稳定产品、量产项目 | 是 |
| FreeRTOS-Labs | 实验版 | 实验性的库和 Demo(如 FAT、POSIX) | 可能重构或废弃,不保证稳定 | 评估、学习、Demo 开发 | 否 |
内核版(FreeRTOS-Kernel) 是 FreeRTOS 的最小集合,只包含任务调度、队列、信号量、软件定时器、事件组等内核功能以及各芯片架构的移植层。如果项目只需要一个 RTOS 内核,协议栈、文件系统等自己选型,直接使用这个仓库最干净;完整版和 LTS 版也是通过 Git 子模块的方式引用它。
完整版(FreeRTOS) 是传统发行版,在内核之外还包含 FreeRTOS-Plus 组件和大量官方 Demo,适合学习和参考。它把内核等组件作为子模块引用,所以 clone 时要带 --recurse-submodules,否则子目录会为空。
长期支持版(FreeRTOS-LTS) 由 AWS 维护,按 YYYYMM-LTS 分支维护,并用 YYYYMM.XX-LTS 标签发布补丁(如 202604.00-LTS、202604.01-LTS)。每个版本锁定一组经过验证的内核和 IoT 库版本,只提供安全更新和关键 Bug 修复,支持周期为 2 年。截至 2026 年 10 月,最新分支为 202604-LTS,最新补丁标签为 202604.01-LTS(内核 V11.3.1),支持到 2028-04-30;202406-LTS 已于 2026-06-30 结束支持。量产项目还可以购买 AWS 的 EMP(Extended Maintenance Plan),最多再延长 10 年维护。
实验版(FreeRTOS-Labs) 中的库和 Demo 虽然可以使用,但可能随时重构或废弃,只适合评估和学习,不要用于生产。
学习为目的建议使用完整版或内核版,能接触到最新的功能;工业开发则建议使用长期支持版,版本组合固定、维护周期明确。本系列以 FreeRTOS-LTS 为例,后续的目录和源码分析都基于 202604.01-LTS。
官网也提供 下载链接 ,但更推荐从 GitHub 获取,方便切换版本和升级。这里以 LTS 版为例,执行以下指令将仓库克隆到本地(--recurse-submodules 会同时拉取子模块):
1 | git clone --recurse-submodules https://github.com/FreeRTOS/FreeRTOS-LTS.git |
如果 clone 时没有加 --recurse-submodules,或者切换了分支/标签,都需要执行 git submodule update --init --recursive,否则组件目录可能为空或版本不匹配。可以用 git submodule status 查看各子模块当前指向的提交。
切换版本时可以选择 202604-LTS 分支跟踪最新补丁,也可以 checkout 某个 YYYYMM.XX-LTS 标签锁定到具体版本。产品开发更推荐锁定标签,避免构建结果随分支更新而变化。

2 FreeRTOS 工程结构
FreeRTOS-LTS 是一个“集成仓库”(hub repo):它本身主要保存清单和子模块引用,真正的内核和组件代码分布在各个子模块仓库中。因此看目录结构时,要同时关注“仓库这一层”和“组件这一层”。理解了这两层,就能快速定位到需要的源码。
2.1 仓库根目录
202604.01-LTS 的仓库根目录如下(省略了部分 GitHub 配置和文档文件):
1 | FreeRTOS-LTS/ |
| 目录/文件 | 说明 |
|---|---|
FreeRTOS/ |
内核和通用中间件,是本文关注的重点 |
aws/ |
AWS IoT 设备影子、Jobs、Defender、Fleet Provisioning、SigV4、MQTT File Streams 等,只有接入 AWS IoT 时才需要 |
manifest.yml |
清单文件,记录每个子模块锁定的版本,是判断“这个 LTS 版本包含什么”的最直接依据 |
.gitmodules |
Git 子模块配置,记录各组件仓库地址 |
README.md / CHANGELOG.md |
发布说明和变更记录 |
LICENSE.md |
MIT 许可证 |
LTS 的价值就在于 manifest.yml 里这一组固定的版本组合:它们是经过验证、可以一起工作的组件。相比自己分别拉取内核和各个中间件的最新代码,使用 LTS 可以显著减少版本兼容问题。
2.2 FreeRTOS 目录:内核与中间件
FreeRTOS/ 目录下是内核和通用组件,目录结构如下:
1 | FreeRTOS/ |
| 组件 | 功能 | 是否必需 |
|---|---|---|
FreeRTOS-Kernel |
任务调度、队列、信号量、事件组、软件定时器等内核功能 | 必需 |
FreeRTOS-Plus-TCP |
完整的 TCP/IP 协议栈 | 可选 |
coreMQTT |
MQTT 客户端 | 可选 |
coreHTTP |
HTTP 客户端 | 可选 |
coreJSON |
轻量 JSON 解析器 | 可选 |
coreSNTP |
SNTP 时间同步 | 可选 |
corePKCS11 |
PKCS#11 加密接口 | 可选 |
FreeRTOS-Cellular-Interface |
蜂窝模组抽象层 | 可选 |
backoffAlgorithm |
网络连接失败后的重试策略 | 可选 |
FreeRTOS 采用模块化设计,以上组件不需要全部使用,只选择项目实际需要的部分即可。这样既能控制代码体积,也能减少不必要的依赖。需要注意,coreHTTP、coreMQTT 等名称中的“core”是 AWS 对这些通用库的命名,并不是内核核心的意思;真正必需的内核只有 FreeRTOS-Kernel。
本文只展开内核部分。协议栈、MQTT、HTTP 等组件各自有独立的仓库和文档,用法会在需要时单独介绍。
2.3 FreeRTOS-Kernel 目录
FreeRTOS-Kernel 是内核仓库。202604.01-LTS 锁定的内核版本为 V11.3.1,其目录结构如下:
1 | FreeRTOS-Kernel/ |
| 文件/目录 | 作用 |
|---|---|
tasks.c |
任务控制块、任务创建/删除/挂起/恢复、优先级管理和调度器核心 |
list.c |
内核双向链表,就绪、阻塞、挂起等列表的基础 |
queue.c |
队列实现;信号量和互斥量也建立在队列机制之上 |
stream_buffer.c |
流缓冲区和消息缓冲区,适合字节流和变长消息 |
event_groups.c |
事件组,用于多个事件的同步 |
timers.c |
软件定时器服务任务和定时器管理 |
croutine.c |
协程支持,属于历史特性,新项目基本不使用 |
include/ |
内核对外头文件 |
portable/ |
编译器、处理器架构和内存管理的移植层 |
examples/ |
最小 CMake 示例和 FreeRTOSConfig.h 模板 |
CMakeLists.txt |
CMake 构建入口;也可以自行加入 Keil、IAR 或 Makefile 工程 |
History.txt / MISRA.md |
变更历史和 MISRA 合规说明 |
这几个 .c 文件并不是必须全部编译。tasks.c 和 list.c 是调度核心;queue.c 在使用队列、信号量或互斥量时必需;timers.c、event_groups.c、stream_buffer.c 可以按需裁剪;croutine.c 基本不用。具体裁剪由 FreeRTOSConfig.h 中的 configUSE_* 宏控制,后文会介绍。
include/ 目录存放内核对外的头文件,常用的几个如下:
| 头文件 | 作用 |
|---|---|
FreeRTOS.h |
内核主头文件,负责引入 FreeRTOSConfig.h、定义基础类型和默认配置;使用其他内核 API 前应先包含它 |
task.h |
任务创建、删除、延时、优先级等 API |
queue.h |
队列 API |
semphr.h |
信号量、互斥量的宏封装(底层仍是队列) |
event_groups.h |
事件组 API |
timers.h |
软件定时器 API |
stream_buffer.h / message_buffer.h |
流缓冲区、消息缓冲区 API |
portable.h |
移植层对外接口(内核内部使用) |
list.h / projdefs.h |
内核链表和公共定义(内部使用) |
mpu_wrappers.h / mpu_prototypes.h |
MPU 端口的系统调用封装 |
stack_macros.h / StackMacros.h |
栈溢出检查相关宏 |
examples/ 目录里有官方提供的最小示例和配置模板。examples/template_configuration/FreeRTOSConfig.h 是应用配置的起点,examples/cmake_example 则演示了如何用 CMake 组织一个最小内核工程。CMakeLists.txt 只是官方提供的一种构建方式,FreeRTOS 本身并不绑定构建系统,内核源码可以加入任何支持 C 的工程。
2.4 portable 移植层
portable/ 是 FreeRTOS 可移植性的核心,存放针对不同编译器、处理器架构和内存管理方案的适配代码。目录很多,可以按类别理解:
1 | portable/ |
| 类别 | 目录/文件 | 功能描述 |
|---|---|---|
| 编译器支持 | GCC/、IAR/、RVDS/、CCS/、MPLAB/ |
不同编译器工具链的适配 |
| 处理器架构 | GCC/ARM_CM3、IAR/ARM_CM4F、ARMv8M/ 等 |
具体内核的 port.c 和 portmacro.h |
| 内存管理 | MemMang/ |
5 种动态内存分配策略 |
| 公共代码 | Common/ |
MPU 封装等公共移植代码 |
| 模板 | template/ |
新端口开发的参考模板 |
| 第三方 | ThirdParty/ |
第三方移植和维护的代码 |
| 其他 | CCRH/、MikroC/、SDCC/、Tasking/ 等 |
小众编译器或专用架构 |
port 的路径通常按“先编译器、后架构”的方式组织,例如 GCC + Cortex-M3 对应 portable/GCC/ARM_CM3/,里面只有两个关键文件:
port.c:任务切换、中断入口、临界区、栈初始化等与硬件紧密相关的实现。portmacro.h:端口相关的类型、宏和内联函数,例如StackType_t、portSTACK_TYPE、临界区进出宏等。
选择 port 时,先确定编译器和芯片内核,再考虑 FPU、MPU、TrustZone 等特性。下面是以 GCC 端口为例的常见对应关系:
| 处理器内核 | GCC 端口示例 | 说明 |
|---|---|---|
| Cortex-M0 / M0+ | ARM_CM0 |
无 FPU |
| Cortex-M3 | ARM_CM3 |
无 FPU |
| Cortex-M4F | ARM_CM4F |
带 FPU,需要保存浮点上下文 |
| Cortex-M7 | ARM_CM7 |
带 FPU |
| Cortex-M23 / M33 | ARM_CM23 / ARM_CM33 |
TrustZone 相关端口可参考 ARMv8M/ |
| 带 MPU 的 M3 / M4 | ARM_CM3_MPU / ARM_CM4_MPU |
使用 MPU 保护时选择 |
Keil MDK 用户可以重点看 portable/RVDS/(ARM Compiler 5)和 portable/ARMClang/(ARM Compiler 6,复用 GCC 端口)。具体选哪个目录,最终要以芯片内核、编译器和官方 port 目录的说明为准。
portable/ 的作用可以概括为以下几点:
- 硬件抽象:将 FreeRTOS 内核与具体硬件隔离。
- 编译器适配:处理不同编译器的汇编语法和调用约定。
- 架构优化:针对特定处理器优化任务切换和中断处理。
- 内存管理:提供可配置的内存分配方案。
2.5 MemMang 内存管理
portable/MemMang/ 提供了 5 种动态内存管理方案,通过选择其中一个 heap_x.c 参与编译来使用:
1 | MemMang/ |
| 方案 | 支持释放 | 碎片处理 | 特点与适用场景 |
|---|---|---|---|
heap_1 |
否 | 无 | 只分配不释放,实现最简单、确定性最好;适合任务和队列在启动时全部创建、运行中不删除的系统 |
heap_2 |
是 | 不合并相邻空闲块 | 使用最佳匹配算法,容易产生碎片;新项目一般直接用 heap_4 |
heap_3 |
是 | 由 C 库决定 | 封装标准 malloc/free,需要提供线程安全的 C 库;不使用 configTOTAL_HEAP_SIZE |
heap_4 |
是 | 合并相邻空闲块 | 最常用,碎片较少;适合一般动态创建、删除任务的场景 |
heap_5 |
是 | 合并相邻空闲块 | 在 heap_4 基础上支持多个不连续内存区域,适合内部 RAM + 外部 RAM 的组合 |
这 5 种方案都提供统一的 pvPortMalloc() / vPortFree() 接口,应用代码不需要关心底层实现。选择哪种方案、如何配置 configTOTAL_HEAP_SIZE,在后续的内存管理文章中会详细展开;这里只需要记住:内存管理也属于 portable 层的一部分,并不是内核强制的固定实现。
3 FreeRTOSConfig.h:应用侧配置
3.1 配置文件的位置与作用
FreeRTOS 内核本身不包含 FreeRTOSConfig.h,它由应用工程提供。内核的 include/FreeRTOS.h 中会直接 #include "FreeRTOSConfig.h",因此这个文件必须位于编译器的头文件搜索路径中,文件名也不能修改。官方模板位于 FreeRTOS-Kernel/examples/template_configuration/FreeRTOSConfig.h,复制到自己的工程中修改即可。
FreeRTOSConfig.h 是应用和内核之间的“合同”:应用通过它选择调度策略、时钟频率、内存方案、可选组件和中断优先级等;内核通过 configASSERT 和 #error 检查配置是否完整、是否冲突。把配置放在应用侧,不修改内核源码,升级内核时只需要对比配置变化,维护成本更低。
3.2 常用配置分类
FreeRTOSConfig.h 里的宏很多,可以按用途分类理解:
| 配置类别 | 常用宏 | 说明 |
|---|---|---|
| 硬件/时钟 | configCPU_CLOCK_HZ、configTICK_RATE_HZ、configTICK_TYPE_WIDTH_IN_BITS |
CPU 频率、系统节拍频率、TickType_t 的位宽 |
| 调度策略 | configUSE_PREEMPTION、configUSE_TIME_SLICING、configUSE_TICKLESS_IDLE |
抢占/协作调度、同优先级时间片、低功耗 Tickless 模式 |
| 任务 | configMAX_PRIORITIES、configMINIMAL_STACK_SIZE、configMAX_TASK_NAME_LEN |
优先级数量、空闲任务栈大小(单位是字)、任务名长度 |
| 内存 | configSUPPORT_STATIC_ALLOCATION、configSUPPORT_DYNAMIC_ALLOCATION、configTOTAL_HEAP_SIZE、configAPPLICATION_ALLOCATED_HEAP |
静态/动态分配、堆大小、堆是否由应用提供 |
| 通信与同步 | configUSE_MUTEXES、configUSE_RECURSIVE_MUTEXES、configUSE_COUNTING_SEMAPHORES、configUSE_QUEUE_SETS、configUSE_TASK_NOTIFICATIONS |
按需开启互斥量、递归互斥量、计数信号量、队列集、任务通知 |
| 可选组件 | configUSE_TIMERS、configUSE_EVENT_GROUPS、configUSE_STREAM_BUFFERS |
软件定时器、事件组、流缓冲区 |
| 软件定时器 | configTIMER_TASK_PRIORITY、configTIMER_QUEUE_LENGTH、configTIMER_TASK_STACK_DEPTH |
定时器服务任务参数,仅在 configUSE_TIMERS=1 时有效 |
| 中断/临界区 | configKERNEL_INTERRUPT_PRIORITY、configMAX_SYSCALL_INTERRUPT_PRIORITY |
Cortex-M 端口的节拍/上下文切换中断优先级,以及允许调用 FreeRTOS API 的中断优先级上限(数值不小于该值的中断才能调用 FromISR 接口) |
| 钩子与调试 | configUSE_IDLE_HOOK、configUSE_TICK_HOOK、configUSE_MALLOC_FAILED_HOOK、configCHECK_FOR_STACK_OVERFLOW、configASSERT |
空闲/节拍/内存分配失败钩子、栈溢出检查、断言 |
| 统计与跟踪 | configUSE_TRACE_FACILITY、configGENERATE_RUN_TIME_STATS、configUSE_STATS_FORMATTING_FUNCTIONS |
任务状态、运行时间统计和调试功能 |
| 可选 API | INCLUDE_vTaskDelay、INCLUDE_vTaskDelete、INCLUDE_vTaskSuspend、INCLUDE_xTaskGetSchedulerState 等 |
控制部分 API 是否参与编译,未使用时关闭可以减小代码体积 |
这些宏的具体取值与芯片、编译器和工程需求相关,尤其涉及中断优先级的配置不能照搬。本文只建立整体认识,移植和配置细节放在进阶篇。
3.3 头文件的包含顺序
使用内核 API 时,FreeRTOS.h 必须放在最前面,然后才是 task.h、queue.h 等。因为 FreeRTOS.h 会引入 FreeRTOSConfig.h 并定义 TickType_t、BaseType_t 等基础类型,顺序颠倒可能导致编译失败。
1 |
portmacro.h 不在 include/ 目录中,而是位于具体 port 目录(如 portable/GCC/ARM_CM3/portmacro.h),由 portable.h 间接引入,定义端口相关的类型、临界区宏和栈类型。
4 工程组织与使用建议
4.1 最小内核组成
一个能跑起来的最小 FreeRTOS 工程通常需要以下内容:
| 类别 | 必需文件 | 说明 |
|---|---|---|
| 调度核心 | tasks.c、list.c |
任务和链表 |
| 通信/同步 | queue.c |
队列、信号量、互斥量的基础;即使只使用任务,实际工程也常保留 |
| 移植层 | portable/<编译器>/<架构>/port.c、portmacro.h |
任务切换、中断入口、临界区 |
| 内存管理 | portable/MemMang/heap_x.c |
使用动态分配时必需;只用静态分配时可以不选 |
| 头文件 | include/ |
内核 API 头文件 |
| 配置 | FreeRTOSConfig.h |
由应用工程提供 |
| 按需 | timers.c、event_groups.c、stream_buffer.c、croutine.c |
分别对应软件定时器、事件组、流/消息缓冲区、协程 |
是否编译某个 .c 文件,主要由 FreeRTOSConfig.h 中的功能开关决定。例如 configUSE_TIMERS=0 时不需要软件定时器,configUSE_EVENT_GROUPS=0 时不需要事件组。裁剪前要先确认没有其他模块依赖。
裁剪能减小代码体积,但会破坏与官方目录结构的一致性,增加升级成本。一般建议保留官方目录结构,只在编译时选择需要的源文件;只有对代码体积非常敏感的项目才做物理裁剪。
4.2 典型工程组织
在实际项目中,通常把 FreeRTOS 当作中间件,放在单独的目录中管理:
1 | project/ |
应用层只通过 FreeRTOS.h、task.h 等头文件使用内核;FreeRTOSConfig.h 放在 cfg 等独立目录,和内核源码分开管理。这样升级内核时,只需替换 FreeRTOS-Kernel,配置文件仍可对比、合并。
FreeRTOS-Kernel 自带 CMakeLists.txt,也可以直接加入 Keil、IAR 或 Makefile 工程。具体如何拷贝文件、添加头文件路径、选择 port 和 heap、处理 SVC_Handler、PendSV_Handler、SysTick_Handler 的冲突,将在进阶篇的移植文章中详细介绍。
4.3 源码阅读入口
如果准备阅读内核源码,可以从调度器启动流程入手,沿着下面这条线看:
main()调用xTaskCreate()创建任务(tasks.c)。vTaskStartScheduler()创建空闲任务、初始化就绪列表,然后调用xPortStartScheduler()(tasks.c->port.c)。xPortStartScheduler()配置 SysTick 和 PendSV/SVC 优先级,调用prvPortStartFirstTask(),再通过SVC异常进入vPortSVCHandler(),恢复第一个任务的上下文并开始运行(Cortex-M 端口)。- 每个节拍中断进入
xPortSysTickHandler()->xTaskIncrementTick();需要切换任务时置位 PendSV,在xPortPendSVHandler()中保存和恢复上下文。
这条线把 tasks.c 和 port.c 串了起来,是理解 FreeRTOS 调度器最直接的入口。任务状态、优先级和时间片的具体规则,在任务管理文章中展开。
5 小结
本文从工程视角梳理了 FreeRTOS:GitHub 上不同仓库的定位、LTS 集成仓库的子模块结构、内核源码文件的分工、portable 移植层和 MemMang 内存方案,以及应用侧 FreeRTOSConfig.h 的作用。可以把一个 FreeRTOS 工程概括为:内核源码 + 移植层 + 内存管理 + 应用配置。
具体的移植步骤(拷贝哪些文件、如何配置 CMake、如何处理中断向量冲突)将在进阶篇中介绍。接下来先进入基础篇的下一篇文章——任务管理。









