本文最后更新于 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 | 单元测试或移植验证代码(一般不使用) |

我们主要使用的是 DemoSource 文件夹,其他可暂时忽略。

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 工程中创建文件夹

  1. 在工程根目录下,新建一个 FreeRTOS 文件夹

  1. FreeRTOS 中创建Sourceportableinclude 三个文件夹。

  2. Source(存放内核源文件)

  3. portable(移植层)

  4. 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宏定义。

我将配置划分为以下几类:

  1. 📌 基础功能配置

  2. ⏱️ 系统节拍与内存配置

  3. 🧠 调试与诊断(钩子函数、断言)

  4. 🧵 同步机制配置(互斥量、信号量)

  5. ⏰ 软件定时器配置

  6. 🧩 可选 API 接口控制

  7. ⚙️ Cortex-M 内核相关配置

  8. 🔁 中断优先级映射

  9. 🪛 中断函数映射到 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_命名要求