📦 准备工作
-
获取FreeRTOS源码:
-
访问 FreeRTOS官网 或其 GitHub仓库 下载最新版内核源码。
-
你也可以使用Git克隆(注意要包含子模块):
git clone https://github.com/FreeRTOS/FreeRTOS.git --recurse-submodules
。
-
-
准备STM32基础工程:
-
使用 STM32CubeMX 生成一个针对你芯片型号的裸机工程(例如一个简单的LED闪烁工程),配置好时钟树、调试接口(如SYS)和你需要的外设(如GPIO、USART等)。
-
确保这个基础工程在你的开发板上能够编译并通过(例如,LED能闪烁)。
-
📁 文件组织与添加
在你的STM32工程目录下(通常与Core
、Drivers
等文件夹同级),创建一个用于存放FreeRTOS源码的文件夹,例如Middlewares/FreeRTOS
。然后按照下表的指引将必要的文件复制到相应位置:
所需文件 | 源路径 (基于FreeRTOS源码根目录) | 目标路径 (你的工程目录) | 作用描述 |
---|---|---|---|
核心源文件(.c) | FreeRTOS/Source/*.c (如 tasks.c , queue.c , list.c 等) | Middlewares/FreeRTOS/Source | FreeRTOS内核的核心功能实现 |
核心头文件(.h) | FreeRTOS/Source/include/*.h | Middlewares/FreeRTOS/Source/include | FreeRTOS内核的头文件,提供API和数据类型定义 |
移植层文件 | FreeRTOS/Source/portable/[Compiler]/[Architecture]/* | Middlewares/FreeRTOS/Source/portable | 与编译器及CPU架构相关的移植代码(关键选择,见下文说明) |
内存管理实现 | FreeRTOS/Source/portable/MemMang/heap_x.c (选一个) | Middlewares/FreeRTOS/Source/portable/MemMang | FreeRTOS的动态内存管理方案(五选一,通常推荐heap_4.c ) |
配置文件 | FreeRTOS/Demo/[Demo项目]/FreeRTOSConfig.h | 通常放在工程Inc 目录或Middlewares/FreeRTOS | FreeRTOS内核的配置文件(需根据你的芯片和需求修改) |
🔧 关键选择说明:
-
移植层文件 ([Compiler]和[Architecture]):
-
[Compiler]: 根据你使用的开发环境选择。
-
Keil MDK: 选择
RVDS
目录。 -
IAR: 选择
IAR
目录。 -
GCC (如STM32CubeIDE, CLion): 选择
GCC
目录。
-
-
[Architecture]: 根据你STM32芯片的Cortex内核型号选择。
-
Cortex-M0:
ARM_CM0
-
Cortex-M3:
ARM_CM3
-
Cortex-M4 (无FPU):
ARM_CM4F
-
Cortex-M7 (有FPU):
ARM_CM7
-
*例如,STM32F103是Cortex-M3,STM32F407是Cortex-M4。*
-
-
-
内存管理实现 (heap_x.c):
FreeRTOS提供了5种内存管理方案,通常选择heap_4.c
(支持内存分配与释放,并能有效减少碎片)。对于极其简单或从不释放内存的应用,也可考虑heap_1.c
。
📝 操作步骤:
-
在你的工程目录下(例如
Middlewares/FreeRTOS
)创建相应的子文件夹:Source
,Source/include
,Source/portable
。 -
根据上表和你的芯片、编译器情况,将FreeRTOS源码包中对应的文件复制到刚刚创建的相应文件夹中。
-
从FreeRTOS源码包的
Demo
文件夹里,找一个与你芯片型号相近的Demo工程,将其中的FreeRTOSConfig.h
文件复制到你的工程目录下(通常放在Inc
目录下便于包含)。
⚙️ 工程配置与修改
1. 添加文件到IDE工程
-
打开你的Keil MDK(或其他IDE)工程。
-
在IDE中创建新的分组(Group),例如 "FreeRTOS_CORE", "FreeRTOS_PORTABLE"。
-
将刚才复制到
Middlewares/FreeRTOS/Source
下的.c
文件(如tasks.c
,queue.c
等)添加到 "FreeRTOS_CORE" 分组。 -
将你选择的内存管理文件(如
heap_4.c
)和移植层文件(如port.c
)添加到 "FreeRTOS_PORTABLE" 分组。 -
不要添加其他未选择的内存管理文件和移植层文件。
2. 添加头文件路径
在IDE的工程设置("Options for Target" -> "C/C++" -> "Include Paths")中,添加以下头文件路径35:
-
../Middlewares/FreeRTOS/Source/include
-
../Middlewares/FreeRTOS/Source/portable/[Compiler]/[Architecture]
(例如../Middlewares/FreeRTOS/Source/portable/RVDS/ARM_CM3
) -
确保也包含了存放
FreeRTOSConfig.h
文件的路径(如../Inc
)。
3. 修改FreeRTOSConfig.h
FreeRTOSConfig.h
是FreeRTOS的核心配置文件,你需要根据你的芯片和项目需求进行修改。以下是一些最关键的配置项34:
配置宏 | 说明与典型设置 |
---|---|
configCPU_CLOCK_HZ | 设置为你STM32芯片的主时钟频率(Hz),例如STM32F103为72000000,STM32F407为168000000。可直接使用 SystemCoreClock 。 |
configTICK_RATE_HZ | 系统节拍频率。通常设置为1000Hz,表示1ms一个时钟节拍。 |
configTOTAL_HEAP_SIZE | FreeRTOS动态内存堆的总大小。根据你计划创建的任务、队列等数量估算。如果不够,任务创建会失败。例如可先设置为(10 * 1024)(10KB),后续再调整。 |
configMAX_PRIORITIES | 系统支持的最大任务优先级数。设置一个够用的值即可,如5-8,不是越大越好。 |
configKERNEL_INTERRUPT_PRIORITY``configMAX_SYSCALL_INTERRUPT_PRIORITY | 中断优先级配置,非常重要!需要根据你芯片的NVIC优先级位数(如STM32F1/F4是4位,即0-15)和你的应用来设置。设置错误可能导致系统不稳定或无法运行。务必仔细查阅FreeRTOS手册和芯片数据手册。 |
configUSE_PREEMPTION | 设置为1启用抢占式调度器,这是最常用的模式。 |
configUSE_TIMERS``configTIMER_TASK_PRIORITY``configTIMER_QUEUE_LENGTH``configTIMER_TASK_STACK_DEPTH | 如果你要使用软件定时器,需要将这些配置使能并设置相关参数。 |
其他常用配置:你还可以根据需求使能或禁用互斥量(configUSE_MUTEXES
)、递归互斥量(configUSE_RECURSIVE_MUTEXES
)、事件组(configUSE_EVENT_GROUPS
)、栈溢出检查(configCHECK_FOR_STACK_OVERFLOW
)等功能。
4. 处理中断服务程序(ISR)
FreeRTOS需要接管SVC、PendSV和SysTick这三个中断246。
-
打开你的工程中
stm32fxxx_it.c
文件(例如stm32f1xx_it.c
或stm32f4xx_it.c
)。 -
找到并注释掉或删除以下三个函数的具体实现:
-
SVC_Handler(void)
-
PendSV_Handler(void)
-
SysTick_Handler(void)
-
-
原因:这些中断的服务程序已经在你之前添加的移植层文件(如
port.c
)中实现了。如果不注释掉,会导致函数重复定义。
注意SysTick的特殊情况:如果你的HAL库仍然使用SysTick作为时基源(HAL_InitTick()
),你可能需要修改SysTick_Handler
而不是简单地删除它。一种常见的做法是28:
c
#include "FreeRTOS.h" #include "task.h"void SysTick_Handler(void) {HAL_IncTick(); // 维持HAL库的时基if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {xPortSysTickHandler(); // 调用FreeRTOS的SysTick Handler} }
确保在FreeRTOSConfig.h
中启用了INCLUDE_xTaskGetSchedulerState
宏。
5. 修改HAL库的时基源(强烈推荐)
STM32的HAL库默认使用SysTick作为其时基源(用于HAL_Delay()
, HAL_GetTick()
等)。而FreeRTOS也使用SysTick作为其任务调度的时钟节拍。虽然通过一些技巧可以让两者共享SysTick,但更推荐的做法是将HAL库的时基源切换到另一个硬件定时器(如TIM1, TIM6等),以避免潜在冲突9。
-
你可以在STM32CubeMX中重新配置:在
SYS
选项下,将Timebase Source
从SysTick
改为其他的硬件定时器(如TIM1
)。 -
或者直接修改代码:在
main.c
的HAL_Init()
调用之后,重新初始化一个定时器作为HAL库的时基源。
🧪 编写测试代码
完成以上步骤后,就可以编写简单的FreeRTOS任务来测试移植是否成功了。
-
包含头文件:在
main.c
中包含FreeRTOS头文件。#include "FreeRTOS.h" #include "task.h" #include "queue.h" // 如果需要使用队列等功能
-
创建任务函数:定义至少一个简单的任务函数。
void vTaskLED(void *pvParameters) {for (;;) {HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); // 假设LED连接在PC13vTaskDelay(500); // 延迟500个时钟节拍,即500ms (假设configTICK_RATE_HZ=1000)} }
-
创建任务并启动调度器:在
main()
函数的初始化代码之后(while (1)
之前)创建任务并启动FreeRTOS调度器。int main(void) {HAL_Init();SystemClock_Config();// ... 其他外设初始化代码// 创建任务xTaskCreate(vTaskLED, "LED_Task", 128, NULL, 2, NULL);// 启动FreeRTOS调度器,永远不会返回vTaskStartScheduler();for (;;) {} // 调度器启动后,不会执行到这里 }
🔬 编译、下载与调试
-
编译工程:解决所有编译错误。常见的错误包括头文件路径不正确、函数未定义(可能是移植层文件没添加或路径错误)、重复定义(中断服务函数没注释掉)等。
-
下载到开发板并运行。
-
观察现象:如果一切正常,LED应该会以你设置的周期闪烁。
-
使用调试器:如果程序运行不正常,使用调试器进行单步调试,检查系统是否能成功创建任务、是否成功启动调度器、是否进入正确的硬件中断等。
⚠️ 常见问题排查
-
编译错误
undefined reference to ...
: 检查FreeRTOS的.c
文件是否都已添加到工程组中,头文件路径是否设置正确。 -
编译错误
redefinition of ...
: 检查stm32fxxx_it.c
中的SVC、PendSV、SysTick中断处理函数是否已注释掉。 -
程序在启动调度器后卡死或进入HardFault:
-
检查
FreeRTOSConfig.h
中的configCPU_CLOCK_HZ
是否设置正确。 -
检查
FreeRTOSConfig.h
中的中断优先级配置(configKERNEL_INTERRUPT_PRIORITY
和configMAX_SYSCALL_INTERRUPT_PRIORITY
)是否正确。这是非常常见的错误来源。 -
检查堆大小
configTOTAL_HEAP_SIZE
是否足够创建初始任务。 -
使用调试器检查是否成功进入
SVC_Handler
(用于启动第一个任务)。
-
-
SysTick中断冲突: 确保HAL库的时基源已切换至非SysTick的定时器,或者按照前述方法修改了
SysTick_Handler
函数。