嵌入式哞哞哥FreeRTOS系统移植视频教程
嵌入式哞哞哥带你吃透 FreeRTOS 移植:从原理到 STM32 实战
“下仔客”: itxt.top/17049/
在嵌入式开发圈,FreeRTOS 凭借轻量、可裁剪、实时性强的特性,成为单片机项目从 "裸机循环" 升级到 "多任务系统" 的首选。但对新手而言,"系统移植" 四个字总带着神秘色彩 ——"底层硬件千差万别,怎么让通用内核适配我的芯片?"" 汇编代码看不懂,是不是学不会移植?"
别急!擅长把复杂技术讲通俗的嵌入式哞哞哥,今天就用 "草原牛进钟表厂" 的比喻,带你从零吃透 FreeRTOS 移植。就像让习惯自由奔跑的牛适配精密工厂的作息,移植本质就是给 FreeRTOS 内核定制一套 "硬件适配手册",哪怕你刚懂 STM32 基础操作,跟着步骤走也能成功让内核在芯片上 "安家落户"。
一、哞哞哥的灵魂三问:先搞懂移植的核心逻辑
动手前先理清三个关键问题,避免盲目堆砌代码 —— 这可是哞哞哥强调的 "先懂原理再动手" 原则:
1. 什么是 FreeRTOS 移植?为啥需要移植?
FreeRTOS 内核本身是用标准 C 语言写的 "通用管理系统",就像一套通用的工厂管理制度,但不同芯片(STM32、ESP32、NXP)的 "硬件车间" 差异巨大:
- 有的芯片堆栈从高地址往低地址长(如 ARM Cortex-M 系列),有的正好相反;
- 中断控制、寄存器操作的指令各不相同;
- 编译器(Keil、IAR、GCC)对汇编代码的支持也有差异。
移植就是给这套通用制度加个 "硬件适配层",让 FreeRTOS 能看懂目标芯片的 "方言",知道怎么切换任务、怎么控制中断、怎么管理内存。简单说:移植 = 通用内核 + 硬件适配层,其中适配层才是移植的核心,占比不到 10% 的代码量。
2. 移植只需要改两个文件?真的假的?
真的!哞哞哥敲黑板:FreeRTOS 的代码结构极清晰,90% 以上的内核代码(tasks.c、queue.c 等)完全通用,移植只需要聚焦两个关键文件:
- portmacro.h:用宏定义适配硬件特性,比如堆栈增长方向、中断屏蔽指令;
- port.c:实现硬件相关的函数,核心是任务上下文切换、系统时钟初始化等。
这就像给牛定制装备:portmacro.h 是 "尺寸表"(告诉内核硬件的基本参数),port.c 是 "操作手册"(告诉内核怎么控制硬件)。其他文件直接复用,根本不用动!
3. 零基础学移植,需要哪些前置知识?
别被 "底层开发" 吓到,哞哞哥说只要掌握 3 样基础就能入门:
- 芯片基础:知道 STM32 的 NVIC(中断控制器)、SysTick(系统滴答定时器)基本概念;
- C 语言基础:能看懂函数指针、宏定义,了解条件编译;
- 工具使用:会用 Keil MDK 创建 STM32 工程,能烧录程序看 LED 灯闪烁。
至于汇编?完全不用怕!移植用的汇编代码就几行固定模板,背下来直接用,哞哥会把每句的意思讲明白。
二、移植前的准备:哞哞哥的 "工具清单"
就像干活前先备齐工具,移植前要准备 3 样东西,10 分钟就能搞定:
1. 核心 "原材料":FreeRTOS 源码
去 FreeRTOS 官网下载最新版源码,解压后重点看这几个文件夹:
- Source:内核核心代码,包含 tasks.c(任务管理)、queue.c(队列)等通用文件;
- Source/portable:移植模板文件夹,里面有不同编译器、芯片的适配示例(比如 GCC/ARM_CM4F 对应 Cortex-M4 内核);
- Source/portable/MemMang:内存管理文件,新手直接用 heap_4.c(支持动态内存分配和释放)。
2. 目标 "工作台":STM32 基础工程
先创建一个能让 LED 闪烁的 STM32 裸机工程(以 STM32F103C8T6 为例),确保:
- 时钟配置正确(比如 72MHz);
- 至少有一个 LED 引脚配置为推挽输出;
- 能正常编译、烧录、运行。
这就像先把钟表厂的车间打扫干净,确保基础设备能正常运转。
3. 适配 "参考图":芯片手册关键页
翻出 STM32F103 的参考手册,重点标记两个部分:
- NVIC 中断控制器:查看 SysTick 中断的优先级配置方法;
- SysTick 定时器:了解怎么配置定时器周期、开启中断。
不用通读手册,哞哥说这两处就像 "安装说明书" 的关键步骤,用到再看。
三、实战移植:跟着哞哞哥改 5 步,内核跑起来
以 "STM32F103C8T6 + Keil MDK" 为例,跟着哞哥的步骤一步步改,30 分钟完成移植:
第 1 步:把 FreeRTOS 源码 "搬进" 工程
- 在裸机工程里新建 "FreeRTOS" 文件夹,再建 3 个子文件夹:
-
- Source:复制源码 Source 文件夹下的所有.c 文件(除了 example 文件夹);
-
- Include:复制源码 Source/include 文件夹下的所有.h 文件;
-
- Portable:复制 Source/portable/Keil/ARM_CM3 文件夹(对应 Cortex-M3 内核)和 Source/portable/MemMang/heap_4.c。
- 在 Keil 中添加文件到工程,分 3 个组:
-
- FreeRTOS_Core:添加 Source 文件夹下的 tasks.c、queue.c、list.c;
-
- FreeRTOS_Portable:添加 Portable 文件夹下的 port.c 和 heap_4.c;
-
- FreeRTOS_Include:把 Include 和 ARM_CM3 文件夹添加到工程包含路径。
第 2 步:改配置文件:FreeRTOSConfig.h
这是移植的 "总开关",新建一个 FreeRTOSConfig.h 文件放到工程里,关键配置哞哥已经帮你写好,新手只需改这几处:
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
// 1. 时钟配置:STM32主频72MHz,所以这里填72000000
#define configCPU_CLOCK_HZ (72000000UL)
// 2. 系统滴答定时器频率:1000Hz(每1ms触发一次中断)
#define configTICK_RATE_HZ (1000UL)
// 3. 任务栈大小:单位是字(4字节),这里设128字=512字节
#define configMINIMAL_STACK_SIZE (128UL)
// 4. 最大任务数:最多创建4个任务
#define configMAX_TASK_NAME_LEN (16)
#define configMAX_PRIORITIES (4UL)
// 5. 启用动态内存分配(因为用了heap_4.c)
#define configSUPPORT_DYNAMIC_ALLOCATION (1)
// 6. 中断相关配置:适配Cortex-M3
#define configPRIO_BITS (4UL)
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0xf
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
#define configKERNEL_INTERRUPT_PRIORITY (configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS))
#define configMAX_SYSCALL_INTERRUPT_PRIORITY (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS))
// 启用必要的API函数
#define INCLUDE_vTaskStartScheduler 1
#define INCLUDE_vTaskDelay 1
#define INCLUDE_xTaskCreate 1
#endif /* FREERTOS_CONFIG_H */
第 3 步:改 portmacro.h:告诉内核硬件 "尺寸"
这个文件在 ARM_CM3 文件夹下,新手基本不用改,重点理解这两个宏:
// 堆栈增长方向:Cortex-M系列堆栈从高地址往低地址长,所以设为-1
#define portSTACK_GROWTH ( -1 )
// 任务切换中断号:用PendSV中断(专门用于任务切换,优先级最低)
#define portNVIC_PENDSV_IRQ_NUMBER 14
哞哥说:这就像告诉牛 "车间的门在左边,走路要靠右侧",是硬件的基本规矩。
第 4 步:改启动文件:让内核接管中断
打开 STM32 的启动文件(startup_stm32f10x_md.s),找到这两处修改:
- 注释掉原来的 SysTick 中断服务函数:
; SysTick_Handler:
; B SysTick_Handler
- 保留 PendSV 和 SysTick 的中断向量:这两个中断是 FreeRTOS 的 "核心指令",PendSV 负责任务切换,SysTick 负责系统时钟。
为啥要注释?因为 FreeRTOS 的 port.c 里已经实现了这两个中断的处理函数,再留着裸机的会冲突。
第 5 步:写测试代码:让 LED"多任务" 闪烁
移植对不对,跑个多任务测试就知道。在 main.c 里写两个任务:一个 LED1 每隔 500ms 闪烁,一个 LED2 每隔 1000ms 闪烁:
#include "stm32f10x.h"
#include "FreeRTOS.h"
#include "task.h"
// 初始化LED引脚
void LED_Init(void) {
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
}
// 任务1:LED1闪烁(PA0)
void Task1_Func(void *pvParameters) {
while(1) {
GPIO_SetBits(GPIOA, GPIO_Pin_0);
vTaskDelay(500); // 延时500ms(FreeRTOS的延时函数,会释放CPU)
GPIO_ResetBits(GPIOA, GPIO_Pin_0);
vTaskDelay(500);
}
}
// 任务2:LED2闪烁(PA1)
void Task2_Func(void *pvParameters) {
while(1) {
GPIO_SetBits(GPIOA, GPIO_Pin_1);
vTaskDelay(1000); // 延时1000ms
GPIO_ResetBits(GPIOA, GPIO_Pin_1);
vTaskDelay(1000);
}
}
int main(void) {
LED_Init();
// 创建两个任务
xTaskCreate(Task1_Func, "Task1", 128, NULL, 1, NULL);
xTaskCreate(Task2_Func, "Task2", 128, NULL, 1, NULL);
// 启动任务调度器(FreeRTOS开始工作)
vTaskStartScheduler();
// 只要调度器启动成功,下面的代码永远不会执行
while(1);
}
编译、烧录后,如果看到两个 LED 按不同频率闪烁,说明移植成功了!这时候 FreeRTOS 已经在后台自动切换两个任务,比裸机的 "循环延时" 优雅 100 倍。
四、哞哞哥的排坑指南:新手最容易踩的 3 个坑
移植时遇到问题别慌,哞哥总结了新手最常踩的 3 个坑,对照排查准能解决:
1. 编译报错 "找不到 FreeRTOSConfig.h"
原因:工程包含路径没加对。
解决:在 Keil 的 "Options for Target"→"C/C++"→"Include Paths" 里,把 FreeRTOS 的 Include 和 ARM_CM3 文件夹路径加上,记得用绝对路径或相对路径(如 "../FreeRTOS/Include")。
2. 程序跑飞,LED 不亮
原因 1:启动文件的 SysTick 中断没注释。FreeRTOS 和裸机的中断服务函数冲突了,注释掉裸机的即可。
原因 2:内存分配问题。heap_4.c 没加进工程,或者 configSUPPORT_DYNAMIC_ALLOCATION 没设为 1,导致任务创建失败。
3. 任务延时没效果,LED 常亮
原因:vTaskDelay () 函数没启用。在 FreeRTOSConfig.h 里把 INCLUDE_vTaskDelay 设为 1,确保调度器能正常切换任务。
哞哥提醒:遇到编译错误先看报错信息的 "文件" 和 "行号",90% 的问题都出在配置文件或路径上,别一上来就怀疑自己的移植能力。
五、进阶思考:移植的本质是 "硬件抽象"
成功移植后,哞哥建议多想一步:FreeRTOS 为啥能跨这么多芯片?核心是 "硬件抽象层(HAL)" 的设计思想 —— 把硬件相关的操作全部封装在 port.c 和 portmacro.h 里,内核完全不碰硬件细节。
就像钟表厂的管理制度(内核)不变,但给不同设备(芯片)配不同的操作手册(移植层),就能让制度在任何设备上运行。理解这一点,以后移植到 ESP32、NXP 芯片也能举一反三:无非是换一套 port 文件,改改配置里的时钟和中断参数。
六、哞哞哥的学习资源推荐
想深入学习?哞哥推荐 3 个免费资源,都是嵌入式圈的 "宝藏":
- 官方文档:FreeRTOS 官网的 "Porting Guide",最权威的移植原理说明,虽然是英文但词汇简单,配合翻译工具能看懂;
- 视频教程:B 站 "嵌入式哞哞哥" 的 FreeRTOS 系列,有手把手移植的实操演示,连 Keil 配置都讲得明明白白;
- 实战案例:GitHub 搜索 "FreeRTOS-STM32-Demo",找 STM32F103 的移植示例,对比自己的代码找差异。
七、结语:移植不是终点,是多任务开发的起点
跟着哞哥做完移植,你会发现:FreeRTOS 移植根本不是什么高深技术,无非是 "配配置、改文件、做测试" 的标准化流程。就像让草原牛适应了钟表厂的作息,接下来就能让它高效完成多任务工作 —— 比如同时处理传感器采集、数据传输、OLED 显示,这才是 RTOS 的真正价值。
嵌入式开发的魅力就在于此:从控制一个 LED,到管理一个复杂系统,每一次技术升级都能带来质的飞跃。现在你已经掌握了 FreeRTOS 移植的核心方法,接下来不妨试试添加队列、信号量,实现任务间的通信,开启你的多任务开发之旅吧!
有疑问加站长微信联系(非本文作者)
