1. 程式人生 > >lpc2200移植ucos-II總結(四 編寫os_cpu_c.c檔案)

lpc2200移植ucos-II總結(四 編寫os_cpu_c.c檔案)

2.4 編寫os_cpu_c.c檔案

OSTaskStkInt()任務堆疊初始化函式,在編寫此函式之前,必須先確定任務的堆疊結構。而任務的堆疊結構是與CPU的體系結構、編譯器有密切的關聯。本移植的堆疊結構見圖2.1所示。

圖2.1 任務堆疊結構圖

1. 函式OSTaskStkInt()程式碼

OS_STK *OSTaskStkInit (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt)

{

    OS_STK *stk;

    opt    = opt;            /* 'opt'  沒有使用。作用是避免編譯器警告  */

    stk    = ptos;           /* 獲取堆疊指標                           */

                             /* 建立任務環境,ADS1.2使用滿遞減堆疊    */

    *stk = (OS_STK) task;    /*  pc   */

    *--stk = (OS_STK) task;  /*  lr   */

    *--stk = 0;                             /*  r12  */

    *--stk = 0;                             /*  r11  */

    *--stk = 0;                             /*  r10  */

    *--stk = 0;                             /*  r9   */

    *--stk = 0;                             /*  r8   */

    *--stk = 0;                             /*  r7   */

    *--stk = 0;                             /*  r6   */

    *--stk = 0;                             /*  r5   */

    *--stk = 0;                             /*  r4   */

    *--stk = 0;                             /*  r3   */

    *--stk = 0;                             /*  r2   */

    *--stk = 0;                             /*  r1   */

    *--stk = (unsigned int) pdata;          /* r0,第一個引數使用R0傳遞  */

    *--stk = (USER_USING_MODE|0x00);        /* spsr,允許 IRQ, FIQ 中斷  */

    *--stk = 0;                             /* 關中斷計數器OsEnterSum;  */

    return (stk);

OsEnterSum是作者定義的一個全域性變數,主要是用它來儲存關中斷的次數,這樣關中斷和開中斷就可以嵌套了。在呼叫OS_ENTER_CRITICAL()時,它的值加1,同時關中斷。在呼叫OS_EXIT_CRITICAL()時,它的值減1,同時開中斷。每個任務都有獨立的OsEnterSum,在任務切換時儲存和恢復各自的OsEnterSum。這樣,各個任務開關中斷的狀態可以不同,任務不必過分考慮關中斷對別的任務的影響。

說明:使用者建立任務時,OSTasKCreat()會呼叫OSTaskStkInt()函式初始化該任務的堆疊,並把返回的堆疊指標儲存到該任務的TCB結構中的最前面的引數OSTCBStkPtr中,當該任務要被恢復時,任務切換函式從其TCB塊中取得其任務堆疊指標,依次將堆疊內容彈到處理器對應的CPSR、r0、r1,…,r12,lr,pc的暫存器中,完成現場的恢復和程式指標PC的返回。

2. 軟體中斷異常SWI服務程式C語言部分

引數SWI_Num為功能號,而Regs為指向堆疊中儲存暫存器的值的位置。軟中斷的0、1號功能並沒有在這裡實現,而是在OS_CUP_A.S中實現。

軟中斷的C語言部分程式碼

        void SWI_Exception(int SWI_Num, int *Regs)

{

    OS_TCB   *ptcb;

    switch(SWI_Num)

    {

         //case 0x00: /* 任務切換函式OS_TASK_SW,參考os_cpu_s.s檔案  */

         //    break;

         //case 0x01: /* 啟動任務函式OSStartHighRdy,參考os_cpu_s.s檔案 */

         //    break;

     case 0x02:       /* 關中斷函式OS_ENTER_CRITICAL(),參考os_cpu.h檔案 */

            __asm    /*C語言內嵌彙編指令關鍵字,通過__asm關鍵字可以在C程式中內嵌彙編程式*/

            {

                MRS     R0, SPSR      /*讀取SPSR暫存器的值,儲存到r0中

                ORR     R0, R0, #NoInt /*暫存器r0與立即數NoInt相或,並儲存到r0中,

                MSR     SPSR_c, R0    /*將r0的值寫入SPSR[7:0],關中斷

            }

            OsEnterSum++;

            break;

     case 0x03:     /* 開中斷函式OS_EXIT_CRITICAL(),參考os_cpu.h檔案 */

            if (--OsEnterSum == 0)

            {

                __asm

                {

                    MRS     R0, SPSR

                    BIC     R0, R0, #NoInt

                    MSR     SPSR_c, R0

                }

            }

            break;

        case 0x80:                      /* 任務切換到系統模式 */

            __asm

            {

                MRS     R0, SPSR

                BIC     R0, R0,  #0x1f  /* 將R0的低5位清零,其它位不變 */

                ORR     R0, R0, #SYS32Mode   

                MSR     SPSR_c, R0

            }

            break;

        case 0x81:                      /* 任務切換到使用者模式 */

            __asm

            {

                MRS     R0, SPSR

                BIC     R0, R0, #0x1f

                ORR     R0, R0, #USR32Mode   

                MSR     SPSR_c, R0

            }

            break;

        case 0x82:                      /* 任務是ARM程式碼     */

            if (Regs[0] <= OS_LOWEST_PRIO)

            {

                ptcb = OSTCBPrioTbl[Regs[0]];/*ptcb指向當前任務堆疊的首

地址,也就是當前任務堆疊結構的OsEnterSum

                if (ptcb != NULL)

                {

                    ptcb -> OSTCBStkPtr[1] &= ~(1 << 5) /*OSTCNStkPtr指向任務堆疊的棧頂,所以OSTCBStkPtr[1]指向任務堆疊的SPSR暫存器,通過改變SPSR相應位切換處理器模式

                }

            }

            break;

        case 0x83:                          /* 任務是THUMB程式碼 */

            if (Regs[0] <= OS_LOWEST_PRIO)

            {

                ptcb = OSTCBPrioTbl[Regs[0]];

                if (ptcb != NULL)

                {

                    ptcb -> OSTCBStkPtr[1] |= (1 << 5);

                }

            }

            break;

        default:

            break;

}

3. 巨集OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()

這裡需要對關中斷函式和開中斷函式作些解釋。與所有的實時核心一樣,μC/OS-Ⅱ需要先禁止中斷再訪問程式碼的臨界段,並且在訪問完畢後重新允許中斷。這就使得μC/OS-Ⅱ能夠保護臨界段程式碼免受多工或中斷服務破壞。實現OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()這2個巨集的定義有3種方法,本移植採用的是第三種方法,即在OS_CPU.H檔案中使OS_CRITICAL_MEHTOD等於3,這種方法是利用某些編譯器提供的擴充套件功能,使用者可以得到當前處理器狀態字的值,並將其儲存在C函式的區域性變數之中,這個變數可以用於恢復PSW,而本ARM核心關中斷和開中斷時,是通過改變程式狀態暫存器CPSR中的相應控制位實現。由於使用了軟體中斷,程式狀態暫存器 CPSR儲存到程式狀態儲存暫存器 SPSR中,軟體中斷退出時會將SPSR恢復到CPSR中。所以程式只要改變程式狀態儲存暫存器SPSR中相應的控制位就可以了。

4. OSStartHighRdy

uC/OS-II啟動多工環境的函式是OSStart()。使用者在呼叫OSStart()之前,必須已經建立了一個或更多工。OSStart()最終呼叫函式OSStartHighRdy()執行多工啟動前優先順序最高的任務,OSStartHighRdy()的程式碼如下。

  void OSStartHighRdy(void)

{

    _OSStartHighRdy();

}

通過函式_OSStartHighRdy()進入軟中斷SoftwareInterrupt,最終呼叫__OSStartHighRdy。__OSStartHighRdy程式碼如下所示

__OSStartHighRdy

        MSR     CPSR_c, #(NoInt | SYS32Mode);/*關中斷,切換到系統模式

        /*告訴uC/OS-II自身已經執行,將1儲存到OSRunning*/

        LDR     R4, =OSRunning /*將OSRunning的地址載入到R4,

R4裡存放的是OSRunning的地址

        MOV     R5, #1

        STRB    R5, [R4]       /*將R5的值儲存到R4指定的地址中,

只儲存1位元組資料

        BL      OSTaskSwHook   ;呼叫鉤子函式

        LDR     R6, =OSTCBHighRdy  /*將OSTCBHighRdy的地址載入到R6

        LDR    R6, [R6]     /*載入R6指定地址上的資料(字),放入R6中

        B       OSIntCtxSw_1

5. 移植增加的特定函式

根據ARM核心的特點和移植目標,為此增加了兩個處理器模式轉換函式(ChangTo-SYSMode()、ChangeToUSRMode())和兩個任務初始指令集設定函式(TaskIsARM()、TaskIsTHUMBle())。他們都是通過中斷指令SWI轉換到系統模式,通過軟體中斷服務程式實現的。

ChangeToSYSMode()把當前任務轉換到系統模式,ChangeToUSRMode()把當前任務轉換到使用者模式。她們改變程式狀態儲存暫存器SPSR的相應位段,而程式狀態儲存暫存器會在軟體中斷退出時複製到程式狀態暫存器CPSR,任務的處理器模式就改變了。

本移植增加了兩個函式TaskIsARM()和TaskIsTHUMB()用於改變任務建立時預設的指令集。函式TaskIsARM()用於宣告指定優先順序的任務的第一條指令是ARM指令集中的指令,而函式TaskIsTHUMB()用於宣告指定優先順序的任務的第一條指令是THUMB指令集中的指令,她們都有唯一的引數,即需要改變的任務的優先順序。值得注意的是,這兩個函式必須在相應的任務建立後但還沒執行時呼叫。這樣,如果在低優先順序的任務中建立高優先順序的任務就十分危險了。此時,解決的方法有三種:

(1) 高優先順序任務使用預設的指令集;

(2) 改變函式OSTaskCreateHook()使任務預設不是出於就緒狀態,建立任務後呼叫函式OSTaskResume()來使任務進入就緒狀態。

(3) 建立任務時禁止任務切換,呼叫函式TaskIsARM()或TaskIsTHUMB()後再允許任務切換。

6. …Hook()函式

uC/OS-II有很多由使用者編寫的…Hook()函式,它在移植中全為空函式,使用者就可以按照uC/OS-II的要求修改它。