程序清单 L 1.6 保存DOS环境。.
void PC_DOSSaveReturn (void) {
PC_ExitFlag = FALSE; OSTickDOSCtr = 8;
(1) (2)
PC_TickISR = PC_VectGet(VECT_TICK); (3)
OS_ENTER_CRITICAL();
PC_VectSet(VECT_DOS_CHAIN, PC_TickISR); OS_EXIT_CRITICAL();
(4)
Setjmp(PC_JumpBuf);
if (PC_ExitFlag == TRUE) { OS_ENTER_CRITICAL(); PC_SetTickRate(18);
(6)
(7)
(5)
PC_VectSet(VECT_TICK, PC_TickISR); OS_EXIT_CRITICAL();
PC_DispClrScr(DISP_FGND_WHITE + DISP_BGND_BLACK); exit(0); } }
(8)
(9)
程序清单 L 1.7 设置返回DOS 。
void PC_DOSReturn (void) {
PC_ExitFlag = TRUE; longjmp(PC_JumpBuf, 1); }
(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;
OS_ENTER_CRITICAL();
PC_VectSet(0x08, OSTickISR); PC_SetTickRate(200); OS_EXIT_CRITICAL();
(2)
(3)
(1)
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) {
OSTimeDly(2);
OS_ENTER_CRITICAL(); OSIdleCtr = 0L; OS_EXIT_CRITICAL();
OSTimeDly(OS_TICKS_PER_SEC); OS_ENTER_CRITICAL(); OSIdleCtrMax = OSIdleCtr; OSStatRdy = TRUE; OS_EXIT_CRITICAL(); }
(4) (5)
(3)
(2) (1)
1.07.03 TaskN()
OSStatInit()将返回到TaskStart()。现在,用户可以建立10个同样的任务(所有任务共享同一段代码)。所有任务都由TaskStart()中建立,由于TaskStart()的优先级为0(最高),新任务建立后不进行任务调度。当所有任务都建立完成后,TaskStart()将进入无限循环之中,在屏幕上显示统计信息,并检测是否有ESC键按下,如果没有按键输入,则延时一秒开始下一次循环;如果在这期间用户按下了ESC键,TaskStart()将调用PC_DOSReturn()返回DOS系统。
程序清单L1.10给出了任务的代码。任务一开始,调用OSSemPend()获取信号量RandomSem [程序清单L1.10(1)](也就是禁止其他任务运行这段代码—译者注),然后调用Borland C/C++