tags: [source-summary] type: source source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 第3章" author: "尚硅谷研究院" date: 2026-07-12 url: ""
来源:尚硅谷 FreeRTOS 教程第 3 章。覆盖:源码结构、HAL 库移植步骤、寄存器移植步骤、FreeRTOSConfig.h 配置详解。
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 工具链配置
从官方源码包取以下文件:
| 来源 | 目标 |
|---|---|
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 末尾添加三个宏,用来将 FreeRTOS port.c 中的中断处理函数名重定义为向量表名称:
/* 必须的三个宏 */
#define xPortPendSVHandler PendSV_Handler
#define vPortSVCHandler SVC_Handler
#define INCLUDE_xTaskGetSchedulerState 1
原理:FreeRTOS port.c 定义了 xPortPendSVHandler() 和 vPortSVCHandler(),而 STM32 的启动文件向量表中写的是 PendSV_Handler 和 SVC_Handler。通过宏替换,预处理器将 port.c 中的函数名直接替换为向量表名称,无需额外修改启动文件。
❗ 此方法依赖宏定义,方向是:
FreeRTOS原名 → 向量表名,不可写反。
CubeMX 生成的 GCC 启动文件是标准 ST 名称(SVC_Handler、PendSV_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 教程无需处理此问题。
添加 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)。
将 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)。
Project/FreeRTOS/
├── portable/ (Keil + RVDS + MemMang,保留 ARM_CM3 + heap_4.c)
└── source/ (7 个 .c 文件)
Core/Inc/FreeRTOSConfig.h (从 Demo 目录拷贝)
FreeRTOS/Source 和 FreeRTOS/Portableport.c + heap_4.cinclude/ 和 RVDS/ARM_CM3/#define xPortPendSVHandler PendSV_Handler
#define vPortSVCHandler SVC_Handler
#define INCLUDE_xTaskGetSchedulerState 1
#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();
}
}
配置项分三类:
| 配置 | 值 | 说明 |
|---|---|---|
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-任务创建删除与挂起恢复]]