任务函数的调用
- 硬件初始化
- 初始化与任务相关的列表
- 创建任务
xTaskCreateStatic
- 添加任务到就绪列表
- 启动调度器
for
循环让任务死循环
实现具体的函数
for
循环;- 任务切换
- 任务切换和调度函数
任务的定义与任务切换的实现
实现任务创建函数
任务的栈,任务的函数实体,实现任务控制块的创建和联系。
静态创建,configSUPPORT_STATIC_ALLOCATION
在 FreeRTOSConfig.h
中定义,配置为 1
。(静态创建)
xTaskCreateStatic()
函数
1 | TaskHandle_t xTaskCreateStatic( |
静态创建任务,内嵌 prvInitialiseNewTask
函数。
TaskFunction_t
:任务函数本身(在projdefs.h
中重定义的空指针)。任务运行时执行此函数,任务函数必须符合void*
类型。- 运行传参:将函数指针传递给调度器,许多任务的具体实现不同,调度器运行时只需调用不同的任务函数,编译不需要硬编码不同的函数,否则调度器需要知道函数的具体实现,将函数的实现拷贝到调度器中。指针容易被赋值。
tskTaskControlBlock
:任务的控制块,任务的状态和各种与调度相关的信息,是 FreeRTOS 调度器用来管理任务的核心结构 (tcb_t
)。
TCB
并不直接暴露给用户,而是通过 TaskHandle_t
来间接访问
TaskHandle_t
:任务句柄,在任务创建时获取,提供给用户,方便任务的挂起、恢复、删除等操作 (tcb_t
)。TaskFunction_t
:void*
类型,任务函数类型。
prvInitialiseNewTask()
函数
用于初始化新任务的 TCB
和相关数据结构。
参数设置
- 任务入口
- 任务名称
- 任务栈大小
- 任务形参
- 任务句柄
- 任务控制块指针
获取栈顶地址
(传入的 任务控制块指针
(此函数参数)内部的 栈起始地址
加上 栈大小
(此函数参数)减 1
作为栈顶地址(被重定义过的整型变量))
- 向下做
8
字节对齐
将任务的名字存储在 TCB
中
- 字符串长度小于
configMAX_TASK_NAME_LEN
的情况下递增循环 - 储存在
xTaskCreateStatic()
函数中控制块索引指针中 - 若出现
0x00
(即以'/0'
结尾则退出) - 强行给最大长度的前一位置
0
初始化 TCB
中的 xStateListItem
(任务节点)
- 使用
vListInitialiseItem
函数初始化链表项xStateListItem
- 使用
listSET_LIST_ITEM_OWNER
配置xStateListItem
的拥有者项
调用函数初始化任务栈,并更新栈顶指针
- 调用
pxPortInitialiseStack()
函数初始化任务栈,并更新栈顶指针 - 任务句柄指向任务控制块
pxPortInitialiseStack()
函数
它的主要作用是为新创建的任务设置初始的栈状态(上文初始化栈状态时用到),以便任务能够正确地开始执行。确保栈的顶部包含正确的初始值。(存储的内容在异常发生时会自动加载到 CPU)
参数
1 | StackType_t *pxTopOfStack, // 栈顶指针 |
初始化栈
xPSR
的bit24
必须置1
(thumb 模式)- 任务的入口地址(这个函数在内存中的地址)
当任务被创建时,FreeRTOS 会将任务入口地址(即任务函数的地址)保存到任务栈中,以便在任务切换时能够恢复这个地址并开始执行任务。
- 任务的返回地址
它是在任务执行过程中保存的,用于在任务切换时恢复任务的执行状态。不过 FreeRTOS 会将
PC
设置为prvTaskExitError
,而不是返回到任务的调用位置。这是为了确保任务结束后不会继续执行不应执行的代码。
FreeRTOS 中的任务函数通常是一个无限循环。 R12
,R3
,R2
和R1
默认初始化为0
。