本章介绍将uC/OS-Ⅱ移植到三星的S3C44BOX微处理器上的方法。所谓移植,是指使一个实时操作系统能够在菜个微处理器平台上运行。uC/OS-Ⅱ的主要代码都是由标准的C语言写成的,移植过程并不复杂。 通过本章的学习,读者可以学习到以下知识:
- 移植I,tC/OS.!I所要满足的条件
- uC/OS一Ⅱ在ARM7微处理器上的移植方法
4.1移植——将心lOS-Ⅱ移植到自己的硬件平台 本节将介绍如何将uC/OS-Ⅱ移植到ARM的处理器上。虽然uC/OS-II的大部分源代码是用C语言写成的,但是,仍需要用C语言和汇编语言完成一些与处理器相关的代码。例如,uC/OS-Ⅱ在读写处理器、寄存器时只能通过汇编语言来实现。因为uC/OS-Ⅱ在设计的时候就已经充分考虑了可移植性,所以,uC/OS-II的移植还是比较容易的。 要使uC/OS-Ⅱ可以正常工作,处理器必须满足如下要求: 1.处理器的C编译器能产生可重入代码。 可重入的代码指的是一段代码(如一个函数)可以被多个任务同时调用,而不必担心会破坏数据。也就是说,可重入型函数在任何时候都可以被中断执行,过一段时间以后又可以继续运行,而不会因为在函数中断的时候被其他的任务重新调用,影响函数中的数据。 下面的两个例子可以比较可重八型函数和非可重入型函数: 程序1:可重入型函数 void swap(int *x,int *y) {int temp; temp=*x; *x=*y; *y=temp; } 程序2:非可重入型函数 int temp; void swap(int *x,int *y) { temp=*x; *x=*y; *y=temp; }
程序l中使用的是局部变temp作为变量。通常的c编译器,把局部变量分配在栈中。所以,多次调用同一个函数,可以保证每次的temp互不受影响。而程序2中temp定义的是全局变量,多次调用函数的时候,必然受到影响。 代码的可重入性是保证完成多任务的基础,除了在c程序中使用局部变量以外,还需要C编译器的支持。笔者使用的是ARM SDT的集成开发环境,可以生成可重入的代码。 2.在程序中可以打开或者关闭中断。 在uC/OS—II中,可以通过OS_ENTER—CRITICAL()或者OS_EXIT__CRYRICAL()宏来控制系统关闭或者打开中断,这需要处理器的支持。在.ARM7TDMI的处理器上,可以设置相应的寄存器来关闭或者打开系统的所有中断。 3.处理器支持中断,并且能产生定时中断(通常在10Hz~1000Hz之间)。 uC/OS-II是通过处理器产生的定时器的中断来实现多任务之间的调度的。在ARM7TDMI的处理器上可以产生定时器中断。 4.处理器支持能够容纳一定量数据的硬件堆栈。 5.处理器有将堆栈指针和其他CPU寄存器存储和读出到堆栈(或者内存)的指令。 uC/OS-II进行任务调度的时候,会把当前任务的CPU寄存器存放到此任务的堆栈中,然后,再从另一个任务的堆栈中恢复原来的工作寄存器,继续运行另一个任务。所以,寄存器的入栈和出栈是uC/OS-II多任务调度的基础。 图4.1给出了uC/OS-II的体系结构以及它与系统硬件之间的关系。
图4—1uC/OS-II硬件和软件体系结构
ARM7TDMI处理器完全满足上述要求。接下来将介绍如何把uC/OS—II移植到samsung公司的一款ARM7TDMI的嵌入式处理器---S3C44BOX上。
4.2 uC/OS一Ⅱ在S3C44BOX上的移植
在uC/OS-Ⅱ的移植过程中,笔者使用的是ARM sDT作为编译器。移植uC/OS-II主要。包括以下几项内容: 4.2.1 设置includes.h中与处理器和编译器相关的代码 #define INT8U unsigned char #define INTl6U unsigned short #def~ne INT32U unsigned 10ng #define OS STK unsigned long #define BOOLEAN int #define OS—CPU SR unsigned long #define INT8S char extern int INTS_OFF(void); extern void INTS_ON(void); #define OS_ENTER_CRITICAL() {Cpu_sr=INTS—OFF();} #define OS—EXIT—CRITICAL() { if(cpu_sr==0)INTS—ON();} #define OS_STK_GROWTH 1 #define STACKSIZE 256
1.与编译器相关的数据类型 因为不同的微处理器有不同的字长,所uC/OS—II的移植包括了一系列的类型定义以确保其可移植性。尤其是uC/OS-II代码从不使用C语言的short,int和long等数据类型,因为它们是与编译器相关的,不可移植。相反的,我们定义的整形数据结构既是可移植的又是直观的。为了方便,虽然uC/OS-II不是用浮点数据,但我们还是定义了浮点数据类型。
例如,INTl6U数据类型总是代表16位的无符号整数。现在,uC/OS—II和用户的应用程序就可以估计出声明为该数据类型的变量的取值范围是0-65535。将uC/OS—II移植到32位的处理器上也就意味着INTl6U实际被声明为无符号短整形数据结构而不是无符号整数数据结构。但是,uC/OS—II所处理的仍然是INTl6U。
用户必须将任务堆栈的数据类型告诉给uC/OS-II。这个过程是通过为OS_STK声明正确的C语言数据类型来完成的。我们的处理器上的堆栈成员是16位的,所以将OS_TSK声明为无符号整形数据类型。所有的任务堆栈都必须用OS_TSK声明数据类型。
2.OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL() 与所有的实时内核一样,uC/OS-II需要先禁止中断再访问代码的临界区,并且在访问完毕后重新允许中断。这就使得uc/OS-II能够保护临界区代码免受多任务或中断服务例程(ISR)的破坏。在S3C44BOX上是通过两个函数(OS_ENTER_CRITICAL()和OS_EXIT CRITICAL())实现开关中断的。 INTS_OFF mrs r0,cpsr 当前CSR mov r1,r0 复制屏蔽 orr r1,r1,#0xc0 屏蔽中断位 msr cpsr,r1 关中断(IRQ and FIQ) and r0,r0,#0x80 从初始CSR返回FIQ位 mov pc,1r 返回 INTS_ON MRS r0,cpsr 当前CSR bic r0,r0,#0xc0 屏蔽中断 msr cpsr,r0 开中断(IRQ and FIQ mov pc,1r 返回
3.OS_STK_GROWTH 绝大多数的微处理器和微控制器的堆栈是从上往下增长的,但是某些处理器是用另外一种方式工作的。uC/OS—II被设计成两种情况都可以处理,只要在结构常量0S_sTK_GROWTH中指定堆栈的生长方式就可以了。 置OS_STK_GROWTH为0表示堆栈从下往上增长。 置OS_STK_GROWTH为1表示堆栈从上往下增长。
4.2.2用C语言编写6个操作系统相关的函数(OS_CPU_C.C) 1.0STaskStklnit OSTaskCreate()和OSTaskCreateExt()通过调用OSTaskStkInit()来初始化任务的堆栈结构。因此,堆栈看起来就像刚发生过中断并将所有的寄存器保存到堆栈中的情形一样。图4-2显示了OSTaskStkInt()放到正被建立的任务堆栈中的东西。这里我们定义了堆栈的增长方向是从高地址向低地址增长。
在用户建立任务的时候,用户传递任务的地址,pdata指针,任务的堆栈栈顶和任务的优先级给OSTaskCreate()和OSTaskCreateExt()。一旦用户初始化了堆栈,OSTaskStklnit()就需要返回堆栈指针所指的地址。OSTaskCreate()和OSTaskCreateExt()会获得该地址并将它保存到任务控制块(OS_TCB)中。

图4-2堆栈初始化(pdata通过堆栈传递)
OS_STK *OSTaskStkInit(void(*task)(void*pd),void*pdata,OS_STK *ptos, INTl6U opt) { unsigned int *stk; stk=(unsigned int*)ptos; /*装载堆栈指针*/ opt++j; /*为新任务建立堆栈*/ *--stk=(unsigned int)task; /* pc */ *--stk=(unsigned int)task; /* 1r */ *--Stk=12; /* r12 */ *--stk=11; /* r11 */ *--stk=10; /* r10 */ *--stk=9 ; /* r9 */ *--stk=8; /* r8 */ *--stk=7; /* r7 */ *--stk=6; /* r6 */ *--Stk=5; /* r5 */ *--Stk=4; /* r4 */ *--Stk=3; /* r3 */ *--stk=2; /* r2 */ *--stk=1; /* r1 */ *--stk=(unsigned int)pdata; /* r0 */ *--stk=(SUPMODE); /* 1r */ *--stk=(SUPMODE); /* 1r */ return((os_stk*)stk); }
2.OSTaskCreateHook 当用OSTaskCreate()和OSTaskoeateExt()建立任务的时候就会调用OSTaskCreateHook()。该函数允许用户扩展uc/OS-II功能。当uC/OS-II设置完了自己的内部结构后,会在调用任务调度程序之前调用OSTaskCreateHook()。该函数被调用的时候中断是禁止的。因此用户应尽量减少该函数中的代码以缩短中断的响应时间。 当OSTaskCreateHook()被调用的时候,它会收到指向已建立任务的OS_TCB的指针,这样它就可以访问所有的结构成员了。 函数原型: void OSTaskCreateHook(OS_TCB *ptcb)
3.OSTaskDelHook 当任务被删除的时候就会调用OSTaskDelHook()。该函数在把任务从uC/OS-II的内部任务链表中解开之前被调用。当OSTaskDelHook()被调用的时候,它会收到指向正被删除任务的OS_TCB的指针,这样它就可以访问所有的结构成员了。OSTaskDelHook()可以用来检验TCB扩展是否被建立(一个非空指针)并进行一些清除操作。 函数原型: void 0STaskDelH00k (OS_TCB *ptcb)
4.OSTaskSwHook 当发生任务切换的时候就会调用OSTaskSwHook()。OSTaskSwHook()可以直接访问0STCBCur和OSTCBHighRdy,因为它们是全局变量。OSTCBCur指向被切换出去的任务0S_TCB,而OSTCBHighRdy指向新任务OS_TCB。注意在调用OSTaskSwHook()期间中断一直是被禁止的。因此用户应尽量减少该函数中的代码以缩短中断的响应时间。 函数原型: void OSTaskSwHook(void) 5.OSTaskStatHook OSTaskStatHook()每秒钟都会被OSTaskStat()调用一次。用户可以用OSTaskStatHook()来扩展统计功能。例如,用户可以保持并显示每个任务的执行时间,每个任务所用的CPU分额,以及每个任务执行的频率等。 函数原型: void OSTaskStatHook(void)
|