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条评论)
登录后参与讨论