1. 程式人生 > >μC/OS-II 移植筆記 2(FreeScale 68HCS12 核微控制器)

μC/OS-II 移植筆記 2(FreeScale 68HCS12 核微控制器)

本文最初寫於 2012-04-20 於 sohu 部落格,這次部落格搬家一起搬到這裡來。

版權所有,轉載請註明出處。

2.2 OS_CPU_A.S

首先是函式和全域性變數的宣告。

;***************************************************************************
;                          PUBLIC DECLARATIONS
;***************************************************************************   
    xdef   OSCPUSaveSR
    xdef   OSCPURestoreSR    
    xdef   OSStartHighRdy
    xdef   OSCtxSw
    xdef   OSIntCtxSw
    xdef   OSTickISR
    
;***************************************************************************
;                      EXTERNAL DECLARATIONS
;***************************************************************************   
    xref   OSIntExit
    xref   OSIntNesting  
    xref   OSPrioCur    
    xref   OSPrioHighRdy
    xref   OSRunning   
    xref   OSTaskSwHook 
    xref   OSTCBCur     
    xref   OSTCBHighRdy 
    xref   OSTimeTick


然後是臨界區的相關程式碼。

OSCPUSaveSR:
    tfr  ccr,b       ; It's assumed that 8-bit return value is in register B
    sei              ; Disable interrupts
    rts              ; Return to caller with B containing the previous CCR

OSCPURestoreSR:
    tfr  b, ccr      ; B contains the CCR value to restore, move to CCR
    rts


下面的程式碼是重點部分,首先是 OSStartHighRdy 函式,雖然這個函式只在 OSStart 函式中被呼叫一次,在之後的程式生命週期中就再也用不到了,但這次呼叫至關重要,決定了使用者任務是否能被排程起來。因此程式碼中給出的註釋儘可能的詳細,我想看過註釋後就不需要我解釋什麼了。

;***********************************************************************
;           START HIGHEST PRIORITY TASK READY-TO-RUN
;
; Description : This function is called by OSStart() to start 
;  the highest priority task that was created by your 
;  application before calling OSStart().
;
; Arguments   : none
;
; Note(s)     : 1) The stack frame is assumed to look as follows:
;   
;                  OSTCBHighRdy->OSTCBStkPtr +  0       CCR
;                                            +  1       B
;                                            +  2       A
;                                            +  3       X (H)
;                                            +  4       X (L)
;                                            +  5       Y (H)
;                                            +  6       Y (L)
;                                            +  7       PC(H)
;                                            +  8       PC(L)
;
;               2) OSStartHighRdy() MUST:
;                      a) Call OSTaskSwHook() then,
;                      b) Set OSRunning to TRUE,
;                      c) Switch to the highest priority task by loading 
;                          the stack pointer of the highest priority task 
;                          into the SP register and execute an 
;                          RTI instruction.
;************************************************************************
OSStartHighRdy:
    jsr    OSTaskSwHook    ;  4~, 呼叫 OSTaskSwHook            

    ldab   #$01            ;  2~, OSRunning = 1
    stab   OSRunning       ;  4~                  

    ldx    OSTCBHighRdy    ;  3~, 將 OSTCBHighRdy 的地址放到暫存器 X
    lds    0, x            ;  3~, 將 OSTCBHighRdy->OSTCBStkPtr 放到 SP 
    nop                                
    rti                    ;  4~, Run task 


其實上面的程式碼也可以放到OS_CPU_C.C 中,下面是個示例:

#pragma CODE_SEG NON_BANKED
#pragma TRAP_PROC SAVE_NO_REGS   
void OSStartHighRdy(void)
{
    __asm jsr OSTaskSwHook ;  //OSTaskSwHook();
    __asm ldab #$01;
    __asm stab OSRunning;     // OSRunning = TRUE;     
    __asm
    {
        ldx    OSTCBHighRdy     
        lds    0, x             
        nop                          
    }
}


上面程式碼中 #pragma TRAP_PROC SAVE_NO_REGS 表示這是個中斷處理函式,編譯器不為之儲存任何暫存器內容。 雖然 OSStartHighRdy 並不是個真正的中斷處理函式,但它模擬卻模擬了中斷處理函式的行為,函式結束時呼叫 rti 而不是 rts 命令。

下面是任務切換的程式碼,註釋已經足夠的詳細了,如果有不明白的地方建議將 Jean J.Labrosse 的書再仔細讀讀。

OSCtxSw:
    ldy    OSTCBCur       ;  3~, OSTCBCur->OSTCBStkPtr = Stack Pointer     
    sts    0, y                                                    
OSIntCtxSw:
    jsr    OSTaskSwHook   ;  4~, Call user task switch hook                       
    ldx    OSTCBHighRdy   ;  3~, OSTCBCur  = OSTCBHighRdy
    stx    OSTCBCur                               
    ldab   OSPrioHighRdy  ;  3~, OSPrioCur = OSPrioHighRdy                        
    stab   OSPrioCur      
    lds    0, x           ;  3~, Load SP into 68HC12                              
    nop    
    rti                   ;  8~, Run task   


可以看到,上面兩個函式公用了大部分的函式體。上面的程式碼也可以直接寫到 OS_CPU_C.C 中,不過寫成 C 函式後就不能公用函式體了。

最後一部分是時鐘中斷程式,我使用RTI中斷作為週期性時鐘源。

OSTickISR:
    inc    OSIntNesting       ;  4~, Notify uC/OS-II about ISR
    ldab   OSIntNesting       ;  4~, if (OSIntNesting == 1) {    
    cmpb   #$01               
    bne    OSTickISR1         
    ldy    OSTCBCur           ;  OSTCBCur->OSTCBStkPtr = Stack Pointer     
    sts    0, y               ;  }                                          
OSTickISR1:
    BSET   $37, #128          ;  CRGFLG_RTIF = 1, 這句是反彙編出來的,應該沒錯
    jsr    OSTimeTick 
    jsr    OSIntExit          ;  6~+, Notify uC/OS-II about end of ISR
    rti        ;  12~, Return from interrupt, no higher priority tasks ready.


中斷程式的大部分程式碼都比較簡答,只有下面這句我下了番功夫才寫出來:
BSET   $37, #128         
與這行程式碼功能相同的 C 程式碼是:CRGFLG_RTIF = 1
我將 C程式碼直接生成彙編程式碼的結果是:BSET  _CRGFLG,#128
可是直接拿到彙編檔案中卻無法編譯,提示說 _CRGFLG 沒有定義。一番查詢才確定了_CRGFLG = 0x37。

2.3 OS_CPU_C.C
由於大部分的移植程式碼都放到了彙編檔案中,OS_CPU_C.C 中的工作就很少了。OS_CPU_C.C 最重要的工作是 OSTaskStkInit 函式,並且網上流傳的大多數 68HC12 核心的移植程式碼的這部分都或多或少的有問題。下面先給出我的程式碼:

OS_STK *OSTaskStkInit (void (*task)(void *pd), void *p_arg, OS_STK *ptos, INT16U opt)
{
    INT16U *wstk;
    INT8U  *bstk;
 
    (void) opt;             /* 'opt' is not used, prevent warning    */
    ptos--;                 /* 需要這麼調整一下棧頂地址,否則存的第一個int16 的低 Byte 會溢位堆疊 */
    wstk  = (INT16U *)ptos;       /* Load stack pointer  */
    *wstk-- = (INT16U)task;       /* Return address.  Format: PCH:PCL */
    *wstk-- = (INT16U)task;       /* Return address.  Format: PCH:PCL */
    *wstk-- = (INT16U)0x2222;     /* Y Register */
    *wstk-- = (INT16U)0x1111;     /* X Register */
    *wstk   = (INT16U)p_arg;      /* Simulate call to function with argument (In D Register) */  
     bstk    = (INT8U *)wstk;     /* Convert WORD ptr to BYTE ptr to set CCR                */
     bstk--;
    *bstk = (0xC0);               /* CCR Register (Disable STOP instruction and XIRQ)       */
    return ((OS_STK *)bstk);      /* Return pointer to new top-of-stack */   
}


其中有幾點需要特別注意:
(1)68HC12 核心與 68HC11 核心一個大的區別就是 SP 指向的是實棧頂。老的68HC11的移植程式碼都是 *--wstk = XXXX。移植到68HC12 核心就要改為*wstk-- = XXXX。否則會浪費掉堆疊的前兩個位元組。
(2)先要執行 ptos--;否則第一個雙位元組會有一半溢位堆疊空間。
(3)任務的引數傳遞是通過暫存器 D 的,而不是堆疊。網上程式碼多數是:

*wstk--   = (INT16U)p_arg;      
*wstk--   = (INT16U)task;     


這樣引數是傳遞不進來的,只有像我的程式碼中這樣寫才是正確的。
(4)程式碼中 *wstk-- = (INT16U)task; 重複了兩遍,千萬別以為這是我的筆誤。堆疊中先存的(INT16U)task實際上是 task 函式的返回地址。雖然 μC/OS-II 要求任務不能返回,但是作為 C 語言的呼叫約定,在呼叫一個 C 函式之前要將 C 函式的返回地址先入棧。因此我將 task 的地址重複了兩次,實際上第一的地址是什麼都不重要,因為程式執行中覺得不會用到。甚至不要這行也行,還能節省堆疊中兩個位元組的空間。不過我還是選擇了保留這行,使其看起來更加符合 C 語言的呼叫規範。
 
除此之外,OS_CPU_C.C 還包括一系列的 Hook 函式:

#if OS_CPU_HOOKS_EN > 0 && OS_VERSION > 203

void  OSInitHookBegin (void)
{
    #if OS_TMR_EN > 0
        OSTmrCtr = 0;
    #endif
}

void  OSInitHookEnd (void)
{
}

#endif


#if OS_CPU_HOOKS_EN > 0 

void  OSTaskCreateHook (OS_TCB *ptcb)
{
    #if OS_APP_HOOKS_EN > 0
        App_TaskCreateHook(ptcb);
    #else
        (void)ptcb;  
    #endif
}

void  OSTaskDelHook (OS_TCB *ptcb)
{
    #if OS_APP_HOOKS_EN > 0
        App_TaskDelHook(ptcb);
    #else
        (void)ptcb;   
    #endif
}


void  OSTaskStatHook (void)
{
    #if OS_APP_HOOKS_EN > 0
        App_TaskStatHook();
    #endif
}

void  OSTaskSwHook (void)
{
    #if OS_APP_HOOKS_EN > 0
        App_TaskSwHook();
    #endif
}

#endif


#if OS_CPU_HOOKS_EN > 0 && OS_VERSION >= 251
void  OSTaskIdleHook (void)
{
    #if OS_APP_HOOKS_EN > 0
        App_TaskIdleHook();
    #endif
}
#endif

#if OS_CPU_HOOKS_EN > 0 && OS_VERSION > 203
void  OSTCBInitHook (OS_TCB *ptcb)
{
#if OS_APP_HOOKS_EN > 0
    App_TCBInitHook(ptcb);
#else
    (void)ptcb;      /* Prevent compiler warning         */
#endif
}
#endif

#if OS_CPU_HOOKS_EN > 0 
void  OSTimeTickHook (void)
{
#if OS_APP_HOOKS_EN > 0
    App_TimeTickHook();
#endif

#if OS_TMR_EN > 0
    OSTmrCtr++;
    if (OSTmrCtr >= (OS_TICKS_PER_SEC / OS_TMR_CFG_TICKS_PER_SEC)) 
    {
        OSTmrCtr = 0;
        OSTmrSignal();
    }
#endif
}
#endif


程式碼中  OS_APP_HOOKS_EN  和 OS_TMR_EN  在v2.52 版本中還沒出現,我在這裡這樣寫是為了移植到後面版本時更輕鬆。

至此,移植程式碼就基本完成了。不過這樣還不能執行,因為兩個中斷處理函式(OSCtxSw和OSTickISR)還沒有和對應的中斷產生關聯。將這二者關聯起來的方法有幾種,比如直接在 PRM 檔案中制定,我用了種比較笨的辦法,從網上找了個 vector.c 檔案,雖然看起來不是很優雅,但確實是正確的程式碼。

/*******************************************************************
 *
 *              Freescale MC9S12DP256 ISR Vector Definitions
 *
 * File Name  : vectors.c
 * Version    : 1.0
 * Date       : Jun/22/2004
 * Programmer : Eric Shufro
 ********************************************************************/

/********************************************************************
*                 EXTERNAL ISR FUNCTION PROTOTYPES
*********************************************************************/

extern void near _Startup(void);    /* Startup Routine.                    */
extern void near  OSTickISR(void);  /* OS Time Tick Routine.               */
extern void near  OSCtxSw(void);    /* OS Contect Switch Routine.          */
extern void near  SCI1_ISR(void);   /* SCI1 Routine.                       */
extern void near  SCI0_ISR(void);   /* SCI0 Routine.                       */


/*
************************************************************************
*               DUMMY INTERRUPT SERVICE ROUTINES
*
* Description : When a spurious interrupt occurs, the processor will 
*               jump to the dedicated default handler and stay there
*               so that the source interrupt may be identified and
*               debugged.
*
* Notes       : Do Not Modify
************************************************************************
*/

#pragma CODE_SEG __NEAR_SEG NON_BANKED 
__interrupt void software_trap64 (void) {for(;;);}
__interrupt void software_trap63 (void) {for(;;);}
__interrupt void software_trap62 (void) {for(;;);}
__interrupt void software_trap61 (void) {for(;;);}
__interrupt void software_trap60 (void) {for(;;);}
__interrupt void software_trap59 (void) {for(;;);}
__interrupt void software_trap58 (void) {for(;;);}
__interrupt void software_trap57 (void) {for(;;);}
__interrupt void software_trap56 (void) {for(;;);}
__interrupt void software_trap55 (void) {for(;;);}
__interrupt void software_trap54 (void) {for(;;);}
__interrupt void software_trap53 (void) {for(;;);}
__interrupt void software_trap52 (void) {for(;;);}
__interrupt void software_trap51 (void) {for(;;);}
__interrupt void software_trap50 (void) {for(;;);}
__interrupt void software_trap49 (void) {for(;;);}
__interrupt void software_trap48 (void) {for(;;);}
__interrupt void software_trap47 (void) {for(;;);}
__interrupt void software_trap46 (void) {for(;;);}
__interrupt void software_trap45 (void) {for(;;);}
__interrupt void software_trap44 (void) {for(;;);}
__interrupt void software_trap43 (void) {for(;;);}
__interrupt void software_trap42 (void) {for(;;);}
__interrupt void software_trap41 (void) {for(;;);}
__interrupt void software_trap40 (void) {for(;;);}
__interrupt void software_trap39 (void) {for(;;);}
__interrupt void software_trap38 (void) {for(;;);}
__interrupt void software_trap37 (void) {for(;;);}
__interrupt void software_trap36 (void) {for(;;);}
__interrupt void software_trap35 (void) {for(;;);}
__interrupt void software_trap34 (void) {for(;;);}
__interrupt void software_trap33 (void) {for(;;);}
__interrupt void software_trap32 (void) {for(;;);}
__interrupt void software_trap31 (void) {for(;;);}
__interrupt void software_trap30 (void) {for(;;);}
__interrupt void software_trap29 (void) {for(;;);}
__interrupt void software_trap28 (void) {for(;;);}
__interrupt void software_trap27 (void) {for(;;);}
__interrupt void software_trap26 (void) {for(;;);}
__interrupt void software_trap25 (void) {for(;;);}
__interrupt void software_trap24 (void) {for(;;);}
__interrupt void software_trap23 (void) {for(;;);}
__interrupt void software_trap22 (void) {for(;;);}
__interrupt void software_trap21 (void) {for(;;);}
__interrupt void software_trap20 (void) {for(;;);}
__interrupt void software_trap19 (void) {for(;;);}
__interrupt void software_trap18 (void) {for(;;);}
__interrupt void software_trap17 (void) {for(;;);}
__interrupt void software_trap16 (void) {for(;;);}
__interrupt void software_trap15 (void) {for(;;);}
__interrupt void software_trap14 (void) {for(;;);}
__interrupt void software_trap13 (void) {for(;;);}
__interrupt void software_trap12 (void) {for(;;);}
__interrupt void software_trap11 (void) {for(;;);}
__interrupt void software_trap10 (void) {for(;;);}
__interrupt void software_trap09 (void) {for(;;);}
__interrupt void software_trap08 (void) {for(;;);}
__interrupt void software_trap07 (void) {for(;;);}
__interrupt void software_trap06 (void) {for(;;);}
__interrupt void software_trap05 (void) {for(;;);}
__interrupt void software_trap04 (void) {for(;;);}
__interrupt void software_trap03 (void) {for(;;);}
__interrupt void software_trap02 (void) {for(;;);}
__interrupt void software_trap01 (void) {for(;;);}
#pragma CODE_SEG DEFAULT   


/***********************************************************************
*               INTERRUPT VECTORS
***********************************************************************/

typedef void (*near tIsrFunc)(void);
const tIsrFunc _vect[] @0xFF80 = {     /* Interrupt table                           */
        software_trap63,      /* 63 RESERVED                               */
        software_trap62,      /* 62 RESERVED                               */
        software_trap61,      /* 61 RESERVED                               */
        software_trap60,      /* 60 RESERVED                               */
        software_trap59,      /* 59 RESERVED                               */
        software_trap58,      /* 58 RESERVED                               */
        software_trap57,      /* 57 PWM Emergency Shutdown                 */
        software_trap56,      /* 56 Port P Interrupt                       */
        software_trap55,      /* 55 CAN4 transmit                          */
        software_trap54,      /* 54 CAN4 receive                           */
        software_trap53,      /* 53 CAN4 errors                            */
        software_trap52,      /* 52 CAN4 wake-up                           */ 
        software_trap51,      /* 51 CAN3 transmit                          */
        software_trap50,      /* 50 CAN3 receive                           */
        software_trap49,      /* 49 CAN3 errors                            */
        software_trap48,      /* 48 CAN3 wake-up                           */ 
        software_trap47,      /* 47 CAN2 transmit                          */
        software_trap46,      /* 46 CAN2 receive                           */
        software_trap45,      /* 45 CAN2 errors                            */
        software_trap44,      /* 44 CAN2 wake-up                           */ 
        software_trap43,      /* 43 CAN1 transmit                          */
        software_trap42,      /* 42 CAN1 receive                           */
        software_trap41,      /* 41 CAN1 errors                            */
        software_trap40,      /* 40 CAN1 wake-up                           */ 
        software_trap39,      /* 39 CAN0 transmit                          */
        software_trap38,      /* 38 CAN0 receive                           */
        software_trap37,      /* 37 CAN0 errors                            */
        software_trap36,      /* 36 CAN0 wake-up                           */        
        software_trap35,      /* 35 FLASH                                  */
        software_trap34,      /* 34 EEPROM                                 */
        software_trap33,      /* 33 SPI2                                   */
        software_trap32,      /* 32 SPI1                                   */
        software_trap31,      /* 31 IIC Bus                                */
        software_trap30,      /* 30 BDLC                                   */
        software_trap29,      /* 29 CRG Self Clock Mode                    */
        software_trap28,      /* 28 CRG PLL lock                           */
        software_trap27,      /* 27 Pulse Accumulator B Overflow           */
        software_trap26,      /* 26 Modulus Down Counter underflow         */
        software_trap25,      /* 25 Port H                                 */
        software_trap24,      /* 24 Port J                                 */
        software_trap23,      /* 23 ATD1                                   */
        software_trap22,      /* 22 ATD0                                   */
        SCI1_ISR,             /* 21 SC11                                   */
        SCI0_ISR,             /* 20 SCI0                                   */                              
        software_trap19,      /* 19 SPI0                                   */
        software_trap18,      /* 18 Pulse accumulator input edge           */
        software_trap17,      /* 17 Pulse accumulator A overflow           */
        software_trap16,      /* 16 Enhanced Capture Timer Overflow        */
        software_trap15,      /* 15 Enhanced Capture Timer channel 7       */        
        software_trap14,      /* 14 Enhanced Capture Timer channel 6       */
        software_trap13,      /* 13 Enhanced Capture Timer channel 5       */
        software_trap12,      /* 12 Enhanced Capture Timer channel 4       */
        software_trap11,      /* 11 Enhanced Capture Timer channel 3       */
        software_trap10,      /* 10 Enhanced Capture Timer channel 2       */
        software_trap09,      /* 09 Enhanced Capture Timer channel 1       */
        software_trap08,      /* 08 Enhanced Capture Timer channel 0       */
        OSTickISR,            /* 07 Real Time Interrupt                    */
        software_trap06,      /* 06 IRQ                                    */
        software_trap05,      /* 05 XIRQ                                   */
        OSCtxSw,              /* 04 SWI - Breakpoint on HCS12 Serial Mon.  */
        software_trap03,      /* 03 Unimplemented instruction trap         */
        software_trap02,      /* 02 COP failure reset                      */
        software_trap01//,    /* 01 Clock monitor fail reset               */
        //_Startup            /* 00 Reset vector                           */
   };            


            
後記:
當我完成全部移植工作並測試通過後,我又重新審視了一遍整個移植過程,發現走了許多彎路。這些彎路基本都是因為我對C編譯器的特性,尤其是內聯彙編的處理不熟悉造成的。比如中斷處理函式,其實可以直接寫到 OS_CPU_C.C 中。就可以省略了 vector.c 檔案了。其實我一開始也是這樣做的,但是最初的中斷處理函式混合了C 語句和彙編語句,產生了各種莫名奇妙的錯誤。比如下面的RTI中斷處理函式程式碼:

interrupt VectorNumber_Vrti void OSTickISR (void)
{ 
    OSIntNesting++;               // 4~, Notify uC/OS-II about ISR
    if (OSIntNesting == 1)
    {   
     __asm 
        {
            ldx    OSTCBCur     //  3~, OSTCBCur->OSTCBStkPtr = Stack Pointer     
         sts    0, x        //  3~,}                                           
        }
    } 
    CRGFLG_RTIF = 1;     // clear interrupt flag.             
    OSTimeTick();        // 6~+, Call uC/OS-II's tick updating function 
    OSIntExit ();        // 6~+, Notify uC/OS-II about end of ISR 
}  


對比後來的彙編程式碼,其實已經離成功很近了,只要將其中的C 語句全部用匯編寫成來大功告成了:

interrupt VectorNumber_Vrti void OSTickISR (void)
{ 
    __asm 
         {
          inc    OSIntNesting  
          ldab   OSIntNesting  
          cmpb   #$01             
          bne    OSTickISR1     
          ldx    OSTCBCur        
          sts    0, x  
OSTickISR1:
          BSET   _CRGFLG, #128 
          jsr    OSTimeTick  
          jsr    OSIntExit                                                      
       }
}  


其他的程式碼也一樣,都這樣改寫後就完全不需要 vector.c 檔案了。但這裡還是將這些本可以省略掉的程式碼保留下來了,是想記錄下一條真實的探索路程。