的库函数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); x = random(80); y = random(16); OSSemPost(RandomSem);
(3)
(4)
(1)
(2)
PC_DispChar(x, y + 5, *(char *)data, DISP_FGND_LIGHT_GRAY); 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()函数建立,其功能是建立其他任务并在屏幕上显示如下的统计数据:
z 每秒种任务切换的次数; z CPU利用率的百分比; z 当前日期和时间; z 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();
OSTaskCreateExt(TaskStart, (void *)0,
(1)
(2)
&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(); if (err == OS_NO_ERR) {