uCOS-Ⅲ中PendSV任务切换详细分析

         当调用OSStartHighRdy()函数,会触发PendSV异常后,就需要编写PendSV异常服务函数,然后在里面进行任务的切换。

         PendSV异常服务中主要完成两个工作,一是保存上文,即保存当前正在运行的任务的环境参数;二是切换下文,即把下一个需要运行的任务的环境参数从任务栈中加载到CPU寄存器,从而实现任务的切换。

         PendSV异常服务中用到了OSTCBCurPtrOSTCBHighRdyPtr这两个全局变量,这两个全局变量在os.h中被定义。

         PendSV异常服务函数名称必须与启动文件里面的向量表中的PendSV的向量名一致,如果不一致则内核是响应不了用户编写的PendSV异常服务函数的,只响应启动文件里面默认的PendSV异常服务函数,启动文件里面为每个异常都编写好默认的异常服务函数,函数体都是一个死循环,当你发现代码跳转到这些启动文件里面默认的异常服务的函数,就要检查下异常函数名称是否写错了,没有跟向量表里面的一致。

        在进入PendSV异常服务函数时,首先是关闭中断(NMI与HardFault除外),防止上下文切换时被中断,当然,在上下文切换完毕之后,会重新打开中断。

        将PSP的值加载到R0寄存器中,判断R0的值,如果为0则跳转到OS_CPU_PendSVHandler_nosave。进行第一次任务切换的时候,PSP在OSStartHighRdy初始化为0,所以此时R0肯定为0。

当执行过一次任务切换之后,则顺序执行到切换下文

首先加载OSTCBCurPtr指针的地址到R0,

加载OSTCBHighRdyPtr指针的地址到R1,

加载OSTCBHighRdyPtr指针到R2,

存储OSTCBHighRdyPtrOSTCBCurPtr,实现下一个要运行的任务的TCB存储到OSTCBCurPtr

加载OSTCBHighRdyPtr指针的地址到R0,而TCB中第一个成员是栈指针StkPtr,所以此时R0=StkPtr。

将任务栈中需要手动加载的内容加载到CPU寄存器R4-R11,同时递增R0,让R0指向空闲栈的栈顶。此时栈的空间分布情况如下图:

当把需要手动加载到CPU的栈内容加载完毕之后,栈的空间分布情况如下图:(注意这个时候StkPtr不变,变的是R0)

更新PSP的值,而且PSP与上图中R0的指向是一致的,设置LR寄存器的位2为1,确保异常退出时使用的堆栈指针是PSP,然后打开中断。

异常返回,这个时候任务堆栈中剩下的内容将会自动加载到xPSR,PC,R14,R1,R3,R0,R2这些寄存器中,同时PSP的值也将更新,即指向任务堆栈的栈顶,这样就切换到了新的任务,这个时候栈空间的分布如图:

手动存储CPU寄存器R4-R11的值到当前任务的堆栈。当异常发生时,进入PendSV异常服务函数的时候,当前CPU寄存器xPSR,PC,R14,R1,R3,R0,R2会自动加载到当前的任务堆栈,同时递减PSP的值,此时当前任务堆栈空间分布如图:

加载OSTCBCurPtr指针的地址到R1,

加载OSTCBCurPtr指针到R1,

存储R0的值到OSTCBCurPtr->OSTCBStkPtr,这个时候R0存的是任务空闲栈的栈顶,此时保存上文完成,此时的堆栈空间分布如图:

发布了26 篇原创文章 · 获赞 40 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/qq_21990661/article/details/102721091
今日推荐