Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
2012 / uCOS / uCOSII_ebook.pdf
Скачиваний:
82
Добавлен:
10.02.2015
Размер:
1.08 Mб
Скачать

void TaskClk (void *data)

{

struct time now; struct date today; char s[40];

data = data; for (;;) {

PC_GetDateTime(s);

PC_DispStr(0, 24, s, DISP_FGND_BLUE + DISP_BGND_CYAN); OSTimeDly(OS_TICKS_PER_SEC);

}

}

Listing 1.18, Clock display task.

1.09 Example #3

Example #3 demonstrates some additional features of µC/OS -II. Specifically, example #3 uses the TCB (Task Control Block) extension capability of OSTaskCreateExt(), the user defined context switch hook (OSTaskSwHook()), the user defined statistic task hook (OSTaskStatHook()), and message queues.

The third example is found in \SOFTWARE\uCOS-II\EX3_x86L and again, consists of a total of 9 tasks. µC/OS-II creates two ‘internal’tasks: the idle task and the task that determines CPU usage. EX3L.C creates the other 7 tasks. As with examples #1 and #2, TaskStart() is created by main() and its function is to create the other tasks and display statistics on the screen.

1.09.01 Example #3, main()

main() (see listing 1.19) looks just like the code for example #2 except that the task is given a name

which saved in a user defined TCB extension L1.19(1) (the declaration for the extension is found in INCLUDES.H and shown in listing 1.20). I decided to allocate 30 characters for the tas k name (including

the NUL character) to show that you can have fairly descriptive task names L1.20(1). I disabled stack checking for TaskStart() because we will not be using that feature in this example L1.19(2).

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();

 

}

Listing 1.19, main() for example #3.

typedef struct {

char TaskName[30]; (1) INT16U TaskCtr;

INT16U TaskExecTime; INT32U TaskTotExecTime;

} TASK_USER_DATA;

Listing 1.20, TCB extension data structure.

1.09.02 Example #3, Tasks

The pseudo code for TaskStart() is shown in listing 1.21. The code hasn’t changed much from example #2 except for three things:

1)A message queue is created L1.21(1) for use by Task1(), Task2() and Task3(),

2)Each task has a name which is stored in the TCB extension L1.21(2) and,

3)Stack checking will not be allowed.

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;

}

}

Listing 1.21, Pseudo-code for TaskStart() for example #3.

Task1() writes messages into the message queue L1.22(1). Task1() delays itself whenever a message is sent L1.22(2). This allows the receiver to display the message at a humanly readable rate. Three different messages are sent.

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);

 

}

 

 

 

}

 

 

 

Listing 1.22, Example #3, Task #1.

Task2() pends on the message queue with no timeout L1.23(1). This means that the task will wait forever for a message to arrive. When the message is received, Task2() displays the message on the screen L1.23(2) and delays itself for 500 mS L1.23(3). This will allow Task3() to receive a message because Task2() will not be checking the queue for a whole 500 mS.

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)

}

 

}

Listing 1.23, Example #3, Task #2.

Task3() also pends on the message queue but, it is willing to wait for only 250 mS L1.24(1). If a message is received, Task3() will display the message number L1.24(3). If a timeout occurs, Task3() will display a ‘T’(for timeout) instead L1.24(2).

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)

}

}

}

Listing 1.24, Example #3, Task #3.

Task4() doesn’t do much except post L1.25(1) and pend L1.25(2) on a mailbox. This basically allows you to measure the time it takes for these calls to execute on your particular PC. Task4() executes every 10 mS L1.25(3).

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)

}

 

 

}

Listing 1.25, Example #3, Task #4.

Task5() does nothing useful except it delays itself for 1 clock tick L1.26(1). Note that all µC/OS-II tasks MUST call a service provided by µC/OS-II to wait for either time to expire or an event to occur. If this is not done, the task would prevent all lower priority tasks from running.

void Task5 (void *data)

 

{

 

data = data;

 

for (;;) {

 

OSTimeDly(1);

(1)

}

 

}

 

Listing 1.26, Example #3, Task #5.

TaskClk() is a task that displays the current date and time every second.

1.09.03 Example #3, Notes

There are things happening behind the scenes and would not be apparent just by looking at EX3L.C. EX3L.C contains code for OSTaskSwHook() that measures the execution time of each task, how

often each task executes, and keeps track of total execution time of each task. This information is stored in the TCB extension so that it can be displayed. OSTaskSwHook() is called every time a context switch

occurs.

The execution time of the task being switched out is obtained by reading a timer on the PC through the function PC_ElapsedStop() L1.27(1). It is assumed that the timer was started by calling PC_ElapsedStart() when the task was switched in L1.27(2). The first context switch will probably read an incorrect value but this is not critical. OSTaskSwHook() then retrieves the pointer to

the TCB extension L1.27(3) and, if an extension exist L1.27(4) for the task, a counter is incremented L1.27(5) which indicates how often the current task has been switched out. Such a counter is useful to determine if a particular task is running. Next, the execution time of the task being switched out is saved in the TCB extension L1.27(6). A separate accumulator is used to maintain the total execution time L1.27(7). This allows you to determine the percentage of time each task takes with respect to other tasks in an application. Note that these statistics are displayed by OSTaskStatHook().

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)

}

 

 

 

}

 

 

 

Listing 1.27, User defined OSTaskSwHook().

µC/OS-II always calls a function called OSTaskStatHook() when you enable the statistic task (i.e. the configuration constant OS_TASK_STAT_EN in OS_CFG.H is set to 1). When enabled, the statistic task OSTaskStat() always calls the user definable function OSTaskStatHook(). This happens every second. I decided to have OSTaskStatHook() display the statistics collected by OSTaskSwHook(). In addition, OSTaskStatHook() also computes the percentage of time that each task takes with respect to each other.

The total execution time of all tasks is computed in L1.28(1). Then, individual statistics are displayed at the proper location on the screen L1.28(2) by a function (i.e. DispTaskStat()) that takes care of

converting the values into ASCII. Next, the percentage of execution time is computed for each task L1.28(3) and displayed L1.28(4).

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;

}

}

}

Listing 1.28, User defined OSTaskStatHook().

Соседние файлы в папке uCOS