RTOS的任务切换核心程序

工作中做项目经常会用到FreeRTOS,但是由于不理解底层的源代码,所以用起来也只是会用,而为什么要这样使用,确很少能够深究。尝试去阅读FreeRTOS的源代码,由于缺少对RTOS的整体架构的理解,总是顾头不顾尾。于是想自己尝试编写RTOS,做一个简单的任务的切换。荣幸听了一位大神的从0到1写RTOS,才有勇气到这里发表一些小小的个人理解。这篇文章主要是我对RTOS任务切换核心程序的理解。
任务开始时,先创建任务栈寄存器,并对对其进行初始化。这里我们创建32字节的数组模拟栈空间,并对其进行如下所示的初始化:

空间大小有32*4 = 128字节大小,初始化的函数如下所示:

假如使用的数组空间分布如下所示:

任务切换的函数核心,也就是pendsv中断函数的核心如下所示:
__asm void PendSV_Handler(void)
{
IMPORT currentTask
IMPORT nextTask

MRS R0,PSP
CBZ R0,PendSVHander_nosave

STMDB R0!,{R4-R11}
LDR R1,=currentTask
LDR R1,[R1]
STR R0,[R1]

PendSVHander_nosave
LDR R0,=currentTask
LDR R1,=nextTask
LDR R2,[R1]
STR R2,[R0]

LDR R0,[R2]
LDMIA R0!,{R4-R11}

MSR PSP,R0
ORR LR,LR,#0x04
BX LR

}
下面我们将对上面程序的主要意思进行讲解。
首次执行第一个任务的时候,PSP会被设置为0。触发PENDSV中断发生后,进行如下所示的操作,将nextTask任务值保存到currentTask中,而此时nextTask的值是0x20000070,从R0(0x20000070)中弹出R4~R11寄存器的值到,弹出寄存器后的R0值为0x20000090,保存R0的值到PSP中,执行寄存器跳转指令,跳出到PC指针指向的函数位置,此时栈空间的地址为0x200000B0,此时开始执行第一个任务。第一个任务执行完毕后,开始执行任务调度函数,判断当前是否是第一个任务,如果是就切换到第二个任务中。nextTask = taskTable[1],进行任务的切换,启动PENDSV中断。
此时的PSP的值从0x200000B0,变化为0x2000000A8,进入到中断后,前面32个字节是硬件自动保存的特殊寄存器,保存后的PSP的值为0x200000088,然后调用STMDB指令,将R0地址循环的减一,保存R4-R11寄存器的值,此时的R0的值变化为0x20000068,需要将这个值保存到currentTask中,而currentTask是和taskTable[0]对应,所以此时taskTable[0]保存了currentTask的值。也就是保存了上一个任务的状态寄存器的值。接下来开始执行将nextTask的值复制到currentTask中,通过LDMIA命令,将R4-R11的返回到R0变量中。赋值R0的变量到PSP中,此时PSP保存的是第二个任务的栈地址,跳出BX LR到寄存器中,运行第二个任务。

要注意的问题;
1、PENDSV函数中,硬件会自动将XPSR,PC,LR,R12,R3,R2,R1保存到SP(R13)表示的地址中。
2、需要创建任务列表tTask taskTable[2],两个列表一个和currentTask对应,一个和nextTask对应;这个两个变量在对应的时候,有一定的技巧,需要注意。第一次运行的时候,没有currentTask,将nextTask赋值到currentTask中,而此时taskTable[0]是赋值给了nextTask的,也就导致了currentTask和taskTable[0]对应关系。
3、一个任务切换到另外一个任务中,需要将手动将R4-R11的值保存到PSP堆栈中,然后将此时新的R0值(此时任务1的最新的地址空间)赋值给currentTask,由于此时currentTask还是和taskTable[0]对应,所以,taskTable[0]就赋值了currentTask新的值。接下来会进行nextTask和currentTask任务的交换,将nextTask的值赋值到currentTask中
4、对ORR LR,LR,#0x04的理解,程序复位后,系统默认使用的是特权级的堆栈,而实际使用中,我们希望使用用户级堆栈。可以对LR寄存器进行如下所示的操作,将第二位赋值为1,表示返回后使用PSP堆栈。如下所示:
RTOS的任务切换核心程序_第1张图片
所以才有了上面的语句。(也可以参考cortexm3权威指南)

你可能感兴趣的:(RTOS的任务切换核心程序)