任务函数的调用

  1. 硬件初始化
  2. 初始化与任务相关的列表
  3. 创建任务 xTaskCreateStatic
  4. 添加任务到就绪列表
  5. 启动调度器
  6. for 循环让任务死循环

实现具体的函数

任务的定义与任务切换的实现

实现任务创建函数

任务的栈,任务的函数实体,实现任务控制块的创建和联系。

静态创建,configSUPPORT_STATIC_ALLOCATIONFreeRTOSConfig.h 中定义,配置为 1。(静态创建)

xTaskCreateStatic() 函数

1
2
3
4
5
6
7
8
TaskHandle_t xTaskCreateStatic(
TaskFunction_t xx, // 任务入口
const char * const xx, // 任务名称,字符串形式
const uint32_t xx, // 任务栈大小
void * const xx, // 任务形参
StackType_t * const xx, // 任务句柄
TCB_t * const xx // 任务控制块
);

静态创建任务,内嵌 prvInitialiseNewTask 函数。

  • TaskFunction_t:任务函数本身(在 projdefs.h 中重定义的空指针)。任务运行时执行此函数,任务函数必须符合 void* 类型。
  • 运行传参:将函数指针传递给调度器,许多任务的具体实现不同,调度器运行时只需调用不同的任务函数,编译不需要硬编码不同的函数,否则调度器需要知道函数的具体实现,将函数的实现拷贝到调度器中。指针容易被赋值。
  • tskTaskControlBlock:任务的控制块,任务的状态和各种与调度相关的信息,是 FreeRTOS 调度器用来管理任务的核心结构 (tcb_t)。

TCB 并不直接暴露给用户,而是通过 TaskHandle_t 来间接访问

  • TaskHandle_t:任务句柄,在任务创建时获取,提供给用户,方便任务的挂起、恢复、删除等操作 (tcb_t)。
  • TaskFunction_tvoid* 类型,任务函数类型。

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
2
3
StackType_t *pxTopOfStack,  // 栈顶指针
TaskFunction_t pxCode, // 任务入口函数
void *pvParameters // 任务形参

初始化栈

  • xPSRbit24 必须置 1(thumb 模式)
  • 任务的入口地址(这个函数在内存中的地址)

    当任务被创建时,FreeRTOS 会将任务入口地址(即任务函数的地址)保存到任务栈中,以便在任务切换时能够恢复这个地址并开始执行任务。

  • 任务的返回地址

    它是在任务执行过程中保存的,用于在任务切换时恢复任务的执行状态。不过 FreeRTOS 会将 PC 设置为 prvTaskExitError,而不是返回到任务的调用位置。这是为了确保任务结束后不会继续执行不应执行的代码。
    FreeRTOS 中的任务函数通常是一个无限循环。

  • R12, R3, R2R1 默认初始化为 0