FreeRTOS源码概述
参考《FreeRTOS入门与工程实践(基于DshanMCU-103)》里《第7章 FreeRTOS源码概述》
相关文章:http://t.csdnimg.cn/QK0aO
1 FreeRTOS目录结构
使用 STM32CubeMX 创建的 FreeRTOS 工程中, FreeRTOS 相关的源码如下:
主要设计两个目录
Core
◼ Inc 目录下的 FreeRTOSConfig.h 是配置文件
◼ Src 目录下的 freertos.c 是 STM32CubeMX 创建的默认任务
Middlewares\Third_Party\FreeRTOS\Source
◼ 根目录下是核心文件,这些文件是通用的
◼ portable 目录下是移植时需要实现的文件
◆ 目录名为: [compiler]/[architecture]
◆ 比如: RVDS/ARM_CM3,这表示 cortexM3 架构在 RVDS 工具上的移植文件
2 核心文件
FreeRTOS的最核心文件只有2个:
- FreeRTOS/Source/tasks.c
- FreeRTOS/Source/list.c
其他文件的作用也一起列表如下:
FreeRTOS/Source/下的文件 | 作用 |
---|---|
tasks.c | 必需,任务操作 |
list.c | 必须,列表 |
queue.c | 基本必需,提供队列操作、信号量(semaphore)操作 |
timer.c | 可选, software timer |
event_groups.c | 可选,提供 event group 功能 |
croutine.c | 可选,过时了 |
3 移植时涉及的文件
移植FreeRTOS时涉及的文件放在FreeRTOS/Source/portable/[compiler]/[architecture]目录下,
比如: RVDS/ARM_CM3,这表示cortexM3架构在RVDS或Keil工具上的移植文件。
里面有2个文件:
- port.c
- portmacro.h
4 头文件
4.1 头文件目录
FreeRTOS需要3个头文件目录:
- FreeRTOS 本身的头文件:
Middlewares\Third_Party\FreeRTOS\Source\include - 移植时用到的头文件:
Middlewares\Third_Party\FreeRTOS\Source\portable[compiler][architecture] - 含有配置文件 FreeRTOSConfig.h 的目录: Core\Inc
4.2 头文件
头文件 | 作用 |
---|---|
FreeRTOSConfig.h | FreeRTOS 的配置文件,比如选择调度算法:configUSE_PREEMPTION 每个 demo 都必定含有 FreeRTOSConfig.h 建议去修改 demo 中的 FreeRTOSConfig.h,而不是从头写一个 |
FreeRTOS.h | 使用 FreeRTOS API 函数时,必须包含此文件。在 FreeRTOS.h 之后,再去包含其他头文件,比如:task.h、 queue.h、 semphr.h、 event_group.h |
5 内存管理
文件在 Middlewares\Third_Party\FreeRTOS\Source\portable\MemMang 下,它也是放在“portable”目录下,表示你可以提供自己的函数。
源码中默认提供了5个文件,对应内存管理的5种方法。
文件 | 优点 | 缺点 |
---|---|---|
heap_1.c | 分配简单,时间确定 | 只分配、不回收 |
heap_2.c | 动态分配、最佳匹配 | 碎片、时间不定 |
heap_3.c | 调用标准库函数 | 速度慢、时间不定 |
heap_4.c | 相邻空闲内存可合并 | 可解决碎片问题、时间不定 |
heap_5.c | 在 heap_4 基础上支持分隔的内存块 | 可解决碎片问题、时间 |
heap_1 只分配 不回收,一点都不浪费空间
heap_2 既分配又释放
假设buf1是申请了100个字节,有头部,buf2是申请了50个字节,也有头部~
假设释放buf1和buf2,这两块内存是紧密相连的,但是它不能合并到一起,buf1仍然最大能分配100字节,buf2仍然是最大能分配50字节,假设buf3把最后面的内存都用完了,现在再想分配120字节的空间,用heap2这种方法就没有办法分配了!原因&缺点:它没有把空闲的紧密相连的空间合并在一起,所以有严重的碎片问题。
heap_3一般不用,调用标准库
heap_4 既分配又释放, heap_4在heap_2的基础上做了一些改进,heap_4合并相邻的空闲内存
针对heap_2的缺点,如果我们再想分配120字节的空间,那就是可以的了,因为buf1的100字节和buf2的50字节合并起来了!
heap_5 用来支持分隔的内存
两块红色的就是分隔的内存,可以用heap_5来管理
空闲链表头,指向第一个堆,再指向第二个堆,我们需要告诉链表头,有多少个离散的堆
一般的都是用heap_4
6 入口函数
在 Core\Src\main.c 的 main 函数里,初始化了 FreeRTOS 环境、创建了任务,然后启动
调度器。源码如下:
/* Init scheduler */
osKernelInitialize(); /* 初始化FreeRTOS运行环境 */
MX_FREERTOS_Init(); /* 创建任务 */
/* Start scheduler */
osKernelStart(); /* 启动调度器 */
7 数据类型和编程规范
FreeRTOS有一套自己的编程规范
7.1 数据类型
每个移植的版本都含有自己的portmacro.h头文件,里面定义了2个数据类型:
TickType_t:
◼ FreeRTOS 配置了一个周期性的时钟中断: Tick Interrupt
◼ 每发生一次中断,中断次数累加,这被称为 tick count
◼ tick count 这个变量的类型就是 TickType_t
◼ TickType_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 位架构中,它就是 uint32_t
◼ 16 位架构中,它就是 uint16_t
◼ 8 位架构中,它就是 uint8_t
◼ BaseType_t 通常用作简单的返回值的类型,还有逻辑值,比如pdTRUE/pdFALSE
7.2 变量名
变量名有前缀:
变量名前缀 | 含义 |
---|---|
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 指针 |
7.3 函数名
函数名的前缀有2部分:返回值类型、在哪个文件定义。
函数名前缀 | 含义 |
---|---|
vTaskPrioritySet | 返回值类型: void,在 task.c 中定义 |
xQueueReceive | 返回值类型: BaseType_t, 在 queue.c 中定义 |
pvTimerGetTimerID | 返回值类型: pointer to void, 在 tmer.c 中定义 |
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 | 0 |