小盒子的小盒

UC/OS-II-1.1

0
阅读(2381)


1.09例3

例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;
 
        }
 
    }
 
}