「FreeRTOS源码解析(二)」任务

一、前言

本期文章讲解FreeRTOS实时操作系统源码,主要是源码分析,实操很少。上节讲了FreeRTOS中的链表,本节将FreeRTOS的任务。

在裸机系统中,系统的主体就是 main 函数里面顺序执行的无限循环,在循环里面 CPU 按照顺序完成各种事情,遇到中断会先完成中断处理,中断处理完成后再回到原来的任务。

在多任务系统中,根据功能的不同,把整个系统分割成一个个独立的且无法返回的函数,这个函数称为任务。

在一个裸机系统中,系统在运行的时候,全局变量放在哪里,子函数调用时,局部变量放在哪里,中断发生时,函数返回地址放哪里,可以不用管,但是如果要写一个 FreeRTOS,这些必须弄清楚他们是如何存储的。在裸机系统中,他们统统放在栈中,栈是单片机 RAM 里面一段连续的内存空间,栈的大小一般在启动文件或者链接脚本里面指定,最后由 C 库函数_main 进行初始化。在多任务系统中,每个任务都是独立的,互不干扰的,所以要为每个任务都分配独立的栈空间,这个栈空间通常是一个预先定义好的全局数组,也可以是动态分配的一段内存空间,但它们都存在于 RAM 中。

本期文章FreeRTOS源码采用V9.0.0版本,源码可以在sourceforge上找到。下节讲解FreeRTOS的就绪列表和调度。

二、实现FreeRTOS任务

1、任务栈

根据需求量定义,这里定义两个测试任务,用作交替运行。

任务栈其实就是一个预先定义好的全局数据,大小由 TASK1_STACK_SIZE 这个宏来定义,默认为 128,单位为字,即 512字节,这也是 FreeRTOS 推荐的最小的任务栈。

2、任务函数

这里的delay函数是真实的延时函数,非FreeRTOS中的vTaskDelay函数。

3、任务控制块

任务控制块结构体tskTaskControlBlock。在FreeRTOS中,任务的执行是由系统调度的。系统为每个任务都定义了一个任务控制块,这个任务控制块里面存有任务的所有信息,如任务的栈指针,任务名称,任务的形参等。系统对任务的全部操作都可以通过这个任务控制块来实现。任务控制块结构体:

  • pxTopOfStack; 栈顶

  • xStateListItem:任务节点

  • pxStack:任务栈起始地址

  • pcTaskName:任务名称,字符串形式

  • uxPriority;:任务优先级

4、任务创建函数

任务创建函数xTaskCreateStatic。FreeRTOS 中,任务的创建有两种方法,分别是动态创建和静态创建。动态创建是任务控制块和栈的内存是创建任务时动态分配的,任务删除时,内存可以释放。静态创建是任务控制块和栈的内存需要事先定义好,是静态的内存,任务删除时,内存不能释放 。任务栈,任务函数实体,任务控制块需要联系起来才能由系统进行统一调度。这个工作就由任务创建函数 xTaskCreateStatic()来实现。

函数参数含义如下:

  • pxTaskCode:任务入口,即任务的函数名称。

  • pcName:任务名称,字符串形式。

  • ulStackDepth:任务栈大小,单位为字。

  • pvParameters:任务形参。

  • uxPriority:优先级。

  • puxStackBuffer:任务栈起始地址。

  • pxTaskBuffer:任务控制块指针。

5、创建任务函数子函数prvInitialiseNewTask

函数参数如下:

  • pxTaskCode:任务入口。

  • pcName:任务名称,以字符串形式。

  • ulStackDepth:表示任务栈大小,单位为字。

  • pvParameters:表示任务形参。

  • pxCreatedTask:任务句柄。

  • *pxNewTCB:任务控制块指针。

函数中几个重点的代码含义:

  • 由于需要兼容浮点运算,将栈顶指针向下做 8 字节对齐。

  • 任务名字的长度不能超过 configMAX_TASK_NAME_LEN,并以’\0’结尾。

  • 使用vListInitialiseItem初始化 TCB 中的 xStateListItem 节点,即初始化该节点所在的链表为空,表示节点还没有插入任何链表。

  • 使用listSET_LIST_ITEM_OWNER设置 xStateListItem 节点的拥有者,即拥有这个节点本身的 TCB。

  • 调用 pxPortInitialiseStack()函数初始化任务栈,并更新栈顶指针,任务第一次运行的环境参数就存在任务栈中。

6、初始化任务栈函数pxPortInitialiseStack

该函数完成步骤如下:

  • 异常发生时,CPU 自动从栈中加载到 CPU 寄存器的内容。包括 8个寄存器,分别为 R0、R1、R2、R3、R12、R14、R15 和 xPSR 的位 24,且顺序不能变。

  • xPSR 的 bit24 必须置 1,即 0x01000000。

  • 得到任务的入口地址。

  • 任务的返回地址,通常任务是不会返回的,如果返回了就跳转到prvTaskExitError,该函数是一个无限循环。

  • R12, R3, R2 and R1 默认初始化为 0。

  • 异常发生时,需要手动加载到 CPU 寄存器的内容,总共有 8 个,分别为 R4、R5、R6、R7、R8、R9、R10和 R11,默认初始化为 0。

  • 返回栈顶指针,任务第一次运行时,就是从这个栈指针开始手动加载 8 个字的内容到 CPU 寄存器:R4、R5、R6、R7、R8、R9、R10 和 R11,当退出异常时,栈中剩下的 8 个字的内容会自动加载到 CPU 寄存器:R0、R1、R2、R3、R12、R14、R15 和 xPSR 的位 24。此时 PC 指针就指向了任务入口地址,从而成功跳转到第一个任务。

任务栈初始化完成后栈空间分布图如下。栈向下生长。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值