uC/OS-II的任务切换机理及中断调度优化
PC出栈了。简单地说,μC/OS-II切换到最高优先级的任务,只是恢复最高优先级任务所有的寄存器并运行中断返回指令(RETI),实际上,所作的只是人为地模仿了一次中断。
(2)中断级的任务切换原理
μC/OS-II的中断服务子程序和一般前后台的操作有少许不同,往往需要这样操作:
保存全部CPU寄存器
调用OSIntEnter()或OSIntNesting++
开放中断
执行用户代码
关闭中断
调用OSIntExit();
恢复所有CPU寄存器
RETI
OSIntEnter()就是将全局变量OSIntNesting加1。OSIntNesting是中断嵌套层数的变量。μC/OS-II通过它确保在中断嵌套的时候,不进行任务调度。执行完用户的代码后,μC/OS-II调用OSIntExit(),一个与OSSched()很像的函数。在这个函数中,系统首先把OSIntNesting减1,然后判断是否中断嵌套。如果不是的话,并且当前任务不是最高优先级的任务,那么找到优先级最高的任务,执行OSIntCtxSw()这一出中断任务切换函数。因为,在这之前已经做好了压栈工作;在这个函数中,要进行R15~R4的出栈工作。而且,由于在之前调用函数的时候,可能已经有一些寄存器被压入了堆栈。所以要进行堆栈指针的调整,使得能够从正确的位置出栈。
3使用μC/OS-II存在的问题和解决方法
由于μC/OS-II在应用的时候会占用单片机上的一些资源,如系统时钟、RAM、Flash或者ROM,从而减少了用户程序对资源的利用。对于MSP430来说,RAM的占用是特别突出的问题。对于8、16位的单片机来说,片内的RAM容量都很小,MSP430也是如此(最大的片内RAM也只有2KB,例如MSP430F149)。如果使用扩展内存,会大大增加设计难度。
通过对μC/OS-II的分析可以得知,μC/OS-II占用的RAM主要是用在每个任务的TCB、每个任务的堆栈等方面。通过进一步分析,发现任务堆栈大的原因是因为MSP430的硬件设计中没有把中断堆栈和任务堆栈分开。这样就造成了在应用μC/OS-II的时候,考虑每个任务的任务堆栈大小时,不单单需要计算任务中局部变量和函数嵌套层数,还需要考虑中断的最大嵌套层数。因为,对于μC/OS-II原始的中断处理的设计、中断处理过程中的中断嵌套中所需要压栈的寄存器大小和局部变量的内存大小,都需要算在每个任务的任务堆栈中,则对于每一个任务都需要预留这一部分内存,所以大量的RAM被浪费。从这里可以看出,解决这一问题的直接方法就是把中断堆栈和每个任务自己的堆栈分开。这样,在计算每个任务堆栈的时候,就不需要把中断处理中(包括中断嵌套过程中)的内存的占用计算到每个任务的任务堆栈中,只需要计算每个任务本身需要的内存大小,从而提高了RAM的利用率,可以缓解内存紧张的问题。
在这种设计方案中,中断堆栈区也就是利用原有的MSP430中的系统堆栈区。在前后台的设计形式中,中断中的压栈和出栈的操作都是在系统的堆栈区完成的。基于μC/OS-II的任务切换的原理,我们对于任务堆栈的功能和系统堆栈的功能做了以下划分:任务在运行过程中产生中断和任务切换的时候,PC和SR以及寄存器Rx都保存在各个任务自己的任务堆栈中;而中断嵌套产生的压栈和出栈的操作都是放在系统堆栈中进行的。这种划分方式是基于尽量将中断任务与普通任务分开的思想设计的。
从前面对于IAREW的默认操作分析来看,堆栈的结构可以有两种。一种是把μC/OS-II的任务堆栈设计成图1所示的形式。这种方法是把编译器默认的压栈操作放在前面,然后再把剩下的寄存器进栈。但是,由于编译器在处理复杂程度不同的中断服务程序的时候,压入栈的寄存器的数量不定,所以会对以后其余寄存器的压栈和出栈操作增加复杂度。这里,我们采用了图2所示的方式生成堆栈。在这种堆栈中,PC和SR压栈后,通过调整SP指针,使得R4~R15寄存器覆盖编译器默认压栈的寄存器。这样,处理的难度会小一点。
对于这样的设计方式,CPU必须能够:
◆有相应的CPU寄存器能够模仿SP的一些功能,能使用相应的指令来完成类似SP的一些操作;
◆作为SP使用的寄存器在编译过程中最好不被编译器默认使用。在IAR的编译器中,有一个选项可以避免在编译过程中使用到R4、R5。
这两点MSP430都可以做到。
下面对一个正在运行的优先级为6的任务中断后,会发生的几种情况进行分析。
1)在中断的处理过程中没有更高优先级的中断产生,即不会产生中断嵌套。
图3所示为中断发生后对于任务优先级为6的任务堆栈所进行的操作。中断发生后,PC和SR被系统压栈②,对于IARC编译器来说,会按照复杂度不同的中断服务程序的要求,默认地进行一些寄存器的压栈操作③。因为我们要求的堆栈格式是如图2所示的,我们要把SP调整到SR后面④,然后进行R4~R15的压栈操作,形成我们所要求的堆栈格式⑤。
进行任务堆栈的压栈工作以后,就可以调整SP的指针到系统堆栈了,如图4所示。压栈后的SP指向最后一个压栈内容①。我们把SP的值赋值给优先级6任务的TCB->OSTCBStkPtr,以便进行任务调度的时候出栈使用②。接着,就把SP调整到系统堆栈处③。在中断处理过程中,可能会出现压栈的操作,那么这种情况下SP的指针会随之移动。由于现在是中断堆栈中,所以不 《uC/OS-II的任务切换机理及中断调度优化(第2页)》
本文链接地址:http://www.oyaya.net/fanwen/view/140261.html
(2)中断级的任务切换原理
μC/OS-II的中断服务子程序和一般前后台的操作有少许不同,往往需要这样操作:
保存全部CPU寄存器
调用OSIntEnter()或OSIntNesting++
开放中断
执行用户代码
关闭中断
调用OSIntExit();
恢复所有CPU寄存器
RETI
OSIntEnter()就是将全局变量OSIntNesting加1。OSIntNesting是中断嵌套层数的变量。μC/OS-II通过它确保在中断嵌套的时候,不进行任务调度。执行完用户的代码后,μC/OS-II调用OSIntExit(),一个与OSSched()很像的函数。在这个函数中,系统首先把OSIntNesting减1,然后判断是否中断嵌套。如果不是的话,并且当前任务不是最高优先级的任务,那么找到优先级最高的任务,执行OSIntCtxSw()这一出中断任务切换函数。因为,在这之前已经做好了压栈工作;在这个函数中,要进行R15~R4的出栈工作。而且,由于在之前调用函数的时候,可能已经有一些寄存器被压入了堆栈。所以要进行堆栈指针的调整,使得能够从正确的位置出栈。
3使用μC/OS-II存在的问题和解决方法
由于μC/OS-II在应用的时候会占用单片机上的一些资源,如系统时钟、RAM、Flash或者ROM,从而减少了用户程序对资源的利用。对于MSP430来说,RAM的占用是特别突出的问题。对于8、16位的单片机来说,片内的RAM容量都很小,MSP430也是如此(最大的片内RAM也只有2KB,例如MSP430F149)。如果使用扩展内存,会大大增加设计难度。
通过对μC/OS-II的分析可以得知,μC/OS-II占用的RAM主要是用在每个任务的TCB、每个任务的堆栈等方面。通过进一步分析,发现任务堆栈大的原因是因为MSP430的硬件设计中没有把中断堆栈和任务堆栈分开。这样就造成了在应用μC/OS-II的时候,考虑每个任务的任务堆栈大小时,不单单需要计算任务中局部变量和函数嵌套层数,还需要考虑中断的最大嵌套层数。因为,对于μC/OS-II原始的中断处理的设计、中断处理过程中的中断嵌套中所需要压栈的寄存器大小和局部变量的内存大小,都需要算在每个任务的任务堆栈中,则对于每一个任务都需要预留这一部分内存,所以大量的RAM被浪费。从这里可以看出,解决这一问题的直接方法就是把中断堆栈和每个任务自己的堆栈分开。这样,在计算每个任务堆栈的时候,就不需要把中断处理中(包括中断嵌套过程中)的内存的占用计算到每个任务的任务堆栈中,只需要计算每个任务本身需要的内存大小,从而提高了RAM的利用率,可以缓解内存紧张的问题。
在这种设计方案中,中断堆栈区也就是利用原有的MSP430中的系统堆栈区。在前后台的设计形式中,中断中的压栈和出栈的操作都是在系统的堆栈区完成的。基于μC/OS-II的任务切换的原理,我们对于任务堆栈的功能和系统堆栈的功能做了以下划分:任务在运行过程中产生中断和任务切换的时候,PC和SR以及寄存器Rx都保存在各个任务自己的任务堆栈中;而中断嵌套产生的压栈和出栈的操作都是放在系统堆栈中进行的。这种划分方式是基于尽量将中断任务与普通任务分开的思想设计的。
从前面对于IAREW的默认操作分析来看,堆栈的结构可以有两种。一种是把μC/OS-II的任务堆栈设计成图1所示的形式。这种方法是把编译器默认的压栈操作放在前面,然后再把剩下的寄存器进栈。但是,由于编译器在处理复杂程度不同的中断服务程序的时候,压入栈的寄存器的数量不定,所以会对以后其余寄存器的压栈和出栈操作增加复杂度。这里,我们采用了图2所示的方式生成堆栈。在这种堆栈中,PC和SR压栈后,通过调整SP指针,使得R4~R15寄存器覆盖编译器默认压栈的寄存器。这样,处理的难度会小一点。
对于这样的设计方式,CPU必须能够:
◆有相应的CPU寄存器能够模仿SP的一些功能,能使用相应的指令来完成类似SP的一些操作;
◆作为SP使用的寄存器在编译过程中最好不被编译器默认使用。在IAR的编译器中,有一个选项可以避免在编译过程中使用到R4、R5。
这两点MSP430都可以做到。
下面对一个正在运行的优先级为6的任务中断后,会发生的几种情况进行分析。
1)在中断的处理过程中没有更高优先级的中断产生,即不会产生中断嵌套。
图3所示为中断发生后对于任务优先级为6的任务堆栈所进行的操作。中断发生后,PC和SR被系统压栈②,对于IARC编译器来说,会按照复杂度不同的中断服务程序的要求,默认地进行一些寄存器的压栈操作③。因为我们要求的堆栈格式是如图2所示的,我们要把SP调整到SR后面④,然后进行R4~R15的压栈操作,形成我们所要求的堆栈格式⑤。
进行任务堆栈的压栈工作以后,就可以调整SP的指针到系统堆栈了,如图4所示。压栈后的SP指向最后一个压栈内容①。我们把SP的值赋值给优先级6任务的TCB->OSTCBStkPtr,以便进行任务调度的时候出栈使用②。接着,就把SP调整到系统堆栈处③。在中断处理过程中,可能会出现压栈的操作,那么这种情况下SP的指针会随之移动。由于现在是中断堆栈中,所以不 《uC/OS-II的任务切换机理及中断调度优化(第2页)》