原创 uC/OS-II在R8C/M16C上的移植详解

2011-2-25 14:06 2016 5 5 分类: MCU/ 嵌入式
来源:瑞萨单片机论坛

uC/OS-II非常适合在R8C/M16C等资源受限制的单片机中使用。使用实时系统的好处是能借助操作系统提供的功能,最大限度的优化程序。另外,由于使用了多任务,程序结构清晰,便于维护和升级等。 
本文希望能帮助R8C/M16C的用户快速的掌握实时系统的移植及使用。 

下面以M16C单片机为例 
系统配置: 
IDE: HEW nc30 for M16C/62P 
CPU: Renesas M16C/62P 
OS: uC/OS-II 2.51 
Emluator: E8a 

uC/OS-II说明: 
该实时操作系统内核为2.51版本。可由用户运行56个任务。具体功能参见《uC/OS-II》。 

移植步骤: 
1.建立编译环境。 
新建一个M16C/62P的汇编工程。当然,也可以使用C工程,但是使用汇编更加方便些。 

2.修改中断向量表。 
本系统使用向量号为32的软中断指令来进行任务切换。另外,系统时钟使用16位定时器TA0溢出中断。 
因此,需要在向量表中填充_OSCtxSw和_OSTickISR。 

在sect30.inc文件中,将向量表修改如下: 
;--------------------------------------------------------------------- 
; variable vector section 
;--------------------------------------------------------------------- 
   .section vector,ROMDATA 
   .org   __VECTOR_ADR__ 
   .glb        dummy_int 
   .glb        _OSCtxSw 
   .glb        _OSTickISR 
   .lword dummy_int   ; vector 0 BRK 
   .lword dummy_int   ; vector 1 
   .lword dummy_int   ; vector 2 
   .lword dummy_int   ; vector 3 
   .lword dummy_int   ; vector 4 (for user) int3 
   .lword dummy_int   ; vector 5 (for user) timerB5 
   .lword dummy_int   ; vector 6 (for user) timerB4 
   .lword dummy_int   ; vector 7 (for user) timerB3 
   .lword dummy_int   ; vector 8 (for user) si/o4/int5 
   .lword dummy_int   ; vector 9 (for user) si/o3/int4 
   .lword dummy_int   ; vector 10 (for user) Bus collision detection 
   .lword dummy_int   ; vector 11 (for user) DMA0 
   .lword dummy_int   ; vector 12 (for user) DMA1 
   .lword dummy_int   ; vector 13 (for user) Key input interrupt 
   .lword dummy_int   ; vector 14 (for user) A-D 
   .lword dummy_int   ; vector 15 (for user) uart2 transmit 
   .lword dummy_int   ; vector 16 (for user) uart2 receive 
   .lword dummy_int   ; vector 17 (for user) uart0 transmit 
   .lword dummy_int   ; vector 18 (for user) uart0 receive 
   .lword dummy_int   ; vector 19 (for user) uart1 transmit 
   .lword dummy_int   ; vector 20 (for user) uart1 receive 
   .lword _OSTickISR   ; vector 21 (for user) timer A0 
   .lword dummy_int   ; vector 22 (for user) timer A1 
   .lword dummy_int   ; vector 23 (for user) timer A2 
   .lword dummy_int   ; vector 24 (for user) timer A3 
   .lword dummy_int   ; vector 25 (for user) timer A4 
   .lword dummy_int   ; vector 26 (for user) timer B0 
   .lword dummy_int   ; vector 27 (for user) timer B1 
   .lword dummy_int   ; vector 28 (for user) timer B2 
   .lword dummy_int   ; vector 29 (for user) int0 
   .lword dummy_int   ; vector 30 (for user) int1 
   .lword dummy_int   ; vector 31 (for user) int2 
   .lword _OSCtxSw   ; vector 32 (for user or MR30) 
   .lword dummy_int   ; vector 33 (for user or MR30) 
   .lword dummy_int   ; vector 34 (for user or MR30) 
   .lword dummy_int   ; vector 35 (for user or MR30) 
   .lword dummy_int   ; vector 36 (for user or MR30) 
   .lword dummy_int   ; vector 37 (for user or MR30) 
   .lword dummy_int   ; vector 38 (for user or MR30) 
   .lword dummy_int   ; vector 39 (for user or MR30) 
   .lword dummy_int   ; vector 40 (for user or MR30) 
   .lword dummy_int   ; vector 41 (for user or MR30) 
   .lword dummy_int   ; vector 42 (for user or MR30) 
   .lword dummy_int   ; vector 43 (for user or MR30) 
   .lword dummy_int   ; vector 44 (for user or MR30) 
   .lword dummy_int   ; vector 45 (for user or MR30) 
   .lword dummy_int   ; vector 46 (for user or MR30) 
   .lword dummy_int    ; vector 47 (for user or MR30) 

3.硬件初始化 
在进入系统之前要初始化时钟,定时器等CPU及片上I/O资源,以便为uC/OS-II建立运行环境。 
注意:M16C有2个栈指针寄存器USP和ISP,其中UIP为非中断栈指针,ISP为中断栈指针。 
UCOS只需要使用一个栈指针,为了程序方便,我们将FLAG寄存器里面的U位设置为0,使用ISP。 

本机系统时钟根据CPU定时器TA0来配置 
// 定时器TA0初始化 
void timer_init(void) 


    ta0mr = 0x80;                                   // Timer mode 
                                                 // Clock source is f32      
    udf = 0x00;                                     // A0 Down count   
    ta0 = (INT16U)(10000000/32/OS_TICKS_PER_SEC);   // Time tick is OS_TICKS_PER_SEC per second.                        
    ta0s = 0;                                       // A0 Stop count 


4.编写os_cpu_c.c文件。 
根据CPU的体系结构,模拟中断发生后的栈状态。 
因为PC寄存器是20位,而栈单元是16位,所以这里在保存PC时,我们做了一些处理。 
OS_STK *OSTaskStkInit (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt) 

    INT16U *pstk16; 
    INT16U   flag; 
    flag       = 0x0040; 
    pstk16     = (INT16U *)ptos; 
                                                      
    *pstk16-- = (flag                   &     0x00FF)            // FLAG & PC(high 4 bits) 
                 | (((INT32U)task >> 8) & 0x00000F00)         
                 | ((flag         << 4) &     0xF000);          
    *pstk16-- = (((INT32U)task      )   & 0x0000FFFF);           // PC (low 16 bits) 
                                                                                        
    *pstk16-- = 0x7777;                    // FB                                      
    *pstk16-- = 0x6666;                                // SB                                           
    *pstk16-- = 0x5555;                    // A1                                           
    *pstk16-- = 0x4444;                    // A0                                           
    *pstk16-- = 0x3333;                    // R3                                           
    *pstk16-- = ((INT32U)pdata >> 16L);                        // R2                                      
    *pstk16-- = ((INT32U)pdata & 0x0000FFFFL);                 // R1                
    *pstk16    = 0x1111;                            // R0                                          
    return ((OS_STK *)pstk16); 


5.编写os_cpu.h文件。 
#define BOOLEAN unsigned char 
#define INT8U   unsigned char 
#define INT8S   signed char 
#define    INT16U unsigned int                
#define    INT16S signed   int 
#define    INT32U unsigned long                
#define    INT32S signed   long 
#define OS_CRITICAL_METHOD    3     
#if      OS_CRITICAL_METHOD == 1 
#define OS_ENTER_CRITICAL() asm("FCLR I")                     /* Disable interrupts                  */ 
#define OS_EXIT_CRITICAL()   asm("FSET I")                     /* Enable interrupts                  */ 
#endif 

#if      OS_CRITICAL_METHOD == 2 
#define OS_ENTER_CRITICAL() asm("PUSHC FLG"); asm("FCLR I")   /* Disable interrupts                  */ 
#define OS_EXIT_CRITICAL()   asm("POPC FLG")                   /* Enable interrupts                  */ 
#endif 
#if      OS_CRITICAL_METHOD == 3 
#define OS_ENTER_CRITICAL() asm("STC FLG, $@", cpu_sr);asm("FCLR I")   /* Disable interrupts         */ 
#define OS_EXIT_CRITICAL()   asm("LDC $@, FLG", cpu_sr)                 /* Enable interrupts         */ 
#endif 

#define OS_STK_GROWTH   1   // stacks grow from high to low 

#define OS_TASK_SW()   asm("INT #32")        // uC/OS-II context switch 
对于软中断指令,也可以使用NC30提供的命令 
#pragma INTCALL 32 OS_TASK_SW() 
extern void OS_TASK_SW(void); 

6.编写os_cpu_a.a30文件。 
   .GLB        _OSTCBCur               
    .GLB        _OSTCBHighRdy          
    .GLB        _OSPrioCur             
    .GLB        _OSPrioHighRdy         
    .GLB        _OSIntNesting          
    .GLB        _OSRunning             
    .GLB        _OSIntExit            
.GLB        _OSIntEnter 
    .GLB        _OSTimeTick 

;------------------------------------------------------------- 
; start multi-tasks 
;------------------------------------------------------------- 

    .SECTION    program 
    .GLB        _OSStartHighRdy 
_OSStartHighRdy: 
    MOV.W       _OSTCBHighRdy, A0              ; ISP = OSTCBHighRdy->OSTCBStkPtr 
    LDC         [A0], ISP 
    MOV.B       #01H, _OSRunning               ; OSRunning = TRUE 
    POPM        R0,R1,R2,R3,A0,A1,SB,FB 
    REIT 
;------------------------------------------------------------- 
; task switch 
;------------------------------------------------------------- 
    .SECTION    program 
    .GLB        _OSCtxSw 

_OSCtxSw: 
    PUSHM       R0,R1,R2,R3,A0,A1,SB,FB 
    MOV.W       _OSTCBCur, A0                   ; OSTCBCur->OSTCBStkPtr = SP 
    STC         ISP, [A0] 
    MOV.W       _OSTCBHighRdy, _OSTCBCur       ; OSTCBCur = OSTCBHighRdy 
    MOV.W       _OSPrioHighRdy, _OSPrioCur      ; OSPrioCur = OSPrioHighRdy 
    MOV.W       _OSTCBHighRdy, A0               ; SP        = OSTCBHighRdy->OSTCBStkPtr 
    LDC         [A0], ISP 
    POPM        R0,R1,R2,R3,A0,A1,SB,FB         ; Restore all processor registers from the new task's stack 
    REIT 

;------------------------------------------------------------- 
; task switch when interrupt occurs 
;------------------------------------------------------------- 
    .SECTION    program 
    .GLB        _OSIntCtxSw 
_OSIntCtxSw: 
    STC         ISP, A0 
ADD.W       #10, A0 
LDC         A0, ISP                         ; SP = SP + 10 

MOV.W       _OSTCBCur, A0 
STC         ISP, [A0]                       ; Save SP 
    MOV.W       _OSTCBHighRdy, _OSTCBCur        ; OSTCBCur = OSTCBHighRdy 
    MOV.W       _OSPrioHighRdy, _OSPrioCur      ; OSPrioCur = OSPrioHighRdy 
    MOV.W       _OSTCBHighRdy, A0               ; SP        = OSTCBHighRdy->OSTCBStkPtr 
    LDC         [A0], ISP 
    POPM        R0,R1,R2,R3,A0,A1,SB,FB         ; Restore all processor registers from the new task's stack 
    REIT 

;------------------------------------------------------------- 
; timer A0 ISR 
;------------------------------------------------------------- 
    .SECTION    program 
    .GLB        _OSTickISR 
_OSTickISR: 
    PUSHM       R0,R1,R2,R3,A0,A1,SB,FB             ; Save current task's registers 

    JSR         _OSIntEnter 
    JSR         _OSTimeTick                         ; OSTimeTick() 
    JSR         _OSIntExit                          ; OSIntExit() 
    POPM        R0,R1,R2,R3,A0,A1,SB,FB             ; Restore registers from the new task's stack 
    REIT 


    .END 

注意: 
在NC30编译器下,进行中断级的任务切换时... 
如果OS_CRITICAL_METHOD 为1,调整3个栈单元(6 Bytes) 
如果OS_CRITICAL_METHOD 为3,调整5个栈单元(10 Bytes) 



总结: 
移植时,要特别注意USP和ISP的使用,最好是使用ISP。 
另外,进行中断级的任务切换时,一定要根据编译器来调整栈指针的偏移量。

文章评论0条评论)

登录后参与讨论
我要评论
0
5
关闭 站长推荐上一条 /3 下一条