UCOS-II在MC9S12XS128上的移植
2021-08-12 来源:eefocus
操作系统是一种与硬件为基础的系统软件,硬件系统不一样,那在其上面运行的操作系统也会不一样。一般来说,操作系统是与芯片相关联的。要在某型号的芯片上运行操作系统,那得在操作系统内核的基础上编写一些与芯片相关的驱动程序,并对内核的配置文件做相应的改动,然后在编译器上编译,链接,然后下载到芯片上。这个过程就是所谓的操作系统的移植了。
UCOS-II是个开源的也就是原代码公开的内核,可以免费用于非商业的各种运用中。其原代码可以在http://micrium.com/page/products/rtos/os-ii上找到。上面有好多在某些芯片上移植成功的实例,如果芯片型号对应,则可以下载下来直接用。这样就可以省去好多移植的工作,而直接在上面编写用户任务就可以了。
UCOS-II的文件有:
一、与处理器无关的代码:
OS_CORE.C(与内核运作有关的文件)
OS_FLAG.C(事件标志组管理)
OS_MBOX.C(消息邮箱)
OS_MEM.C(内存管理)
OS_MUTEX.C(互斥型信号量)
OS_Q.C(消息列队)
OS_SEM.C(信号量)
OS_TASK.C(任务管理)
OS_TIME.C(时钟管理)
UCOS_II.C()
UCOS_II.H()
二、UCOS-II的配置文件(需要用户根据具体的运用来配置)
OS_CFG.H(配置使用哪些系统函数,按需裁剪操作系统)
INCLUDES.H(系统总头文件,把所有用的到头文件都加进来)
三、UCOS-II的移植文件(与处理器相对应的功能实现)
OS_CPU.H(定义数据类据、临界代码实现方法、堆栈方向、任务切换宏)
OS_CPU_C.C(与硬件相关的代码)
OS_CPU_A.S (若移植使用的C编译器允许在C代码中插入汇编语句,则汇编部分的代码可以放到OS_CPU_C.C中,此时0S_CPU_A.S就可以不要了)
要移植UCOS-II的芯片必须满足以下要求:
1、 处理器的C语言编译器能产生可重入型代码;
2、 处理器支持中断,并且能产生定时中断(通常为10~100Hz);
3、 用C语言就可以开或关中断;
4、 处理器能支持一定数量的数据存储硬件堆栈;
5、 处理器有将堆栈指针以及其他CPU寄存器的内容读出并存储到堆栈或内存中去的指令。
下面结合UCOS-II在MC9S12XS128上的移植来讨论一下UCOS-II的移植方法及过程。
UCOS-II的移植分成几个步骤:
一、文件OS_CPU.H的编写,要求对处理器的内部结构有所了解:
#ifndef OS_CPU_H
#define OS_CPU_H
【定义数据类型。因为不同的编译器对数据的定义不尽相同,而UCOS-II系统中采用统一的数据类型名,以便于移植。故这里要把操作系统中的数据类型和所用的编译器的数据类型对应起来。】
typedef unsigned char BOOLEAN; 【布尔变量类型】
typedef unsigned char INT8U; 【无符号8位整数】
typedef signed char INT8S; 【有符号8位整数】
typedef unsigned int INT16U; 【无符号16位整数】
typedef signed int INT16S; 【有符号16位整数】
typedef unsigned long INT32U; 【无符号32位整数】
typedef signed long INT32S; 【有符号32位整数】
typedef float FP32; 【单精度浮点数】
typedef double FP64; 【双精度浮点数】
//
typedef unsigned char OS_STK; 【堆栈的数据类型,这由处理器决定,堆栈变量都由OS_STK定义】
typedef unsigned short OS_CPU_SR; 【CPU状态寄存器PSW的数据类型,因为在临界代码方法3中,会把PSW中的值存入到OS_CPU_SR定义的变量cpu_sr中】
【三个函数声明】
void OSStartHighRdy(void);
void OSIntCtxSw (void);
void OSCtxSw (void);
#define OS_CRITICAL_METHOD 3 【这里可以是1、2或者3,选择3表示用方法3进入临界代码区】
【这里定义临界代码方法3的具体程序实现,这跟处理器结构相关】
#if OS_CRITICAL_METHOD == 3
#define OS_ENTER_CRITICAL() (cpu_sr =OS_CPU_SR_Save())
#define OS_EXIT_CRITICAL() (OS_CPU_SR_Restore(cpu_sr))
#endif
/
#define OS_TASK_SW() _asm('swi') 【这里定义一个宏,是在UCOS-II做任务切换时必须用到的。在UCOS-II中,处于就绪态的任务的堆栈结构看起来就像刚发生过中断一样,所有的寄存器都保存大堆栈中。要运行就绪的任务时,系统要做的就是从任务堆栈中恢复处理器所有的寄存器,并执行中断返回指令。任务调度时,可以通过执行OS_TASK_SW()模仿中断的产生。绝大多数处理器会提供软中断或指令陷阱来完成这项功能。在XS128中就是通过软中断SWI来完成的。】
/
#define OS_STK_GROWTH 1 【用于根据处理器堆栈的方向来配置操作系统,当处理器的堆栈是从下(低地址)往上(高地址)递增时,OS_STK_GROWTH定为0;当处理器的堆栈是从上往下递减时,OS_STK_GROWTH定为1。】
【声明两个与用方法3实现临界代码有关的函数】
#if OS_CRITICAL_METHOD == 3
OS_CPU_SR OS_CPU_SR_Save(void);【返回PSW寄存器的值,保存在cpu_sr中,并关中断】
void OS_CPU_SR_Restore(OS_CPU_SR os_cpu_sr); 【把cpu_sr的值恢复到PSW寄存器中去】
#endif
#endif
二、文件OS_CPU_C.C的编写,要求对处理器的堆栈结构有所了解:
在这个文件中,得编写10个函数,分别是:
OSTaskStkInit()
OSTaskCreateHook()
OSTaskDelHook()
OSTaskSwHook()
OSTaskIdleHook()
OSTaskStatHook()
OSTimeTickHook()
OSInitHookBegin()
OSInitHookEnd()
OSTCBInitHook()
除了OSTaskStkInit()外,其他9个函数必须声明,但不一定要包含任何代码。
只有OS_CFG.H文件中的OS_CPU_HOOKS_EN置为1时,才会生成下面OS_XXXHook()的这些代码,这个由程序中的条件编译可以看出。这些函数相当于是用户对操作系统功能扩展的接口函数,它的设置是为了方便用户根据需要扩展UCOS-II的功能。这些函数在其相应的函数中初调用,来实现用户的扩展功能;如果不需要扩展功能,则只需声明一个空函数。强调一点是,不管是否需要扩展UCOS-II的功能,都必须得声明这些函数。
OS_CPU_C.C文件中的程序如下:
#include 'includes.h'
【下面这个函数是堆栈初始化函数,需要在了解处理器堆栈结构的基础上编写。其中的参数分别表示:task是指向任务的函数指针,即任务的起始地址,其中pd为task指向的任务的参数;p_arg为任务开始执行时传递给任务的参数的指针;ptos为堆栈栈顶值,也就是传入堆栈的初始值; opt为OS_TCB的选择项,用于设定OSTaskCreateExt()的选项,指定是否允许堆栈检验,是否将堆栈清0,任务是否要进行浮点操作等,在OSTaskCreate()中没有opt参数,故当OSTaskCreate()调用OSTaskStkInit()时,将opt设置为0X0000。】
OS_STK *OSTaskStkInit (void (*task)(void*pd), void *p_arg, OS_STK *ptos, INT16U opt)
{
【因为要压入堆栈中的数据有十六位的,也有八位的,所以就定义了两个指针,一个十六位,一个八位。】
INT16U *wstk;
INT8U *bstk;
opt = opt; 【这条语句没有实际意义,因为这个参数没有用到,为了避免编译时发出警告,就令opt =opt,表示这个参数已经用过了,就不会有这个警告了。】
wstk = (INT16U *)ptos; 【载入堆栈的初始值】
*--wstk= (INT16U)p_arg; 【任务中传递的参数】
*--wstk= (INT16U)(((INT32U)task) >>8); 【把任务的地址压入堆栈,相当于PC值入栈】
*--wstk = (INT16U)(((INT32U)task) >> 8); 【????】
*--wstk = (INT16U)0x2222; 【Y寄存器】
*--wstk = (INT16U)0x1111; 【X寄存器】
*--wstk = (INT16U)0xBBAA; 【D寄存器】
*--wstk = (INT16U)0x0080; 【CCR寄存器】
bstk = (INT8U *)wstk; 【十六位指针强制类型转化为八位后赋给八位指针bstk】
*--bstk =*(INT8U *)0x10; 【GPAGE寄存器】
*--bstk = *(INT8U*)0x17; 【EPAGE寄存器】
*--bstk =*(INT8U *)0x16; 【RPAGE寄存器】
*--bstk = (INT8U )task; 【任务的PPAGE寄存器值】
return ((OS_STK*)bstk); 【返回当前堆栈的值】
}
【OS_VERSION的值表示当前所用的UCOS-II的版本号,在文件UCOS_II.H中有定义。203表示的版本号是2.03;252表示的版本号是2.52 。这里用到条件编译,表示2.03以上的版本中才会用到OSInitHookBegin (void)和OSInitHookEnd (void)】
#if (OS_CPU_HOOKS_EN > 0) &&(OS_VERSION > 203)
void OSInitHookBegin (void)
{
}
#endif
#if (OS_CPU_HOOKS_EN > 0) &&(OS_VERSION > 203)
void OSInitHookEnd (void)
{
}
#endif
#if (OS_CPU_HOOKS_EN > 0)
void OSTaskCreateHook (OS_TCB *ptcb)
{
(void)ptcb;
}
#endif
#if (OS_CPU_HOOKS_EN > 0)
void OSTaskDelHook (OS_TCB *ptcb)
{
(void)ptcb;
}
#endif
#if (OS_CPU_HOOKS_EN > 0) &&(OS_VERSION >= 251)
void OSTaskIdleHook (void)
{
}
#endif
#if (OS_CPU_HOOKS_EN > 0)
void OSTaskStatHook (void)
{
}
#endif
#if (OS_CPU_HOOKS_EN > 0)
void OSTaskSwHook (void)
{
}
#endif
#if (OS_CPU_HOOKS_EN > 0) &&(OS_VERSION > 203)
void OSTCBInitHook (OS_TCB *ptcb)
{
(void)ptcb;
}
#endif
#if (OS_CPU_HOOKS_EN > 0)
void OSTimeTickHook (void)
{
}
#endif
三、编写OS_CPU_A.ASM文件(对于CW编译器,这部分代码可以放在OS_CPU_C.C文件中,以C语言插入汇编语言的方式出现)
在这部分中要编写的函数有:
1、 OSStartHighRdy() 【OSStart()函数调用OSStartHighRdy()来使就绪态任务中优先级最高的任务开始运行】
2、 OSCtxSw() 【任务级的任务切换,通过执行软中断指令来实现】
3、 OSIntCtxSw() 【中断级的任务切换】
4、 OSTickISR() 【时钟节拍中断服务函数】
四、产生时钟节拍中断
UCOS-II的运行要求处理器提供一个时钟中断节拍,用于任务的延时或挂起等功能,因此得编写代码产生时钟节拍中断。在XS128中可以利用实时中断模块RTI来实现时钟中断节拍功能。其实就两句:
RTICTL=0XCF; 【分频】
CRGINT|=0X80; 【开实时中断】
开实时中断的动作必须在UCOS-II初始化之后,多任务开始之前,所以这两句语句一般放在第一个建立的任务函数中。
最后要修改CW建立工程时自动生成的XXX.prm链连文件,把时钟节拍中断向量放到中断向量表中,即在XXX.prm文件后面添加语句VECTOR 7 OSTickISR。
到这里,移植工作就算是完毕了。在添加任务之前可以先编译一下,以及调试一下程序,排除在移植过程中产生的错误。
当然要运用这个操作系统的工作还没完成:
1、 把所有的头文件都包进系统总头文件INCLUDES.H中。
2、 根据实际运用需要在OS_CFG.C文件中配置及裁剪系统。
3、 编写main.c文件及任务函数。
UCOS-II在51单片机上的移植方面的信息可与杨屹先生沟通,其网址为www.armecos.com ,电子邮箱为asdjf@163.com。UCOS-II在ARM核上移植方面的信息可与周立功先生联系,其电子邮箱为zlg3@zlgmcu.com。
参考文献
[1]嵌入式实时操作系统UC/OS-II(第二版) [美] Jean J.Labrosse 著 邵贝贝等译 北京航空航天大学出版社
[2]嵌入式实时操作系统UC/OS-II原理及应用 任哲 编著 北京航空航天大学出版社
[3]单片机与嵌入式系统开发方法 薛涛 宫辉 龚光华 邵贝贝等编著 清华大学出版社