ucosII详解 下载本文

调用PC_GetDateTime()函数可得到PC中的日期和时间,并且以SACII字符串形式返回。格式是

MM-DD-YY HH:MM:SS,用户需要19个字符来存放这些数据。该函数使用了Borland C/C++的gettime()和getdate()函数,其它DOS环境下的C编译应该也有类似函数。

PC_GetKey() 函数检查是否有按键被按下。如果有按键被按下,函数返回其值。这个函数使用了Borland C/C++的kbhit()和getch()函数,其它DOS环境下的C编译应该也有类似函数。 函数PC_SetTickRate()允许用户为 μC /OS-II定义频率,以改变钟节拍的速率。在DOS下,每秒产生18.20648次时钟节拍,或每隔54.925ms一次。这是因为82C54定时器芯片没有初始化,而使用默认值65,535的结果。如果初始化为58,659,那么时钟节拍的速率就会精确地为20.000Hz。笔者决定将时钟节拍设得更快一些,用的是200Hz(实际是上是 199.9966Hz)。注意OS_CPU_A.ASM中的OSTickISR()函数将会每11个时钟节拍调用一次DOS中的时钟节拍处理,这是为了保证在DOS下时钟的准确性。如果用户希望将时钟节拍的速度设置为20HZ,就必须这样做。在返回DOS以前,要调用

PC_SetTickRate(),并设置18为目标频率,PC_SetTickRate()就会知道用户要设置为18.2Hz,并且会正确设置82C54。

PC.C中最后两个函数是得到和设置中断向量,笔者是用Borland C/C++中的库函数来完成的,但是PC_VectGet()和PC_VectSet()很容易改写,以适用于其它编译器。 1.06 应用 μC/OS-II 的范例

本章中的例子都用Borland C/C++编译器编译通过,是在Windows95 的DOS窗口下编译的。可执行代码可以在每个范例的OBJ子目录下找到。实际上这些代码是在Borland IDE (Integrated Development Environment)下编译的,编译时的选项如表1.1所示:

表 T1.1 IDE中编译选项。 Code generation Model : Large

Options : Treat enums as ints

Assume SS Equals DS : Default for memory model Advanced code generation Floating point : Emulation Instruction set : 80186

Options : Generate underbars Debug info in OBJs Fast floating point Optimizations

Optimizations Global register allocation Invariant code motion Induction variables Loop optimization

Suppress redundant loads Copy propagation

Dead code elimination Jump optimization

In-line intrinsic functions Register variables Automatic

Common subexpressions Optimize globally Optimize for Speed

笔者的Borland C/C++编译器安装在C:\\CPP目录下,如果用户的编译器是在不同的目录下,可以在Options/Directories的提示下改变IDE的路径。

μC/OS-II是一个可裁剪的操作系统,这意味着用户可以去掉不需要的服务。代码的削减可以通过设置OS_CFG.H中的#defines OS_???_EN 为0来实现。用户不需要的服务代码就不生成。本章的范例就

用这种功能,所以每个例子都定义了不同的OS_???_EN。 1.07例1

第一个范例可以在\\SOFTWARE\%uCOS_II\\EX1_x86L目录下找到,它有13个任务(包括 μC/OS-II 的空闲任务)。μC/OS-II 增加了两个内部任务:空闲任务和一个计算CPU利用率的任务。例1建立了11个其它任务。TaskStart()任务是在函数main()中建立的;它的功能是建立其它任务并且在屏幕上显示如下统计信息:

? 每秒钟任务切换次数; ? CPU利用百分率; ? 寄存器切换次数; ? 目前日期和时间; ? μC/OS-II的版本号;

TaskStart()还检查是否按下ESC键,以决定是否返回到DOS。

其余10个任务基于相同的代码——Task();每个任务在屏幕上随机的位置显示一个0到9的数字。 1.07.01 main()

例1基本上和最初μC/OS中的第一个例子做一样的事,但是笔者整理了其中的代码,并且在屏幕上加了彩色显示。同时笔者使用原来的数据类型(UBYTE, UWORD等)来说明μC/OS-II向下兼容。

main()程序从清整个屏幕开始,为的是保证屏幕上不留有以前的DOS下的显示[L1.5(1)]。注意,笔者定义了白色的字符和黑色的背景色。既然要请屏幕,所以可以只定义背景色而不定义前景色,但是这样在退回DOS之后,用户就什么也看不见了。这也是为什么总要定义一个可见的前景色。

μC/OS-II要用户在使用任何服务之前先调用OSInit() [L1.5(2)]。它会建立两个任务:空闲任务和统计任务,前者在没有其它任务处于就绪态时运行;后者计算CPU的利用率。

程序清单 L 1.5 main(). void main (void) {

PC_DispClrScr(DISP_FGND_WHITE + DISP_BGND_BLACK); (1) OSInit(); (2) PC_DOSSaveReturn(); (3) PC_VectSet(uCOS, OSCtxSw); (4) RandomSem = OSSemCreate(1); (5) OSTaskCreate(TaskStart, (6) (void *)0,

(void *)&TaskStartStk[TASK_STK_SIZE-1], 0);

OSStart(); (7) }

当前DOS环境是通过调用PC_DOSSaveReturn()[L1.5(3)]来保存的。这使得用户可以返回到没有运行μC/OS-II以前的DOS环境。跟随清单L1.6中的程序可以看到PC_DOSSaveReturn()做了很多事情。PC_DOSSaveReturn()首先设置PC_ExitFlag为FALSE[L1.6(1)],说明用户不是要返回DOS,然后初始化OSTickDOSCtr为1[L1.6(2)],因为这个变量将在OSTickISR()中递减,而0将使得这个变量在

OSTickISR()中减1后变为255。然后,PC_DOSSaveReturn()将DOS 的时钟节拍处理(tick handler)存入一个自由向量表入口中[L1.6(3)-(4)],以便为μC/OS-II的时钟节拍处理所调用。接着

PC_DOSSaveReturn()调用jmp()[L1.6(5)],它将处理器状态(即所有寄存器的值)存入被称为PC_JumpBuf的结构之中。保存处理器的全部寄存器使得程序返回到PC_DOSSaveReturn()并且在调用setjmp()之后立即执行。因为PC_ExitFlag被初始化为FALSE[L1.6(1)]。PC_DOSSaveReturn()跳过if状态语句 [L1.6(6)–(9)] 回到main()函数。如果用户想要返回到DOS,可以调用

PC_DOSReturn()(程序清单 L 1.7),它设置PC_ExitFlag为TRUE,并且执行longjmp()语句[L1.7(2)],这时处理器将跳回 PC_DOSSaveReturn()[在调用 setjmp()之后] [L1.6(5)],此时PC_ExitFlag为TRUE,故if语句以后的代码将得以执行。 PC_DOSSaveReturn()将时钟节拍改为 18.2Hz[L1.6(6)],恢复PC 时钟节拍中断服务[L1.6(7)],清屏幕[L1.6(8)],通过exit(0)返回DOS [L1.6(9)]。

程序清单 L 1.6 保存DOS环境。. void PC_DOSSaveReturn (void) {

PC_ExitFlag = FALSE; (1) OSTickDOSCtr = 8; (2) PC_TickISR = PC_VectGet(VECT_TICK); (3)

OS_ENTER_CRITICAL();

PC_VectSet(VECT_DOS_CHAIN, PC_TickISR); (4) OS_EXIT_CRITICAL();

Setjmp(PC_JumpBuf); (5) if (PC_ExitFlag == TRUE) { OS_ENTER_CRITICAL();

PC_SetTickRate(18); (6) PC_VectSet(VECT_TICK, PC_TickISR); (7) OS_EXIT_CRITICAL();

PC_DispClrScr(DISP_FGND_WHITE + DISP_BGND_BLACK); (8) exit(0); (9) } }

程序清单 L 1.7 设置返回DOS 。 void PC_DOSReturn (void) {

PC_ExitFlag = TRUE; (1) longjmp(PC_JumpBuf, 1); (2) }

现在回到main()这个函数,在程序清单 L 1.5中,main()调用PC_VectSet()来设置μCOS-II中的 CPU寄存器切换。任务级的CPU寄存器切换由80x86 INT指令来分配向量地址。笔者使用向量0x80(即128),因为它未被DOS和BIOS使用。

这里用了一个信号量来保护Borland C/C++库中的产生随机数的函数[L1.5(5)],之所以使用信号量保护一下,是因为笔者不知道这个函数是否具备可重入性,笔者假设其不具备,初始化将信号量设置为1,意思是在某一时刻只有一个任务可以调用随机数产生函数。

在开始多任务之前,笔者建立了一个叫做TaskStart()的任务[L1.5(6)],在启动多任务OSStart()之前用户至少要先建立一个任务,这一点非常重要[L1.5(7)]。不这样做用户的应用程序将会崩溃。实际上,如果用户要计算CPU的利用率时,也需要先 建立一个任务。μCOS-II的统计任务要求在整个一秒钟内没有任何其它任务运行。如果用户在启动多任务之前要建立其它任务,必须保证用户的任务代码监控全局变量OSStatRdy和延时程序 [即调用 OSTimeDly()]的执行,直到这个变量变成TRUE。这表明μC/OS-II的CPU利用率统计函数已经采集到了数据。 1.07.02 TaskStart()

例1中的主要工作由TaskStart()来完成。TaskStart()函数的示意代码如程序清单 L 1.8所示。TaskStart()首先在屏幕顶端显示一个标识,说明这是例1 [L1.8(1)]。然后关中断,以改变中断向

量,让其指向μC/OS-II的时钟节拍处理,而后,改变时钟节拍率,从DOS的 18.2Hz 变为 200Hz [L1.8(3)]。在处理器改变中断向量时以及系统没有完全初始化前,当然不希望有中断打入!注意main()这个函数(见程序清单 L 1.5)在系统初始化的时候并没有将中断向量设置成μC/OS-II的时钟节拍处理程序,做嵌入式应用时,用户必须在第一个任务中打开时钟节拍中断。

程序清单 L 1.8 建立其它任务的任务。 void TaskStart (void *data) {

Prevent compiler warning by assigning ?data‘ to itself;

Display banner identifying this as EXAMPLE #1; (1)

OS_ENTER_CRITICAL();

PC_VectSet(0x08, OSTickISR); (2) PC_SetTickRate(200); (3) OS_EXIT_CRITICAL();

Initialize the statistic task by calling ?OSStatInit()‘; (4)

Create 10 identical tasks; (5)

for (;;) {

Display the number of tasks created; Display the % of CPU used;

Display the number of task switches in 1 second; Display uC/OS-II‘s version number If (key was pressed) {

if (key pressed was the ESCAPE key) { PC_DOSReturn(); } }

Delay for 1 Second; } }

在建立其他任务之前,必须调用OSStatInit()[L1.8(4)]来确定用户的PC有多快,如程序清单L1.9所示。在一开始,OSStatInit()就将自身延时了两个时钟节拍,这样它就可以与时钟节拍中断同步[L1.9(1)]。因此,OSStatInit()必须在时钟节拍启动之后调用;否则,用户的应用程序就会崩溃。当μC/OS-II调用OSStatInit()时,一个32位的计数器OSIdleCtr被清为0 [L1.9(2)],并产生另一个延时,这个延时使OSStatInit()挂起。此时,uCOS-II没有别的任务可以执行,它只能执行空闲任务(μC/OS-II的内部任务)。空闲任务是一个无线的循环,它不断的递增OSIdleCtr[L1.9(3)]。1秒以后,uCOS-II重新开始OSStatInit(),并且将OSIdleCtr保存在OSIdleMax中[L1.9(4)。所以OSIdleMax是OSIdleCtr所能达到的最大值。而当用户再增加其他应用代码时,空闲任务就不会占用那样多的CPU时间。OSIdleCtr不可能达到那样多的记数,(如果拥护程序每秒复位一次OSIdleCtr)CPU利用率的计算由μC/OS-II 中的OSStatTask()函数来完成,这个任务每秒执行一次。而当OSStatRdy置为TRUE[L1.9(5)],表示μC/OS-II将统计CPU的利用率。

程序清单 L 1.9 测试CPU速度。 void OSStatInit (void) {