03-FreeRTOS移植指南.md 8.5 KB


tags: [source-summary] type: source source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 第3章" author: "尚硅谷研究院" date: 2026-07-12 url: ""

created: 2026-07-12

FreeRTOS 移植指南

来源:尚硅谷 FreeRTOS 教程第 3 章。覆盖:源码结构、HAL 库移植步骤、寄存器移植步骤、FreeRTOSConfig.h 配置详解。

VSCode + GCC + CMake 移植步骤(STM32F103 实战)

目录结构

CMake_HAL_Key/
├── Core/             CubeMX 生成的 HAL 代码
├── Drivers/          STM32 HAL 驱动
├── Hardware/         用户硬件驱动(LED、Key)
├── Middle/
│   └── FreeRTOS/     FreeRTOS 源码(手工复制)
│       ├── croutine.c
│       ├── event_groups.c
│       ├── list.c
│       ├── queue.c
│       ├── stream_buffer.c
│       ├── tasks.c
│       ├── timers.c
│       ├── include/
│       │   ├── FreeRTOS.h
│       │   ├── FreeRTOSConfig.h    ← 从 Demo 复制并修改
│       │   ├── task.h
│       │   └── ...
│       └── portable/
│           ├── GCC/
│           │   └── ARM_CM3/        ← STM32F103 = Cortex-M3
│           │       ├── port.c
│           │       └── portmacro.h
│           └── MemMang/
│               └── heap_4.c        ← 推荐
└── cmake/            CMake 工具链配置

步骤一:复制 FreeRTOS 源码到 Middle/

从官方源码包取以下文件:

来源 目标
Source/*.c(7 个核心 .c) Middle/FreeRTOS/
Source/include/*.h Middle/FreeRTOS/include/
Source/portable/GCC/ARM_CM3/port.c Middle/FreeRTOS/portable/GCC/ARM_CM3/
Source/portable/GCC/ARM_CM3/portmacro.h Middle/FreeRTOS/portable/GCC/ARM_CM3/
Source/portable/MemMang/heap_4.c Middle/FreeRTOS/portable/MemMang/
Demo/CORTEX_STM32F103_GCC_Rowley/FreeRTOSConfig.h Middle/FreeRTOS/include/

步骤二:修改 FreeRTOSConfig.h

在 FreeRTOSConfig.h 末尾添加三个宏,用来将 FreeRTOS port.c 中的中断处理函数名重定义为向量表名称:

/* 必须的三个宏 */
#define xPortPendSVHandler PendSV_Handler
#define vPortSVCHandler SVC_Handler
#define INCLUDE_xTaskGetSchedulerState 1

原理:FreeRTOS port.c 定义了 xPortPendSVHandler()vPortSVCHandler(),而 STM32 的启动文件向量表中写的是 PendSV_HandlerSVC_Handler。通过宏替换,预处理器将 port.c 中的函数名直接替换为向量表名称,无需额外修改启动文件。

❗ 此方法依赖宏定义,方向是:FreeRTOS原名 → 向量表名,不可写反。

CubeMX 生成的 GCC 启动文件是标准 ST 名称(SVC_HandlerPendSV_Handler),不能被 CubeMX 重新生成时覆盖,所以不能改;因此 GCC 方向相反——用宏重命名 FreeRTOS 的函数来匹配启动文件。

configCHECK_FOR_STACK_OVERFLOW 默认来自 CORTEX_STM32F103_GCC_Rowley Demo,值为 2(启用栈溢出检测)。但该检测需要用户提供 vApplicationStackOverflowHook 函数,否则链接报错。

两种解决方式(二选一):

方式 A(本实战采用):关闭栈溢出检测

#define configCHECK_FOR_STACK_OVERFLOW 0

方式 B:保留检测,在任意 .c 添加 hook 函数

void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName) {
    for (;;) {}
}

Keil Demo(CORTEX_STM32F103_Keil)的 FreeRTOSConfig.h 未定义此项,默认 0,所以 Keil 教程无需处理此问题。

步骤三:修改 stm32f1xx_it.c

添加 FreeRTOS 头文件:

/* USER CODE BEGIN Includes */
#include "FreeRTOS.h"
#include "task.h"
/* USER CODE END Includes */

注释掉与 FreeRTOS 冲突的空中断处理函数(由 FreeRTOS port.c 通过宏提供):

// void SVC_Handler(void) { ... }
// void PendSV_Handler(void) { ... }

修改 SysTick_Handler,让 HAL 和 FreeRTOS 共用 SysTick:

extern void xPortSysTickHandler(void);
void SysTick_Handler(void) {
    if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
        xPortSysTickHandler();
    }
}

注意:HAL 默认也依赖 SysTick 做时基。如果 FreeRTOS 抢占 SysTick 后 HAL 的 HAL_Delay() 会失效,建议在 CubeMX 中将 HAL 时基换为其他定时器(如 TIM7)。

步骤四:修改 CMakeLists.txt

将 FreeRTOS 源码和头文件路径加入构建:

target_sources(${CMAKE_PROJECT_NAME} PRIVATE
    Hardware/LED.c
    Hardware/Key.c
    Middle/FreeRTOS/croutine.c
    Middle/FreeRTOS/event_groups.c
    Middle/FreeRTOS/list.c
    Middle/FreeRTOS/queue.c
    Middle/FreeRTOS/stream_buffer.c
    Middle/FreeRTOS/tasks.c
    Middle/FreeRTOS/timers.c
    Middle/FreeRTOS/portable/GCC/ARM_CM3/port.c
    Middle/FreeRTOS/portable/MemMang/heap_4.c
)

target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
    Hardware
    Middle/FreeRTOS/include
    Middle/FreeRTOS/portable/GCC/ARM_CM3
)

步骤五:构建验证

cmake -S . -B build/Debug -G Ninja -DCMAKE_BUILD_TYPE=Debug
cmake --build build/Debug

预期输出 Project.elf,无链接错误。

源码结构

FreeRTOS/
├── Demo/           演示例程(多芯片架构)
├── License/        许可
├── Source/         核心源码
│   ├── include/    头文件(通用)
│   ├── portable/   移植文件(编译器相关)
│   │   ├── Keil/  → 指向 RVDS
│   │   ├── RVDS/  不同内核芯片移植文件
│   │   │   └── ARM_CM3/   port.c + portmacro.h
│   │   └── MemMang/  heap_1~5.c
│   ├── tasks.c     任务管理
│   ├── queue.c     队列
│   ├── timers.c    软件定时器
│   ├── event_groups.c
│   ├── stream_buffer.c
│   └── list.c      列表
└── Test/           测试代码

port.c:上下文切换等核心汇编代码,由 FreeRTOS 官方为各内核编写。 portmacro.h:数据类型和宏定义。

MemMang 五个 heap 文件必须使用一个(初学推荐 heap_4.c)。

HAL 库移植步骤

目录添加源码

Project/FreeRTOS/
├── portable/   (Keil + RVDS + MemMang,保留 ARM_CM3 + heap_4.c)
└── source/     (7 个 .c 文件)
Core/Inc/FreeRTOSConfig.h  (从 Demo 目录拷贝)

工程添加文件

  1. 新建 Group FreeRTOS/SourceFreeRTOS/Portable
  2. Source 组添加 7 个 .c 文件
  3. Portable 组添加 port.c + heap_4.c
  4. 添加头文件路径:include/RVDS/ARM_CM3/

系统配置

#define xPortPendSVHandler    PendSV_Handler
#define vPortSVCHandler       SVC_Handler
#define INCLUDE_xTaskGetSchedulerState   1

修改 stm32f1xx_it.c

#include "FreeRTOS.h"
#include "task.h"

// 注释掉 SVC_Handler() 和 PendSV_Handler()

extern void xPortSysTickHandler(void);
void SysTick_Handler(void) {
    if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
        xPortSysTickHandler();
    }
}

注意:HAL 和 FreeRTOS 都默认依赖 SysTick,建议在 CubeMX 中将 HAL 时基换成其他定时器。

寄存器移植步骤

与 HAL 移植类似,但不需要 HAL 相关配置。main.c 中直接添加 SysTick_Handler:

extern void xPortSysTickHandler(void);
void SysTick_Handler(void) {
    if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
        xPortSysTickHandler();
    }
}

FreeRTOSConfig.h 配置

配置项分三类:

  1. INCLUDE_ 开头:函数使能(1=可用,0=禁用)
  2. config 开头:功能配置(基本、内存、钩子、中断)
  3. 其他:PendSV/SVC 宏定义

关键配置项

配置 说明
configUSE_PREEMPTION 1 抢占式调度
configCPU_CLOCK_HZ SystemCoreClock CPU 主频
configTICK_RATE_HZ 1000 系统节拍频率
configMAX_PRIORITIES 32 最大优先级数
configMINIMAL_STACK_SIZE 128 空闲任务栈大小(Word)
configTOTAL_HEAP_SIZE 10*1024 堆大小(Byte)
configUSE_MUTEXES 1 使能互斥信号量
configUSE_TIMERS 1 使能软件定时器
configUSE_TICKLESS_IDLE 0 低功耗模式
configMAX_SYSCALL_INTERRUPT_PRIORITY 5<<4 FreeRTOS 管理的最高中断优先级

配套代码

移植完成的 HAL 库基础工程模板:

  • code/00_HAL_Key/ — HAL 库按键检测模板(FreeRTOS 移植基础工程)

后续章节:[[04-数据类型与命名规范]]、[[05-任务创建删除与挂起恢复]]