1.09例3<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
例3中使用了许多uCOS-II提供的附加功能。任务3使用了OSTaskCreateExt()中TCB的扩展数据结构,用户定义的任务切换对外接口函数(OSTaskSwHook()),用户定义的统计任务(statistic task )的对外接口函数(OSTaskStatHook())以及消息队列。例3的磁盘文件是\SOFTWARE\uCOS-II\EX3_x86L,它包括9个任务。除了空闲任务(idle task)和统计任务(statistic task ),还有7个任务。与例1,例2一样,TaskStart()由main()函数建立,其功能是建立其他任务,并显示统计信息。
1.09.01 main()
main()函数[程序清单L1.19]和例2中的相不多,不同的是在用户定义的TCB扩展数据结构中可以保存每个任务的名称[程序清单L1.19(1)](扩展结构的声明在INCLUDES.H中定义,也可参看程序清单L1.20)。笔者定义了30个字节来存放任务名(包括空格)[程序清单L1.20(1)]。本例中没有用到堆栈检查操作,TaskStart()中禁止该操作[程序清单L1.19(2)]。
| 程序清单 L 1.19 例3的main()函数 |
| void main (void) |
| { |
| PC_DispClrScr(DISP_FGND_WHITE + DISP_BGND_BLACK); |
| OSInit(); |
| PC_DOSSaveReturn(); |
| PC_VectSet(uCOS, OSCtxSw); |
| PC_ElapsedInit(); |
|
|
| Strcpy(TaskUserData[TASK_START_ID].TaskName, "StartTask"); (1) |
| OSTaskCreateExt(TaskStart, |
| (void *)0, |
| &TaskStartStk[TASK_STK_SIZE-1], |
| TASK_START_PRIO, |
| TASK_START_ID, |
| &TaskStartStk[0], |
| TASK_STK_SIZE, |
| &TaskUserData[TASK_START_ID], |
| 0); (2) |
| OSStart(); |
| } |
| 程序清单 L 1.20 TCB扩展数据结构。 |
| typedef struct { |
| char TaskName[30]; (1) |
| INT16U TaskCtr; |
| INT16U TaskExecTime; |
| INT32U TaskTotExecTime; |
| } TASK_USER_DATA; |
1.09.02任务
TaskStart()的伪码如程序清单L1.21所示,与例2有3处不同:
l 为任务1,2,3建立了一个消息队列[程序清单L1.21(1)];
l 每个任务都有一个名字,保存在任务的TCB扩展数据结构中[程序清单L1.21(2)];
l 禁止堆栈检查。
| 程序清单 L 1.21 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 a message queue; (1) |
| Create a task that will display the date and time on the screen; |
| Create 5 application tasks with a name stored in the TCB ext.; (2) |
| 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向消息队列发送一个消息[程序清单L1.22(1)],然后延时等待消息发送完成[程序清单L1.22(2)]。这段时间可以让接收消息的任务显示收到的消息。发送的消息有三种。
| 程序清单 L 1.22 任务1。 |
| void Task1 (void *data) |
| { |
| char one = '1'; |
| char two = '2'; |
| char three = '3'; |
|
|
|
|
| data = data; |
| for (;;) { |
| OSQPost(MsgQueue, (void *)&one); (1) |
| OSTimeDlyHMSM(0, 0, 1, 0); (2) |
| OSQPost(MsgQueue, (void *)&two); |
| OSTimeDlyHMSM(0, 0, 0, 500); |
| OSQPost(MsgQueue, (void *)&three); |
| OSTimeDlyHMSM(0, 0, 1, 0); |
| } |
| } |
任务2处于等待消息的挂起状态,且不设定最大等待时间[程序清单L1.23(1)]。所以任务2将一直等待直到收到消息。当收到消息后,任务2显示消息并且延时500mS[程序清单L1.23(2)],延时的时间可以使任务3检查消息队列。
| 程序清单 L 1.23 任务2。 |
| void Task2 (void *data) |
| { |
| INT8U *msg; |
| INT8U err; |
|
|
|
|
| data = data; |
| for (;;) { |
| msg = (INT8U *)OSQPend(MsgQueue, 0, &err); (1) |
| PC_DispChar(70, 14, *msg, DISP_FGND_YELLOW+DISP_BGND_BLUE); (2) |
| OSTimeDlyHMSM(0, 0, 0, 500); (3) |
| } |
| } |
任务3同样处于等待消息的挂起状态,但是它设定了等待结束时间250mS[程序清单L1.24(1)]。如果有消息来到,任务3将显示消息号[程序清单L1.24(3)],如果超过了等待时间,任务3就显示“T”(意为timeout)[程序清单L1.24(2)]。
| 程序清单 L 1.24任务3 |
| void Task3 (void *data) |
| { |
| INT8U *msg; |
| INT8U err; |
|
|
|
|
| data = data; |
| for (;;) { |
| msg = (INT8U *)OSQPend(MsgQueue, OS_TICKS_PER_SEC/4, &err); (1) |
| If (err == OS_TIMEOUT) { |
| PC_DispChar(70,15,'T',DISP_FGND_YELLOW+DISP_BGND_RED); (2) |
| } else { |
| PC_DispChar(70,15,*msg,DISP_FGND_YELLOW+DISP_BGND_BLUE); (3) |
| } |
| } |
| } |
任务4的操作只是从邮箱发送[程序清单L1.25(1)]和接收[程序清单L1.25(2)],这使得用户可以测量任务在自己PC上执行的时间。任务4每10mS执行一次[程序清单L1.25(3)]。
| 程序清单 L 1.25 任务4。 |
| void Task4 (void *data) |
| { |
| OS_EVENT *mbox; |
| INT8U err; |
|
|
|
|
| data = data; |
| mbox = OSMboxCreate((void *)0); |
| for (;;) { |
| OSMboxPost(mbox, (void *)1); (1) |
| OSMboxPend(mbox, 0, &err); (2) |
| OSTimeDlyHMSM(0, 0, 0, 10); (3) |
| } |
| } |
任务5除了延时一个时钟节拍以外什么也不做[程序清单L1.26(1)]。注意所有的任务都应该调用uCOS-II的函数,等待延时结束或者事件的发生而让出CPU。如果始终占用CPU,这将使低优先级的任务无法得到CPU。
| 程序清单 L 1.26 任务5。 |
| void Task5 (void *data) |
| { |
| data = data; |
| for (;;) { |
| OSTimeDly(1); (1) |
| } |
| } |
同样, TaskClk()函数[程序清单L1.18]显示当前日期和时间。
1.09.03注意
有些程序的细节只有请您仔细读一读EX3L.C才能理解。EX3L.C中有OSTaskSwHook()函数的代码,该函数用来测量每个任务的执行时间,可以用来统计每一个任务的调度频率,也可以统计每个任务运行时间的总和。这些信息将存储在每个任务的TCB扩展数据结构中。每次任务切换的时候OSTaskSwHook()都将被调用。
每次任务切换发生的时候,OSTaskSwHook()先调用PC_ElapsedStop()函数[程序清单L1.27(1)] 来获取任务的运行时间[程序清单L1.27(1)],PC_ElapsedStop()要和PC_ElapsedStart()一起使用,上述两个函数用到了PC的定时器2(timer 2)。其中PC_ElapsedStart()功能为启动定时器开始记数;而PC_ElapsedStop()功能为获取定时器的值,然后清零,为下一次计数做准备。从定时器取得的计数将拷贝到time变量[程序清单L1.27(1)]。然后OSTaskSwHook()调用PC_ElapsedStart()重新启动定时器做下一次计数[程序清单L1.27(2)]。需要注意的是,系统启动后,第一次调用PC_ElapsedStart()是在初始化代码中,所以第一次任务切换调用PC_ElapsedStop()所得到的计数值没有实际意义,但这没有什么影响。如果任务分配了TCB扩展数据结构[程序清单L1.27(4)],其中的计数器TaskCtr进行累加[程序清单L1.27(5)]。TaskCtr可以统计任务被切换的频繁程度,也可以检查某个任务是否在运行。TaskExecTime [程序清单L1.27(6)]用来记录函数从切入到切出的运行时间,TaskTotExecTime[程序清单L1.27(7)]记录任务总的运行时间。统计每个任务的上述两个变量,可以计算出一段时间内各个任务占用CPU的百分比。OSTaskStatHook()函数会显示这些统计信息。
| 程序清单 L 1.27 用户定义的OSTaskSwHook() |
| void OSTaskSwHook (void) |
| { |
| INT16U time; |
| TASK_USER_DATA *puser; |
|
|
|
|
| time = PC_ElapsedStop(); (1) |
| PC_ElapsedStart(); (2) |
| puser = OSTCBCur->OSTCBExtPtr; (3) |
| if (puser != (void *)0) { (4) |
| puser->TaskCtr++; (5) |
| puser->TaskExecTime = time; (6) |
| puser->TaskTotExecTime += time; (7) |
| } |
| } |
本例中的统计任务(statistic task)将调用对外接口函数OSTaskStatHook()(设置OS_CFG.H文件中的OS_TASK_STAT_EN为1允许对外接口函数)。统计任务每秒运行一次,本例中OSTaskStatHook()用来计算并显示各任务占用CPU的情况。
OSTaskStatHook()函数中首先计算所有任务的运行时间[程序清单L1.28(1)],DispTaskStat()用来将数字显示为ASCII字符[程序清单L1.28(2)]。然后是计算每个任务运行时间的百分比[程序清单L1.28(3)],显示在合适的位置上 [程序清单L1.28(4)]。
| 程序清单 L 1.28 用户定义的OSTaskStatHook(). |
| void OSTaskStatHook (void) |
| { |
| char s[80]; |
| INT8U i; |
| INT32U total; |
| INT8U pct; |
|
|
|
|
| total = 0L; |
| for (I = 0; i < 7; i++) { |
| total += TaskUserData[i].TaskTotExecTime; (1) |
| DispTaskStat(i); (2) |
| } |
| if (total > 0) { |
| for (i = 0; i < 7; i++) { |
| pct = 100 * TaskUserData[i].TaskTotExecTime / total; (3) |
| sprintf(s, "%3d %%", pct); |
| PC_DispStr(62, i + 11, s, DISP_FGND_YELLOW); (4) |
| } |
| } |
| if (total > 1000000000L) { |
| for (i = 0; i < 7; i++) { |
| TaskUserData[i].TaskTotExecTime = 0L; |
| } |
| } |
| } |
1589

被折叠的 条评论
为什么被折叠?



