首先是函数和全局变量的声明。
- ;***************************************************************************
- ; 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 文件了。但这里还是将这些本可以省略掉的代码保留下来了,是想记录下一条真实的探索路程。
上一篇:Freescale 9S12 系列单片机应用笔记(ECT 模块) 1
下一篇:μC/OS-II 移植笔记 1(FreeScale 68HCS12 核单片机)
推荐阅读最新更新时间:2024-03-16 14:53