【FreeRTOS学习】第3章 任务

1. 任务简介

  • 什么是任务?

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

  • 什么是栈?什么是任务栈?

在裸机系统中,他们统统放在一个叫栈的地方,栈是单片机RAM里面一段连续的内存空间,栈的大小一般在启动文件或者链接脚本里面指定,最后由C库函数_main进行初始化

每个任务都是独立的,互不干扰的,所以要为每个任务都分配独立的栈空间,这个栈空间通常是一个预先定义好的全局数组,也可以是动态分配的一段内存空间,但它们都存在于RAM中。

  • 定义任务函数

任务是一个独立的函数,函数主体无限循环且不能返回。

  • 定义任务控制块

任务控制块就相当于任务的身份证,里面存有任务的所有信息,比如任务的栈指针,任务名称,任务的形参等。

定义一个任务控制块需要一个新的数据类型,该数据类型在task.c这C头文件中声明(为了tskTCB这个数据类型能在其它地方使用,讲解的时候我把这个任务控制块的声明放在了FreeRTOS.h这个头文件)。

2. 创建任务

  • 实现任务创建函数xTaskCreateStatic()

​ 任务创建函数xTaskCreateStatic()将任务栈、任务函数实体等与任务控制块联系起来,最终统一管理任务控制块。

​ 这个函数在task.c中定义,(task.c第一次使用需要自行在文件夹freertos中新建并添加到工程的freertos/source组),在task.h中声明,所有跟任务相关的函数都在这个文件定义。

  • prvInitialiseNewTask()

将栈顶指针向下做8字节对齐、设置任务的名字、初始化TCB中的任务节点、节点的拥有者就是任务本身

  • pxPortInitialiseStack()

初始化任务栈。
在这里插入图片描述

3. 实现就绪列表

3.1 定义

就绪列表实际上就是一个List_t类型的数组,数组的大小由决定最大任务优先级的宏configMAX_PRIORITIES决定,目前我们将configMAX_PRIORITIES在FreeRTOSConfig.h中默认定义为5,最大支持256个优先级。数组的下标对应了任务的优先级,同一优先级的任务统一插入到就绪列表的同一条链表中。
在这里插入图片描述

3.2 就绪列表初始化

就绪列表初始化函数:
prvInitialiseTaskLists()
在这里插入图片描述

3.3 将任务插入到就绪列表

任务控制块里面有一个xStateListItem成员,数据类型为ListItem_t,我们将任务插入到就绪列表里面,就是通过将任务控制块的xStateListItem这个节点插入到就绪列表中来实现的。
在这里插入图片描述

4. 实现调度器

4.1 简介

调度器是操作系统的核心,其主要功能就是实现任务的切换,即从就绪列表里面找到优先级最高的任务,然后去执行该任务。

4.2 vTaskStartScheduler()

调度器的启动由vTaskStartScheduler()函数来完成,该函数在task.c中。

指定运行的任务,然后调用xPortStartScheduler()函数

4.3 xPortStartScheduler()

配置PendSV 和 SysTick 的中断优先级为最低。

调用函数prvStartFirstTask()启动第一个任务,启动成功后,则不再返回,该函数由汇编编写,在port.c实现。

使用CPS指令把全局中断打开, Cortex-M内核专门设置了一条 CPS 指令

CPSID I ;PRIMASK=1    ;关中断
CPSIE I ;PRIMASK=0    ;开中断
CPSID F ;FAULTMASK=1  ;关异常
CPSIE F ;FAULTMASK-0  ;开异常

4.4 Cortex-M内核中断屏蔽寄存器组

名字功能描述
PRIMASK这是个只有单一比特的寄存器。 在它被置 1 后,就关掉所有可屏蔽的异常,只剩下 NMI 和硬 FAULT可以响应。它的缺省值是 0,表示没有关中断。
FAULTMASK这是个只有 1 个位的寄存器。当它置 1 时,只有 NMI 才能响应,所有其它的异常,甚至是硬 FAULT,也通通闭嘴。它的缺省值也是 0,表示没有关异常。
BASEPRI这个寄存器最多有 9 位( 由表达优先级的位数决定)。它定义了被屏蔽优先级的阈值。当它被设成某个值后,所有优先级号大于等于此值的中断都被关(优先级号越大,优先级越低)。但若被设成 0,则不关闭任何中断, 0 也是缺省值。

4.5 vPortSVCHandler()

SVC中断要想被成功响应,其函数名必须与向量表注册的名称一致SVC_Handler ,我们在FreeRTOSConfig.h中添加添加宏定义的方法来修改vPortSVCHandler()。
在这里插入图片描述

当从SVC中断服务退出前,通过向r14寄存器最后4位按位或上0x0D,使得硬件在退出时使用进程堆栈指针PSP完成出栈操作并返回后进入任务模式、返回Thumb状态。在SVC中断服务里面,使用的是MSP堆栈指针。

当r14的bit0为1表示返回thumb状态,bit1和bit2分别表示返回后sp用msp还是psp以及返回到特权模式还是用户模式。

4.6 任务切换

任务切换就是在就绪列表中寻找优先级最高的就绪任务,然后去执行该任务。但是目前我们还不支持优先级,仅实现两个任务轮流切换。
任务切换函数:taskYIELD()。

portYIELD的实现很简单,实际就是将PendSV的悬起位置1,当没有其它中断运行的时候响应PendSV中断,去执行我们写好的PendSV中断服务函数,在里面实现任务切换。

通过宏定义将xPortPendSVHandler()定义为PendSV。

4.7 xPortPendSVHandler()

上文保存:将PSP的值存储到r0。当进入PendSVC Handler时,上一个任务运行的环境即: xPSR,PC(任务入口地址),R14,R12,R3,R2,R1,R0(任务的形参)这些CPU寄存器的值会自动存储到任务的栈中,剩下的r4~r11需要手动保存,同时PSP会自动更新(在更新之前PSP指向任务栈的栈顶)。
在这里插入图片描述

以r0作为基址,指针先递减,再操作,将CPU寄存器r4~r11的值存储到任务栈,同时更新r0的值。
在这里插入图片描述
下文切换:将R3和R14临时压入堆栈(在整个系统中,中断使用的是主堆栈,栈指针使用的是MSP),因为接下来要调用函数vTaskSwitchContext,调用函数时,返回地址自动保存到R14中,所以一旦调用发生,R14的值会被覆盖(PendSV中断服务函数执行完毕后,返回的时候需要根据R14的值来决定返回处理器模式还是任务模式,出栈时使用的是PSP还是MSP),因此需要入栈保护。R3保存的是当前正在运行的任务(准确来说是上文,因为接下来即将要切换到新的任务)的TCB指针(pxCurrentTCB)地址,函数调用后pxCurrentTCB的值会被更新,后面我们还需要通过R3来操作pxCurrentTCB,但是运行函数vTaskSwitchContext时不确定会不会使用R3寄存器作为中间变量,所以为了保险起见,R3也入栈保护起来。
调用函数vTaskSwitchContext()。该函数在task.c中定义,作用只有一个,选择优先级最高的任务,然后更新pxCurrentTCB。

退出vTaskSwitchContext()函数后,从主堆栈中恢复寄存器r3和r14的值,此时的sp使用的是MSP,加载r3指向的内容到r1。r3存放的是pxCurrentTCB的地址,即让r1等于pxCurrentTCB。pxCurrentTCB在上面的vTaskSwitchContext函数中被更新,指向了下一个将要运行的任务的TCB。
加载r1指向的内容到r0。
以r0作为基地址,先取值,再递增指针,将下一个要运行的任务的任务栈的内容加载到CPU寄存器r4~r11。

更新psp的值(此时psp指向r0),等下异常退出时,会以psp作为基地址,将任务栈中剩下的内容自动加载到CPU寄存器。

总的来说:当异常发生时,R14中保存异常返回标志,包括返回后进入任务模式还是处理器模式、使用PSP堆栈指针还是MSP堆栈指针。此时的r14等于0xfffffffd,最表示异常返回后进入任务模式,SP以PSP作为堆栈指针出栈,出栈完毕后PSP指向任务栈的栈顶。当调用 bx r14指令后,系统以PSP作为SP指针出栈,把接下来要运行的新任务的任务栈中剩下的内容加载到CPU寄存器:R0(任务形参)、R1、R2、R3、R12、R14(LR)、R15(PC)和xPSR,从而切换到新的任务。

5. 实验效果

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值