FreeRTOS --- 任務切換 xPortPendSVHandler


知識點:

MSP:主堆棧指針,系統復位后,默認使用MSP指針,MSP指針用於操作內核以及處理異常和中斷(異常是中斷的一種,中斷服務程序默認強制使用MSP指針,這是硬件自動設置的)

    不使用OS,非中斷函數和中斷函數都使用MSP

PSP:進程堆棧指針,任務(進程)使用PSP指針,在vPortSVCHandler中斷服務函數中,通過修改 R14 的值從MSP指針切換到PSP指針

    使用OS,main函數和中斷使用MSP,各個task使用PSP

R13: 堆棧指針SP,分為MSP和PSP

在執行異常的時候,SP以MSP為棧指針,在執行任務時SP以PSP為棧指針

每個任務都有一個任務棧

棧的地址是從高往低生長,棧指針最低兩位永遠是0,意味着棧總是4字節對齊

任務TCB數據結構第一個成員是一個指針變量(pxTopOfStack),指向任務當前棧的棧頂

 

 

 

R14作用:連接寄存器。調用子函數時存儲返回地址;異常(中斷)發生時,R14中保存異常返回標志,包括返回后進入線程模式還是處理器模式、指定SP使用PSP堆棧指針還是MSP堆棧指針。當調用 BX r14 指令后,硬件會知道要從異常返回,然后出棧,這個時候堆棧指針PSP已經指向了新任務堆棧的正確位置,當新任務的運行地址被出棧到PC寄存器后,新的任務也會被執行。

PC(R15):程序計數器,記錄下一個要執行指令的地址,改變它的值就能改變程序的執行流

R0:任務形參

通用寄存器:用於數據操作

 

 

切換流程:

1、進入PendSV中斷前,硬件自動將xPSR、PC(R15)、LR(R14)、R12、R0~R3壓入任務堆棧中(使用PSP堆棧指針)

2、手動將R4~R11壓入堆棧,將PSP棧頂值存入TCB(任務控制塊)

3、關 FreeRTOS 可屏蔽中斷

4、調用任務切換函數獲得新的任務控制塊

5、開中斷

6、通過新的任務控制塊更新PSP棧頂值,手動將R4~R11出棧

7、退出中斷時,硬件自動將xPSR、PC、LR、R12、R0~R3任務堆棧中出棧(使用PSP堆棧指針)

8、根據新的寄存器值和PSP執行新的任務

 

 

 

xPortPendSVHandler:
    mrs r0, psp
    isb
    ldr    r3, =pxCurrentTCB            /* Get the location of the current TCB. */
    ldr    r2, [r3]

    stmdb r0!, {r4-r11}                /* Save the remaining registers. */
    str r0, [r2]                    /* Save the new top of stack into the first member of the TCB. */

    stmdb sp!, {r3, r14}

  /* 接下來兩句是進入臨界區,即關FreeRTOS可屏蔽中斷 */
mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY msr basepri, r0 dsb isb bl vTaskSwitchContext
  /* 接下來兩句是退出臨界區,即開中斷 */
mov r0, #0 msr basepri, r0 ldmia sp!, {r3, r14} ldr r1, [r3] ldr r0, [r1] /* The first item in pxCurrentTCB is the task top of stack. */ ldmia r0!, {r4-r11} /* Pop the registers. */ msr psp, r0 isb bx r14

 

isb:清流水線,保證此操作完成后再執行下一條指令

ldr  r3, =pxCurrentTCB:pxCurrentTCB是一個指針,相當於兩個指針變量的賦值操作

ldr  r2,  [r3]:把r3指向的內容賦值給r2,因為指針pxCurrentTCB指向的結構體第一個成員變量是一個指針(pxTopOfStack),執行棧頂,所以此時 r2 為第一個成員變量指針

stmdb  r0!,  {r4-r11}:先將 r0 指向的地址遞減,再以新 r0 將 CPU 的 R4-R11 手動入棧,此時 R0 具體指向如下圖:

str r0, [r2]:將R2指向的內容更新為R0的值,即將棧頂值存到第一成員指針變量指向的位置,完成了上文的保存

stmdb  sp!,  {r3,  r14}:將 R3 和 R14 入棧,此刻的 SP 指向 MSP。對這兩個寄存器入棧的原因:此刻的 R3 指向了 pxCurrentTCB(R3 保存了 pxCurrentTCB 的地址),在接下來的函數調用可能被修改,而這個值在函數調用后還要用到;R14 值在函數調用后同樣要用到,而函數調用會修改 R14 的值(存儲調用子函數后的返回地址)

ldr  r1,  [r3]: 調用函數 vTaskSwitchContext 后,pxCurrentTCB指向的內容就是新的TCB,進而 R3 指向的內容也是新的TCB

 

參考鏈接:

https://blog.csdn.net/qingzhuyuxian/article/details/80604129

https://blog.csdn.net/qq_15100379/article/details/86166994


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM