FreeRTOS源码分析

一、FreeRTOS源码文件目录结构

Middlewares\Third_Party\FreeRTOS\Source

Source 目录是 FreeRTOS 的核心部分,包含内核源码和头文件

  • include 子目录包含 FreeRTOS 的核心头文件,提供 API 和数据类型的定义。

  • portable 子目录包含针对特定处理器架构的移植代码,例如 ARM、MIPS 等。开发者需要根据目标处理器选择对应的子目录。

  • 内存管理文件位于 portable/MemMang,提供多种堆实现方式,如 heap_1 到 heap_5,分别支持不同的内存管理需求。

  • 核心文件包括 tasks.c(任务操作)、list.c(列表操作)和 queue.c(队列操作)。其他可选功能文件如 timers.c(软件定时器)和 event_groups.c(事件组)。

Source\portable\RVDS\ARM_CM3\移植层核心文件:

  • port.c:架构相关实现(任务切换、中断管理、SysTick 配置等核心逻辑);
  • portmacro.h:架构相关宏定义(如任务栈类型、中断开关函数);这两个文件是移植的关键,必须根据项目的编译器和 MCU 架构选择对应目录下的文件。

二、FreeRTOS源码文件核心文件

存放内核实现代码,编译时需将这些文件加入项目(无需全部添加,按需裁剪)

核心文件对应功能如下:

文件名 对应功能模块 是否必选
task.c 任务管理(创建、调度、挂起 / 恢复等核心逻辑)
queue.c 消息队列(数据传递、同步)+ 信号量 / 互斥锁(基于队列实现) 是(核心)
timer.c 软件定时器(基于系统节拍的虚拟定时器) 否(按需)
event_groups.c 事件组(多事件同步) 否(按需)
croutine.c 协程(轻量级任务,适用于资源极受限场景,极少用) 否(按需)
stream_buffer.c 流缓冲区(用于大量数据传输,如串口数据接收) 否(按需)
heap_1.c ~ heap_5.c 5 种内存管理实现(选 1 种即可,如 heap_4.c 支持内存碎片合并)

三、移植时涉及的文件

FreeRTOS 移植文件的核心存放逻辑就是 「按编译器 + MCU 架构分类」,portable/[compiler]/[architecture] 目录是连接内核与硬件的关键,里面的文件直接决定 FreeRTOS 能否在目标 MCU 上运行。

层级 含义(作用) 常见取值 / 示例
[compiler] 编译器 / 开发工具链,即你使用的编译环境 RVDS(Keil MDK 早期名称,兼容 Keil5)、GCC(ARM-GCC)、IAR(IAR Embedded Workbench)
[architecture] MCU 的内核架构(而非具体型号),聚焦 CPU 核心指令集、寄存器、中断机制 ARM_CM3(Cortex-M3)、ARM_CM4F(Cortex-M4F,带 FPU)、ARM_CM7(Cortex-M7)、ARM_CM0(Cortex-M0)

实例:

  • portable/RVDS/ARM_CM3:适用于 Keil5 编译器 + Cortex-M3 架构 MCU(如 STM32F103)
  • portable/GCC/ARM_CM4F:适用于 ARM-GCC 编译器 + Cortex-M4F 架构 MCU(如 STM32F407)
  • portable/IAR/ARM_CM7:适用于 IAR 编译器 + Cortex-M7 架构 MCU(如 STM32H743)

关键提醒:

  • Keil5 虽然现在叫 MDK-ARM,但移植目录仍沿用 RVDS(历史原因,RVDS 是 Keil 的前身工具链),选择时直接选 RVDS/[对应架构] 即可;
  • 带 F 的架构目录(如 ARM_CM4F)是专门适配带浮点单元(FPU)的 MCU,若 MCU 支持硬件浮点,必须选带 F 的目录,否则浮点运算会异常。

每个 [compiler]/[architecture] 目录下,核心文件只有 2 个,这是移植的核心文件:

文件名 作用说明(FreeRTOS 与硬件交互的核心)
port.c 1. 任务切换实现:负责保存 / 恢复任务上下文(寄存器值、栈指针),是 FreeRTOS 多任务调度的硬件基础;2. 系统节拍(SysTick)配置:初始化 SysTick 定时器,生成系统节拍中断(任务调度的触发源);3. 中断管理:实现 FreeRTOS 中断开关函数(如 portDISABLE_INTERRUPTS()),确保调度器安全运行;4. 栈初始化:定义任务栈的初始化方式(如动态创建任务时的栈空间分配)。
portmacro.h 1. 架构相关宏定义:任务优先级类型(UBaseType_t)、栈大小单位(portSTACK_TYPE)、中断优先级分组宏等;2. 汇编指令封装:将关键硬件操作(如中断开关、任务切换触发)封装为宏,供内核调用;3. 编译器适配:定义编译器特定的关键字(如 __irq 中断函数修饰符)、内存对齐方式。

补充文件(部分目录有,辅助功能):

  • portasm.s(或 .asm):部分架构目录会将任务切换的汇编代码单独抽出(如 ARM_CM3 架构),本质是 port.c 的汇编实现部分,必须与 port.c 配套使用;
  • heap_x.c:内存管理实现(如 heap_4.c),不放在这个目录下,而是在 portable/MemMang/ 目录,属于通用移植代码,所有架构共用,选 1 个添加到项目即可。

四、头文件目录

目录路径 核心作用 关键文件 / 意义
 Middlewares\Third_Party\FreeRTOS\Source\include 内核通用头文件目录(所有平台共用) 包含 FreeRTOS.h(内核主头文件)、task.h(任务 API)、queue.h(队列 API)等,是调用 FreeRTOS 功能的基础。
Middlewares\Third_Party\FreeRTOS\Source\portable\[compiler]\[architecture] 硬件移植头文件目录(编译器 + 架构专属) 仅包含 portmacro.h(移植层核心头文件),定义架构相关宏(如中断开关、栈类型),内核通过它适配具体硬件。
Core\Inc(或自定义目录) 项目专属配置文件目录 仅包含 FreeRTOSConfig.h(唯一项目专属文件),用于裁剪内核功能、配置时钟 / 优先级 / 堆大小等,是 FreeRTOS 适配项目的关键。

五、入口函数

Core\Src\main.cmain函数里,初始化了FreeRTOS环境、创建了默认任务,然后启动调度器

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Init scheduler */
  osKernelInitialize(); // 初始化FreeRTOS的运行环境

  /* Call init function for freertos objects (in cmsis_os2.c) */
  MX_FREERTOS_Init(); // 负责完成FreeRTOS的各项初始化工作,包括默认任务创建、队列初始化、信号量
设置等,为FreeRTOS调度器的启动做好准备

  /* Start scheduler */
  osKernelStart(); // 启动调度器

  /* We should never get here as control is now taken by the scheduler */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

六、数据类型与编码规范

FreeRTOS 为了保证跨平台兼容性和代码可读性,定义了一套统一的数据类型和编码规范,核心原则是 “屏蔽硬件差异、规范命名风格、降低维护成本”。

数据类型

FreeRTOS 不直接使用 intlong 等原生 C 数据类型(不同架构下长度可能不同,如 16 位 MCU 的 int 是 16 位,32 位 MCU 是 32 位),而是通过宏定义封装了一套平台无关的数据类型,所有类型定义在 FreeRTOS.h 中。

#ifndef INC_FREERTOS_H
#define INC_FREERTOS_H

/*
 * Include the generic headers required for the FreeRTOS port being used.
 */
#include <stddef.h>

/*
 * If stdint.h cannot be located then:
 *   + If using GCC ensure the -nostdint options is *not* being used.
 *   + Ensure the project's include path includes the directory in which your
 *     compiler stores stdint.h.
 *   + Set any compiler options necessary for it to support C99, as technically
 *     stdint.h is only mandatory with C99 (FreeRTOS does not require C99 in any
 *     other way).
 *   + The FreeRTOS download includes a simple stdint.h definition that can be
 *     used in cases where none is provided by the compiler.  The files only
 *     contains the typedefs required to build FreeRTOS.  Read the instructions
 *     in FreeRTOS/source/stdint.readme for more information.
 */
#include <stdint.h> /* READ COMMENT ABOVE. */

#ifdef __cplusplus
extern "C" {
#endif

/* Application specific configuration options. */
#include "FreeRTOSConfig.h"

/* Basic FreeRTOS definitions. */
#include "projdefs.h"

/* Definitions specific to the port being used. */
#include "portable.h"

/* Must be defaulted before configUSE_NEWLIB_REENTRANT is used below. */
#ifndef configUSE_NEWLIB_REENTRANT
	#define configUSE_NEWLIB_REENTRANT 0
#endif

/* Required if struct _reent is used. */
#if ( configUSE_NEWLIB_REENTRANT == 1 )
	#include <reent.h>
#endif
基础数据类型
FreeRTOS 数据类型 本质(32 位架构) 核心用途
TickType_t uint32_t 系统节拍计数类型,用于 vTaskDelay()xQueueSend() 等函数的超时参数
BaseType_t int32_t 函数返回值类型(如 pdPASS/pdFALSE)、布尔型判断(替代 bool
UBaseType_t uint32_t 无符号基础类型,用于任务优先级、队列长度等无符号数值
StackType_t uint32_t 任务栈元素类型,由移植层 portmacro.h 定义(与 MCU 寄存器宽度一致)
TaskHandle_t void* 或结构体指针 任务句柄(标识任务的唯一指针),用于 vTaskSuspend()vTaskDelete() 等
QueueHandle_t void* 或结构体指针 队列句柄,用于队列相关 API 调用
SemaphoreHandle_t QueueHandle_t 信号量 / 互斥锁句柄(FreeRTOS 信号量基于队列实现,复用队列句柄类型)
TimerHandle_t void* 或结构体指针 软件定时器句柄
常用宏定义
宏定义 含义 用途示例
pdPASS 函数执行成功(值为 1) if (xQueueSend() == pdPASS)
pdFALSE 逻辑假(值为 0) BaseType_t xFlag = pdFALSE;
pdTRUE 逻辑真(值为 1) if (xFlag == pdTRUE)
portMAX_DELAY 无限等待(值为 0xFFFFFFFF) xQueueSend(queue, &item, portMAX_DELAY)
NULL 空指针 TaskHandle_t xTask = NULL;

编码规范

函数名前缀含义

FreeRTOS 函数名前缀有明确规则,通过前缀可快速判断函数的返回值类型和模块:

前缀 含义(返回值 / 功能) 模块示例
v 返回值为 void vTaskDelay()vQueueDelete()
x 返回值为 BaseType_t 或句柄 xTaskCreate()(返回 pdPASS/pdFALSE)、xQueueCreate()(返回队列句柄)
ul 返回值为 unsigned long ulTaskGetTickCount()(返回当前节拍数)
c 返回值为 char cQueueReceiveFromISR()(较少用)
pv 返回值为 void* pvPortMalloc()(内存分配函数)
prv 私有函数(仅内核内部调用) prvTaskSwitchContext()(任务切换函数,用户不可调用)
命名规范
元素类型 命名规则 示例
函数名 小写前缀 + 大写单词拼接(前缀标识模块) vTaskDelay()v=void 返回值,Task= 模块)、xQueueSend()x= 返回 BaseType_t / 句柄)
数据类型名 大写前缀 + 大写单词拼接,末尾加 _t TickType_tBaseType_tTaskHandle_t
宏定义 全大写,单词间用下划线分隔 pdPASSconfigTICK_RATE_HZportMAX_DELAY
全局变量 小写 g_ 前缀 + 大写单词拼接 g_xTaskList(全局任务列表)
局部变量 小写字母,单词间用下划线分隔(或驼峰) xResultpxMessagep= 指针,x= 类型标识)
指针变量 前缀加 p(或 pxx 标识类型) pxTaskHandle(指向任务句柄的指针)、pcBufferc=char 类型)

结语:

无论你是初学者还是有经验的开发者,我希望我的博客能对你的学习之路有所帮助。如果你觉得这篇文章有用,不妨点击收藏,或者留下你的评论分享你的见解和经验,也欢迎你对我博客的内容提出建议和问题。每一次的点赞、评论、分享和关注都是对我的最大支持,也是对我持续分享和创作的动力

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐