天天看點

uCOS-Ⅲ中PendSV任務切換詳細分析(運作模式切換會自動加載的内容)

當調用OSStartHighRdy()函數,會觸發PendSV異常後,就需要編寫PendSV異常(可以手動觸發,也叫軟中斷)服務函數,然後在裡面進行任務的切換。

         PendSV異常服務中主要完成兩個工作,一是儲存上文,即儲存目前正在運作的任務的環境參數;二是切換下文,即把下一個需要運作的任務的環境參數從任務棧中加載到CPU寄存器,進而實作任務的切換。

         PendSV異常服務中用到了OSTCBCurPtr和OSTCBHighRdyPtr這兩個全局變量,這兩個全局變量在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,

存儲OSTCBHighRdyPtr到OSTCBCurPtr,實作下一個要運作的任務的TCB存儲到OSTCBCurPtr。

加載OSTCBHighRdyPtr指針的位址到R0,而TCB中第一個成員是棧指針StkPtr,是以此時R0=StkPtr。

将任務棧中需要手動加載的内容加載到CPU寄存器R4-R11,同時遞增R0,讓R0指向空閑棧的棧頂。此時棧的空間分布情況如下圖:

uCOS-Ⅲ中PendSV任務切換詳細分析(運作模式切換會自動加載的内容)

當把需要手動加載到CPU的棧内容加載完畢之後,棧的空間分布情況如下圖:(注意這個時候StkPtr不變,變的是R0)

uCOS-Ⅲ中PendSV任務切換詳細分析(運作模式切換會自動加載的内容)

更新PSP的值,而且PSP與上圖中R0的指向是一緻的,設定LR寄存器的位2為1,確定異常退出時使用的堆棧指針是PSP,然後打開中斷。

異常傳回(此時會發生CPU運作模式切換),這個時候任務堆棧中剩下的内容将會自動加載到xPSR,PC,R14,R1,R3,R0,R2這些寄存器中,這個和cortex-a核心運作模式切換自動加載内容不一樣(可以看這篇文章arm cortex-a的運作模式切換自動完成哪些事情),同時PSP的值也将更新,即指向任務堆棧的棧頂,這樣就切換到了新的任務,這個時候棧空間的分布如圖:

uCOS-Ⅲ中PendSV任務切換詳細分析(運作模式切換會自動加載的内容)

手動存儲CPU寄存器R4-R11的值到目前任務的堆棧。當異常發生時,進入PendSV異常服務函數的時候,目前CPU寄存器xPSR,PC,R14,R1,R3,R0,R2會自動加載到目前的任務堆棧,同時遞減PSP的值,此時目前任務堆棧空間分布如圖:

uCOS-Ⅲ中PendSV任務切換詳細分析(運作模式切換會自動加載的内容)

加載OSTCBCurPtr指針的位址到R1,

加載OSTCBCurPtr指針到R1,

存儲R0的值到OSTCBCurPtr->OSTCBStkPtr,這個時候R0存的是任務空閑棧的棧頂,此時儲存上文完成,此時的堆棧空間分布如圖:

uCOS-Ⅲ中PendSV任務切換詳細分析(運作模式切換會自動加載的内容)