调用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 DSAdvanced code generation Floating point Instruction set
: Default for memory model
: Emulation : 80186
Options : Generate underbars
Optimizations Optimizations
Debug info in OBJs Fast floating point
Global register allocation Invariant code motion
Register variables Common subexpressions Optimize for
Induction variables Loop optimization Suppress redundant loads Copy propagation Dead code elimination Jump optimization In-line intrinsic functions Automatic Optimize globally 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()中建立的;它的功能是建立其它任务并且在屏幕上显示如下统计信息:
z 每秒钟任务切换次数; z CPU利用百分率; z 寄存器切换次数; z 目前日期和时间; z µ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); OSInit();
PC_DOSSaveReturn();
PC_VectSet(uCOS, OSCtxSw); RandomSem = OSSemCreate(1); OSTaskCreate(TaskStart, (void *)0,
(void *)&TaskStartStk[TASK_STK_SIZE-1], 0); OSStart(); }
(7)
(1)
(2)
(3)
(4) (5) (6)
当前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)]。