目录
FreeRTOS源码结构介绍
获取源码
1、 官网下载
2、 Github下载
Github地址:https://github.com/FreeRTOS/FreeRTOS/releases
源码结构介绍
源码整体结构
名称 | 描述 |
FreeRTOS | FreeRTOS内核 |
FreeRTOS-Plus | FreeRTOS组件,一般我们会选择使用第三方的组件 |
tools | 工具 |
GitHub-FreeRTOS-Home | FreeRTOS的GitHub仓库链接 |
Quick_Start_Guide | 快速入门指南官方文档链接 |
Upgrading-to-FreeRTOS-xxx | 升级到指定FreeRTOS版本官方文档链接 |
History.txt | FreeRTOS历史更新记录 |
其他 | 其他 |
FreeRTOS文件夹结构
名称 | 描述 |
Demo | FreeRTOS演示例程,支持多种芯片架构、多种型号芯片 |
License | FreeRTOS相关许可 |
Source | FreeRTOS源码,最重要的文件夹 |
Test | 公用以及移植层测试代码 |
Source文件夹结构如下
名称 | 描述 |
include | 内包含了FreeRTOS的头文件 |
portable | 包含FreeRTOS移植文件:与编译器相关、keil编译环境 |
croutine.c | 协程相关文件:进程下细分线程,线程下细分协程 |
event_groups.c | 事件相关文件 |
list.c | 列表相关文件 |
queue.c | 队列相关文件 |
stream_buffer.c | 流式缓冲区相关文件 |
tasks.c | 任务相关文件 |
timers.c | 软件定时器相关文件 |
include文件夹和.c文件是通用的头文件和 C 文件,这两部分的文件适用于各种编译器和处理器,是通用的。标红的是移植必需的,其他.c文件根据需要选取。
portable文件夹里根据编译器、内核等实际环境对应选取。
portable文件夹结构
FreeRTOS操作系统归根到底是一个软件层面的东西,需要跟硬件联系在一起,portable文件夹里面的东西就是连接桥梁。由于我们使用MDK开发,因此这里只重点介绍其中的部分移植文件。
名称 | 描述 |
Keil | 指向RVDS文件夹 |
RVDS | 不同内核芯片的移植文件 |
MemMang | 内存管理相关文件 |
Keil文件夹里只有一个See-also-the-RVDS-directory.txt,意思是让我们看RVDS文件夹。
RVDS文件夹
RVDS 文件夹包含了各种处理器相关的文件夹,FreeRTOS 是一个软件,单片机是一个硬件,FreeRTOS 要想运行在一个单片机上面,它们就必须关联在一起。
关联还是得通过写代码来关联,这部分关联的文件叫接口文件,通常由汇编和 C 联合编写。这些接口文件都是跟硬件密切相关的,不同的硬件接口文件是不一样的,但都大同小异。编写这些接口文件的过程我们就叫移植,移植的过程通常由 FreeRTOS 和 mcu 原厂的人来负责,移植好的这些接口文件就放在 RVDS 这个文件夹的目录下。
FreeRTOS 为我们提供了 cortex-m0、m3、m4 和 m7 等内核的单片机的接口文件,根据mcu的内核选择对应的接口文件即可。其实准确来说,不能够叫移植,应该叫使用官方的移植, 因为这些跟硬件相关的接口文件,RTOS 官方都已经写好了,我们只是使用而已。
以 ARM_CM3 这个文件夹为例,里面只有“port.c”与“portmacro.h” 两个文件,
- port.c文件:里面的内容是由 FreeRTOS 官方的技术人员为 Cortex-M3 内核的处理器写的接口文件,里面核心的上下文切换代码是由汇编语言编写而成,对技术员的要求比较高,我们只是使用的话只需拷贝过来用即可。
- portmacro.h文件:port.c文件对应的头文件,主要是一些数据类型和宏定义。
MemMang文件夹
MemMang 文件夹下存放的是跟内存管理相关的,总共有五个 heap 文件以及一个 readme 说明文件。
这五个 heap 文件在移植的时候必须使用一个,因为 FreeRTOS 在创建内核对象的时候使用的是动态分配内存,而这些动态内存分配的函数则在这几个文件里面实现,不同的分配算法会导致不同的效率与结果,后面在内存管理中我们会讲解每个文件的区别,由于现在是初学,所以我们选用 heap4.c 即可。
FreeRTOS在基于寄存器项目中移植步骤
目录添加源码文件
在例程的根路径下,新建“FreeRTOS”文件夹,并且在里面新建“portable”和“source”两个空文件夹。
拷贝FreeRTOS源码的Source文件夹的7个.c文件到例程的source文件夹。
拷贝FreeRTOS源码portable文件夹下的Keil、RVDS、MemMang到例程的portable文件夹下。
其中例程的MemMang可只保留heap_4.c:
其中例程的RVDS可只保留ARM_CM3(对应我们的芯片内核)。
拷贝FreeRTOS源码include文件夹到例程的FreeRTOS文件夹下。
FreeRTOSConfig.h 文件是 FreeRTOS 的工程配置文件,因为 FreeRTOS 是可以裁剪的 实时操作内核,应用于不同的处理器平台,用户可以通过修改这个 FreeRTOS 内核的配置头文件来裁剪 FreeRTOS 的功能,所以我们把它拷贝一份放在 user 这个文件夹下面。
工程添加源码文件
工程新建Group“FreeRTOS/Source”和“FreeRTOS/Portable”。
系统配置文件修改
FreeRTOSConfig.h中添加如下3个配置:
#define xPortPendSVHandler PendSV_Handler
#define vPortSVCHandler SVC_Handler
#define INCLUDE_xTaskGetSchedulerState 1
第一行:中断服务程序的名称---可挂起的系统服务---可以等当前的中断服务程序结束然后执行
第二行:中断服务程序的名称---通过SWI指令的系统服务调用---可以使用硬件资源
第三行:开关选项(include...)获取调度器的状态
main.c中添加如下代码
FreeRTOS使用滴答定时器来实现的系统时基, 需要实现滴答定时器的中断,并在中断中添加下面的代码.
extern void xPortSysTickHandler(void);
void SysTick_Handler(void)
{
if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
{
xPortSysTickHandler();
}
}
FreeRTOS在基于HAL库项目中移植步骤
目录添加源码文件
在例程的根路径下,新建“FreeRTOS”文件夹,并且在里面新建“portable”和“source”两个空文件夹。
拷贝FreeRTOS源码的Source文件夹的7个.c文件到例程的source文件夹。
拷贝FreeRTOS源码portable文件夹下的Keil、RVDS、MemMang到例程的portable文件夹下。
其中例程的MemMang可只保留heap_4.c:
其中例程的RVDS可只保留ARM_CM3(对应我们的芯片内核)。
拷贝FreeRTOS源码include文件夹到例程的FreeRTOS文件夹下。
FreeRTOSConfig.h 文件是 FreeRTOS 的工程配置文件,因为 FreeRTOS 是可以裁剪的 实时操作内核,应用于不同的处理器平台,用户可以通过修改这个 FreeRTOS 内核的配置 头文件来裁剪 FreeRTOS 的功能,所以我们把它拷贝一份放在 user 这个文件夹下面。
在源码“..\FreeRTOS\Demo”文件夹下面找到 “ CORTEX_STM32F103_Keil ” 这个文件夹下,找到 “FreeRTOSConfig.h”文件,然后拷贝到我们工程下的 “Core/Inc” 文件夹下即可,等下我们需要对这个文件进行修改。
工程添加源码文件
工程新建Group“FreeRTOS/Source”和“FreeRTOS/Portable”。
FreeRTOS/Source添加.c文件。
FreeRTOS/Portable添加port.c和heap_4.c文件。
添加配置头文件。
添加头文件。
FreeRTOS 的源码已经添加到开发环境的组文件夹下面,编译的时候需要为这些源文件指定头文件的路径,不然编译会报错。FreeRTOS 的源码里面只有 include 和RVDS\ARM_CM3这两个文件夹下面有头文件,只需要将这两个头文件的路径在开发环境里面指定即可。
同时我们还将 FreeRTOSConfig.h 这个头文件拷贝到了工程根目录下的 Core/Inc 文件夹下,这个路径本身就在开发环境里面。(放其他路径也可以, 就是一个.h文件)
系统配置文件修改
FreeRTOSConfig.h中添加如下3个配置:
#define xPortPendSVHandler PendSV_Handler
#define vPortSVCHandler SVC_Handler
#define INCLUDE_xTaskGetSchedulerState 1
修改stm32f1xx_it.c(hal库自动生成的文件)
引入头文件
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "FreeRTOS.h"
#include "task.h"
/* USER CODE END Includes */
注释掉2个函数
// void SVC_Handler(void)
// {
// }
// void PendSV_Handler(void)
// {
// }
添加SysTick时钟中断服务函数
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
extern void xPortSysTickHandler(void);
/* USER CODE END PV */
void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */
/* USER CODE END SysTick_IRQn 0 */
HAL_IncTick();
/* USER CODE BEGIN SysTick_IRQn 1 */
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
{
xPortSysTickHandler();
}
/* USER CODE END SysTick_IRQn 1 */
}
注意:HAL本身和FreeRTOS都需要依赖SysTick,可能出现。
SysTick以最低的中断优先级运行,而FreeRTOS的临界区功能需要屏蔽中断,可能导致不可预知的问题。为了保险起见,可以考虑在SYS选择时钟源的时候换成其他的。
系统配置文件说明
FreeRTOSConfig.h 配置文件作用:对FreeRTOS的功能进行配置和裁剪,以及API函数的使能等。
官网中文说明:https://www.freertos.org/zh-cn-cmn-s/a00110.html
整体的配置项可以分为三类:
- INCLUDE开头:一般是“INCLUDE_函数名”,函数的使能,1表示可用,0表示禁用。
- config开头:FreeRTOS的一些功能配置,比如基本配置、内存配置、钩子配置、中断配置等。
- 其他配置:PendSV宏定义、SVC宏定义。
FreeRTOS数据类型
针对每个移植定义四种类型。即:
TickType_t
如果 configUSE_16_BIT_TICKS 设置为非零 (true) ,则将 TickType_t 定义为无符号的 16 位类型。如果 configUSE_16_BIT_TICKS 设置为零(假),则将 TickType_t 定义为无符号的 32 位类型。
这个类型的变量, 通常用来表示系统节拍计数器的值。FreeRTOS系统中,每隔一段时间会进行一次滴答定时器中断处理,这个时间间隔就是系统的节拍周期。TickType_t类型的变量记录了系统过去的节拍次数。
BaseType_t
架构中最有效、最自然的类型。例如,在 32 位架构上,BaseType_t 会被定义为 32 位类型。在 16 位架构上,BaseType_t 会被定义为 16 位类型。是有符号的。
UBaseType_t
是无符号的BaseType_t
StackType_t
意指架构用于存储堆栈项目的 类型。通常是 16 位架构上的 16 位类型和 32 位架构上的 32 位类型,但也有例外情况。供 FreeRTOS 内部使用。
FreeRTOS的命名规范
了解FreeRTOS的编码规范,有助于我们理解和学习FreeRTOS的使用.
变量
- 变量名称使用驼峰式大小写,具有明确的描述性,并使用完整的单词(没有缩写,但普遍接受的缩写除外)。
- uint32_t 类型变量以 ul 为前缀,其中“u”表示“unsigned” ,“l”表示“long”。
- uint16_t 类型变量以 us 为前缀,其中“u”表示“unsigned” , “s”表示“short”。
- uint8_t 类型变量以 uc 为前缀,其中“u”表示“unsigned” , “c”表示“char ”。
- 非 stdint 类型的变量以 x 为前缀。例如,BaseType_t 和 TickType_t,二者分别是可移植层定义的定义类型,主要架构的自然类型或最有效类型,以及用于保存 RTOS ticks 计数的类型。
- 非 stdint 类型的未签名变量存在附加前缀 u。例如,UBaseType_t(无符号的BaseType_t)类型变量以 ux 为前缀。
- size_t 类型变量也带有 x 前缀。
- 枚举变量以 e 为前缀
- 指针以附加 p 为前缀,例如,指向 uint16_t 的指针将以 pus 为前缀。
- 根据 MISRA 指南,无符号 char 类型仅可包含 ASCII 字符,并以 c 为前缀。
- 根据 MISRA 指南,char * 类型变量仅可包含指向 ASCII 字符串的指针,并以 pc 为前缀。
函数
- 函数名称使用驼峰式大小写,具有明确的描述性,并使用完整的单词(无缩写,但普遍接受的缩写除外)。
- 文件作用域静态(私有)函数以 prv 为前缀。
- 根据变量定义的相关规定,API 函数以其返回类型为前缀,并为 void 添加前缀 v。
- API 函数名称以定义 API 函数文件的名称开头。
比如一个函数 vTaskDelay , 从函数名可以得到如下信息:v表示这个函数的返回值是void, Task表示这个函数定义在Task.c文件中, Delay表示函数的功能
宏
- 宏具有明确的描述性,并使用完整的单词(无缩写,但普遍接受的缩写除外)。
- 宏以定义宏的文件为前缀。前缀为小写。例如,在 FreeRTOSConfig.h 中定义 configUSE_PREEMPTION。
- 除前缀外,所有宏均使用大写字母书写,并使用下划线来分隔单词。