1 导言
在选用前后台体系软件规划形式的嵌入式体系中,主程序是一个无限循环,单使命次序履行,经过设置一个或多个中止来处理异步事情。这种体系关于简略的运用是能够 的,但关于实时性要求比较高的、处理使命较多的运用,就会暴露出实时性差、体系牢靠性低、安稳性差等缺陷。μC/OS-II 是一个源代码揭露、可移植、可裁剪的实时多任
务操作体系,具有低成本、安稳牢靠、实时性好等长处,是专门针对微处理器和微操控器规划的实时内核,它的内核能够做到很小,很适合在单片机体系上移植。移植了μC/OS-II 的嵌入式体系能够使各个使命独立作业,互不干涉,很简单完结按时并且无误履行,使实时运用程序的规划和扩展变得简单,使运用程序的规划进程大为减化。本
文选用飞思卡尔(Freescale)公司的16 位单片机MC9S12DG128 作为硬件渠道,针对MC9S12DG128 的存储器安排和体系结构,对μC/OS-II 源代码作了相应的改写,终究实
现了μC/OS-II 操作体系在MC9S12DG128 上的移植。经过μC/OS-II 在MC9S12DG128 上的移植,能够把握移植和测验μC/OS-II 的本质内容,一起也很简单将其移植到其它的CPU 渠道上。
2 MC9S12DG128 的体系结构(存储器的安排)
作者以为深入了解MC9S12DG128 微操控器的体系结构和存储器安排是移植成功的一个关键步骤。MC9S12DG128 是16 位的高性能单片机,它具有极低的电源功耗和可高达
25MHz 的内部总线频率,片内资源包含1KB 的内部寄存器、8KB RAM、128KB FLASH、2KBEEPROM。MC9S12DG128 选用普林斯顿总线结构,程序存储器、数据存储器和I/O 端口为一致编址办法,总的寻址空间为64 KB,但DG128 内部有128KB Flash,明显存储空间超过了S12MCU 可寻址的64KB 空间,因而引进了页面拜访机制,S12CPU 在内存的$8000~$BFFF 这一段开了一个窗口,这儿有8 个16KB 的页面(其间$3E 和$3F 有固定地址),能够经过页面寄存器(PPAGE)挑选其间的一页。关于64KB 以外的存储区,运用专用指令CALL 调用子程序,然后经过RTC 指令回来。
微操控器内部不同的存储器占用不同的存储空间,也便是说,不同的地址规模,它们均占有特定的地址空间,这些存储器和内部集成模块的地址分配并不是固定不变的,用户自己能够从头分配,但建不要容易改动默许的映射空间,应直接选用默许地址映射空间。图1 是MC9S12DGl28 复位后的内存空间分配状况。其间,地址$0000~$03FF 为1KB 寄存器空间; $0000~$1FFF 为8KB RAM(可见7KB);$0000~$07FF 为2KBEEPROM(不行见)。
图1 MC9S12DGl28 复位后的内存空间散布状况
能够经过设置INITRG,INITRM,INIteE 寄存器来从头分配各存储器的方位。这些寄存器只能写一次,主张在初始化时分配存储器的方位。假如映射呈现地址堆叠时,S12CPU 内部的优先级操控逻辑会主动屏蔽等级较低的资源,保存等级最高的资源。寄存器具有最高优先级,与其堆叠的RAM 和EEPROM 此刻无效。存储器的优先级如下表所列。
3 μC/OS-II 在MC9S12DG128 上的移植
μC/OS-II 运转时要占用一部分ROM 和RAM 空间,但μC/OS-II 操作体系内核方针代码最小能够裁剪到小于2KB,MC9S12DG128 有8KB 的RAM 存储器和128KB 的Flash 存
储器,所以μC/OS-II 操作体系完全能够移植并运转在MC9S12DG128 上。
μC/OS-II 的95%代码是由ANSI C 写成的,具有很好的移植性。要完结μC/OS-II向S12 的移植,首要是做两方面的作业,一是从头界说内核的巨细和功用;二是为内核编写与硬件相关的代码。μC/OS-II 的文件结构如图2 所示。
图2 μC/OS-II 的文件结构
能够看到,μC/OS-II 与CPU 类型无关的C 代码文件包含许多文件,它们是μC/OS-II的内核和许多功用函数,其间OS_CORE.C、OS_TIME.C 和OS_TASK.C 这三个文件是必定要用的,其他几个文件用于使命间通讯,运用程序中或许只用到其间的几个,不必的能够不包含进去,以防止编译时生成没有代码。这部分代码与CPU 类型无关,在移植时,这些文件一个也不要动。
与CPU 类型有关的代码文件首要有:OS_CFG.H、OS_CPU.H、OS_CPU_A.ASM 和OS_CPUC.C。OS_CFG.H 是装备文件,需求依据运用装备,首要作用是确认用户运用程序运用μC/OS-II 供给的哪些体系功用函数,这个文件移植时要修正。OS_CPU.H 文件界说用于特定CPU 的数据类型、界说相关的宏。OS_CPU_A.ASM 是用汇编言语写的硬件有关的代码,OS_CPUC.C 文件是用C 言语写的与硬件有关的代码。假如移植运用的C 穿插编译东西在C 代码中能够刺进汇编句子,那么在移植中,能够将OS_CPU_A.ASM 合到S_CPUC.C文件中。
3.1 从头界说内核的巨细和功用
公共头文件INCLUDES.H,这个文件会被一切的C 源程序引证。在本例中此文件的代码如下。
#include
#include
#include
#include
#include
#include
#include
前四个头文件是C 函数库、预界说的类型等,和移植没有关系,是否必定要加取决于所用的编译器。后三个头文件有必要被引证,用户能够增加自己的头文件,但必定要放
在最终面。
需求依据运用修正的文件是OS_CFG.H,这个文件用于装备内核的特点。用于设置与微操控器CPU 中心相关的特点,包含各种数据类型对应的存储长度等等。OS_CPU.H 包含了用#define 句子界说的、与处理器相关的常数、宏及类型等。由于不同的处理器有不同的字长,所以μC/OS-II 的移植包含的一系列数据类型界说,以保证其可移植性。μC/OS-II 代码不运用言语中的short,int,及long 等数据类型,由于它们是与编译器相关的,是不行移植的。选用界说的整形数据结构等既是可移植的,又很直观。
typedef unsigned char BOOLEAN; /* 布尔变量*/
typedef unsigned char INT8U; /* 无符号8 位整型变量*/
typedef signed char INT8S; /* 有符号8 位整型变量 */
typedef unsigned int INT16U; /* 无符号16 位整型变量*/
typedef signed int INT16S; /* 有符号16 位整型变量*/
……
用户还有必要将使命仓库的数据类型告知给μC/OS-II。S12CPU 的是仓库是16 位的,所以界说OS_STK 为INT16U。一切的使命仓库都有必要用OS_STK 来声明数据类型。
#define OS_STK INT16U /* 仓库是16 位宽度*/
关于不同的处理器而言,数据入仓库时仓库指针的增加方向也是不相同的,MC9S12DG128 单片机的仓库指针是由高地址向低地址增加的,所以,要预先设定仓库的
增加方向:
#define OS_STK_GROWTH 1 /*仓库指针由高地址向低地址增加*/
μC/OS-II 需求先制止中止再拜访代码的临界段,并且在拜访完毕后从头答应中止。这就使得μC/OS-II 能够维护临界段代码免受多使命或中止服务例程的损坏。制止和答应中止的宏是OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL(),界说这两个宏的有三种办法,移植时选用的是办法1,进入临界代码前关中止,脱离临界代码后开中止[2]。办法1在OS_CPU.H 中是这样界说的:
#if OS_CRITICAL_METHOD == 1 //办法一
#define OS_ENTER_CRITICAL( ) asm SEI
#defien OS_EXIT_CRITICAL () asm CLI
#endif
3.2 编写与硬件相关的代码
接下来需求编写与硬件相关的代码。这部分代码能够用C 言语,也能够用汇编言语。移植中与硬件相关的文件中最首要的是OS_CPU_C.C 和汇编文件OS_CPU_A.ASM。由于移植运用的是Metrowerks 公司供给的CodeWarrior CW12 V4.6 版别的C 穿插编译东西,而CW12 V4.6 答应在C 代码中刺进汇编句子,所以能够把OS_CPU_A.ASM 这个文件兼并
到OS_CPU_C.C 文件中去。以下是具体的移植进程。
3.2.1 中止服务子程序OSTickISR()
中止服务子程序所运用的中止能够用实时时钟产生,也能够用单片机片内的定时器模块来产生。本次移植选用的是用模数计数器产生准确时钟节拍中止,用S12 的模数计数器能够完结恣意时刻的准确中止,这儿的中止为每秒30 次。
时钟节拍中止产生时,CPU12 会主动CPU 把CPU 寄存器推入仓库,然后是清中止标志。可是页面寄存器PPAGE 并没有被推入仓库,假如CPU12 的寻址规模超过了64KB,则要把PPAGE 也推入仓库,本文中没有用到PPAGE 寄存器。
时钟节拍中止服务子程序或许激活一个优先级高于当时被中止使命的优先级的使命。时钟节拍中止服务子程序要接连调用:OSIntEnter()、OSTimerTick()和OSIntExit()
这三个函数。OSIntEnter()告知μC/OS-II 进入中止服务子程序了。OSTimerTick()给要求推迟若干时钟节拍的使命推迟计数器减1,减1 后为0 则该使命进入安排妥当态。
OSIntExit()函数告知μC/OS-II 时钟节拍中止服务子程序完毕了,假如这时有更高优先级的使命进入了安排妥当态,OSIntExit()就会调用中止级的使命切换函数OSIntCtxSw()
做使命切换,以便让更高的优先级的使命运转。以下是函数代码:
void OSTickISR(void)
{
/*依据需求决议是否保存PPAGE 寄存器,此处没有保存*/
OSIntEnter();
MCFLG_MCZF=1; //铲除模计数器中止标志位
OSTimeTick();
OSIntExit(); //退出中止并进行使命切换
}
3.2.2 使命仓库初始化函数OSTaskStkInit()
这个C言语写的函数是与CPU硬件相关的。这个函数初始化使命的仓库,由树立使命的函数OSTaskCreate()或扩展的树立使命函数OSTaskCreatExit()调用。树立使命的函数带有4个形式参数,扩展的树立使命的函数有8个参数。其间pdata用于向使命传递参数。利用了这个参数将页面寄存器PPAGE 参数传给树立的使命。在改写该函数的时分必定要深入了解S12CPU在中止产生时各个CPU寄存器的入栈的次序,不然,μC/OS-II是运转不起来的。中止产生时S12CPU各个寄存器入栈的次序如图3所示。由于该函数是被树立使命的函数所调用的,所以各个CPU寄存器的初始值并不重要。但要CCR寄存器的内容需求留意:假如挑选使命发动后答应中止产生,则一切的使命运转期间中止都答应;相同,假如挑选使命发动后制止中止,则一切的使命都制止中止产生,而不能有所挑选。本文挑选在使命发动时敞开中止。以下是函数代码:
void *OSTaskStkInit (void (*task)(void *pd), void *pdata, void *ptos, INT16U opt)
{
INT16U *stk;
pt = opt; // 'opt'未运用,此处可防止编译器的正告
stk = (INT16U *)ptos; //载入仓库指针
*–stk = (INT16U)(pdata); //放置向函数传递的参数pdata
*–stk = (INT16U)(task); //函数回来地址PC
*–stk = (INT16U)(0x1122); //寄存器 Y
*–stk = (INT16U)(0x3344); //寄存器 X
((INT8U *)stk)–; // 寄存器A 仅需求1 个字节
*(INT8U *)stk = (INT8U)(0x55); //寄存器 A
((INT8U *)stk)–; // 寄存器B 仅需求1 个字节
*(INT8U *)stk = (INT8U)(0x66); //寄存器 B
((INT8U *)stk)–; // 寄存器CCR 仅需求1 个字节
*(INT8U *)stk = (INT8U)(0x00); //寄存器 CCR,开中止
return ((void *)stk);
}
3.2.3 让优先级最高的安排妥当态使命开端运转OSStartHightRdy()
OSStartHighRdy()是在多使命发动时被OSStart()调用的,μC/OS-II 做完一切的初始化作业之后,OSStart()就发动运转多使命,而OSStart()调用OSStartHighRdy()
函数运转多个安排妥当使命中优先级最高的使命。留意,仓库指针总是贮存在使命操控块的最初。
图3 中止产生时S12CPU寄存器入栈的次序
OSStartHighRdy()将CPU 的仓库指针SP 的值,改成优先级最高的安排妥当态使命的仓库指针的值,然后将该使命的状况字由非运转态“FALSE”,改为运转态“TRUE”,然后
履行中止回来指令RTI 以开端运转这个使命。以下是具体代码:
void OSStartHighRdy(void)
{
OSTaskSwHook(); //调用钩子函数
asm{
ldx OSTCBCur // 加载OSTCBCur 的地址到 x
lds 0,x //把OSTCBStrPtr 载入仓库指针 sp
ldaa OSRunning
inca // SRunning = TRUE
staa OSRunning
rti
}
}
3.2.4 使命级使命切换函数OSCtxSw()和中止级使命切换函数OSIntCtxSw()
使命级的切换是经过履行软中止指令来完结的。OSCtxSw()实际上便是软中止服务子程序,软中止服务子程序的向量地址指向OSCtxSw()。假如当时使命调用μC/OS-II
供给的功用函数,并使更高优先级使命进入了安排妥当状况,则μC/OS-II 就会凭借上面说到的向量地址找到OSCtxSw()。在体系服务调用的最终,μC/OS-II 会调用使命调度函
数OSSched(),并由此推断出当时使命不再是需求运转的最重要的使命。
OSIntCtxSw()函数中的绝大多数代码同OS_TASK_SW()函数是相同的。而中止退出函数则是经过函数OSIntCtxSw()来从ISR 中履行切换功用,差异仅仅由于ISR 现已保存了
CPU 的寄存器,而不再需求在OSIntCtxSw()函数中保存CPU 的寄存器。以下只给出使命级使命切换函数OSCtxSw()的代码:
void OSCtxSw(void)
{
asm{
ldx OSTCBCur // 加载当时使命的仓库指针
sts 0,x // 保存到当时使命的TCB 中
}
OSTaskSwHook(); //调用钩子函数
STCBCur = OSTCBHighRdy; // 改动使命的 OSTCBCur 和OSPrioCur
SPrioCur = OSPrioHighRdy;
asm{
ldx OSTCBCur // 得到新使命的仓库指针
lds 0,x // 加载新使命的仓库指针到 sp
rti
}
}
4 移植代码的测验
为了验证移植成果是否正确,对移植后μC/OS-II 代码进行了测验,这是移植中很重要的一个环节。首要对内核本身的运转状况进行了测验,待内核本身的运转正常作业
后,又创立三个使命:使命1 经过PORTA 口点亮LED 灯,该使命每秒运转一次;使命2和使命3 都经过串输出字符串,这两个使命都是每2 秒运转一次,并经过信号量来完结
互斥,以使得每个使命每次运转时均可完结一切字符的输出。试验测验证明在μC/OS-II办理与调度下,使得这三个使命都能正确、牢靠地相继运转。
5 小结
经过μC/OS-II在MC9S12DG128上的移植,加深了对μC/OS-II内核作业原理和使命调度完结办法的了解,把握了μC/OS-II移植的一般办法,测验成果表明移植代码能够安稳牢靠的运转,完结了多使命的办理和调度。μC/OS-II实时操作体系的引进,不光能够进步体系的实时性、牢靠性和安稳性,还进步了运用软件的可移植性,降低了开发人员的作业量。