编译器的选择
由于μC/OS-II绝大部分代码是用标准的C语言编写的,所以C语言开发工具对于μC/OS-II是必不可少的。由于μC/OS-II是一个可剥夺行的占先式内核,所以要求C编译器可以产生可重入型代码。笔者选择Keil C51集成开发环境作为开发工具。该开发工具有C编译器,汇编器和链接定位器等工具构成。链接器用来将不同模块(编译过或汇编过的文件)链接成目标文件,定位器则允许将代码和数据放置在目标处理器的指定内存中。Keil C51 还可以生成HEX格式的编程文件用于编程EPROM或是FLASH,同时可以实现完整软件仿真支持。Keil C51支持所有8051变种的微控制器。通过设置编译控制选项,它完全可以满足编译μC/OS-II源代码的要求。
可重入函数问题
可重入函数可以被一个以上的任务调用,而不必担心数据被破坏。可重入函数任何时候都可以被中断,一段时间后又可以继续运行,而相应的数据不会丢失。由于μC/OS-II是抢占式的实时多任务内核,同一个函数可能会被不同的任务调用,也可能会被中断,因此,移植μC/OS-II要求C语
言编译器可以产生可重入函数。但是正常情况下Keil C51编译器中的函数不能重入。原因是由于8051系列微控制 器的硬件堆栈很小,硬件堆栈指针SP最多只能在内部256字节的RAM内移动,不能够指向64K的外部RA M空间。所以编译器使用固定的RAM地址来存储函数的参数和局部变量,而不是使用堆栈来存储。为了在Keil C51中实现可重入函数,可以使用“reentrant”关键字声明该函数是可重入的。编译器可根据编译模式为可重入函数在内部RAM或外部RAM空间开辟一个模拟堆栈来存储可重入函数的参数和局部变量。可重入函数的返回地址仍然保存在硬件堆栈中。Cx51编译手册不推荐使用模拟堆栈,原因是受8051寻址方式的限制,模拟堆栈访问的效率很低。但是这是在Keil C51中实现可重入函数的唯一方法。可重入函数模拟堆栈拥有独立于硬件堆栈指针的模拟堆栈指针。模拟堆栈及其指针在启动代码文件“STARTUP.A51”中定义和初始化。
μC/OS-II源文件移植
在了解了P89V51RD2微处理器和Keil C51 编译器的技术细节的基础上,就可以开始μC/OS-II源文件移植的工作了。真正编写移植代码的工作就相对比较简单了。图1表示了基于μC/OS-II的应用的系统结构结构。由图1可以看出由于μC/OS-II自生的绝大部分代码是使用ANSI C编写的,而且代码的层次结构十分干净,与平台相关的移植代码仅仅存在于OS_CPU_A.ASM、OS_CPU_C.C以及OS_CPU.H这三个文件当中。下面分别解释各个文件在P89V51RD2上的移植。

图1 μC/OS-II软件体系结构和各模块之间的关系
μC/OS-II中与处理器CPU 类型无关的代码:uCOS_II.H和uCOS_II.C,其中uCOS_II.C 文件包含以下文件:OS_CORE.C OS_TASK.C OS_TIME.C OS_SEM.C OS_MBOX.C OS_MUTEX.C 和OS_FLAG.C 也就是说原则上这些文件可以直接添加不用修改。但是由于Keil C51编译器的特殊性,这些代码仍要多处改动,因为Keil C51缺省情况下编译的代码不可重入而多任务系统要求并发操作导致重入,所以要在每个C 函数及其声明后标注reentrant 关键字,另外“pdata” 和“data” 在uCOS中用做一些函数的形参,但它同时又是Keil C51 的关键字,会导致编译错误。我通过把“pdata”改成“ppdata”,“data”改成“ddata”解决了此问题。OSTCBCur、OSTCBHighRdy、OSRunning、OSPrioCur、OSPrioHighRdy 这几个变量在汇编程序中用到了,为了使用寄存器R0或R1访问而不用DPTR,应该用Keil C51扩展关键字IDATA将它们定义在内部RAM中。
OS_CPU.H的移植
OS_CPU.H包括了用#define语句定义的、与处理器相关的常数、宏及类型。因为不同的处理器有不同的字长,所以μC/OS-II的移植包括的一系列数据类型定义,以确保其可移植性。μC/OS-II代码不使用语言中的short,int,及long等数据类型,因为它们是与编译器相关的,是不可移植的。采用定义的整形数据结构等既是可移植的,又很直观。参考Cx51编译手册,可以完成OS_CPU.H里所有数据类型的定义。
与所有的实时内核一样,μC/OS-II需要先关中断,再处置临界段代码,并且在处置完毕后重新开中断。这样可以保护临界段代码免受多任务或中断服务子程序的破坏。为了隐藏不同编译器提供的不同的关中断和开中断的实现方法,增强可移植性,μC/OS-II在OS_CPU.H中定义了2个宏,来开中断和关中断:OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()。根据P89V51RD2的结构和Keil C51提供的方法,我们通过置位或清零中断允许位来实现。