μC/OS-II 移植笔记 2(FreeScale 68HCS12 核单片机)

来源:互联网 发布:网络的使用界限 编辑:程序博客网 时间:2024/05/01 14:12

本文最初写于 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 CCROSCPURestoreSR:    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 > 203void  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 >= 251void  OSTaskIdleHook (void){    #if OS_APP_HOOKS_EN > 0        App_TaskIdleHook();    #endif}#endif#if OS_CPU_HOOKS_EN > 0 && OS_VERSION > 203void  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 文件了。但这里还是将这些本可以省略掉的代码保留下来了,是想记录下一条真实的探索路程。

原创粉丝点击