第3章 FreeRTOS移植与配置:从源码到STM32L4的实战落地
好,咱们进入正题。这一章我打算聊聊FreeRTOS的移植和配置。说实话,很多初学者觉得移植是个很神秘的事,其实说白了,就是把FreeRTOS的源码放到你的工程里,然后告诉它“嘿,你现在跑在STM32L4上了”。就这么简单。
但我得提醒你,配置这一步才是真正的坑。我见过太多人,移植完了跑不起来,或者跑起来功耗奇高,最后发现都是FreeRTOSConfig.h里某个宏没配对。嗯,咱们今天就把这些坑一个一个填上。
3.1 FreeRTOS源码结构:别被文件夹吓到
我第一次打开FreeRTOS源码包时,说实话有点懵。文件夹套文件夹,看着挺唬人。但你仔细看,其实就三块核心内容:
- Source:内核本体,所有RTOS的核心代码都在这里
- Portable:移植层,针对不同MCU的适配代码
- Demo:示例工程,参考用的
咱们真正要关心的,只有Source和Portable。Demo可以删掉,省地方。
核心文件清单(你只需要这几个)
Source/tasks.c— 任务管理,最核心的文件Source/timers.c— 软件定时器Source/queue.c— 队列和信号量Source/list.c— 内核链表操作Source/portable/MemMang/heap_4.c— 内存管理,我推荐用heap_4Source/portable/GCC/ARM_CM4F/— STM32L4的移植层文件
为什么推荐heap_4?因为它支持碎片合并,而且不会像heap_2那样容易把内存搞碎。我在一个需要长期运行的传感器节点上吃过heap_2的亏,跑了三天,任务创建不出来了。换成heap_4,稳如老狗。
3.2 在STM32L4上移植FreeRTOS:三步走
移植其实就三步,别想复杂了:
- 把源码加进工程 — Source目录全部加,Portable只加ARM_CM4F和MemMang
- 配置FreeRTOSConfig.h — 这是重头戏,下面细讲
- 改中断处理函数 — 把SVC_Handler、PendSV_Handler、SysTick_Handler换成FreeRTOS的版本
第三步很多人会忘。你想想看,STM32的启动文件里默认有这三个中断处理函数,但FreeRTOS需要接管它们。不改的话,任务调度根本跑不起来。
我的习惯做法:在stm32l4xx_it.c里把这三个函数注释掉,然后在FreeRTOS的port.c里它们已经定义好了。别重复定义,否则链接会报错。
3.3 FreeRTOSConfig.h关键配置详解
这个文件是FreeRTOS的灵魂。我每次做新项目,第一件事就是打开这个文件,逐行检查。下面这几个宏,你一个都不能漏:
| 宏定义 | 推荐值 | 说明 |
|---|---|---|
configUSE_PREEMPTION |
1 | 抢占式调度,低功耗场景必须开 |
configCPU_CLOCK_HZ |
系统时钟频率 | STM32L4一般是4000000或8000000,看你的时钟配置 |
configTICK_RATE_HZ |
1000 | 1ms一个tick,低功耗场景建议别低于100 |
configMAX_PRIORITIES |
5 | 够用就行,别设太大,浪费RAM |
configMINIMAL_STACK_SIZE |
128 | 单位是字,不是字节。128字=512字节 |
configTOTAL_HEAP_SIZE |
根据RAM定 | STM32L4一般给4096~8192就够 |
configUSE_TICKLESS_IDLE |
1 | 低功耗核心配置,后面章节会细讲 |
注意:configCPU_CLOCK_HZ 必须和你实际配置的系统时钟一致。我曾经在一个项目里配错了,系统时钟80MHz,这里写成了8MHz,结果所有延时都慢了10倍。排查了整整一天才找到原因。
3.4 中断优先级配置:最容易翻车的地方
FreeRTOS对中断优先级有严格的要求。STM32L4用的是Cortex-M4内核,支持4位优先级(0~15)。但FreeRTOS需要你设置一个最高优先级,内核自己用的。
关键宏有两个:
configLIBRARY_LOWEST_INTERRUPT_PRIORITY— 最低优先级,一般设15configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY— 可调用FreeRTOS API的最高中断优先级
什么意思呢?就是说,比这个优先级高的中断,不能调用任何FreeRTOS的API函数。比如队列发送、信号量释放这些,都不行。否则系统会崩。
推荐配置:
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
这意味着优先级0~4的中断不能调用FreeRTOS API,优先级5~15的中断可以。
我个人习惯把时间关键的中断(比如电机控制、通信协议)放在优先级0~4,它们不依赖RTOS。而普通外设中断(比如按键、UART接收)放在5~15,可以放心用FreeRTOS的同步机制。
3.5 知识结构总览
下面这张图,把本章的核心逻辑串起来了。你可以把它当成移植检查清单:
3.6 避坑指南:我踩过的几个雷
最后,分享几个实战中容易踩的坑。这些都是真金白银换来的教训:
- SysTick冲突:如果你用了HAL库,HAL_Delay依赖SysTick。但FreeRTOS也用了SysTick。解决办法是让FreeRTOS用别的定时器,或者把HAL_Delay重写。我一般让FreeRTOS用TIM2做tick,SysTick留给HAL。
- 堆栈溢出:configMINIMAL_STACK_SIZE设太小,任务一跑就崩。调试时打开
configCHECK_FOR_STACK_OVERFLOW,能帮你定位。 - 中断优先级分组:STM32L4默认是4位抢占优先级,0位子优先级。FreeRTOS要求所有优先级都用抢占位,别改分组。改了的话,优先级配置全乱套。
一个小技巧:移植完成后,先创建一个空任务,里面只放一个延时。如果能跑起来,说明移植基本没问题。然后再逐步加功能。别一上来就搞复杂逻辑,否则出了问题你都不知道是移植的锅还是代码的锅。