任务函数的调用
- 硬件初始化
- 初始化与任务相关的列表
- 创建任务
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。