1.1 FreeRTOS目录结构
以Keil工具下的STM32F103芯片为例,它的FreeRTOS目录如下:
主要涉及2个目录:
- Demo
- Demo目录下是工程文件,以“芯片和编译器组合成一个名字”
- 比如:CORTEX_STM32F103_Keil
- Source
- 根目录下是核心文件,这些文件就是FreeRTOS的源码,这些文件是通用的
- portable目录是移植时需要实现的文件,翻译过来就是硬件接口层,FreeRTOS和芯片之间存在这个硬件接口层。
- 目录名为:[complier]/[architecture]
- 比如:RVDS/ARM_CM3,这表示cortexM3架构在RVDS工具上的移植文件
1.2 核心文件
FreeRTOS的最核心的文件严格来说只有2个:
- FreeRTOS/Source/task.c
- FreeRTOS/Source/list.c
广义上的 queue.c一般也是经常用到的,但不是必须要的,有的地方也会把queue.c这个文件也认为是必须的。
其他文件的作用也一起列表如下:
FreeRTOS/Source下的文件 | 作用 |
tasks.c | 必需,任务操作 |
list.c | 必需,列表操作 |
queue.c | 基本必需,提供队列操作,信号量(semaphore)操作 |
timer.c | 可选,software timer |
event_groups.c | 可选,提供event group功能 |
croutine.c | 可选,协程,FreeRTOS已经不更新了,过时了 |
1.3 移植时涉及的文件
移植FreeRTOS时涉及的文件放在FreeRTOS/Source/portable/[complier]/[architecture]目录下,
比如:RVDS/ARM_CM3,这表示cortexM3架构在RVDS或Keil工具上的移植文件
里面有2个文件:
- port.c
- portmacro.h
1.4 头文件相关
1.4.1 头文件目录
FreeRTOS需要三个头文件目录:
- FreeRTOS本身的头文件:FreeRTOS/Source/include
- 移植时用到的头文件,FreeRTOS/Source/portable/[complier]/[architecture]
- 含有配置文件的FreeRTOSConfig.h的目录
1.4.2 头文件
头文件 | 作用 |
FreeRTO\quSConfig.h | FreeRTOS的配置文件,比如选择调度算法:configUSE_PREEMPTION每个demo都必定含有FreeRTOSConfig.h建议去修改demo中的FreeRTOSConfig.h,而不是从头写一个,其实这个文件就是提供给用户自己裁剪FreeRTOS系统,根据需要定义 |
FreeRTOS.h | 使用FreeRTOS API函数时,必须包含此文件,在FreeRTOS.h之后,再去包含其他头文件,比如:task.h、queue.h、semphr.h、event_group.h |
1.5 内存管理
文件在FreeRTOS/Source/portable/MemMang下,它也是放在portable目录下,表示你可以提供自己的函数。
源码中默认提供了5个文件,对应内存管理的5种方法。
文件 | 优点 | 缺点 |
heap_1.c | 分配简答, 时间确定 | 只分配不回收 |
heap_2.c | 动态分配,最佳匹配 | 碎片,时间不定 |
heap_3.c | 调用标准库函数 | 速度慢,时间不定 |
heap_4.c | 相邻空闲内存可合并 | 可解决碎片问题,时间不定 |
heap_5.c | 在heap_4基础上支持分隔的内存块 | 可解决碎片问题,时间不定 |
1.6 Demo
Demo目录下是预先配置好的,没有编译错误的工程,目的是让你可以基于它进行修改,以适配你的单板。
这些Demo还可以继续精简,以使得FreeRTOS显得干净清爽。
1.7 数据类型和编程规范
每个移植的版本都含有自己的portmacro.h头文件,里面定义了2个数据类型:
- TickType_t:
- FreeRTOS配置了一个周期性的时钟中断:Tick Interrupt
- 每发生一次中断,中断次数累加,这被称为tick count
- tick count 这个变量的类型就是Tick Type_t
- Tick Type_t可以是16位的,也可以是32位的
- FreeRTOSConfig.h中定义configUSE_16_BIT_TICKS时,TickType_t就是uint16_t
- 否则TickType_t就是uint32_t
- 对于32位架构,建议把TickType_t配置为uint32_t
- BaseType_t:
- 这是该架构最高效的数据类型
- 32位架构中,它就是uin32_t
- 16位架构中,它就是uint16_t
- 8位架构中,它就是uint8_t
- BaseType_t通常用作简单的返回值的类型,还有逻辑值,比如pdTRUE/pdFALSE
1.7.2 变量名
变量名有前缀
变量名前缀c | 含义 |
c | char |
s | int16_t .short |
l | int32_t,long |
x | BaseType_t,其他非标准的类型:结构体,task handle、queue handle等 |
u | unsigned |
p | 指针 |
uc | uint8_t , unsigned char |
pc | char指针 |
1.7.3 函数名
函数名的前缀有2部分:返回值类型,在哪个文件定义。
函数名前缀 | 含义 |
vTaskPrioritySet | 返回值类型:void 在task.c中定义 |
xQueueReceive | 返回值类型:BaseType_t 在queue.c中定义 |
pvTimerGetTimerID | 返回值类型:pointer to void 在timer.c中定义 |
1.7.4 宏的名
宏的名字是大写,可以添加小写的前缀。前缀用来表示:宏在哪个文件中定义。
宏的前缀 | 含义:在那个文件中定义 |
port(比如portMAX_DELAY) | portable.h或portmacro.h |
task(比如taskENTER_CRITICAL()) | task.h |
pd(比如pdTRUE) | projdefs.h |
config(比如configUSE_PREEMPTION) | FreeRTOSConfig.h |
err(比如errQUEUE_FULL) | projdefs.h |
通用的宏定义如下:
宏 | 值 |
pdTRUE | 1 |
pdFALSE | 0 |
pdPASS | 1 |
pdFAIL | 1 |