uCOS-II在Cortext-M3(STM32)上的任务切换示意

根据STM32F103上uCOS-II代码中OS_CPU_PendSVHandler的函数代码整理。


uCOS-II是非剥夺式的操作系统,任务切换只发生在以下几种情况:

1> 外部中断引起变化,导致高优先级任务就绪。

    例如串口中断收到数据,在中断例程中接收数据后调用OSSemPost(),互斥量的Post使uCOS-II知道相应的串口接收任务转变到就绪状态。

2> SysTick的定时中断,导致之前处于OSTimeDly挂起的高优先级任务就绪。

3> 当前任务执行如OSSemPend、OSTimeDly等操作导致当前任务挂起,需要切换到其他次优先级的任务。


前两种都是在中断退出后引起任务切换。

外部中断退出时调用OSIntExit函数激活PendSV中断,真正的task切换在PendSV中断例程中实现。

SysTick中断优先级较高,也不直接做任务切换,其同样在中断退出时调用OSIntExit函数激活PendSV中断,真正的task切换在PendSV中断例程中实现。

第3种是在需要任务切换时调用OS_Sched,后者会调用OSCtxSw,而OSCtxSw也是激活PendSV中断,再由PendSV中断例程完成真正的任务切换。

所以所有的任务切换最终都是由PendSV中断例程完成的。


PendSV的中断优先级必须是系统所有中断中最低的。


PendSV中断由OS_CPU_PendSVHandler函数处理。

OS_CPU_PendSVHandler函数的代码:

OS_CPU_PendSVHandler
    CPSID   I                                                   ; Prevent interruption during context switch
    MRS     R0, PSP                                             ; PSP is process stack pointer
    CBZ     R0, OS_CPU_PendSVHandler_nosave                     ; Skip register save the first time

    SUBS    R0, R0, #0x20                                       ; Save remaining regs r4-11 on process stack
    STM     R0, {R4-R11}

    LDR     R1, =OSTCBCur                                       ; OSTCBCur->OSTCBStkPtr = SP;
    LDR     R1, [R1]
    STR     R0, [R1]                                            ; R0 is SP of process being switched out

                                                                ; At this point, entire context of process has been saved
OS_CPU_PendSVHandler_nosave
    PUSH    {R14}                                               ; Save LR exc_return value
    LDR     R0, =OSTaskSwHook                                   ; OSTaskSwHook();
    BLX     R0
    POP     {R14}

    LDR     R0, =OSPrioCur                                      ; OSPrioCur = OSPrioHighRdy;
    LDR     R1, =OSPrioHighRdy
    LDRB    R2, [R1]
    STRB    R2, [R0]

    LDR     R0, =OSTCBCur                                       ; OSTCBCur  = OSTCBHighRdy;
    LDR     R1, =OSTCBHighRdy
    LDR     R2, [R1]
    STR     R2, [R0]

    LDR     R0, [R2]                                            ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr;
    LDM     R0, {R4-R11}                                        ; Restore r4-11 from new process stack
    ADDS    R0, R0, #0x20
    MSR     PSP, R0                                             ; Load PSP with new process SP
    ORR     LR, LR, #0x04                                       ; Ensure exception return uses process stack
    CPSIE   I
    BX      LR                                                  ; Exception return will restore remaining context




1)任务切换前的状态

假设系统中有两个任务,Task1和Task2,Task1是当前正在运行的任务(由OSTCBCur指出),Task2处于挂起状态。

那么进入OS_CPU_PendSVHandler中断前,堆栈状态如下图所示。

CPU处于线程状态,使用PSP堆栈工作,PSP指向Task1的堆栈。

CPU中的各寄存器是Task1当前任务的寄存器值。

Task2处于挂起状态,Task2的堆栈指针由TCB2的SP变量保存着。在Task2的堆栈底部,保存有两部分数据,一部分是CPU中断时自动保存到堆栈的寄存器变量(包括xPSR,PC,LR,R12,R0~R3),另一部分是uCOS额外保存的寄存器变量(R4~R11),这些寄存器保存了Task2挂起前的所有数据。

uCOS-II在Cortext-M3(STM32)上的任务切换示意_第1张图片


2)任务切换后进入中断例程时的状态

当条件变化导致Task1需要切换到Task2时(OSTCBHighRdy会指向Task2的TCB2),PendSV中断被激发。

进入OS_CPU_PendSVHandler中断时,根据Cortex-M3的中断流程,一部分动作由CPU自动执行:

  • CPU将xPSR,PC,LR,R12,R0~R3自动保存到当前堆栈,由于PSP是指向Task1的堆栈的,所以这些寄存器会自动保存到Task1的堆栈中。
  • CPU切换到Handler模式,使用MSP作为中断例程的工作堆栈。
  • PC指向中断例程,执行中断例程。

进入OS_CPU_PendSVHandler中断时,堆栈状态如下图所示:


uCOS-II在Cortext-M3(STM32)上的任务切换示意_第2张图片


3)uCOS保存当前任务现场后的状态

进入OS_CPU_PendSVHandler后,由于CPU只自动保存了部分寄存器值,uCOS需要将其余寄存器也保存下来,以便切回任务时能完整恢复现场。

OS_CPU_PendSVHandler会根据PSP的值得到Task1的堆栈底部,然后将额外的寄存器R4~R11保存到Task1的堆栈底部。

并且将更新后的Task1的堆栈值保存到TCB1的SP变量中。

OS_CPU_PendSVHandler保存完当前任务数据后的堆栈状态如下图所示:


uCOS-II在Cortext-M3(STM32)上的任务切换示意_第3张图片




4)uCOS恢复目标任务数据后的状态

之后OS_CPU_PendSVHandler需要恢复Task2任务的现场数据。

OS_CPU_PendSVHandler从OSTCBHighRdy获取需要切换到的任务块(此时其等于TCB2),然后从TCB2的SP变量获取该任务的堆栈指针。

得到Task2的堆栈指针后,OS_CPU_PendSVHandler从其堆栈底部恢复R4~R11寄存器的值(这部分是先前由uCOS保存的),然后调整CPU的PSP指针指向Task2堆栈中先前CPU自动保存数据的地方,如下图所示。

此时CPU的R4~R11寄存器已恢复为Task2挂起前的值,但R0~R3、R12、LR、PC、xPSR这些尚未恢复,后面这些寄存器将在中断返回时由CPU自动恢复。

最后OS_CPU_PendSVHandler调用BX LR执行中断返回(LR中的值是EXC_RETURN值,以通知CPU做中断返回动作)。

OS_CPU_PendSVHandler在中断返回前的堆栈状态如下:


uCOS-II在Cortext-M3(STM32)上的任务切换示意_第4张图片


5)uCOS从中断返回,完成任务切换后的状态

OS_CPU_PendSVHandler调用BX LR后,由CPU完成剩余的现场恢复:
  • CPU从PSP堆栈中恢复xPSR,PC,LR,R12,R0~R3这些寄存器的值,由于PSP已指向了Task2的堆栈,所以这些寄存器的值被恢复为Task2堆栈中的值,即Task2任务挂起前的寄存器值。
  • CPU的PC值也从堆栈中恢复到Task2任务被中断时的PC值。
  • CPU退出Handler模式,切换到线程模式,重新使用PSP堆栈作为工作堆栈(此时PSP已指向Task2的堆栈),使用Task2的堆栈作为工作堆栈。
  • CPU已恢复到Task2挂起前的现场,从Task2被中断的PC处继续运行。
对比任务切换前的状态,Task1与Task2的状态完全对调了,所以完成了Task1与Task2的切换。
中断返回后,完成Task2任务切换的堆栈状态如下图所示:


uCOS-II在Cortext-M3(STM32)上的任务切换示意_第5张图片





你可能感兴趣的:(uCOS-II在Cortext-M3(STM32)上的任务切换示意)