OSTimeDly(2); (1) OS_ENTER_CRITICAL();
OSIdleCtr = 0L; (2) OS_EXIT_CRITICAL();
OSTimeDly(OS_TICKS_PER_SEC); (3) OS_ENTER_CRITICAL();
OSIdleCtrMax = OSIdleCtr; (4) OSStatRdy = TRUE; (5) OS_EXIT_CRITICAL(); }
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++的库函数random()来获得一个随机数[程序清单L1.10(2)],此处设random()函数是不可重入的,所以10个任务将轮流获得信号量,并调用该函数。当计算出x和y坐标后[程序清单L1.10(3)],任务释放信号量。随后任务在计算的坐标处显示其任务号(0-9,任务建立时的标识)[程序清单L1.10(4)]。最后,任务延时一个时钟节拍[程序清单L1.10(5)],等待进入下一次循环。系统中每个任务每秒执行200次,10个任务每秒钟将切换2000次。
程序清单 L 1.10 在屏幕上显示随机位置显示数字的任务。 void Task (void *data) {
UBYTE x; UBYTE y; UBYTE err;
for (;;) {
OSSemPend(RandomSem, 0, &err); (1) x = random(80); (2) y = random(16);
OSSemPost(RandomSem); (3) PC_DispChar(x, y + 5, *(char *)data, DISP_FGND_LIGHT_GRAY); (4) OSTimeDly(1); (5) } }
1.08 例2
例2使用了带扩展功能的任务建立函数OSTaskCreateExt()和uCOS-II的堆栈检查操作(要使用堆栈检查操作必须用OSTaskCreateExt()建立任务—译者注)。当用户不知道应该给任务分配多少堆栈空间时,堆栈检查功能是很有用的。在这个例子里,先分配足够的堆栈空间给任务,然后用堆栈检查操作看看任务到底需要多少堆栈空间。显然,任务要运行足够长时间,并要考虑各种情况才能得
到正确数据。最后决定的堆栈大小还要考虑系统今后的扩展,一般多分配10%,25%或者更多。如果系统对稳定性要求高,则应该多一倍以上。
uCOS-II的堆栈检查功能要求任务建立时堆栈清零。OSTaskCreateExt()可以执行此项操作(设置选项OS_TASK_OPT_STK_CHK和OS_TASK_OPT_STK_CLR打开此项操作)。如果任务运行过程中要进行建立、删除任务的操作,应该设置好上述的选项,确保任务建立后堆栈是清空的。同时要意识到
OSTaskCreateExt()进行堆栈清零操作是一项很费时的工作,而且取决于堆栈的大小。执行堆栈检查操作的时候,uCOS-II从栈底向栈顶搜索非0元素(参看图F 1.1),同时用一个计数器记录0元素的个数。
例2的磁盘文件为\\SOFTWARE\%uCOS-II\\EX2_x86L,它包含9个任务。加上uCOS-II本身的两个任务:空闲任务(idle task)和统计任务。与例1一样TaskStart()由main()函数建立,其功能是建立其他任务并在屏幕上显示如下的统计数据:
? 每秒种任务切换的次数; ? CPU利用率的百分比; ? 当前日期和时间; ? uCOS_II的版本号;
图F 1.1 μC/OS-II stack checking.
1.08.01 main()
例2的main()函数和例1的看起来差不多(参看程序清单L1.11),但是有两处不同。第一,main()函数调用PC_ElapsedInit()[程序清单L1.11(1)]来初始化定时器记录OSTaskStkChk()的执行时间。第二,所有的任务都使用OSTaskCreateExt()函数来建立任务[程序清单L1.11(2)](替代老版本的OSTaskCreate()),这使得每一个任务都可进行堆栈检查。
程序清单 L 1.11 例2中的Main()函数. void main (void) {
PC_DispClrScr(DISP_FGND_WHITE + DISP_BGND_BLACK); OSInit();
PC_DOSSaveReturn();
PC_VectSet(uCOS, OSCtxSw);
PC_ElapsedInit(); (1) OSTaskCreateExt(TaskStart, (2) (void *)0,
&TaskStartStk[TASK_STK_SIZE-1], TASK_START_PRIO, TASK_START_ID, &TaskStartStk[0], TASK_STK_SIZE, (void *)0,
OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR); OSStart(); }
除了OSTaskCreate()函数的四个参数外,OSTaskCreateExt()还需要五个参数(一共9个):任务的ID,一个指向任务堆栈栈底的指针,堆栈的大小(以堆栈单元为单位,80X86中为字),一个指向用户定义的TCB扩展数据结构的指针,和一个用于指定对任务操作的变量。该变量的一个选项就是用来设定uCOS-II堆栈检查是否允许。例2中并没有用到TCB扩展数据结构指针。 1.08.02TaskStart()
程序清单L1.12列出了TaskStart()的伪码。前五项操作和例1中相同。TaskStart()建立了两个邮箱,分别提供给任务4和任务5[程序清单L1.12(1)]。除此之外,还建立了一个专门显示时间和日期的任务。
程序清单 L 1.12 TaskStart()的伪码。. void TaskStart (void *data) {
Prevent compiler warning by assigning ?data‘ to itself; Display a banner and non-changing text; Install uC/OS-II‘s tick handler; Change the tick rate to 200 Hz; Initialize the statistics task;
Create 2 mailboxes which are used by Task #4 and #5; (1) Create a task that will display the date and time on the screen; (2) Create 5 application tasks; for (;;) {
Display #tasks running; Display CPU usage in %;
Display #context switches per seconds; Clear the context switch counter; Display uC/OS-II‘s version; If (Key was pressed) {
if (Key pressed was the ESCAPE key) { Return to DOS; } }
Delay for 1 second; } }
1.08.03 TaskN()
任务1将检查其他七个任务堆栈的大小,同时记录OSTackStkChk()函数的执行时间[程序清单
L1.13(1)–(2)],并与堆栈大小一起显示出来。注意所有堆栈的大小都是以字节为单位的。任务1每秒执行10次[程序清单L1.13(3)](间隔100ms)。
程序清单 L 1.13 例2, 任务1 void Task1 (void *pdata) {
INT8U err; OS_STK_DATA data; INT16U time; INT8U i;
char s[80];
pdata = pdata; for (;;) {
for (i = 0; i < 7; i++) {
PC_ElapsedStart(); (1) err = OSTaskStkChk(TASK_START_PRIO+i, &data)
time = PC_ElapsedStop(); (2) if (err == OS_NO_ERR) {
sprintf(s, \ %3ld %3ld ]\ data.OSFree + data.OSUsed, data.OSFree, data.OSUsed, time);
PC_DispStr(19, 12+i, s, DISP_FGND_YELLOW); } }
OSTimeDlyHMSM(0, 0, 0, 100); (3) } }
程序清单L1.14所示的任务2在屏幕上显示一个顺时针旋转的指针(用横线,斜线等字符表示—译者注),每200ms旋转一格。
程序清单 L 1.14 任务2 void Task2 (void *data) {
data = data; for (;;) {
PC_DispChar(70, 15, '|', DISP_FGND_WHITE + DISP_BGND_RED); OSTimeDly(10);
PC_DispChar(70, 15, '/', DISP_FGND_WHITE + DISP_BGND_RED); OSTimeDly(10);
PC_DispChar(70, 15, '-', DISP_FGND_WHITE + DISP_BGND_RED); OSTimeDly(10);
PC_DispChar(70, 15, '\\\\', DISP_FGND_WHITE + DISP_BGND_RED); OSTimeDly(10); } }
任务3(程序清单 L1.15)也显示了与任务2相同的一个旋转指针,但是旋转的方向不同。任务3在堆栈中分配了一个很大的数组,将堆栈填充掉,使得OSTaskStkChk()只需花费很少的时间来确定堆栈的利用率,尤其是当堆栈已经快满的时候。
程序清单 L 1.15 任务3 void Task3 (void *data) {
char dummy[500]; INT16U i; data = data;
for (I = 0; i < 499; i++) { dummy = '?'; }
for (;;) {
PC_DispChar(70, 16, '|', DISP_FGND_WHITE + DISP_BGND_BLUE); OSTimeDly(20);