推荐FreeRTOS教程
链接一:FreeRTOS官网
链接二:百问网FreeRTOS教程
链接三:百问网移至文件
1. 核心源文件(Source 根目录)
这些文件是 FreeRTOS 内核的通用实现,与具体硬件无关,因此放在Source根目录下,所有移植版本共用。
| 文件 | 作用 | 为什么放在这里 |
|---|---|---|
tasks.c | 任务管理:创建、删除、延时、调度、优先级管理 | 内核最核心的功能,与硬件无关 |
list.c | 链表数据结构实现,内核内部使用(就绪链表、延时链表等) | 纯软件数据结构,与硬件无关 |
queue.c | 队列、信号量、互斥量的实现 | 内核同步机制,与硬件无关 |
timers.c | 软件定时器实现 | 依赖 tick,但本身逻辑与硬件无关 |
event_groups.c | 事件组实现 | 内核同步机制,与硬件无关 |
croutine.c | 协程(Co-routine)实现(可选,已较少使用) | 与硬件无关 |
stream_buffer.c | 流缓冲区(Stream Buffer)实现 | 与硬件无关 |
message_buffer.c | 消息缓冲区(Message Buffer)实现(基于流缓冲区) | 与硬件无关 |
为什么这些文件放在 Source 根目录?
因为它们只依赖 C 语言和 FreeRTOS 内部接口,不直接操作寄存器或特定硬件。放在根目录可以避免重复,所有工程共享同一份代码。
2. 移植相关文件(portable 目录)
portable目录下按[compiler]/[architecture]分子目录,例如RVDS/ARM_CM3。
| 文件 | 作用 | 为什么放在这个子目录 |
|---|---|---|
port.c | 实现硬件相关的底层操作:上下文切换、启动第一个任务、SysTick 中断处理、临界区进入/退出 | 这些操作依赖具体 CPU 架构和编译器,必须为每种组合单独实现 |
portmacro.h | 定义硬件相关的数据类型(如TickType_t、BaseType_t)、临界区宏、上下文切换相关的宏 | 同port.c,与架构和编译器强相关 |
为什么按
[compiler]/[architecture]分目录?
不同编译器(RVDS、IAR、GCC)的内联汇编、符号修饰、数据对齐等特性不同;不同架构(Cortex-M3、M4、RISC-V)的寄存器和指令集不同。分目录可以清晰隔离这些差异。
3. 内存管理文件(portable\MemMang)
| 文件 | 作用 | 为什么放在 portable 下 |
|---|---|---|
heap_1.c | 只分配不释放,适用于简单应用 | 内存分配策略依赖应用场景,用户可以根据需求选择或自定义 |
heap_2.c | 支持分配和释放,但不合并相邻空闲块 | 同上 |
heap_3.c | 包装标准malloc/free,线程安全 | 同上 |
heap_4.c | 支持分配、释放,并合并相邻空闲块(最常用) | 同上 |
heap_5.c | 同heap_4,但支持多个非连续内存区域 | 同上 |
为什么放在
portable目录?
内存管理方案与硬件内存布局、编译器特性、应用需求相关,因此被设计为可替换组件。portable目录的本意就是“用户可以提供自己的实现”。
4. 头文件目录
4.1Source\include
包含所有内核公开的头文件:
| 头文件 | 作用 |
|---|---|
FreeRTOS.h | 总包含文件,引入其他头文件 |
task.h | 任务管理 API 声明 |
queue.h | 队列、信号量、互斥量 API 声明 |
semphr.h | 信号量相关宏和函数声明 |
timers.h | 软件定时器 API 声明 |
event_groups.h | 事件组 API 声明 |
stream_buffer.h | 流缓冲区 API 声明 |
message_buffer.h | 消息缓冲区 API 声明 |
projdefs.h | 通用宏定义(如pdTRUE、pdFALSE) |
portable.h | 移植层接口声明 |
list.h | 链表结构声明(内核内部使用) |
stack_macros.h | 栈检查相关宏 |
4.2portable\[compiler]\[architecture]
包含portmacro.h,定义硬件相关数据类型和宏,是所有源文件需要包含的移植头文件。
4.3Core\Inc(或工程配置目录)
包含FreeRTOSConfig.h,这是用户配置文件,用于裁剪和配置内核功能(如是否启用软件定时器、tick 频率、堆大小等)。
为什么
FreeRTOSConfig.h放在工程目录而不是 Source 目录?
因为它是工程特定的配置,每个工程可以有不同的配置,不应修改内核源文件。放在工程目录可以独立于内核源码进行管理。
5. 入口函数与启动流程
入口函数通常位于main.c中,与 STM32CubeMX 生成的代码结合:
osKernelInitialize();// 初始化 FreeRTOS 内核(创建空闲任务、初始化链表等)MX_FREERTOS_Init();// 创建用户任务(由 CubeMX 生成,位于 freertos.c)osKernelStart();// 启动调度器,开始任务调度osKernelInitialize()和osKernelStart()是 CMSIS-RTOS 封装,底层调用 FreeRTOS 的vTaskStartScheduler()等函数。freertos.c中定义的任务创建代码最终会调用xTaskCreate()。
总结:目录划分的设计思想
FreeRTOS/Source ├── include # 内核公共头文件(API 声明、内部数据结构) ├── *.c # 内核核心实现(与硬件无关) ├── portable │ ├── [compiler]/[arch] # 移植层(上下文切换、硬件相关数据类型) │ └── MemMang # 可替换的内存管理实现 └── (工程目录) ├── FreeRTOSConfig.h # 工程配置 └── main.c / freertos.c # 入口和任务创建- 核心代码与移植代码分离:方便支持不同硬件平台。
- 配置与源码分离:
FreeRTOSConfig.h放在工程目录,避免修改内核源码。 - 可替换组件独立:内存管理、协程等可选功能放在
portable下,用户可按需选择。
如果你需要我根据具体的文件列表(比如你提到的image2、image3中的内容)逐一解释,请将图片中的文字或文件名贴出来,我可以继续补充。