移值UCOS2到M4核與M3核的區別

以前移值過ucos2到stm32f2系列的單片機,這個單片機是屬於arm的m3內核的。最近在學習永磁同步電機的控制,對於這個電機的控制,有比較多的數學計算,甚至於還有浮點的運算。因此用到了stm32f4系列的單片機,這個單片機內置FPU,能夠用幾條指令就能夠處理單精度的浮點數據,而它是屬於M4內核的。由於原先移植過M3的基礎,想着應該很快會搞定,沒想到移植了幾天的時間才搞清楚,下面就記錄下M3與M4內核的ucos2的移植不一樣之處。其實M3與M4內核相差不大,對於我應用的來講,其實最大的不一樣一是M4的最大主頻提升了,二是M4帶浮點功能。函數

一、ucos2移植到M3核的重點(對於ucos2移植到M3內核詳細的講解以及ucos2的結構後續會單獨寫一篇博客描述)學習

M3覈對於操做系統的支持很好,它有一個NVIC向量中斷控制器,它管理着對CM3的全部中斷請求。其中有幾個隊操做系統很重要的中斷:this

a、SVC(系統服務調用),用於產生系統函數的調用請求,例如操做系統一般不讓用戶程序直接訪問硬件,而是經過提供一些系統服務函數,讓用戶程序使用SVC發出對系統服務函數的呼叫請求,以這種方法調用它們來間接訪問硬件。所以,當用戶程序想要控制特定的硬件時,它就要產生一個SVC異常,而後操做系統提供的SVC異常服務例程獲得執行,它再調用相關的操做系統函數,後者完成用戶程序請求的服務。這與傳統的arm核好比arm7與amr9等的SWI的軟件中斷異常相似。spa

b、PendSV(可懸起的系統中斷),它是能夠像普通的中斷同樣被懸起的(不像SVC那樣會上訪)。OS能夠利用它「緩期執行」一個異常——直到其它重要的任務完成後才執行動做。懸起PendSV 的方法是:手工往NVIC的PendSV懸起寄存器中寫1。懸起後,若是優先級不夠高,則將緩期等待執行。因此它經常 被用來進行任務的切換,UCOS2的任務切換就是在這個中斷裏面實現的。操作系統

c、SysTick定時器被捆綁在NVIC中,用於產生SysTick異常(異常號:15)。SysTick定時器能產生中斷,CM3爲它專門開出一個異常類型,而且在向量表中有它的一席之地。它用於操做系統的滴答時鐘。在之前,操做系統還有全部使用了時基的系統,都必須一個硬件定時器來產生須要的「滴答」中斷,做爲整個系統的時基。滴答中斷對操做系統尤爲重要。例如,操做系統能夠爲多個任務許以不一樣數目的時間片,確保沒有一個任務能霸佔系統;或者把每一個定時器週期的某個時間範圍賜予特定的任務等,還有操做系統提供的各類定時功能,都與這個滴答定時器有關。指針

 

ucos2對每個任務都會分配一個任務控制塊,任務控制塊的首地址存放着該任務的堆棧指針,它存放的數據的格式如在函數,能夠看到它的堆棧是向下遞增的,也就是說堆棧頂部的位置始終是數值大的地址。首先存放的是M3內核發生異常時自動保存的數據,能夠看到任務的函數地址也被保存在內,它的值其實就是任務發生切換時PC的值,若是後續要切換回這個任務了只要作一次PendSV中斷,切換PSP指針爲這個任務的堆棧指針,當從PendSV中斷返回時task的值就會自動的存放到PC寄存器中,這樣就作到了任務切換。接着保存M3內核發生異常時沒有自動保存的寄存器,剩下的堆棧內容就保存任務函數的一些變量以及函數調用等等。rest

OS_STK *OSTaskStkInit (void (*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT16U opt)
{
    OS_STK *p_stk;


    (void)opt;                                                  /* 'opt' is not used, prevent warning                   */
    p_stk       = ptos;                            /* Load stack pointer                                 */
                                                                /* Registers stacked as if auto-saved on exception      */
    *(--p_stk) = (OS_STK)0x01000000uL;                          /* xPSR                                                 */
    *(--p_stk) = (OS_STK)task;                                  /* Entry Point                                          */
    *(--p_stk) = (OS_STK)OS_TaskReturn;                         /* R14 (LR)                                             */
    *(--p_stk) = (OS_STK)0x12121212uL;                          /* R12                                                  */
    *(--p_stk) = (OS_STK)0x03030303uL;                          /* R3                                                   */
    *(--p_stk) = (OS_STK)0x02020202uL;                          /* R2                                                   */
    *(--p_stk) = (OS_STK)0x01010101uL;                          /* R1                                                   */
    *(--p_stk) = (OS_STK)p_arg;                                 /* R0 : argument                                        */
                                                                /* Remaining registers saved on process stack           */
    *(--p_stk) = (OS_STK)0x11111111uL;                          /* R11                                                  */
    *(--p_stk) = (OS_STK)0x10101010uL;                          /* R10                                                  */
    *(--p_stk) = (OS_STK)0x09090909uL;                          /* R9                                                   */
    *(--p_stk) = (OS_STK)0x08080808uL;                          /* R8                                                   */
    *(--p_stk) = (OS_STK)0x07070707uL;                          /* R7                                                   */
    *(--p_stk) = (OS_STK)0x06060606uL;                          /* R6                                                   */
    *(--p_stk) = (OS_STK)0x05050505uL;                          /* R5                                                   */
    *(--p_stk) = (OS_STK)0x04040404uL;                          /* R4                                                   */
    return (p_stk);
}

接着看到UCOS2在M3內核下運行時的任務切換代碼,能夠看到任務切換函數,主要是將被打斷任務的堆棧保存好,而後將須要運行的任務的堆棧出棧,任何以從進程堆棧中作出棧操做,進行任務切換code

PendSV_Handler                                                  //;中斷處理函數
    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           ;檢查是不是從進程中進來的,若是不是,直接跳到OS_CPU_PendSVHandler_nosave

    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}                                               //;//只是爲了調用OSTaskSwHook函數

    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 取出新任務的R4-R11寄存器值
    ADDS    R0, R0, #0x20
    MSR     PSP, R0                                            // ; Load PSP with new process SP 切換新任務指針給PSP
    ORR     LR, LR, #0x04                                      // ; Ensure exception return uses process stack  從進程堆棧中做出出棧操做
    CPSIE   I                                                  // ; 開啓中斷
    BX      LR                                                 // ; Exception return will restore remaining context 任務切換,切換到新任務被切換時的地址

    END

 

二、ucos2移植到M4核blog

UCOS2在M3與M4運行任務切換時的原理是同樣的,都是利用的PendSV中斷。可是保存的寄存器有所不一樣,由於M4內核能夠支持單精度浮點操做,這樣就會有了浮點相關的寄存器的保存。這就是與M3內核最大的不一樣之處。支持浮點運算的M4內核比M3內核多一個FPU模塊,它裏面有33個寄存器,其中S0-S16是異常上下文會自動保存的。進程

 

a、M4內核堆棧的初始化,與M3相比多了保存34個寄存的內容

 

OS_STK *OSTaskStkInit (void (*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT16U opt)
{
    OS_STK *p_stk;

    (void)opt;                                                  /* 'opt' is not used, prevent warning                   */
    p_stk       = ptos;                            /* Load stack pointer                                 */

        #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
        *(--p_stk) = (INT32U)0x00000000L; //No Name Register  
        *(--p_stk) = (INT32U)0x00001000L; //FPSCR
        *(--p_stk) = (INT32U)0x00000015L; //s15
        *(--p_stk) = (INT32U)0x00000014L; //s14
        *(--p_stk) = (INT32U)0x00000013L; //s13
        *(--p_stk) = (INT32U)0x00000012L; //s12
        *(--p_stk) = (INT32U)0x00000011L; //s11
        *(--p_stk) = (INT32U)0x00000010L; //s10
        *(--p_stk) = (INT32U)0x00000009L; //s9
        *(--p_stk) = (INT32U)0x00000008L; //s8
        *(--p_stk) = (INT32U)0x00000007L; //s7
        *(--p_stk) = (INT32U)0x00000006L; //s6
        *(--p_stk) = (INT32U)0x00000005L; //s5
        *(--p_stk) = (INT32U)0x00000004L; //s4
        *(--p_stk) = (INT32U)0x00000003L; //s3
        *(--p_stk) = (INT32U)0x00000002L; //s2
        *(--p_stk) = (INT32U)0x00000001L; //s1
        *(--p_stk) = (INT32U)0x00000000L; //s0
        #endif

                                                                /* Registers stacked as if auto-saved on exception      */
    *(--p_stk) = (OS_STK)0x01000000uL;                          /* xPSR                                                 */
    *(--p_stk) = (OS_STK)task;                                  /* Entry Point                                          */
    *(--p_stk) = (OS_STK)OS_TaskReturn;                         /* R14 (LR)                                             */
    *(--p_stk) = (OS_STK)0x12121212uL;                          /* R12                                                  */
    *(--p_stk) = (OS_STK)0x03030303uL;                          /* R3                                                   */
    *(--p_stk) = (OS_STK)0x02020202uL;                          /* R2                                                   */
    *(--p_stk) = (OS_STK)0x01010101uL;                          /* R1                                                   */
    *(--p_stk) = (OS_STK)p_arg;                                 /* R0 : argument                                        */
        
        #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
        *(--p_stk) = (INT32U)0x00000031L; //s31
        *(--p_stk) = (INT32U)0x00000030L; //s30
        *(--p_stk) = (INT32U)0x00000029L; //s29
        *(--p_stk) = (INT32U)0x00000028L; //s28
        *(--p_stk) = (INT32U)0x00000027L; //s27
        *(--p_stk) = (INT32U)0x00000026L; //s26    
        *(--p_stk) = (INT32U)0x00000025L; //s25
        *(--p_stk) = (INT32U)0x00000024L; //s24
        *(--p_stk) = (INT32U)0x00000023L; //s23
        *(--p_stk) = (INT32U)0x00000022L; //s22
        *(--p_stk) = (INT32U)0x00000021L; //s21
        *(--p_stk) = (INT32U)0x00000020L; //s20
        *(--p_stk) = (INT32U)0x00000019L; //s19
        *(--p_stk) = (INT32U)0x00000018L; //s18
        *(--p_stk) = (INT32U)0x00000017L; //s17
        *(--p_stk) = (INT32U)0x00000016L; //s16
        #endif
                                                                /* Remaining registers saved on process stack           */
    *(--p_stk) = (OS_STK)0x11111111uL;                          /* R11                                                  */
    *(--p_stk) = (OS_STK)0x10101010uL;                          /* R10                                                  */
    *(--p_stk) = (OS_STK)0x09090909uL;                          /* R9                                                   */
    *(--p_stk) = (OS_STK)0x08080808uL;                          /* R8                                                   */
    *(--p_stk) = (OS_STK)0x07070707uL;                          /* R7                                                   */
    *(--p_stk) = (OS_STK)0x06060606uL;                          /* R6                                                   */
    *(--p_stk) = (OS_STK)0x05050505uL;                          /* R5                                                   */
    *(--p_stk) = (OS_STK)0x04040404uL;                          /* R4                                                   */
        
    return (p_stk);
}

 

a、M4內核的任務切換,與M3相比多了保存16個寄存器

PendSV_Handler                                                  //;中斷處理函數
    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           ;檢查是不是從進程中進來的,若是不是,直接跳到OS_CPU_PendSVHandler_nosave

        
        //;Is the task using the FPU context? If so, push high vfp registers.支持M4內核保存S16-S31寄存器
        TST     R14, #0x10
        IT         EQ
        VSTMDBEQ R0!, {S16-S31} 
    
    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}                                               //;//只是爲了調用OSTaskSwHook函數

    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 取出新任務的R4-R11寄存器值
    ADDS    R0, R0, #0x20
    
    ;Is the task using the FPU context? If so, push high vfp registers.支持M4內核出棧S16-S31寄存器
        TST     R14, #0x10
        IT         EQ
        VLDMIAEQ R0!, {S16-S31} 
    
    MSR     PSP, R0                                            // ; Load PSP with new process SP 切換新任務指針給PSP
    ORR     LR, LR, #0x04                                      // ; Ensure exception return uses process stack  從進程堆棧中做出出棧操做
    CPSIE   I                                                  // ; 開啓中斷
    BX      LR                                                 // ; Exception return will restore remaining context 任務切換,切換到新任務被切換時的地址

    END

 

 

 以上就是移值UCOS2到M4核與M3核的區別

相關文章
相關標籤/搜索