|
SH-Stick使用的目标芯片为SH2/Tiny系列的SH7125,内置大容量FLASH和RAM,以及丰富的外设,非常适合工业,家电等领域。 下面我们针对SH-Stick上的目标芯片SH7125来移植一个比较简单的实时操作系统uC/OS-II。通过本次移植,相信读者一定会快速的了解SH2系列处理器的内部结构。
SH2体系介绍: SH7125内部有16个通用寄存器R0 ~ R15。 其中R0 ~ R14为通用寄存器,用于日常的一些运算处理。R15为硬件栈指针寄存器,也就是我们平时说的SP寄存器,用于处理器发生异常时的栈操作。 另外还有PC,SR,GBR,VBR,MACH,MACL,PR寄存器。PC就是程序寄存器,SR为状态寄存器,GBR为全局基址寄存器,VBR为向量基址寄存器,MACH/MACL为乘法寄存器,PR为子程序返回地址寄存器。 如图1所示。
好了,我们熟悉了SH2的硬件结构,下面该介绍软件了。 笔者选用的RTOS为uC/OS-II 2.52版本,相信大家对这个OS已经很熟悉了,那就不用再介绍了。如果有不清楚的,可以去参考这本书的原著或者翻译本。
移植前的准备工作: SH-Stick开发板和附送的串口仿真器 HEW平台(安装了SH编译器)
一。新建一个SH7125的HEW工程。 至于怎么建工程,附送的SH-STICK光盘里已经有很详细的例子了,大家参考一下。我这里就不再啰嗦了。
二。确保工程能连上电脑。 串口波特率建议大家选高一点,因为串口仿真会比较慢。相关教材在光盘里,不再赘述。
三。将uC/OS-II的源码加入到工程中。 为了让整个工程更好的理解,笔者分了3个部分来装载源码:boot,app,kernel,分别对应bootloader文件,应用程序,内核程序。
我们将这3个文件放在HEW工程目录下,相关的源码放入其中(读者可以参考帖子附带的工程)。之后就要设置源码路径,以便HEW能找到源文件。 设置路径如下: Build -- SuperH Toolchain 里面,点击Add,选择Workspace directory,在里面填上源文件在HEW工程中的目录,如图2所示。 我们要添加3个目录进去(读者可以参考帖子附带的工程)。
图2
四。编写uC/OS-II的BSP以及与体系结构有关的源码。
1,BSP工作。
板级支持包BSP就是OS启动之前的一段代码,负责把引导OS内核,保证内核能正常启动。 uC/OS-II将使用SH7125的定时器A0作为时钟节拍。定时器A0初始化是直接参考SH-Stick附带的源码,每10ms产生一个中断,如下: /************************************************************* 名称:InitTimer 描述:初始化定时器 初始化定时器MTU2的通道0用作定时器模式 参数:无 返回值:无 输入:无 输出:无 **************************************************************/ VOID InitTimer( ){ STB.CR4.BIT._MTU2 = 0; //向MTU2模块提供时钟 MTU20.TIER.BYTE = 0; //不使能中断 MTU2.TSTR.BIT.CST0 = 0; //停止定时器 MTU20.TMDR.BYTE = 0x00; //设定为定时器模式 MTU20.TCR.BYTE = 0x21; //TGRA作为清除源,上升沿计数,计数源为MP/4 MTU20.TIOR.BYTE.H = 0; // MTU20.TIOR.BYTE.L = 0; MTU20.TSR.BYTE = 0xC0; //清除所有的中断标志 MTU20.TIER.BIT.TGIEA = 1; //使能TGRA匹配中断 MTU20.TCNT = 0; MTU20.TGRA = 60000; //计数频率为24MHz*8(PLL)/8(Divider)/4/60000=100Hz INTC.IPRD.BIT._MTU20G = 8; //设定中断优先级 // MTU2.TSTR.BIT.CST0 = 1; //启动定时器MTU2通道0 }
另外,uC/OS-II将使用SH7125的向量号为32的陷阱指令,类似于软中断,来进行任务切换。 因为,我们需要修改定时器A0和向量号为32的陷阱指令对应的中断向量表。具体过程可参考源码。
2.体系结构有关的源码
下面我们来编写uC/OS-II在SH7125上的移植代码。只有2个文件:os_cpu_c.c, os_cpu_c.src, os_cpu.h。 os_cpu_c.c里面主要实现一个函数*OSTaskStkInit,该函数用于创建任务时,对任务栈内容的初始化。 如下: OS_STK *OSTaskStkInit (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt) { OS_STK *stk; opt = opt; stk = (OS_STK *)ptos; *stk-- = 0x00000000; /* SR */ *stk-- = (OS_STK)task; /* PC */ *stk-- = 0xeeeeeeee; /* R14 */ *stk-- = 0xdddddddd; /* R13 */ *stk-- = 0xcccccccc; /* R12 */ *stk-- = 0xbbbbbbbb; /* R11 */ *stk-- = 0xaaaaaaaa; /* R10 */ *stk-- = 0x99999999; /* R9 */ *stk-- = 0x88888888; /* R8 */ *stk-- = 0x77777777; /* R7 */ *stk-- = 0x66666666; /* R6 */ *stk-- = 0x55555555; /* R5 */ *stk-- = (OS_STK)pdata; /* R4 */ *stk-- = 0x33333333; /* R3 */ *stk-- = 0x22222222; /* R2 */ *stk-- = 0x11111111; /* R1 */ *stk-- = 0x00000000; /* R0 */ *stk-- = 0x11000000; // GBR *stk-- = 0x22000000; // MACH *stk-- = 0x33000000; // MACL *stk = 0x44000000; // PR return ((OS_STK *)stk); }
os_cpu_c.src是汇编文件,实现四个函数: _OSStartHighRdy 任务开始 _OSCtxSw 任务切换 _OSIntCtxSw 中断级的任务切换 _OSTickISR 定时器中断处理 ;-------------------------------------------------- ;任务开始 ;-------------------------------------------------- _OSStartHighRdy: MOV.L #_OSTCBHighRdy,R2 ;指向指针的指针(指针的地址) MOV.L @R2,R2 ;指针值 MOV.L @R2,SPR ;将指针指向的值送入SP MOV.L #_OSRunning,R0 ;OSRunning = TRUE MOV.B #1,R1 MOV.B R1,@R0 POP_CS POP_Rn RTE ;中断返回,开始任务 NOP
;-------------------------------------------------- ;任务切换 ;-------------------------------------------------- _OSCtxSw: ;该ISR的入口地址对应指令(TRAPA #32) PUSH_Rn PUSH_CS MOV.L #_OSTCBCur,R3 ;保存当前任务SP MOV.L @R3,R2 MOV.L SPR,@R2 ;保存当前任务栈指针SP到OSTCBCur首地址 MOV.L #_OSTaskSwHook,R4 ;执行用户自定义程序 JSR @R4 NOP MOV.L #_OSPrioHighRdy,R0 ;OSPrioCur = OSPrioHighRdy MOV.B @R0,R0 MOV.L #_OSPrioCur,R1 MOV.B R0,@R1 MOV.L #_OSTCBHighRdy,R0 ;OSTCBCur = OSTCBHighRdy MOV.L @R0,R0 MOV.L #_OSTCBCur,R3 MOV.L R0,@R3 MOV.L #_OSTCBHighRdy,R5 ;载入新任务的SP MOV.L @R5,R5 MOV.L @R5,SPR POP_CS ;寄存器出栈 POP_Rn RTE ;中断返回,开始新任务 NOP ;-------------------------------------------------- ;中断级的任务切换 ;--------------------------------------------------- _OSIntCtxSw: ADD #OFFSET,SPR MOV.L #_OSTCBCur,R7 ;保存当前SP MOV.L @R7,R7 MOV.L SPR,@R7 MOV.L #_OSTaskSwHook,R0 ;执行用户自定义程序 JSR @R0 NOP MOV.L #_OSTCBHighRdy,R0 ;OSTCBCur = OSTCBHighRdy MOV.L @R0,R0 MOV.L #_OSTCBCur,R3 MOV.L R0,@R3 MOV.L #_OSPrioHighRdy,R2 ;OSPrioCur = OSPrioHighRdy MOV.B @R2,R2 MOV.L #_OSPrioCur,R3 MOV.B R2,@R3 MOV.L #_OSTCBHighRdy,R6 ;载入新任务SP MOV.L @R6,R6 MOV.L @R6,SPR POP_CS ;寄存器出栈 POP_Rn RTE ;中断返回,新任务开始 NOP ;--------------------------------------------------- ;系统时钟中断 ;--------------------------------------------------- _OSTickISR: PUSH_Rn PUSH_CS MOV.L #_OSIntEnter,R0 ;进入中断 JSR @R0 NOP MOV.L #_OSTimeTick,R0 ;时间节拍处理 JSR @R0 NOP MOV.L #_OSIntExit,R5 ;中断退出处理 JSR @R5 NOP POP_CS POP_Rn
RTE ;定时中断返回 NOP
os_cpu.h里面定义一些数据结构类型,开关中断,任务切换指令等。 我们使用set_imask()实现开关中断,使用trapa()实现任务切换。
五。编译,验证。 完成以上几个步骤后,笔者尝试写了一个简单的任务来验证移植是否正确。 该任务的作用是让SH-Stick上的P13对应的LED每隔1秒闪烁一次。 /* * uC/OS-II启动之后的第一个任务 * * 该任务的功能是: * PE13对应的LED每秒闪烁一次 */ void task1(void *pdata) { pdata = pdata; set_imask(0); // 开总中断 MTU2.TSTR.BIT.CST0 = 1; // 启动定时器 while(1){ PE.DRL.BIT.B13 = 1; OSTimeDlyHMSM(0, 0, 1, 0); PE.DRL.BIT.B13 = 0; OSTimeDlyHMSM(0, 0, 1, 0); } }
我们开始编译工程。第一次因为要编译库文件,时间比较长。排除一些错误和警告后,生成的abs调试文件通过串口下载到SH-Stick上。点击HEW上的“运行”按钮,SH-Stick上的P13对应的LED开始有节奏的闪烁。 本次移植工作宣告成功! 源程序:http://www.renesas-mcu.com/read.php?tid=507 |
|