02_STM32_Freertos_移植篇
本文最后更新于 2025-06-10,学习久了要注意休息哟
第一章 Freertos_源码结构
1.1 源码获取
1.1.1 官网获取
我们可以从 FreeRTOS 官网获取最新版本的内核源码,适合初学者查阅官方文档与下载稳定版本。
官网地址:www.freertos.org
1.1.2 GitHub 获取
如果你想获取 FreeRTOS 的最新开发版本或完整仓库结构(包括 Plus 模块),可以从 GitHub 下载:
下载地址:FreeRTOS · GitHub
1.1 源码结构
下载并解压 FreeRTOS 源码压缩包后,会得到如下目录结构:
文件结构说明如下:
| 名称 | 描述说明 |
| ——————— | ——————– |
| FreeRTOS | FreeRTOS 内核源码 |
| FreeRTOS-Plus | 附加组件(如 TCP/IP、文件系统等) |
| tools | 构建工具、模拟工具等 |
| GitHub-FreeRTOS-Home | GitHub 页面跳转链接 |
| Quick_Start_Guide | 快速入门引导文档(网页跳转) |
| Upgrading-to-FreeRTOS | FreeRTOS 升级指南 |
| History.txt | 版本更新历史记录 |
| 其他 | 可能包含的一些额外说明或无关内容 |
🔹 开发与移植中,我们实际只需要关注 FreeRTOS
这个文件夹,其他内容可以忽略。
1.2.1 FreeRTOS 文件夹
打开 FreeRTOS
文件夹,目录结构如下图所示:
各文件夹说明如下:
| 名称 | 描述 |
|———–|————————–|
| Demo | FreeRTOS 示例工程 |
| License | 软件授权协议 |
| Source | FreeRTOS 内核源码 |
| Test | 单元测试或移植验证代码(一般不使用) |
✅ 我们主要使用的是 Demo
和 Source
文件夹,其他可暂时忽略。
1.2.2 Source 文件夹
打开 FreeRTOS\Source
文件夹后,你会看到如下内容:
| 名称 | 描述 | 是否必须 |
| ————— | ——————- | —- |
| include | FreeRTOS 内核头文件 | ✅ 必须 |
| portable | 与平台相关的移植文件(如STM32) | ✅ 必须 |
| croutine.c | 协程功能源文件(可选) | ❌ 可选 |
| event_groups.c | 事件组功能源文件(可选) | ❌ 可选 |
| list.c | FreeRTOS 核心列表结构支持 | ✅ 必须 |
| queue.c | 消息队列功能实现 | ✅ 必须 |
| stream_buffer.c | 流式缓冲区(高级功能,可选) | ❌ 可选 |
| tasks.c | 核心任务调度逻辑实现 | ✅ 必须 |
| timers.c | 软件定时器实现(如 xTimer
) | ❌ 可选 |
1、include 文件夹
此文件夹中包含了所有 FreeRTOS 内部使用的 .h
头文件,在程序开发时必须包含。
2、portable 文件夹
该文件夹是平台相关的移植层实现,比如 Cortex-M4 架构下的 ARM_CM4F
文件夹中会包含上下文切换、启动文件等代码。
1.2.2 Demo 文件夹
该文件夹中提供了大量示例工程,用于不同芯片、开发板的快速上手。我们只需找到与我们目标芯片匹配的文件夹即可。
✅ 本课程使用的芯片为 STM32F407IGH6
,所以选择路径:
Demo/CORTEX_M4F_STM32F407ZG-SK/
在这个示例工程中,最重要的文件是 FreeRTOSConfig.h
,它是整个系统配置的核心文件,几乎所有内核行为都由此文件控制。
第二章 Freertos_系统移植
2.1 添加内核源码文件
2.1.1 工程中创建文件夹
- 在工程根目录下,新建一个
FreeRTOS
文件夹:
在
FreeRTOS
中创建Source
、portable
、include
三个文件夹。Source
(存放内核源文件)portable
(移植层)include
(头文件)
2.1.2 复制Source
文件
将 FreeRTOS 内核中的 .c
文件,复制到工程的 FreeRTOS/Source
文件夹中。
文件说明:
tasks.c
:任务调度器主逻辑queue.c
:队列机制实现list.c
:内核链表支持- 其他文件为扩展功能,如
timers.c
可选
复制完成示意:
2.1.3 复制include
文件
将官方源码中的 include
文件夹下所有 .h
文件复制到工程中的 FreeRTOS/include/
文件夹:
这些头文件是任务/时间管理/内存分配等 API 的声明集合,必须添加。
2.1.4 复制portable
文件
1、复制 ARM_CM4F
文件夹
此部分为架构相关移植代码,平台不同拷贝内容不同。本工程为 STM32F407(Cortex-M4F):
去到 RVDS
文件夹中,复制 ARM_CM4F
文件。
FreeRTOS/portable/RVDS/ARM_CM4F
2、复制 MemMang
文件夹
复制内存算法文件夹
只需一种内存管理方式,例如FreeRTOS/portable/MemMang/heap_4.c
2.1.5 复制配置文件
复制源码的 Demo\CORTEX_M4F_STM32F407ZG-SK\FreeRTOSConfig.h
文件到 工程目录下的 Core
文件夹下。
这个文件非常重要,系统功能裁剪与参数配置都靠它控制!
2.2 配置工程环境
2.2.1 工程中添加源码分组
在 Keil 的 Group 中创建两个新分组:
- FreeRTOS\Source
- FreeRTOS\portable
2.2.2 添加头文件搜索路径
依次添加如下路径(保证 #include
无报错):
.\FreeRTOS\include
.\FreeRTOS\portable\RVDS\ARM_CM4F
操作如下:
2.2.3 工程中添加文件
1、向 FreeRTOS\portable
中添加
port.c
:平台上下文切换逻辑heap_4.c
:内存管理实现
2、向 FreeRTOS\Source
中添加
包括:
tasks.c
queue.c
list.c
等核心文件
3、添加 FreeRTOSConfig.h
文件
将 FreeRTOSConfig.h
添加到 Core 分组中:
2.3 裁剪内核源码
完成前面文件复制与添加后,在编译工程时,可能会出现如下报错:
..\FreeRTOS\portable\ARM_CM4F\port.c(788): error: #20: identifier "SystemCoreClock" is undefined
portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;
这是因为 FreeRTOS 的内核代码中使用了 SystemCoreClock
,但当前编译器未包含它的声明。
1、修改 FreeRTOSConfig.h
中的条件编译
打开 FreeRTOSConfig.h
,找到如下语句(大约在第 44 行):
#ifdef __ICCARM__
将其修改为:
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
这样就可以在不同编译器下正常声明 SystemCoreClock
。
2、解决中断函数重复定义问题
修改完后重新编译,可能会遇到如下报错:
..\Output\Objects\temp.axf: Error: L6200E: Symbol PendSV_Handler multiply defined (by port.o and stm32f4xx_it.o).
..\Output\Objects\temp.axf: Error: L6200E: Symbol SVC_Handler multiply defined (by port.o and stm32f4xx_it.o).
..\Output\Objects\temp.axf: Error: L6200E: Symbol SysTick_Handler multiply defined (by port.o and stm32f4xx_it.o).
这说明 port.c
中的中断处理函数,与 stm32f4xx_it.c
中重复定义了。
解决办法:打开 stm32f4xx_it.c
文件,注释掉以下三个中断函数:
// void SVC_Handler(void) { ... }
// void PendSV_Handler(void) { ... }
// void SysTick_Handler(void) { ... }
3、钩子函数未定义问题
再次编译,可能会出现以下链接错误:
..\Output\Objects\temp.axf: Error: L6218E: Undefined symbol vApplicationMallocFailedHook (referred from heap_4.o).
..\Output\Objects\temp.axf: Error: L6218E: Undefined symbol vApplicationIdleHook (referred from tasks.o).
..\Output\Objects\temp.axf: Error: L6218E: Undefined symbol vApplicationStackOverflowHook (referred from tasks.o).
..\Output\Objects\temp.axf: Error: L6218E: Undefined symbol vApplicationTickHook (referred from tasks.o).
这说明你在配置文件中启用了 HOOK 钩子函数,但工程中并未定义它们。
4、临时关闭所有 HOOK 钩子函数
打开 FreeRTOSConfig.h
,将以下几项修改为 0
:
#define configUSE_IDLE_HOOK 0
#define configUSE_TICK_HOOK 0
#define configCHECK_FOR_STACK_OVERFLOW 0
#define configUSE_MALLOC_FAILED_HOOK 0
这表示暂时不使用钩子机制,让系统先正常启动起来。
当然,你也可以直接在主函数中,编写这些勾子函数,如下
// 空闲钩子函数
// 当系统中没有其他任务可运行时,空闲任务会执行这个函数。
// 目前我们不需要在空闲时做额外事情,所以留空即可。
void vApplicationIdleHook(void) { }
// Tick钩子函数
// 每次系统节拍中断(比如每 1ms)都会调用一次此函数。
// 当前不使用该钩子功能,留空实现。
void vApplicationTickHook(void) { }
// 内存分配失败钩子函数
// 如果使用了动态内存(如 xTaskCreate),而内存不足时会触发此函数。
// 建议进入死循环,防止继续运行造成更严重问题。x
void vApplicationMallocFailedHook(void)
{
while (1); // 内存分配失败,程序停在这里
}
// 堆栈溢出钩子函数
// 如果任务堆栈使用超过限制(需配置堆栈溢出检测)会调用此函数。
// 通常用于捕捉任务栈异常,可打断点调试。
void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName)
{
while (1); // 任务堆栈溢出
}
2.4 编写任务调度示例
#include "FreeRTOS.h"
#include "task.h"
// ------------ 任务函数定义 ------------
void Task1(void *pvParameters)
{
printf("Task1\r\n");
while (1)
{
// D2
GPIO_ToggleBits( GPIOF , GPIO_Pin_8);
vTaskDelay(500); // 延时500个Tick,等于500ms
}
}
void Task2(void *pvParameters)
{
printf("Task2\r\n");
while (1)
{
GPIO_ToggleBits( GPIOA , GPIO_Pin_15 );
vTaskDelay(300); // 延时500ms
}
}
int main()
{
// 初始化
Main_Init();
// 创建任务
xTaskCreate(Task1, "Task1", 128, NULL, 2, NULL);
xTaskCreate(Task2, "Task2", 128, NULL, 1, NULL);
// 启动调度器
vTaskStartScheduler();
while(1) {}
}
第三章 Freertos_配置文件
FreeRTOSConfig.h 配置文件作用:对FreeRTOS的功能进行配置和裁剪,以及API函数的使能等。
整体的配置项可以分为三类:
INCLUDE开头:一般是“INCLUDE_函数名”,函数的使能,1表示可用,0表示禁用。
config开头:FreeRTOS的一些功能配置,比如基本配置、内存配置、钩子配置、中断配置等。
其他配置:PendSV宏定义、SVC宏定义。
我将配置划分为以下几类:
📌 基础功能配置
⏱️ 系统节拍与内存配置
🧠 调试与诊断(钩子函数、断言)
🧵 同步机制配置(互斥量、信号量)
⏰ 软件定时器配置
🧩 可选 API 接口控制
⚙️ Cortex-M 内核相关配置
🔁 中断优先级映射
🪛 中断函数映射到 CMSIS
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
/*-----------------------------------------------------------
* FreeRTOS 配置文件(适用于 STM32F407 + Keil)
*----------------------------------------------------------*/
/* 兼容不同编译器,声明 SystemCoreClock 变量 */
#if defined(__ICCARM__)||defined(__CC_ARM)||defined(__GNUC__)
#include <stdint.h>
extern uint32_t SystemCoreClock;
#endif
/*-----------------------------------------------------------
* 基础功能配置
*----------------------------------------------------------*/
#define configUSE_PREEMPTION 1 // 启用抢占式调度(0 为协作式)
#define configUSE_IDLE_HOOK 0 // 禁用空闲钩子函数(可防止链接报错)
#define configUSE_TICK_HOOK 0 // 禁用滴答钩子函数(同上)
#define configCPU_CLOCK_HZ ( SystemCoreClock ) // 系统核心时钟频率
#define configTICK_RATE_HZ ( ( TickType_t )1000 ) // 系统节拍频率:1ms
#define configMAX_PRIORITIES 5 // 可用的最大任务优先级数
#define configMINIMAL_STACK_SIZE ( ( unsigned short )130 ) // 最小堆栈字节数(单位:word)
#define configMAX_TASK_NAME_LEN 10 // 任务名称最大长度
#define configUSE_16_BIT_TICKS 0 // 使用 32 位 tick 计数器
#define configIDLE_SHOULD_YIELD 1 // 空闲任务是否主动让出 CPU
/*-----------------------------------------------------------
* 系统内存配置
*----------------------------------------------------------*/
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 75 * 1024 ) ) // 动态内存堆大小(75KB)
#define configUSE_MALLOC_FAILED_HOOK 0 // 禁用内存分配失败钩子(不写函数也能跑)
#define configCHECK_FOR_STACK_OVERFLOW 0 // 禁用堆栈溢出检测(后期可开启)
/*-----------------------------------------------------------
* 调试追踪配置(Trace、任务标签)
*----------------------------------------------------------*/
#define configUSE_TRACE_FACILITY 1 // 启用调试追踪功能(如任务状态统计)
#define configUSE_APPLICATION_TASK_TAG 0 // 不使用任务标签机制
#define configGENERATE_RUN_TIME_STATS 0 // 不生成运行时统计信息(需用户实现)
/*-----------------------------------------------------------
* 同步机制配置
*----------------------------------------------------------*/
#define configUSE_MUTEXES 1 // 启用普通互斥量
#define configUSE_RECURSIVE_MUTEXES 1 // 启用递归互斥量
#define configUSE_COUNTING_SEMAPHORES 1 // 启用计数型信号量
#define configQUEUE_REGISTRY_SIZE 8 // 队列注册表大小(调试用)
/*-----------------------------------------------------------
* 软件定时器配置
*----------------------------------------------------------*/
#define configUSE_TIMERS 1 // 启用软件定时器
#define configTIMER_TASK_PRIORITY ( 2 ) // 定时器任务优先级
#define configTIMER_QUEUE_LENGTH 10 // 定时器队列长度
#define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE * 2 ) // 定时器任务堆栈深度
/*-----------------------------------------------------------
* 可选 API 函数启用(设置为 1 可用,0 不可用)
*----------------------------------------------------------*/
#define INCLUDE_vTaskPrioritySet 1
#define INCLUDE_uxTaskPriorityGet 1
#define INCLUDE_vTaskDelete 1
#define INCLUDE_vTaskCleanUpResources 1
#define INCLUDE_vTaskSuspend 1
#define INCLUDE_vTaskDelayUntil 1
#define INCLUDE_vTaskDelay 1
/*-----------------------------------------------------------
* Cortex-M 内核相关配置
*----------------------------------------------------------*/
#ifdef __NVIC_PRIO_BITS
#define configPRIO_BITS __NVIC_PRIO_BITS // 由 CMSIS 定义(通常为 4)
#else
#define configPRIO_BITS 4 // 默认 4:即 0~15 级优先级
#endif
/*-----------------------------------------------------------
* 中断优先级映射配置
*----------------------------------------------------------*/
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0xf // 可设置的最低中断优先级
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5 // 使用 FreeRTOS API 的最高中断优先级
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/*-----------------------------------------------------------
* 断言定义(错误时卡死)
*----------------------------------------------------------*/
#define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); }
/*-----------------------------------------------------------
* 中断服务函数映射(CMSIS 兼容)
*----------------------------------------------------------*/
#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
#define xPortSysTickHandler SysTick_Handler
#endif /* FREERTOS_CONFIG_H */
第四章 FreeRTOS_数据类型
第五章 FreeRTOS_命名要求
- 感谢你赐予我前进的力量