在函数 OSSched()和函数 OSIntExit()中会挂起 PendSV 异常,那么 PendSV 异常就能够在所有中断的中断服务函数处理完成后,执行任务切换的操作。
µC/OS-III 提供了 PendSV 异常的中断服务函数,以完成任务切换的操作,该函数为定义在文件 os_cpu_a.asm 中的标号 OS_CPU_PendSVHandler,具体的代码如下所示:OS_CPU_PendSVHandler
; 屏蔽受 uC/OS-III 管理的中断
; 此处加上开关中断的操作,是为了解决 Cortex-M7,写入 BASEPRI 后不能即时生效的 Bug
CPSID I
MOV32 R2, OS_KA_BASEPRI_Boundary
LDR R1, [R2]
MSR BASEPRI, R1
DSB
ISB
CPSIE I
; 获取此时的 PSP,此时 PSP 为当前任务的任务的栈顶指针,R0 = PSP
MRS R0, PSP
; 判断是否使能 FPU; 如果使能了 FPU; 还需要判断 R14 的 bit4 是否为 1; 若是,则需要将浮点寄存器中的数据保存到任务栈中
IF {FPU} != "SoftVFP"
TST R14, #0x10
IT EQ
VSTMDBEQ R0!, {S16-S31}
ENDIF
; 将部分 CPU 寄存器的值模拟入栈到当前任务栈中; 另外,部分的 CPU 寄存器的值会在进入中断之前,自动入栈到任务栈中,; 其中就包含了进入中断前,任务执行到的位置(PC 值)
STMFD R0!, {R4-R11, R14}
; 更新当前任务的任务栈顶指针
; OSTCBCurPtr->StkPtr = R0
MOV32 R5, OSTCBCurPtr
LDR R1, [R5]
STR R0, [R1]
; 调用任务切换钩子函数,; 在调用之前保存 LR 寄存器的值
; R4 = LR
; OSTaskSwHook()
MOV R4, LR
BL OSTaskSwHook
; 设置 OSPrioCur 为最高就绪态任务优先级; OSPrioCur = OSPrioHighRdy
MOV32 R0, OSPrioCur
MOV32 R1, OSPrioHighRdy
LDRB R2, [R1]
STRB R2, [R0]
; 设置 OSTCBCurPtr 为最高就绪态任务; OSTCBCurPtr = OSTCBHighRdyPtr
MOV32 R1, OSTCBHighRdyPtr
LDR R2, [R1]
STR R2, [R5]
; 参考《The Definitive Guide to ARM Cortex-M3 and ARM Cortex-M4 Processors》第8.1.4 小节的 EXC_RETURN 章节; LR |= 0x2 ; 确保返回任务后使用 PSP
ORR LR, R4, #0x04
; 获取任务切换后任务的栈顶
; R0 = OSTCBHighRdyPtr->StkPtr
LDR R0, [R2]
; 从任务栈中模拟出栈 CPU 寄存器的值到 CPU 寄存器中; 另外,部分的 CPU 寄存器的值会在退出中断之后,自动从任务栈中出栈到 CPU 寄存器中; 其中就包含了任务上次被切换时,执行到的位置(PC 值)
LDMFD R0!, {R4-R11, R14}
; 判断是否使能 FPU; 如果使能了 FPU; 还需要判断 R14 的 bit4 是否为 1; 若是,则需要从任务栈中恢复初浮点寄存器中的数据
IF {FPU} != "SoftVFP"
TST R14, #0x10
IT EQ
VLDMIAEQ R0!, {S16-S31}
ENDIF
; 更新 PSP 指针为此时任务切换后任务的任务栈顶; 这个 PSP 为任务栈,在退出中断后,会自动从这个栈中出栈 CPU 寄存器的值,其中就包含了任务在上次被切换时,执行到的位置(PC 值)
; 那么在退出中断后,任务就可以从上次被切换时执行到的位置继续执行 PSP = R0
MSR PSP, R0
; 取消中断屏蔽,此处加上开关中断的操作,是为了解决 Cortex-M7,写入 BASEPRI 后不能即时生效的 Bug
MOV32 R2, #0
CPSID I
MSR BASEPRI, R2
DSB
ISB
CPSIE I
; 退出中断,此时就会根据 PC 指针的值跳转到任务切换后的任务中去执行
BX LR