《uCOOS-II原理与ARM应用程序设计》课件第4章_第1页
《uCOOS-II原理与ARM应用程序设计》课件第4章_第2页
《uCOOS-II原理与ARM应用程序设计》课件第4章_第3页
《uCOOS-II原理与ARM应用程序设计》课件第4章_第4页
《uCOOS-II原理与ARM应用程序设计》课件第4章_第5页
已阅读5页,还剩129页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第四章µC/OS-Ⅱ内核与面向任务程序设计

4.1µC/OS-Ⅱ内核OS_CORE.C

4.2任务OS_TASK.C

4.3时间OS_TIME.C

4.4本章小结

µC/OS-Ⅱ是多任务的实时操作系统,它的工作原理很简单,这里通过一个实例来说明。例如,一家公司有255个员工,所有这些员工按职位分为255级(即不存在职位相同的员工),公司里只有一个电话总机,每个员工都有一个分机,每个员工打外线电话,都需要通过同一个总机接外线。4.1µC/OS-Ⅱ内核OS_CORE.C假定每个员工每次打电话的时间绝不会超过1分钟。总机处有一个接线员管理。公司总经理的职位最高,他打电话的优先级为0;第一副经理的职位第二,他打电话的优先级为1;依次类推,职位最低的员工打电话的优先级为254。因此,公司运作过程中总机电话业务有以下情况(假设接线员8点0分上班后,每1分钟视察一下总机,这里的1分钟即时钟节拍):

(1)接线员上班后,打开总机,检查拨号的电话。如果此时所有员工同时拨号,当然,接线员会使总经理得到总机使用权,其他员工进入队列。等总经理打完后,再按优先级调度,依次接通。

(2)如果在某个时刻总机空闲,此时,任一员工拨号,等到接线员视察总机时进行调度,把总机分配给他。

(3)如果在某个时刻总机空闲,此时,有多个员工同时拨号,等到接线员视察总机时,进行调度,把总机分配给最高职位的员工,其他员工进入等待队列,等高职位的员工打完后,再依次接通。

(4)如果某个时刻总机正在被某个员工使用,此时,比他职位高的人正在拨号,则等到接线员视察总机时,中断当前的通话,使职位高的人先接通,等职位高的人打完电话后,再恢复刚才被中断的员工的通话。如果此时又有多个人拨号,此时,接线员将进行调度,让被中断的员工和所有新拨号的员工中职位最高的人先使用总机,其他员工进入等待队列。从这一步可以看出,如果某个职位低的员工正在使用总机,此时,即使总经理拨号,也必须等到时钟节拍来到后(即接线员视察或系统调度时)才能使总经理得到总机,最长等待时间为1分钟。但是如果该职位低的员工使用完总机后,还没有到接线员视察总机的时刻,那么总机会自动调度给新拨号的高职位员工。即空闲总机不会调度,但是每个员工打完电话后都会自动调度一次。

(5)如果某个时刻总机正在被使用,更高职位的员工紧急拨号,此时,可通过“中断”通知接线员立即进行调度,使高职位的员工使用总机,中断原来的通话,使职位低的员工进入中断状态。

从接线员的角度出发来看待这一问题,其运作过程是这样的:接线员每隔1分钟视察一下总机,如果总机空闲,则什么都不做;如果此时只有一人在拨号中,则帮他接通;如果此时有多人在拨号中,则帮职位最高的人接通,把其他人放入等待队列中,等职位高的人打完电话后,依次按职位高低自动接通;如果接线员在间隔中受到“中断”指示,则立即进行临时调度,如果当前有员工在使用总机,则中断他,使正在拨号的优先级最高的人接通电话,其他人进入队列。每个员工打完电话后,都会自动调度一次,这次调度不需要接线员干预。显然,接线员相当于总机切换的时钟节拍,如果没有接线员,总机无法切换,只能保持空闲状态,整个公司的电话业务就会停止。如果把上述的员工视为任务,接线员视为时钟节拍,则µC/OS-Ⅱ的基本工作原理也是如此。在µC/OS-Ⅱ中,创建好若干任务后,一般地,系统按任务的优先级依次执行一次后,各任务均进入延时等待状态,时钟节拍相当于心脏的脉搏,在每个时钟节拍处,将检查处于就绪态的任务,进行任务(切换)调度,使就绪态任务中最高优先级的任务得到CPU使用权,其他任务进入等待队列。每个任务单次运行CPU占用的时间不能大于一个时钟节拍。除了系统创建的空闲任务外,用户创建的任务运行完后,系统将进行一次任务调度。时钟节拍用于更新各个任务的延时。上述这种情况是各个任务都是独立运行、互不通信的情况,µC/OS-Ⅱ允许多个任务间进行数据通信,此时,系统运行稍微复杂一点,即在任务调度时,会检查用于任务间通信的信号量和邮箱。

所谓的任务,本质上是带有一个void*指针参数、无返回值的死循环函数。在µC/OS-ⅡV2.86版本中,用户最多可以创建252个任务(µC/OS-ⅡV2.86最多支持255个任务,其中,系统创建的3个任务是空闲任务、统计任务和定时器任务)。µC/OS-Ⅱ应用程序就是由一个个的任务组成的,任务中再调用函数完成特定的功能。每个任务具有不同的优先级,当一个任务执行完成后,把CPU的使用权交给µC/OS-Ⅱ内核,进行任务调度,使处于就绪态的最高优先级任务得到执行权。当µC/OS-Ⅱ内核进行任务调度后,去执行就绪态的具有最高优先级的任务的过程,不是通常意义下的函数调用(可以说,任务在“创建”时就被调用了),而是µC/OS-Ⅱ意义下的函数“返回”(或称任务调用),即返回到那个死循环函数(任务)中执行。通常意义下的函数调用是指:①保存当前调用函数(程序)的环境,程序指针跳转到被调用函数入口处,执行完被调用函数后,从堆栈中恢复调用函数的环境,继续执行原程序;②调用函数和被调用函数共用相同的堆栈,实际上被调用函数没有堆栈;③被调用函数的调用执行是由调用函数发出的;④被调用函数被调用后立即执行。而µC/OS-Ⅱ意义下的任务调用是指:①每个任务都有独立的堆栈空间,µC/OS-Ⅱ下的任务调用是先把当前任务的执行环境保存在它自己的堆栈中,然后从被调用任务的堆栈恢复被调用任务的环境,这两个任务占用不同的堆栈空间;②被调用任务的入口地址来自其堆栈,而不是函数标号;③被调用函数的调用执行是由µC/OS-Ⅱ调度器发出的,即由µC/OS-Ⅱ内核调用的,而不是某个任务;④被调用任务进入就绪态,即可以执行,但有可能不会立即执行。所以,一定意义上,任务的调用可以理解为返回到那个函数去执行,而不是调用那个函数来执行。任务永远不会返回,当前任务完成特定的功能后,释放CPU占用权,进入等待态,等待下一个“该函数返回”。CPU空闲时,µC/OS-Ⅱ执行系统定义的优先级最低的空闲任务,这时,每个时钟节拍到达时进行任务就绪态检查和调度管理。

µC/OS-Ⅱ下的任务可以有5种状态,即就绪态、执行态、被中断态、等待态和休眠态。就绪态表示该任务已经准备好,随时可以执行,但由于其优先级比正在运行的任务优先级低,暂时得不到CPU执行权,而在就绪列表中排队;执行态表示该任务获得了CPU执行权,正在运行;被中断态表示该任务被中断服务程序中断了;等待态表示该任务在等待运行(一般地,等待某一事件的发生或超时信号,处于等待态的任务位于等待列表队列中,与就绪态任务的区别在于,即使获得了CPU使用权,等待态任务也不能得到执行);休眠态专指一个任务被删除后的状态(任务不能被从内存中删除,只是从任务列表中删除,仍然位于内存中,这种任务状态称为休眠态),可以调用创建任务函数,使任务转到就绪态。任务中可以调用各种函数,被任务调用的函数是通常意义上的函数,但是,在µC/OS-Ⅱ中,要求这些被调用函数为可重入函数。所谓的可重入函数,是指只使用局部变量而不使用全局变量的函数,这样被多个任务调用时,每个任务下可重入函数都有自己独立的数据,不会造成数据变量使用的混乱。如果一个函数使用了全局变量,而这个全局变量被某个任务修改后,后继调用这个函数的任务都将使用被修改了的全局变量,从而得到意想不到的结果。所以,在µC/OS-Ⅱ下编写的函数都应该是可重入函数,即不使用全局变量、只使用局部变量的函数。4.1.1任务控制块

每个任务都与一个任务控制块结构体变量相关联,用于保存该任务的工作状态。任务控制块结构体类型在ucos_ii.h中定义,代码如下:上述代码中预编译常量均在os_cfg.h中定义(实际上,所有的预编译常量均在os_cfg.h中定义,通过修改这些常量的值为0,可以大幅度地裁剪µC/OS-Ⅱ代码长度)。第2行OSTCBStkPtr表示指向当前任务的堆栈指针,每个任务都具有独立的堆栈空间,且空间大小随意指定。第4~10行表示如果使用OSTaskCreateExt函数创建任务,其任务控制块中需要有第5~9行的成员,即OSTCBExtPtr指向用户定义的数据结构,用于扩展任务控制块数据;OSTCBStkBottom是指向该任务堆栈栈底的指针;OSTCBStkSize为堆栈大小;

OSTCBOpt为选项,一般只能取四个值或其组合,即OS_TASK_OPT_NONE(在ucos_ii.h中宏定义为0)、OS_TASK_OPT_STK_CHK(值为1)、OS_TASK_OPT_STK_

CLR(值为2)或OS_TASK_OPT_SAVE_FP(值为4),其中,OS_TASK_OPT_STK_CHK表示使能堆栈检查,OS_

TASK_OPT_STK_CLR表示创建堆栈时将堆栈空间清为0,这二者常组合在一起使用;OSTCBId表示任务ID号,取值为0~65535,用户可以使用这个ID号作为数组索引,在µC/OS-Ⅱ内核中并没有意义。第12~13行为任务控制块结构体指针,OSTCBNext指向下一个任务控制块,OSTCBPrev指向前一个任务控制块,这两个指针把所有任务的任务控制块连成一个双向链表,每个任务在创建时,将其任务控制块插入链表中。第16行OSTCBEventPtr为指向事件控制块的指针;第20行为指向多事件控制块的指针,多事件请求处理机制是µC/OS-ⅡV2.86新添加的特性。第24行表示指向传递给任务的消息或消息队列的指针。第29行为指向事件标志节点的指针;第31行为事件标志就绪状态变量。第34行OSTCBDly表示任务的延时节拍数;第35行OSTCBStat保存任务状态;第36行OSTCBStatPend保存任务挂起状态;第37行OSTCBPrio为任务优先级,0级最高。第39~47行与任务就绪表有关,OSTCBX是优先级分组的位索引,OSTCBY是优先级就绪表的索引,OSTCBBitX是访问就绪表位的位屏蔽,OSTCBBitY是访问优先级分组位的位屏蔽。第41行判断OS_LOWEST_PRIO是否小于等于63,如果是的话,OSTCBBitX和OSTCBBitY均为8位无符号整型变量,否则为16位无符号整型变量。

第50行表示任务是否需要删除自身。第53~59行为5个任务调度变量,分别记录任务切换次数、任务执行总时钟周期、任务循环计数、任务堆栈首地址和堆栈使用量等调试信息。第62行为任务名称,主要用作调试信息。在第4.2节的实例中,可以单步执行查看每个任务的任务控制块内容,在调试模式下,通过菜单“View|Locals”查看。4.1.2事件控制块

事件控制块结构体用于记录任务间通信的信息,该结构体类型将在第五章中使用,其在ucos_ii.h中的定义如下:上述代码第3行OSEventType表示事件类型,在ucos_ii.h中定义的事件类型有7种,即

#defineOS_EVENT_TYPE_UNUSED 0u

#defineOS_EVENT_TYPE_MBOX 1u

#defineOS_EVENT_TYPE_Q 2u

#defineOS_EVENT_TYPE_SEM 3u

#defineOS_EVENT_TYPE_MUTEX 4u

#defineOS_EVENT_TYPE_FLAG 5u

#defineOS_TMR_TYPE

100u分别表示空类型、邮箱、队列、信号量、互斥量、标志和定时器类型。第4行OSEventPtr为指向邮箱或队列的指针。第5行OSEventCnt为信号量计数值,如果为其他事件类型则该成员不使用。第6~12行的OSEventGrp和OSEventTble分别为等待事件的任务分组和任务列表。第15行为事件名,一般用作调试。

在第5章实例ex5_1中可以查看事件控制块变量的内容。

在ucos_ii.h中还定义了事件标志控制块、消息邮箱数据结构、内存控制块、互斥量数据结构、消息列队数据结构、信号量数据结构、定时器数据结构等数据类型,在使用这些数据类型时再作介绍,这里不详细说明了,读者可直接参考ucos_ii.h文件。任务堆栈数据结构定义如下:

1#ifOS_TASK_CREATE_EXT_EN>0

2typedefstructos_stk_data{

3INT32UOSFree; /*Numberoffreebytesonthestack*/

4INT32UOSUsed;/*Numberofbytesusedonthestack*/

5}OS_STK_DATA;

6#endif

上述代码第3行OSFree表示堆栈中没有使用的字节数;第4行表示堆栈中已使用的字节数。4.1.3就绪表

µC/OS-Ⅱ是多任务操作系统,系统的核心功能是任务调度,即当一个任务发生切换时,µC/OS-Ⅱ需要在众多已经就绪的任务中找到优先级最高的任务。为了实现快速查找,J.J.Labrosse使用查表的方法。他设计了一个表,表中的元素为位,可以根据任务的优先级将就绪任务填入表格的某位,同样,由表格中的一些位可直接算得任务的优先级。这样,任务一旦就绪,其表格中相应的某位就按既定算法置位;任务调度时,根据表格中所有已置位的位,直接算出优先级最高的就绪任务。设ptcb为某任务的任务控制块变量,则使用以下语句将就绪任务填入就绪表中,这里的OSRdyGrp和OSRdyTbl即为Labrosse设计的表。

OSRdyGrp|=ptcb->OSTCBBitY; /*No,Makeready*/

OSRdyTbl[ptcb->OSTCBY]|=ptcb->OSTCBBitX;

而OSTCBBitY和OSTCBBitX由以下语句算出(位于os_core.c中第1960行):

结合图4-1来理解任务就绪表的赋值操作。图4-1中HPT表示最高优先级任务,LPT表示最低优先级任务,HPT(0)表示0为最高优先级任务。OSRdyGrp和OSRdyTbl的大小依赖于OS_LOWEST_PRIO(位于os_cfg.h中,在ex4_1实例中修改为254)。图4-1任务就绪表根据上述代码和图4-1,如果任务的优先级为190(大于63),则OSTCBY=11,OSTCBX=14,OSTCBBitY=0x0800,OSTCBBitX=0x4000;如果该任务就绪了,且OSRdyGrp和OSRdyTbl都为0,则OSRdyGrp=0x0800,OSRdyTbl[11]为0x4000。在这个基础上,另一个优先级为28的任务就绪了,则它的OSTCBY=1,OSTCBX=12,OSTCBBitY=0x0002,OSTCBBitX=0x1000。此时任务就绪表的OSRdyGrp=0x0802,OSRdyTbl[1]=0x1000,OSRdyTbl[11]=0x4000。然后,优先级为0x31的任务也就绪了,且上述两个任务还没有进入执行状态,可以算得OSRdyGrp=0x0802,OSRdyTbl[1]=0x9000,OSRdyTbl[11]=0x4000,如图4-2所示。图4-2表格中的空格均为0。图4-2三个任务的就绪表从图4-2及上述推算过程知,有几个任务就绪,OSRdyTbl中就有多少个1;如果OSRdyTbl中的某一行至少有一个1,则OSRdyGrp中对应的位就为1。

由上述过程,可以依据一个任务的优先级得到它在就绪表中的位置。相反地,可以由就绪表的位,直接算得就绪表中的最高优先级的任务的优先级号。

假设就绪表如图4-2所示(这里只有三个就绪任务,读者可以自己演算更复杂的),在os_core.c中定义常量数组OSUnMapTbl如下:1INT8UconstOSUnMapTbl[256]={

20,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, /*0x00to0x0F*/

34,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, /*0x10to0x1F*/

45,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, /*0x20to0x2F*/

54,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, /*0x30to0x3F*/

66,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, /*0x40to0x4F*/

74,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, /*0x50to0x5F*/

85,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, /*0x60to0x6F*/

94,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, /*0x70to0x7F*/107,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, /*0x80to0x8F*/

114,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, /*0x90to0x9F*/

125,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, /*0xA0to0xAF*/

134,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, /*0xB0to0xBF*/

146,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, /*0xC0to0xCF*/

154,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, /*0xD0to0xDF*/

165,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, /*0xE0to0xEF*/

174,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 /*0xF0to0xFF*/

18};

由图4-2推算就绪任务的最高优先级号的算法(来自os_core.c)为按上述代码和图4-2易推算得,y=1,*ptbl=0x9000,就绪任务中的最高优先级为OSPrioHighRdy=16+4+8=28。µC/OS-Ⅱ最多支持255个优先级,使用查表方法速度很快,占用RAM空间也不多,而最新的µC/OS-Ⅲ支持无限多个优先级,显然不能用查表方法,作者可能使用了一种基于链表的新算法。4.1.4空闲任务和统计任务

在os_cfg.h中定义了OS_LOWEST_PRIO后,系统将自动把该优先级分配给空闲任务(即没有用户任务执行的状态下运行的任务,位于os_core.c中),其代码如下:上述代码是一个简单的死循环体,空闲记数器OSIdleCtr不断累加(第12行)。用户可以添加自定义代码(添加到OSTaskIdleHook钩子函数中),使进入空闲状态的任务点亮LED灯提示或使CPU工作于省电模式。

统计任务(位于os_core.c中)的代码如下:由上述代码可知,统计任务开始工作(即进入死循环)后,要计算CPU的使用率OSCPUUsage(第24行),然后进行任务堆栈的检查,最后,延时0.1秒进入任务切换。用户可以通过钩子函数OSTaskStatHook插入自定义的代码。统计任务需要一个初始化函数OSStatInit,这个函数需在调用OSInit后和进入多任务前调用。

µC/OS-Ⅱ代码中有大量的预编译指令代码,这些预编译常量一般在os_cfg.h中定义,用于对µC/OS-Ⅱ内核代码进行裁剪。对于UP-Star实验板来说,由于其具有32MB的RAM空间,无需进行裁剪。对于RAM空间较小的单片机系统板,则往往需要裁剪。4.1.5时钟节拍

µC/OS-Ⅱ系统借助一个时钟节拍实现任务的延时管理和超时调度,时钟节拍由定时器中断服务程序调用,必须在调用了OSStart后才能开启时钟节拍!

在后面的实例ex4_1中,使用S3C2410A的内部定时器4实现时钟节拍功能,即在其中断服务函数中调用OSTimeTick,完整代码可参阅bsp.c文件,这里不再列出。时钟节拍的任务调度均由汇编语言编写,位于os_cpu_a.asm(或os_cpu_a.s)中,这里也不再罗列。

为了使时钟节拍的频率为OS_TICKS_PER_SEC(位于os_cfg.h中第51行)所宏定义的值,必须设置定时器4的中断信号频率为OS_TICKS_PER_SEC,一般为100Hz。

由上述内容可知,本小节内容与µC/OS-Ⅱ的移植关系密切。4.1.6µC/OS-Ⅱ初始化

将第4.2节的实例ex4_1的主程序代码罗列如下:由上述代码可知,µC/OS-Ⅱ系统的启动分为三步,即调用OSInit初始化系统、创建任务(这里使用了OSTaskCreateExt)、调用OSStart启动多任务。一般地,在第一个创建的任务代码中添加创建其他任务的代码,这个在主函数(main)中创建的任务称为“引导”任务,而在其中创建的任务称为“工作”任务。引导任务只用来创建一些工作任务,本身没有太大意义;工作任务是用来完成特定功能的任务。

进入到os_core.c中可查得OSInit的代码如下:上述代码完成了µC/OS-Ⅱ系统的初始化,包括初始化全局变量、任务控制块、事件控制块、内存控制块、创建统计任务、初始化堆栈、定时器管理任务等内容,读者可通过单步调试工程ex4_1获得更多的信息。其实,Labrosse先生完全可以把OSStatInit函数通过预编译指令加入到OSInit中(第29行以后)。

在os_core.c中可查得OSStart函数的代码如下:第3行OSRunning为真(OS_TRUE)时表示多任务工作开始了;如果为OS_FALSE,则表示系统还没有工作,此时第4~8行查找最高优先级的任务,然后开始多任务调度工作。在OSStartHighRdy中将OSRunning置为OS_TURE(即1)。

µC/OS-Ⅱ意义下的任务是一个无限循环的函数,即任务被创建时即相当于被“执行”了,这里的“执行”只是完成了任务的入栈操作,并没有执行任务代码;当任务被“调用”时,即程序切换到该任务执行时,相当于程序返回到该任务去执行,即有一个出栈过程。任务中可以加入一些初始化代码,但其主体代码位于while(1)或for(;;)死循环中。如果只想让任务执行一次就退出,则需在死循环中加入OSTaskDel(OS_PRIO_SELF)语句。

本节将介绍第一章表1-4中一些函数的使用方法。首先建立一个工程ex4_1。4.2任务OS_TASK.C4.2.1工程ex4_1

在工程ex3_2的基础上新建工程ex4_1,保存目录为D:\ZYUCOSII\ex4_1,此时的工程ex4_1与ex3_2完全相同,只是工程文件名更改为ex4_1,如图4-3所示。图4-3工程ex4_1的最初版本在图4-3的基础上,需要修改的文件有app.c、appfun.c、app.h、includes.h、bsp.c、os_cfg.h等。其中,os_cfg.h中仅是把其第39行中的OS_LOWEST_PRIO宏定义值由63更改为254;includes.h文件中添加了一行“#include"math.h"”,用于计算数学函数;bsp.c文件中把函数myInitUART0中的串口0初始化波特率值设为115200bps,这个函数的代码如下:

1//InitializeUART0

2voidmyInitUART0(void)

3{

4//SetGPH3:2asRXD0:TXD0

5GPHCON=0xA0;6

7//UBRDIV0=0x270,SetUART0Baudrate:4800bps,8-bit,1-bitstop

8//UBRDIV0=0x19,SetUART0Baudrate:115200bps,8-bit,1-bitstop

9UFCON0=0x0;

10UMCON0=0x0;

11ULCON0=0x03;

12UCON0=0x05;

13UBRDIV0=0x19;

14}

app.h文件中添加了一些任务(或函数)的原型定义,完整的代码如下:

1/*FileName:app.h

2**Byzhnyong@21

3**@2009-4-4

4**CopyrightReserved

5*/

6

7#ifdefMY_APP_GLOBALS

8#defineMY_APP

9#else

10#defineMY_APPextern

11#endif上述代码中,第42~45行宏定义了4个任务的优先级,为5~8;第47行宏定义了AppTaskStart任务的堆栈大小;第48行宏定义了其他任务的堆栈大小。第34行N_TASKS表示用户创建任务的个数,其ID号在第36~39行宏定义,堆栈在第50~54行定义,任务的原型在第65、67~69行声明。

app.c和appfun.c的内容将在第4.2.2和第4.2.3节介绍。

工程ex4_1运行时,可以看到三个LED灯每隔1秒闪烁,同时,串口调试助手中显示一些信息,如图4-4和图4-5所示。图4-4工程ex4_1运行情况图4-5串口调试助手显示情况比较图4-4和图4-5可以看出,使用C-SPY显示的系统堆栈信息和串口调试助手显示的信息完全一致,工程ex4_1中共有7个任务,4个用户创建任务,CPU利用率约1%,每秒任务切换约为42(图4-5由于运行时间太短,显示的切换统计数33不太准确,请读者自己分析原因,并根据图4-4推算准确任务切换数)。图4-5中还显示了当前µC/OS-Ⅱ的版本号为V2.86,并且显示了30度角的正弦值。4.2.2主程序app.c

app.c的完整代码如下:

1/*FileName:app.c

2**Byzhnyong@21

3**@2009-4-4

4**MainRoutine

5**CopyrightReserved

6*/

7

8#include"includes.h"

9

10voidmain(void)

11{基于µC/OS-Ⅱ的主函数main,首先调用OSInit进行系统初始化(第12行),然后调用OSTaskCreateExt创建一个用户任务AppTaskStart(第13行),之后调用OSStart函数开始多任务调度(第22行)。

µC/OS-Ⅱ中有两个创建任务的函数,即OSTaskCreate和OSTaskCreateExt,这两个函数的原型位于ucos_ii.h中,如下所示:

OSTaskCreate有4个参数,依次传递任务名(函数名)、任务参数、任务堆栈顶地址、任务优先级(第2~5行)。OSTaskCreateExt是OSTaskCreate的扩展,有9个参数,依次传递任务名、任务参数、任务堆栈顶地址、任务优先级、任务ID号、任务堆栈底地址、任务堆栈大小、用户自定义数据地址、指定是否允许堆栈检查和清零等的选项值。

Labrosse建议用户使用OSTaskCreateExt创建任务,这类任务可以用C-SPY调试器查看堆栈使用情况。例如,在文件app.c中的第13行,创建的任务名为AppTaskStart;任务参数为空;栈顶地址为AppTaskStartStk[TASK_START_STK_SIZE-1];优先级为AppTaskStartPrio;ID号为AppTaskStartID;栈底地址为AppTaskStartStk[0];栈大小为TASK_START_STK_SIZE;用户数据为空;选项为OS_TASK_OPT_STK_CHK|OS_TASK_

OPT_STK_CLR,表示任务堆栈检查和清零。4.2.3任务程序appfun.c

文件appfun.c的完整代码如下:

1/*FileName:appfun.c

2**Byzhnyong@21

3**@2009-4-4

4**CopyrightReserved

5*/

6

7#defineMY_APP_GLOBALS

8#include"includes.h"

9

上述代码第10~60行为LED相关的代码,只需对第41~60行的代码作一说明即可。参考第一章图1-31,三个LED灯接在GPC5~7引脚上,第44行读取这三个引脚的值;第48行为读出值与0x0020异或,这样,只有LED1灯的状态翻转,其他两个灯的状态不变,这表现为LED1会闪烁;同理,第51、54和57行分别实现LED2、LED3以及LED1~3的状态变化,即实现LED灯的闪烁。

第61~69行为开定时器4中断。第70~113行为串口0相关的函数,实现了串口0的字节读/写以及字符串的输出功能。第117~156行为AppTaskStart任务的代码。第129行调用OSVersion函数获得µC/OS-Ⅱ的版本号,这里返回的值为286;第130行将os_version变量的值格式化赋给字符串sOsVer;第131行通过串口0输出;第134~136行计算30度角的正弦值,并通过串口输出。

第138~140行开定时器4。第138行为关中断操作,这样第139行代码执行过程中不受干扰;第140行恢复CPU的状态(注意,这里只是恢复,并没有开中断。如果中断原来是关闭的,则第140行执行完后,中断仍然是关闭的)。

第142行调用OSStatInit初始化统计任务用到的一些全局量。第144行调用自定义函数TaskStartCreateTasks创建一些任务,后面会介绍。

第146行将当前任务命名为AppTaskStart。

第148~155为死循环体。第150行将CPU利用率和任务切换数格式化输出到字符串s;第151行将s通过串口输出;第153行清除任务切换数;第154行延时3秒。

第157~190行借助OSTaskCreateExt创建了三个任务。与第4.2.2节app.c中第20行不同的地方是,这里的第168、178、188行均使用一个自定义结构体类型变量,用于传递用户数据,这里的TaskUserData变量在app.h中定义,有任务名、任务运行次数、任务单次运行时间等三个成员。第191~256行为三个任务的函数体,这里只解释第191~224行的任务1。第200行为任务1命名,OSTaskNameSet函数有三个参数,分别为任务优先级(OS_PRIO_SELF表示任务本身)、任务名字符串和调用成功与否的提示信息。第205行调用OSTaskStkChk函数检查任务堆栈,OSTaskStkChk有两个参数,即任务的优先级和指向记录堆栈信息的结构体变量指针,该结构体类型OS_STK_DATA参见第4.1.2节。第209~215行将堆栈使用情况通过串口0输出。第218行调用OSTimeGet获得系统时钟节拍的总计数值。对比第222、242和254行,可知任务1、2、3的延时分别为10秒、5秒和1秒,因此它们的执行次数的比值为1:2:10,在图4-5中向下拉动窗口滚动条会观察到这种情况。第258~263行定义了一个函数DispTaskStat,输入参数为任务的优先级,该函数输出自定义结构体数组变量TaskUserData的内容。

综上所述,AppTaskStart任务的功能为:当第一次执行时,输出µC/OS-Ⅱ版本号,计算并输出sin(30°)的值;然后,打开定时器4中断,初始化统计任务全局变量,创建三个任务,设置任务名为AppTaskStart;进入任务循环体后,每隔3秒输出任务数、CPU利用率和任务每秒切换次数。AppTask_1任务的功能为:第一次执行时,设置任务名为AppTask_1;进入任务循环体后,每隔10秒,输出三个用户任务使用堆栈的情况以及总的时钟节拍计数。

AppTask_2任务的功能为:第一次执行时,初始化TaskUserData的成员TaskCtr,将任务命名为AppTask_2;进入任务循环体后,每隔5秒调用函数DispTaskStat显示各个任务的用户信息,即任务名和执行次数。AppTask_3任务的功能为:第一次执行时,关闭所有LED灯,将任务命名为AppTask_3;进入任务循环体后,每隔1秒,三个LED灯闪烁一次。

本小节中使用的OSVersion、OSTaskNameSet、OSTimeDly、OSTaskCreateExt、OSTimeDlyHMSM等函数的详细说明可以参考“µC/OS-ⅡReferenceManual”手册(位于第1.1节中的Micrium-uCOS-Ⅱ-V286.ZIP压缩包中)。这个手册中包含了几乎所有µC/OS-Ⅱ的常用函数。4.2.4工程ex4_2

第一章表1-4中函数OSTaskCreate在第三章已用到了,工程ex4_1中没有出现的函数有OSTaskDel、OSTaskDelReq、OSTaskSuspend、OSTaskResume、OSTaskNameGet、OSTaskQuery和OSTaskChangePrio。为了介绍这些函数的使用方法,在工程ex4_1的基础上,新建工程ex4_2,保存在目录D:\ZYUCOSII\ex4_2中(新建的工程与ex4_1完全相同,只是工程文件名改为ex4_2)。然后,仅修改appfun.c文件的AppTask_1和AppTask_3函数即得工程ex4_2。

任务AppTask_1和AppTask_3的内容如下:

1voidAppTask_1(void*pdata)

2{

3OS_STK_DATAstkData;

4INT8Uerr;

5INT8Ui;

6INT8Us[80];

7INT32UstkSize;修改的内容从第36行开始,判断时钟节拍计数值是否大于4000而小于6000,即经过40秒而不超过60秒时,第38~39行判断优先级为AppTask_3_Prio的任务(即任务3)是否存在,如果存在,则第41行调用OSTaskDelReq函数请求任务3删除自己。这里的OSTaskDelReq函数只有一个参数,即任务优先级,该函数向将被删除的任务发出删除请求,它本身不删除任务。在第107行任务3判断有没有任务请求它删除它自身,如果有,则第109行调用OSTaskDel删除任务3。当tickCur>4000时,即经过40秒后,任务3将被删除,这里,三个LED灯不再闪烁。可结合串口调试助手观察时间的变化。

从第45行开始,将判断时钟节拍是否大于6000而小于8000,如果是,则进一步判断任务3是否存在(第47~48行),如果不存在,则调用OSTaskCreateExt重新创建任务3。也就是说,到60秒的时候,三个LED灯间隔了20秒后重新开始闪烁了。

第62行,即时钟节拍到了8000次以上而不超过10000次时,将调用OSTaskSuspend挂起任务3。OSTaskSuspend只有一个参数,即任务的优先级,与OSTaskResume的作用相反,被挂起的任务只能调用OSTaskResume恢复运行。因此,到80秒的时候,三个LED灯又不再闪烁了。间隔20秒后,到了第67~70行,调用OSTaskResume恢复任务3,这时三个LED灯继续开始闪烁了。

然后,到了第72行,即到

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论