μC/OS-II在S3C44BOX处理器上的移植

2010年11月17日 12:46    发布者:designer
介绍实时操作系统μC/OS-II的特点和内核结构,给出μC/OS-II在Samsung嵌入式S3C44BOX ARM7微处理器上的移植的步骤及详细相关代码,同时阐述μC/OS-II在应用中应注意的问题。

μC/OS-II功能强大,支持56个用户任务,其内核为占先式,支持信号量、邮箱、消息队列等多种常用的进程间通信机制,现已成功应用到众多商业嵌入式系统中,是一个成熟稳定的实时内核。与大多商用RTOS不同的是,μC/OS-II公开所有的源代码,90%的代码使用标准的ANSI C语言书写,程序可读性强、移植性好;同时它可免费获得,即使商业应用也只收取少量的许可费用。因此,对μC/OS-II实时操作系统的学习研究、开发、应用具有重要意义。  

Samsung S3C44B0X微处理器是三星公司专为手持设备和其它嵌入式应用提供的高性价比的微控制器解决方案。它使用ARM公司的16位/32位RISC结构,内核是ARM7TDMI,工作在66MHz,片上集成了以下部件:8K Cache、外部存储器控制器、LCD控制器、4个DMA通道、2个UART、1个多主I2C总线控制器、1个I2C总线控制器,以及5通道PWM定时器和1个内部定时器、8通道12位ADC等,能够与常用的外围设备实现无缝连接,功能强大。目前,国内应用较为广泛。





1 μC/OS-II实时操作系统结构  

图1说明了μC/OS-II的软硬件体系结构。应用程序处于整个系统的顶层,每个任务都可以认为自已独占了CPU,因而可以设计成为一个无限循环。μC/OS-II处理器无关的代码提供了μC/OS-II的系统服务,应用程序可以使用这些API函数进行内存管理、任务间通信及创建、删除任务等。  

大部分的μC/OS-II代码是使用ANSI C语言书写的,因此μC/OS-II的可移植性好,然而仍需要使用C和汇编语言写一些处理器相关代码。μC/OS-II的移植需要满足以下要求:  

①处理器的C编译器可以产生可重入代码;  
②可以使用C调用进入和退出临界区代码;  
③处理器必须支持硬件中断,并且需要一个定时中断源;  
④ 处理器需要能够容纳一定数据的硬件堆栈;  
⑤处理器需要有能够在CPU寄存器与内核和堆栈交换数据的指令。  

S3C44B0X处理器完全满足上述要求。  

2 实时内核μC/OS-II在S3C44B0X上的移植  

我们使用ARM SDT编译器,移植μC/OS-II主要包括以下几个步骤。  

(1)设置OS_CPU.H中与处理器和编译器相关的代码  
*************************************************  
与编译器相关的数据类型  
*************************************************  
typedef unsigned char BOOLEAN;  
typedef unsigned char INT8U; /*8位无符号整数*/  
typedef signed char INT8S; /*8位有符号整数*/  
typedef unsigned short INT16U; /*16位有符号整数*/  
typedef signed short INT16S; /*16位无符号整数*/  
typedef unsigned long INT32U; /*32位无符号整数*/  
typedef signed long INT32S; /*32位有符号整数*/  
typedef float FP32; /*单精度浮点数*/  
typedef double FP64; /*双精度浮点数*/  
typedef unsigned int OS_STK;/*堆栈入口宽度为16位*/与ARM处理器相关的代码:  
#define OS_ENTER_CRITICAL () ARMEnableInt() /*开启中断*/  
#define OS_STK_GROWTH 1 /*堆栈由高地址向低地址增长*/  

(2)用C语言编写6个操作系统相关的函数(OS_CPU_C.C)  
void OSTaskStkInit(void(task)(void *pd),void *pdata,void *ptos,INT16U opt)  
{ unsigned int *stk;  
opt =opt; /*因“opt”变量没有用到,防止编译器产生警告*/  
stk =(unsigned int *)ptos; /*装载堆栈指针*/  
/*为新任务创建上下文*/  
*--stk=(unsigned int)task; /*lr*/  
*--stk=(unsigned int)task /*pc*/  
*--stk=0; /*r12*/  
*--stk=0; /*r11*/  
*--stk=0; /*r10*/  
*--stk=0; /*r9*/  
*--stk=0; /*r8*/  
*--stk=0; /*r7*/  
*--stk=0; /*r6*/  
*--stk=0; /*r5*/  
*--stk=0; /*r4*/  
*--stk=0; /*r3*/  
*--stk=0; /*r2*/  
*--stk=0; /*r1*/  
*--stk=(unsigned int)pdata; /*r0*/  
*--stk=(SVC32MODE|0x0|); /*cpsr IRQ,  
*--stk=(SVC32MODE|0x0); /*spsr IRQ,关闭FIQ*/  
return((void*)stk);  
}  
后5个函数是钩子函数,可以不加代码:  
void OSTaskCreateHook(OS_TCB *ptcb)  
void OSTaksDelHool (OS_TCB *ptcb)  
void OSTaskSwHook(void)  
void OSTaskStatHook(void)  

(3)用汇编语言编写4个与处理器相关的函数(OS_CPU.ASM)  
OSStartHighRdy() ;运行优先级最高的就绪任务  
LDR r4,addr_OSTCBCur ;得到当前任务的TCB地址  
LDR r5,addr_OSTCBHighRdy ;得到高优先级任务的TCB地址  
LDR r5,addr_OSTCBHighRdy ;得到高优先级任务的TCB地址  
LDR r5, ;得到堆栈指针  
LDR sp, ;切换到新的堆栈  
STR r5, ;设置新的当前任务的TCB地址  
LDMFD sp!,{r4}  
MSR CPSR_cxsf,r4  
LDMFD sp!,{r0-r12,lr,pc} ;开始新的任务  
END  
OSCtxsw() 任务级的任务切换函数  
STMFD sp!,{lr} ;保存PC指针  
STMFD sp!,{lr} ;保存lr指针  
STMFD sp!,{r0-r12} ;保存寄存器文件和返回地址  
MRS r4,CPSR  
STMFD sp!,{r4} ;保存当前PSR  
MRS r4,SPSR  
STMFD sp!,{r4}  
;OSPrioCur=OSPrioHighRdy  
LDR r4,addr_OSPrioCur  
LDR r5,addr_OSPrioHighRdy  
LDRB r6,  
STRB r6,  
;得到当前任务的TCB地址  
LDR r4,addr_OSTCBCur  
LDR r5,  
STR sp, ;保存栈指针在占先任务的TCB上  
;取得高优先级任务的TCB地址  
LDR r6,addr_OSTCBHighRdy  
LDR r6,  
LDR sp, ;得到新任务的堆栈指针  
;OSTCBCur=OSTCBHighRdy  
STR r6,  
;得到当前新任务的TCB地址  
LDMFD sp!,{r4}  
MSR SPSR_cxsf,r4  
LDMFD sp!,{r4}  
MSR SPSR_cxsf,r4  
LDMFD sp!,{r0-r12,lr pc}  
OSIntCtxSw() ;中断级的任务切换函数  
LDMIA sp!,{al-vl,lr}  
SUBS pc,lr,#4  
SUB lr,lr,#4  
MOV r12,lr  
MRS lr,SPSR  
AND lr,lr,#0XFFFFFE0  
ORR lr,lr,#0XD3  
MSR CPSR_CXSF,lr  
OSTickISR() ;中断服务函数  
STMDB sp!,{r0-r11,lr}  
MRS r0,CPSR  
ORR r0,r0,#0x80; ;设置中断禁止标志  
MSR CPSR_cxsf,r0 ;中断结束  
LDR r0,I_ISPC  
LDR r1,=BIT_TIMER0  
STR r1,{r0}  
BL IrqStart  
BL osTimeTick  
BL IrqFinish  
LDR r0,=need_to_swap_context  
LDR R2,  
CMP r2,#1  
LDREQ pc,=_CON_SW  
完成上述工作后,μC/OS-II就可以运行在ARM处理器上了。  

3 使用μC/OS-II系统应注意的问题  

①μC/OS-II和Linux等分时操作系统不同,不支持时间片轮转法。它是一个基于优先级的实时操作系统。每一个任务的优先级必须不同(分析它的源码会发现,μC/OS-II把任务的优先级当作任务在标识来使用,如果优先级相同,任务将无法区分)。进入就绪态的优先级最高的任务首先得到CPU的使用权,只有等它交出CPU的使用权后,其它任务才可以被执行。所以,它只能就是多任务,不能就是多进程,至少不是我们所熟悉的那种多进程。  

②μC/OS-II对共享资源提供了保护的机制。μC/OS-II是一个支持多任务的操作系统。我们可以把一个完整的程序划分成几个任务,不同的任务执行不同的功能。对于共享资源(比如串口),μC/OS-II也提供了很好的解决办法,一般情况下使用的是信号量方法。我们创建一个信号量并对它进行初始化,当一个任务需要使用一个共享资源时,它必须先申请得到这个信号量。在这个过程中即使有优先权更高的任务进入了就绪态,因为无法得到信号量,也不能使用该资源。在μC/OS-II中称为优先级反转。简单地说,就是高优先级任务必须等待低优先级任务的完成。在上述情况下,在两个任务之间发生优先级后转是无法避免的。所以不在使用μC/OS-II时,必须对所开发的系统了解清楚才能选择对于某种共享资源是否使用信号量。  

③μC/OS-II内存管理不够完善。在分析许多μC/OS-II的应用实例中发现,任务栈空间和内存分区的创建采用了定义全局数组的方法,这样实现起来固然简单,但不够灵活有效。  

编译器会将全局数组作为未初始化的全局变量,放到应用程序映像的数据段。数组的大小是固定的,生成映像后不可能在使用中动态地改变。对于任务栈空间来说,数组定义大了会造成内存浪费;定义小了任务栈溢出,会造成系统崩溃。对于内存分区,在不知道系统初始化后给用户留下了多少自由内存空间的情况下,很难定义内存分区所使用数组的大小。此外,现在μC/OS-II只支持固定大小的内存分区,容易造成内存浪费。μC/OS-II将来应该被改进以支持可变大小的内存分区。因此,系统初始化后能清楚地掌握自由内存空间的情况是很重要的。所以,应避免使用全局数组分配内存空间,关键是要知道整个应用程序在编译、链接后代码段和数据段的大小,在目标板内存中是如何定位,以及目标板内存的大小。  

总之,随着各种智能嵌入式系统的复杂化和系统实时性需求的提高,伴随应用软件朝着系统化发展的加速,功能强大的实时操作系统μC/OS-II将会有更大的发展。