说明

本文为个人学习 FreeRTOS 过程中的整理笔记,主要用于知识复习、技术交流与学习分享,并非 FreeRTOS 官方文档。

本文将从 FreeRTOS 的基本概念、源码结构、移植层选择以及 FreeRTOSConfig.h 配置等内容展开,并在文末提供 基于 STM32F103C8T6 + FreeRTOS V11.1.0 完成移植并验证运行的工程,方便读者结合实际工程理解整个移植过程。

文中部分知识点结合 FreeRTOS 官方资料、源码以及其他优秀技术文章进行学习整理;其中部分图片来自B站博主 “铁头山羊” 的相关内容,图片版权归原作者所有。若涉及转载或使用不当,请联系删除或修改。

本文中的文字整理、代码分析以及 FreeRTOS 移植工程均基于本人实际学习和实验过程整理。文末工程主要用于学习和交流,实际项目中请根据具体 MCU、FreeRTOS 版本、编译器及硬件环境重新检查相关配置。

本文实验环境:

STM32F103C8T6 + Cortex-M3 + Keil ARMCC5 + FreeRTOS V11.1.0

不同 FreeRTOS 版本、芯片型号及开发环境下,部分源码、API、配置宏和移植方式可能存在差异,具体请以 FreeRTOS 官方文档及对应版本源码为准。

如文中存在理解错误、表述不严谨或工程配置问题,欢迎交流指正。

FreeRTOS 移植

FreeRTOS 下载: https://freertos.org/

在这里插入图片描述


1. FreeRTOS 到底是什么

FreeRTOS 不是一种编程语言,也不是 STM32 芯片内部自带的功能。

它本质上是:

一套使用 C 语言和少量汇编语言编写的实时操作系统内核源码。

可以简单理解为:

你的 STM32 工程
│
├── 你自己写的代码
│   ├── main.c
│   ├── led.c
│   ├── can.c
│   └── adc.c
│
└── FreeRTOS 源码
    ├── tasks.c
    ├── queue.c
    ├── list.c
    ├── port.c
    └── heap_4.c

当我们把 FreeRTOS 源码加入工程后,FreeRTOS 源码会和自己编写的代码一起参与:

编译
 ↓
链接
 ↓
生成 STM32 能够执行的机器码
 ↓
下载到 Flash

所以:

FreeRTOS 不是一个独立运行的软件,而是会被编译进 STM32 程序中,与应用程序一起运行。


2. FreeRTOS 移植文件选择

在这里插入图片描述

FreeRTOS 需要支持很多不同的 CPU 架构,例如:

Cortex-M0
Cortex-M3
Cortex-M4
Cortex-M7
RISC-V
ESP32

这些 CPU 的:

寄存器
指令集
中断机制

都可能不同。

但是:

任务管理
队列
链表

等内核逻辑,在不同 CPU 上基本是相似的。

因此,FreeRTOS 源码可以大致分成两部分:

FreeRTOS
├── 通用内核
└── 移植层

2.1 通用内核是什么

通用内核是与具体 CPU 架构关系相对较小的部分。

典型文件包括:

tasks.c
queue.c
list.c
timers.c
event_groups.c
stream_buffer.c

主要实现:

任务优先级管理
任务状态管理
就绪链表管理
阻塞链表管理
队列收发逻辑
信号量逻辑
软件定时器逻辑
事件组逻辑

这些逻辑在 Cortex-M3 和 Cortex-M4 等平台上基本一致,所以可以复用同一套 C 语言内核代码。


2.2 移植层

移植层英文通常称为:

Portable Layer

简称:

Port

移植层负责处理:

与具体 CPU 架构和编译器有关的底层操作。

例如:

任务栈最开始怎样初始化
任务上下文怎样保存
任务上下文怎样恢复
PendSV 中断怎样切换任务
SVC 怎样启动第一个任务
临界区怎样关闭中断
栈的数据类型是什么
CPU 对齐要求是什么

这些内容和 CPU 架构密切相关。

例如 STM32F103 使用的是:

ARM Cortex-M3

因此需要选择适用于:

Cortex-M3

的移植层。


2.3 工具链

在这里插入图片描述

即使使用相同的 Cortex-M3 内核,也可能使用不同的编译器:

Keil ARMCC
Keil ARMClang
GCC
IAR

虽然 CPU 都是 Cortex-M3,但不同编译器在以下方面可能存在差异:

汇编语法
函数属性写法
内联汇编语法
调用约定
关键字
编译器宏

因此选择 FreeRTOS 移植层时,不能只看 CPU,还需要看编译器。

需要同时满足:

CPU 架构正确
+
编译器正确

例如:

STM32F1 系列:

CubeIDE + STM32F1
=
GCC + ARM_CM3

Keil ARMCC5 + STM32F1
=
RVDS + ARM_CM3

而使用较新的 Keil ARMClang 编译器时,需要选择与 ARMClang 对应的移植目录。


3. FreeRTOS 内核文件架构

3.1 FreeRTOS 源码整体目录结构

在这里插入图片描述

在这里插入图片描述


3.2 tasks.c

tasks.c 主要负责:

任务管理和调度相关功能。

大量任务相关 API 都在其中实现,例如:

// 动态创建任务
xTaskCreate();

// 相对延时,使当前任务进入阻塞状态
vTaskDelay();

// 删除指定任务
vTaskDelete();

// 挂起任务
vTaskSuspend();

// 恢复被挂起的任务
vTaskResume();

// 启动 FreeRTOS 调度器
vTaskStartScheduler();

可以简单记忆:

tasks.c = FreeRTOS 的任务管理中心。


3.3 queue.c

queue.c 内部不仅实现了 Queue,还包含与以下内核对象相关的底层逻辑:

Semaphore
二值信号量
用于任务间同步、中断通知任务

Mutex
互斥锁
用于共享资源独占保护

Recursive Mutex
递归互斥锁
用于需要递归获取同一个互斥量的场景

Counting Semaphore
计数信号量
用于多资源或事件计数

因此:

FreeRTOS 内部的信号量和互斥量都建立在 Queue 的底层机制之上。


3.4 list.c

list.c 主要实现:

FreeRTOS 内部使用的链表数据结构。

例如:

就绪任务列表
├── Task1
└── Task2

延时任务列表
├── TaskA
└── TaskB

这些任务状态管理都需要使用链表。

因此:

list.c 不是普通的工具文件,而是 FreeRTOS 调度器的重要数据结构基础。


3.5 port.c——CPU 适配层

可以先建立一个简单理解:

tasks.c 决定什么时候需要进行任务切换,而 port.c 负责完成与 CPU 架构相关的实际切换工作。

当系统准备切换任务时,内核需要处理:

STM32 寄存器怎样保存
SP 怎样处理
PSP 怎样恢复
PendSV 怎样执行
任务上下文怎样恢复

这些操作与 CPU 架构密切相关。

因此,真正涉及:

保存 R4~R11
恢复 R4~R11
处理 PSP
PendSV 上下文切换

等 Cortex-M3 底层操作的代码,主要位于移植层。

可以简单记忆:

port.c = 与 CPU 架构相关的移植代码。


3.6 portmacro.h——CPU 相关类型和宏定义

portmacro.h 会告诉 FreeRTOS 内核:

BaseType_t 是什么类型
TickType_t 是什么类型
StackType_t 是什么类型

例如:

// 定义任务栈基本数据类型
typedef uint32_t StackType_t;

对于 STM32 Cortex-M3:

一个 StackType_t
=
32 bit
=
4 Byte

因此在理解 FreeRTOS:

任务栈大小
栈深度
CPU 数据宽度

等问题时,会用到 portmacro.h 中的定义。


3.7 heap_4.c——FreeRTOS 动态内存管理器

在使用动态创建方式时,FreeRTOS 中很多内核对象需要从 Heap 中申请空间,例如:

任务栈
TCB
Queue
Semaphore
Mutex

当前使用:

heap_4.c

作为 FreeRTOS 的动态内存管理方案。


3.8 文件职责归纳

文件主要职责是否与 CPU 相关
tasks.c任务管理、调度器、任务 API❌ 基本与 CPU 无关
queue.c队列、信号量、互斥量❌ 基本与 CPU 无关
list.c链表数据结构,维护各种任务列表❌ 基本与 CPU 无关
timers.c软件定时器❌ 基本与 CPU 无关
event_groups.c事件组❌ 基本与 CPU 无关
stream_buffer.c流缓冲区、消息缓冲区❌ 基本与 CPU 无关
heap_4.cFreeRTOS 动态内存管理❌ 基本与 CPU 无关
port.c上下文切换、异常处理、临界区等移植实现✅ CPU / Port 相关
portmacro.hCPU 相关类型定义和宏✅ CPU / Port 相关
FreeRTOSConfig.h用户配置、功能裁剪、系统参数⚠️ 与平台和项目配置相关

4. 高频问题

4.1 为什么更换 STM32 型号时,大部分 FreeRTOS 源码不用修改?

因为 tasks.cqueue.clist.c 等主要实现的是通用内核逻辑,它们与具体 CPU 架构的耦合较低。

真正依赖 CPU 寄存器、异常机制和上下文切换方式的是 port.cportmacro.h 等移植层代码。

因此,更换 CPU 架构时,主要需要重新选择与目标 CPU 和编译器匹配的 Port。


4.2 为什么 port.c 不能删除?

因为 port.c 实现了与 CPU 架构相关的底层功能,例如任务上下文切换、异常处理和临界区相关操作。

对 Cortex-M3 Port 来说,任务切换会依赖 PendSV、SVC 等异常机制。

如果没有正确的移植层,FreeRTOS 内核就无法正常完成任务启动和上下文切换。


三、FreeRTOSConfig.h 文件配置

1. 配置原则

FreeRTOSConfig.h 并不是:

功能打开得越多越好。

也不能为了让当前简单示例快速运行,就把大量功能全部关闭。

配置项可以分为三类。

1.1 平台适配项

必须与当前使用的平台一致:

STM32F103C8
Cortex-M3
ARMCC5
对应的 FreeRTOS Port

如果配置错误,可能导致:

编译失败
调度器无法启动
进入 HardFault
系统 Tick 错误
延时时间错误
中断优先级配置异常

1.2 稳定性与调试项

建议从项目早期就考虑开启,例如:

configASSERT
栈溢出检测
Malloc 失败 Hook
任务栈水位查询

这些配置有助于后续定位:

内存不足
栈溢出
非法配置
任务栈使用异常

等问题。


1.3 项目规模相关项

下面这些配置不存在适用于所有项目的唯一数值:

Tick 频率
最大优先级数量
Heap 大小
软件定时器任务栈

需要根据:

任务数量
RAM 使用情况
系统实时性要求
实际测量结果

进行调整。


2. FreeRTOSConfig.h 修改总表

原始位置宏或配置项建议类型
43 后#include "stm32f10x.h"新增必须 / 推荐
54configCPU_CLOCK_HZ改为 SystemCoreClock必须检查
69configSYSTICK_CLOCK_HZ保持注释必须确认
78configTICK_RATE_HZ通用学习基线可先设为 1000项目相关
90 后configNUMBER_OF_CORES新增为 1推荐明确
98configUSE_PORT_OPTIMISED_TASK_SELECTION改为 1推荐
109configMAX_PRIORITIES可先设为 8,后续根据项目架构调整项目相关
152configQUEUE_REGISTRY_SIZE调试版本建议设为 8调试推荐
195configSTATS_BUFFER_MAX_LENGTH建议设为 1024调试推荐
218configUSE_TIMERS保持 1,同时编译 timers.c功能一致性
248configUSE_EVENT_GROUPS保持 1,同时编译 event_groups.c功能一致性
259configUSE_STREAM_BUFFERS保持 1,同时编译 stream_buffer.c功能一致性
283configTOTAL_HEAP_SIZESTM32F103C8 初始可设为 6 KB项目相关
311、319内核 / 系统调用中断优先级改为基于 __NVIC_PRIO_BITS 的写法强烈推荐
323configMAX_API_CALL_INTERRUPT_PRIORITYconfigMAX_SYSCALL_INTERRUPT_PRIORITY 保持一致推荐
335configUSE_MALLOC_FAILED_HOOK改为 1稳定性推荐
357configCHECK_FOR_STACK_OVERFLOW保持 2稳定性推荐
373configUSE_TRACE_FACILITY调试版本可改为 1调试可选
409configASSERT改成可记录文件和行号的版本强烈推荐
562configKERNEL_PROVIDED_STATIC_MEMORY保持 1与静态分配配置一致
572configENABLE_TRUSTZONE改为 0必须
579configRUN_FREERTOS_SECURE_ONLY改为 0必须
583configENABLE_MPU改为 0必须
587configENABLE_FPU改为 0必须
594configENABLE_MVE改为 0必须
612configCHECK_HANDLER_INSTALLATION保持 1推荐
639INCLUDE_uxTaskGetStackHighWaterMark改为 1调试推荐
641INCLUDE_eTaskGetState改为 1调试推荐
648 前SVC、PendSV、SysTick 映射新增三个宏必须
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐