μC/OS-Ⅱ的移植集中在OS_CPU.h,OS_CPU_A.s,OS_CPU.c这三个文件上,下面分别详细介绍三个文件中的函数和需要修改或者编写的代码。
1. OS_CPU.h的移植
该文件定义了和处理器及编译器相关的定义及一些全局函数声明。由于ARM7 处理器字长为32位,半字长为16位,字节为8位,因此在OS_CPU.h文件修改与编译器相关的定义如下:
typedef unsigned char BOOLEAN;
typedef unsigned char INT8U;
typedef signed char INT8S;
typedef unsigned short INT16U;
typedef signed short INT16S;
typedef unsigned long INT32U;
typedef signed long INT32S;
typedef float FP32;
typedef double FP64;
typedef unsigned long OS_STK;
#define OS_CRITICAL_METHOD 2
#define OS_ENTER_CRITICAL() ARMDisableInt()
#define OS_EXIT_CRITICAL() ARMEnableInt()
#define OS_STK_GROWTH 1
#define OS_TASK_SW OSCtxSw
extern void OSCtxSw(void);
extern void OSIntCtxSw(void);
extern void ARMDisableInt(void);
extern void ARMEnableInt(void);
extern void OSTickISR(void);
2. OS_CPU_C.C文件
移植OS_CPU_C.C文件时,需要编写的是任务堆栈初始化函数OSTaskStkInit和时钟节拍中断服务钩子函数OSTimeTickHook。
在μC/OS-II中,每一个任务都有自己的任务堆栈,当发生任务切换或者中断时,其CPU使用权被剥脱,为了任务能被再次运行,那么这个被打断的任务所用到的处理器的寄存器内容均应得到保存,按照ARM7 处理器的压栈和入栈指令的特点,设计任务堆栈如下图2:
CPSR
R0
R1
……
R12
LR(R14)
PC(R15)
图2 任务堆栈的结构
根据任务堆栈结构示意图,OS_STK函数编写如下:
#define SVCMODE 0x13
OS_STK * OSTaskStkInit (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt){
OS_STK *stk;
opt = opt;
stk = (OS_STK) ptos;
*--stk = (OS_STK) task;
*--stk = (OS_STK) task;
*--stk = 0;
*--stk = 0;
*--stk = 0;
*--stk = 0;
*--stk = 0;
*--stk = 0;
*--stk = 0;
*--stk = 0;
*--stk = 0;
*--stk = 0;
*--stk = 0;
*--stk = 0;
*--stk = (INT32U) pdata;
*--stk = (SVC32MODE|0x40);
return ((OS_STK *)stk);
}
说明:用户创建任务时,OSTaskCreat()会调用OSTaskStkInit函数初始化该任务的堆栈,并把返回的堆栈指针保存到该任务的TCB结构中的最前面的参数OSTCBStkPtr中,当该任务要被恢复时,任务切换函数从其TCB块中取得其任务堆栈指针,依次将堆栈内容弹到处理器对应的 CPSR、r0,r1,…,r12,lr,pc的寄存器中,完成现场的恢复和程序指针PC的返回。
另一个需要编写的函数是OSTimeTickHook,该函数被时钟节拍中断服务函数OSTickISR中的OSTimeTick函数调用,用来清除时钟节拍中断发生设备的请求。本移植方案使用S3C44B0X处理器的RTC模块的tick中断作为时钟节拍中断,该函数编写如下:
void OSTimeTickHook(void){
rI_ISPC =((INT32U)0x01) << 20;
}
注意:用户也可不修改此函数,但是必须在OSTickISR中执行清除发生节拍中断的设备的中断请求标志,为便于说明,本文将利用内核提供给用户的OSTimeTickHook函数来完成清中断的任务。
另外几个hook函数不必去改它们。至此,OS_CPU.C编写完成。
3. OS_CPU_A.S文件的移植
该文件是移植过程中唯一需要用汇编语言来实现的文件,也是移植的重点和难点所在。在这个文件里,需要编写的函数有OSStartHighRdy,OSCtxSW,OSIntCtxSW,OSTickISR,ARMDisableInt,ARMEnableInt几个。
下面先结合us/os的任务切换的过程分析一下这几个函数的作用。
1)OSStartHighRdy()函数
当程序执行内核的OSStart函数时,表示多任务系统开始启动, OSStart函数将调用OSStartHighRdy函数从最高优先级任务的TCB块中获得该任务的堆栈指针,通过该指针,依次从该任务的任务堆栈中恢复CPU的现场。由于任务在堆栈初始化时,已经设定了弹出到程序指针寄存器PC的是该任务函数的入口地址,因此,OSStartHighRdy函数只需依次弹出任务栈内容到处理起寄存器,该任务便将得以运行。
2)OSCtxSw()函数
该函数是任务级的上下文切换函数,当任务被阻塞而主动请求CPU开始任务调度时执行,其过程是将当前任务的的CPU现场保存到该任务堆栈中去,然后从 OSTCBHighRdy中获得更高优先级任务的堆栈指针,再从该指针指向的堆栈中恢复此任务的CPU现场,使之继续执行,从而完成一次任务级别的切换。表2为OSCtxSw函数的伪代码。
void OSCtxSw(void) {
保存处理器寄存器;
OSTCBCur->OSTCBStkPtr = sp;
OSTCBCur = OSTCBHighRdy;
SP = OSTCBHighRdy->OSTCBStkPtr;
恢复该任务的现场();
执行中断返回指令;
}
表2 OSCtxSw函数的伪代码
3) OSIntCtxSw() 函数
该函数用于中断级的上下文切换。由于CPU响应时钟节拍中断后,处理器从svc进入了irq模式,并进入时钟节拍中断服务函数OSTickISR, OSTickISR函数发现若有高优先级任务需要运行,则系统不返回中断前的任务,而直接调度就绪的高优先级任务使之尽快得到执行,以保证实时性能。但是由于OSTickISR函数一开始已经保存过任务中断前的CPU现场,因此OSIntCtxSW()不需要再进行类似的操作。当OSTickISR调用 OSIntExit函数找出需要运行的更高优先级任务后,OSIntExit会将该任务的TCB指针放在OSTCBHighRdy中,然后 OSIntExit在最后调用OSIntCtxSW函数来从OSTCBHighRdy中获取堆栈指针然后恢复该高优先级任务的现场,使得其继续执行,并不再返回时钟节拍中断服务程序。显然,OSIntCtxSW函数的过程和OSCtxSW函数的后半部分操作相同,因此,OSCtxSW可以借用 OSIntCtxSW的代码。
4) OSTickISR()函数
在 CPU响应时钟节拍中断后,程序指针PC发生跳转后进入该函数,由于OSTickISR调用OSTimeTick函数使得所有的延时节拍不为0的任务延时节拍数减1,并调用OSIntExit函数来找出就绪的高优先级任务,若需要切换,则最后由OSIntCtxSw来完成新任务的调度,否则仍然返回到被时钟节拍中断的任务。OSTickISR函数的伪码和注释见表3。
5) ARMDisableInt和ARMEnableInt函数
ARMDisableInt 是用来暂时禁止FIQ及IRQ中断的函数,ARMEnableInt则是恢复ARMDisableInt执行前的中断使能状态,二者成对使用,用来保护临界段代码不被中断破坏。本移植使用方式2,即在进入临界段代码前关中断,完成后恢复先前的中断使能状态。
void OSTickISR(void) {
保存处理器寄存器;
调用OSIntEnter();
给产生中断的设备清中断;
调用OSTimeTick();
调用OSIntExit();
恢复处理器寄存器;
执行中断返回指令;
}
表3 OSTickTime函数的伪码
下面给出OS_CPU_A.S的全部内容和注释。
; *****OS_CPU_A.S文件汇编代码开始*****
AREA |subr|, CODE, READONLY ;声明为代码段
;***** OSStartHighRdy代码开始*****
OSStartHighRdy ; 使就绪表中任务最高的优先级的任务开始运行
; *****下面开始OSCtxSw函数,完成任务级的任务切换*****
OSCtxSw
; *****下面OSCtxSw准备恢复优先级更高的就绪任务,这部分可共用OSIntCtxSw的代码*****
; *****OSIntCtxSw函数开始*****
OSIntCtxSw ;准备任务切换
; *****OSTickISR开始*****
OSTickISR ;时钟节拍中断服务程序入口,需要用户在主函数中安装
;******OSTickISR函数代码完成,下面是临界段代码前后开关中断的函数******
ARMDisableInt
;*****OS_CPU_A.S文件结束******