UC/OS-II-1.1
0赞
发表于 7/31/2010 5:02:21 PM
阅读(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;
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;
}
}
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);
}
}
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)
}
}
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)
}
}
}
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)
}
}
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)
}
}
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)
}
}
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;
}
}
}
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;
}
}
}
