__asm void xPortPendSVHandler( void )
{
extern uxCriticalNesting;
extern pxCurrentTCB;
extern vTaskSwitchContext;
PRESERVE8 //棧的8字節對齊
mrs r0, psp //讀取當前psp進程指針,存入r0
isb
/* 獲取當前任務控制塊 */
ldr r3, =pxCurrentTCB //把當前任務控制塊的指針給r3
ldr r2, [r3] //把r3地址中的值給r2,r2中就存儲當前的任務控制塊
/* 是否使用了FPU,使用的話要手動保存s16~s31 */
tst r14, #0x10
it eq
vstmdbeq r0!, {s16-s31}
/* Save the core registers. */
stmdb r0!, {r4-r11, r14} //含義::依次壓棧r0 = r0 - 4,先壓r14,r0 = r14(即將r14中的內容放入r0所指的內存地址)
// r0 = r0 - 4,再壓r11,r0 = r11。
// r0 = r0 - 4,再壓r10,r0 = r10......r0 = r0 - 4,最后壓r4,r0 = r4。
// 則r0中就保存最新的棧頂指針值
/* 保存最新的棧頂指針到當前任務控制塊的第一字段*/
str r0, [r2] //把r0的值存入r2的地址,相當於*r2 = r0,[r2]中已經保存了最新的任務的控制塊,經過上面的兩個ldr指令,r2中已經保存最新的任務控制塊的地址
stmdb sp!, {r3} //將寄存器R3的值臨時壓棧,寄存器r3中仍然保存着當前任務的任務控制塊,
//而接下來要調用函數vTaskSwitchContext,防止r3的值被改寫,故臨時壓棧
mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY
msr basepri, r0 //關中斷,進入臨界區
dsb
isb
bl vTaskSwitchContext //調用函數vTaskSwitchContext,此函數用來獲取下一個要運行的任務,並將pxCurrentTCB更新為要運行的這個任務
mov r0, #0
msr basepri, r0 //開中斷,退出臨界區
ldmia sp!, {r3} //剛剛保存的寄存器R3的值出棧,恢復寄存器R3的值。注意,經過調用函數vTaskSwitchContext,此時
//pxCurrentTCB的值已經改變了,所以讀取R3所保存的地址處的數據就會發現其值改變了,成
//為了下一個要運行的任務的任務控制塊。
ldr r1, [r3]
ldr r0, [r1] //獲取新的運行任務的棧頂,並存到r0中去
/* 彈出內核寄存器 */
ldmia r0!, {r4-r11, r14} //含義::依次出棧 r0 = r0 + 4,先彈出r14,r0 = r14(即將r14中的內容放入r0所指的內存地址)
// r0 = r0 + 4,再彈r11,r0 = r11。
// r0 = r0 + 4,再彈r10,r0 = r10......r0 = r0 - 4,最后出r4,r0 = r4。
/*是夠使用了FPU,使用了徐要手動保存s16~s31*/
tst r14, #0x10
it eq
vldmiaeq r0!, {s16-s31}
msr psp, r0 //更新進程棧指針PSP的值
isb
#ifdef WORKAROUND_PMU_CM001 /* XMC4000 specific errata *///這塊暫時不用管
#if WORKAROUND_PMU_CM001 == 1
push { r14 }
pop { pc }
nop
#endif
#endif
bx r14 //執行此行代碼以后硬件自動恢復寄存器R0~R3、R12、LR、PC和xPSR的值,確定異常返回以后應該進入處理器模式還是進程模式,使用主棧指針(MSP)還是進程棧指針(PSP)。
很明顯這里會進入進程模式,並且使用進程棧指針(PSP), 寄存器PC值會被恢復為即將運行的任務的任務函數,新的任務開始運行!至此,任務切換成功。
}