单片机学ucosii和我一起看书实践源码001 004第1章_第1页
单片机学ucosii和我一起看书实践源码001 004第1章_第2页
单片机学ucosii和我一起看书实践源码001 004第1章_第3页
单片机学ucosii和我一起看书实践源码001 004第1章_第4页
单片机学ucosii和我一起看书实践源码001 004第1章_第5页
已阅读5页,还剩20页未读 继续免费阅读

下载本文档

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

文档简介

在这一章里将提供三个范例来说明如何使用μC/OS-II。笔者之所以在本书一开始就写这一章是为了让读者尽快开始使用μC/OS-II。在开始讲述这些例子之前,笔者想先说明一些在这本书里的约定。这些例子曾经用BorlandC/C++编译器(V3.1)编译过,用选择项产生In 式下编译)的代码。这些代码实际上是在In PentiumIIPC(300MHz)上运行和测试过,In C/C+产生的 的80x86CPU兼容。安装μC/OS-INSTALLdrive 放在\SOFTWARE\HPLISTC\SOURCE 下。DOS下的可执行文件(HPLIST.EXE)存放在\SOFTWARE\TO\EXE这个中存放的是和范例TO相关的文件(请看附录D,HPLISTCTO)。源文件TO.C\SOFTWARE\TO\SOURCE中,DOS下的可执行文件(TO.EXE)存放在\SOFTWARE\TO\EXE中。注意TO需要一个TO.TBL文件,它必须放在根下。用户可以在\SOFTWARE\TO\EXE下找到TO.TBL文件。如果要运行TO.EXE,必须将TO.TBL到根下。与μC/OS-II相关的文件都放在这 用户将注意到本书中所有的*.C#includeINCLUDE.H可以使用户不必在工程项目中每个*.C文件中都考虑需要什么样的头文件。换句话说,所有的例子使用一个共同的头文件INCLUDES.H,3个副本分别存放在\SOFTWARE\uCOS-II\EX1_x86L,\SOFTWARE\uCOS-II\EX2_x86L,以及\SOFTWARE\uCOS-II\EX3_x86LINCLUDES.H以添不同的微处理器有不同的字长,μC/OS-II的移植文件包括很多类型定义以确保可移植性(\STARE\uO-I\I6L\S_CU.H,它是针对0x6的实模式,在大模式下编译)。OS-II不使用C语言中的hor,nlong等数据类型的定义,因为它们与处理器类型有关,隐含着不可移植性。笔者代之以移植性强的整数数据类型,这样,既直观又可移植,如表.1所示。为了方便起见,还定义了浮点数数据CO-II程序L1.1TypedefunsignedcharBOOLEAN;TypedefunsignedcharINT8U;Typedefsigned charINT8S;Typedefunsignedint Typedefsigned TypedefunsignedlongINT32U;Typedefsigned longINT32S;Typedeffloat Typedefdouble #defineBYTE #define #define #define #define #define 无符号整型数据,而是一个无符号短整型数据。然而将无论μOS-II用到哪里,都会当作I16U表1.1是以Bland /C++编译器为例,为8x86提供的定义语句。为了和OS兼容,还定义了,WORONG以及相应的无符号变量。这使得用户可以不作任何修改就能将COS的代码移植到COS-I有来RD321请以下是如何定义全局变量。众所周知,全局变量应该是得到内存分配且可以被其他模块通过C语言中extenC.H讨论的方法只需用在头文件中定义一次。虽然有点不易懂,但用户一旦掌握,使用起来却很灵活。表2中.程序L1.2 #definexxx_EXT#definexxx_EXTexternH#definexxx_GLOBALS#include"includes.h"当编译器处理.C文件时,它强制xxxET(在相应.H文件中可以找到)为空,(因为xxxGLOBALS已经定)。所以编译器给每个全局变量分配内存空间,而当编译器处理其他.C文件时,xxx_GOBAL没有定义,xxEXT被定义为xtrn,这样用户就可以调用外部全局变量。为了说明这个概念,可以参见uII. #defineOS_EXT#defineOS_EXTextern#defineOS_GLOBALS#include“includes.h” extern extern extern 在这种情况下,不产生内存分配,而任何C文件都可以使用这些变量。这样的就只需在.H文件中定义OS_ENTER_CRITICAL()和_ETRRITIA()关中断;而OS_EXIT_CRITICAL()开中断。关中断和开中断是为了保护临界段代码。程 L1.3进入正确部分的宏#define #ifOS_CRITICAL_METHOD==#defineOS_ENTER_CRITICAL()asmCLI#defineOS_EXIT_CRITICAL()asmSTI#ifOS_CRITICAL_METHOD==#defineOS_ENTER_CRITICAL()asm{PUSHF;#defineOS_EXIT_CRITICAL() asmPOPF基于PC于PCμC/OS-II以前的版本(μC/OS)不同,笔者希望集中这些函数以避免在各个例子为了性能更好,显示函数直接向显示内存区中写数据。在VGA显示器中,显示内存从绝对地址0x000B8000开始(或用段、偏移量表示则为B800:0000)。在单色显示器中,用户可以把#definePC.C中的显示函数用x和y坐标来直接向显示内存中写AII字符。PC的显示可以达到25行80列一共,000个字符。每个字符需要两个字节来显示。第一个字节是用户想要显示的字符,第二个字节用来确定前景色和背景色。前景色用低四位来表示,背景色用第4位到6 Clearthescreen Clearasinglerow(orline) yasingleASCIIcharacteranywhereonthescreen yanASIIstringanywhereonthescreen程序代码是放在函数PC_ElapsedStart()和PC_ElapsedStop()之间来测量的。在用这两个函数之前,应该调用PC_ElapsedInit()来初始化,它主要是计算运行这两个函数本身所附加的的时间。这样, ,不要有多个任务同时调用这两个函数。表1.4说明了如何测量PC_Dis yChar()的执行时间。 L INT16UPC_DispChar(40,24,‘A’,time=程序一样编译和用户程序。所生成的.EXE程序可以在DOS下装载和运行,当然应用程序应该从main()函数开始。因为μC/OS-II是多任务,而且为每个任务开辟一个堆栈,所以单线程的DOS环境应该保存,在退出μC/OS-II程序时返回到DOS。调用PC_DOSSaveReturn()可以保存当前DOS环境,而调用PC_DOSReturn()可以返回到DOS。PC.C中使用ANSIC的setjmp(),longjmp()函数来分别保存和恢复DOS环境。BorlandC/C++编译库提供这些函数,多数其它的编译程序也应有这类函数。YYHH:MM:SS,用户需要19个字符来存放这些数据。该函数使用了BorlandC/C++的gettime()和getdate()PC_GetKey()函数检查是否有按键被按下。如果有按键被按下,函数返回其值。这个函数使用了BorlandC/C++的kbhit()和getch()函数,其它DOS环境下的C函数PC_SetTickRate()允许用户为μC/OS-II定义频率,以改变钟节拍的速率。在DOS下,每秒产生18.20648次时钟节拍,或每隔54.925ms一次。这是因为82C54定时器没有初始化,而使用默认值65,53558,659,20.000Hz设得更快一些,用的是20z(实际是上是199.96H)。注意OS_CPUA.AM中的OSickIS()函数将会每1个时钟节拍调用一次DOS中的时钟节拍处理,这是为了保证在OS下时钟的准确性。如果用户希望将时钟节拍的速度设置为20HZ,就必须这样做。在返回S以前,要调用PC_SetTikRat(),并设置8为目标频PC_tickRa8.2H825。 本章中的例子都用BorlandC/C++编译器编译通过,是在Windows95的DOS窗口下编译的。可执行代码可以在每个范例的OBJ子下找到。实际上这些代码是在BorlandIDE(IntegratedDevelopment表 Code::Treatenumsas:Defaultformemory::DebuginfoinOBJsFastfloatingpointInvariantcodemotionAssumeSSEqualsAdvancedcodeFloatingLoopSuppressredundantCopypropagationJumpoptimizationOptimizeCommonOptimize笔者的BorlandC/C++编译器安装在 下,如果用户的编译器是在不同 下,可以OS_CFG.H中的#definesOS_???_EN为0来实现。用户不需要的服务代码就不生成。本章的范例就用这种功第一个范例可以在\SOFTWAE\uCOS_II\EX1_x86L下找到,它有3个任务(包括C/OS-I的空闲任务)。C/-I增加了两个内部任务:空闲任务和一个计算CPU利用率的任务。例1建立了11个其它任务。Tasktar()任务是在函数ain()中建立的;它的功能是建立其它任务并且在屏幕上显示如下统计信彩色显示。同时笔者使用原来的数据类型(UBYTE,UWORD等)来说明μC/OS-II向下兼容。μC/OS-II要用户在使用任何服务之前先调用OSInit()[L1.5(2)]。它会建立两个任务:空闲任务和程序L voidmain{PC_DispClrScr(DISP_FGND_WHITE+DISP_BGND_BLACK);//Clearthe //InitializeuC/OS-II //SaveenvironmenttoreturntoRandomSem=(void(void*)&TaskStartStk[TASK_STK_SIZE-1],}当前S环境是通过调用PC_DOSveRtur()[L1.]来保存的。这使得用户可以返回到没有运行C/II以前的DOS环境。跟随L1.6中的程序可以看到_DOSSaRetu)做了很多事情。()首先设置PC_itFlg为ASE[L1.(1)],说明用户不是要返回DOS,然后初始化STickDr为1[L16(2)],因为这个变量将在TickIS()中递减,而0将使得这个变量在OSTckIS中减1后变为55。然后,PC_DOSSveRetur()将DOS的时钟节拍处理tickadler)存入一个自由向量表中L1.6(3)(4)],以便为μ-I的时钟节拍处理所调用。接着P_DOSSaRtur()调用mp()L1.6(5)],它将处理器状态(即所有寄存器的值)存入被称为PCumpBuf的结构之中。保存处理器的全部寄存器使得程序返回到PC_DSaveRetu()并且在调用sejmp()之后立即执行。因为P_ExitFlag被初始化为FSE[L1.61)]。PCOSSavetu()跳过if状态语句[L1.6–()]回到an()函数。如果用户想要返回到OS,可以调用DOSRet()(程序L),它设置PC_ExiFlag为E,并且执行longm()语句[L1.)],这时处理器将跳回P_DOSSaveetur之后]L1.6(5)],此时PCxitFlag为RE,故f语句以后的代码将得以执行。PC_DOSSveRetur()将时钟节拍改为18Hz[L1.6()],恢复C时钟节拍中断服务[L6(7)],清屏幕[L1.6(8)],通过xt(0)返回OS[L1程序L voidPC_DOSSaveReturn{PC_ExitFlag=OSTickDOSCtr

=if(PC_ExitFlag=={

}}

PC_VectSet(VECT_TICK,PC_TickISR);PC_DispClrScr(DISP_FGND_WHITE+DISP_BGND_BLACK);程序L voidPC_DOSReturn{PC_ExitFlag=}

现在回到main()这个函数,在程序L1.5中,main()调用PC_VectSet()来设置μCOS-II中的CPU寄存器切换。任务级的CPU寄存器切换由80x86INT指令来分配向量地址。笔者使用向量0x80(即这里用了一个信号量来保护BorlandC/C++库中的产生随机数的函数[L1.5(5)],之所以使用信号量保何其它任务运行。如果用户在启动多任务之前要建立其它任务,必须保证用户的任务代码全局变量OSStatRdy和延时程序[即调用OSTimeDly()]的执行,直到这个变量变成TRUE。这表明μC/OS-II的例1中的主要工作由TaskStart()来完成。TaskStart()函数的示意代码如程序L1.8所示。TaskStart()首先在屏幕顶端显示一个标识,说明这是例1[L1.8(1)]。然后关中断,以改变中断向量,让其指向μC/OS-II的时钟节拍处理,而后,改变时钟节拍率,从DOS的18.2Hz变为200Hz[L1.8(3)]。在序L1.5)在系统初始化的时候并没有将中断向量设置成μC/OS-II的时钟节拍处理程序,做嵌入式应程序L voidTaskStart(void{Preventcompilerwarningbyassigning‘data’toitself; ybanneridentifyingthisasEXAMPLE#1;

InitializethestatistictaskbycallingCreate10identicalfor(;;) ythenumberoftaskscreated; ythe%ofCPUused; ythenumberoftaskswitchesin1second; yuC/OS-II’sversionnumberIf(keywaspressed)if(keypressedwastheESCAPE{}}Delayfor1}}在建立其他任务之前,必须调用OSStatInit()[L1.8(4)]来确定用户的PC有多快,如程序L1.9所示。在一开始,OSStatInit()就将自身延时了两个时钟节拍,这样它就可以与时钟节拍中断同步[L1.9(1)]。因此,OSStatInit()必须在时钟节拍启动之后调用;否则,用户的应用程序就会。当μC/OS-II调用OSStatInit()时,一个32位的计数器OSIdleCtr被清为0[L1.9(2)],并产生另一个延时,这个延时使OSStatInit()挂起。此时,uCOS-II没有别的任务可以执行,它只能执行空闲任务(μC/OS-II的内部任务)。空闲任务是一个无线的循环,它不断的递增OSIdleCtr[L1.9(3)]。1秒以后,uCOS-II多的记数,(如果拥护程序每秒复位一次OSIdleCtr)CPU利用率的计算由μC/OS-II中的OSStatTask()函数来完成,这个任务每秒执行一次。而当OSStatRdy置为TRUE[L1.9(5)],表示μC/OS-II将统计CPU的利用率。程序L voidOSStatInit{ =

OSIdleCtrMax= =}

OSSttIni()将返回到TaskStar()。现在,用户可以建立10个同样的任务(所有任务共享同一段代)。所有任务都由TaStar()中建立,由于TaskStr()的优先级为0(最高),新任务建立后不进行任务调度。当所有任务都建立完成后,askStr()将进入无限循环之中,在屏幕上显示统计信息,并检测是否有ESC键按下,如果没有按键输入,则延时一秒开始下一次循环;如果在这期间用户按下了ESCaskStaPC_DOStuDOS程序L1.10给出了任务的代码。任务一开始,调用OSSemPend()获取信号量RandomSem[程序L1.10(1)](也就是其他任务运行这段代码—译者注),然后调用BorlandC/C++的库函数random()来获得一个随机数[程序L1.10(2)],此处设random()函数是不可重入的,所以10个任务将轮流获得信号量,并调用该函数。当计算出x和y坐标后[程序L1.10(3)],任务释放信号量。随后任务在计算的坐标处显示其任务号(0-9,任务建立时的标识)[程序L1.10(4)]。最后,任务延时一个时钟节拍[程序L1.10(5)],等待进入下一次循环。系统中每个任务每秒执行200次,10个任务每秒钟将切换2000次。voidTask(void{UBYTEUBYTEfor(;;)OSSemPend(RandomSem,0,x=y=random(16);

}}

PC_DispChar(x,y+5,*(char*)data,例2使用了带扩展功能的任务建立函数OSskCreaxt()和uO-II的堆栈检查操作(要使用堆栈检查操作必须用OTaskCrteExt(建立任务—译者注)。当用户不知道应该给任务分配多少堆栈空间务到底需要多少堆栈空间。显然,任务要运行足够长时间,并要考虑各种情况才能得到正确数据。最后决定的堆栈大小还要考虑系统今后的扩展,一般多分配0%5%或者。如果系统对稳定性要求高,则务的操作,应该设置好上述的选项,确保任务建立后堆栈是清空的。同时要OSTaskCreateExt()进行堆栈操作是一项很费时的工作,而且取决于堆栈的大小。执行堆栈检查操作的时候,uCOS-II从栈底向栈顶搜索非0元素(参看图F1.1),同时用一个计数器记录0元素的个数。例2的磁盘文件为\SOFTWAE\O-II\EX2_x8L,它包含9个任务。加上C-I本身的两个任务:空闲任务(dleta)和统计任务。与例1一样Tasktat(由n()函数建立,其功能是建立其他任图F 例2的main()函数和例1的看起来差不多(参看程序L1.11),但是有两处不同。第一,main()函数调用PC_ElapsedInit()[程序L1.11(1)]来初始化定时器记录OSTaskStkChk()的执行时间。第二,所有的任务都使用OSTaskCreateExt()函数来建立任务[程序L1.11(2)](替代老版本的OSTaskCreate voidmain{(void

(voidOS_TASK_OPT_STK_CHK|}除了OTaskCr()函数的四个参数外,OSTaskreateEx()还需要五个参数一共9个):任务的D,一个指向任务堆栈栈底的指针,堆栈的大小(以堆栈单元为单位,8086中为字),一个指向用户定义的B扩展数据结构的指针,和一个用于指定对任务操作的变量。该变量的一个选项就是用来设定2TCB程序L1.12列出了TaskStart()的伪码。前五项操作和例1中相同。TaskStart()建立了两个邮箱,分别提供给任务4和任务5[程序L1.12(1)]。除此之外,还建立了一个专门显示时间和日期的任务。voidTaskStart(void{Preventcompilerwarningbyassigning‘data’toitself; yabannerandnon-changingtext;InstalluC/OS-II’stickhandler;Changethetickrateto200Hz;Initializethestatisticstask;Create2mailboxeswhichareusedbyTask#4andCreateataskthatwill ythedateandtimeontheCreate5applicationtasks;for(;;){ y#tasksrunning; yCPUusagein y#contextswitchesperseconds;Clearthecontextswitchcounter; yuC/OS-II’sIf(Keywaspressed)if(KeypressedwastheESCAPE{Returnto}}Delayfor1}}任务1将检查其他七个任务堆栈的大小,同时记录OSTackStkChk()函数的执行时间[程序L1.13(1)–(2)],并与堆栈大小一起显示出来。注意所有堆栈的大小都是以字节为单位的。任务1每秒执行10次voidTask1(void{ OS_STK_DATAdata; pdata=pdata;for(;;){for(i=0;i<7;i++){err=OSTaskStkChk(TASK_START_PRIO+i,&data)time=PC_ElapsedStop();if(err==OS_NO_ERR)sprintf(s,"%3ld +data.OSUsed,PC_DispStr(19,12+i,s,}}OSTimeDlyHMSM(0,0,0,}}程序L4所示的任务2在屏幕上显示一个顺时针旋转的指针(用横线,斜线等字符表示译者)200msvoidTask2(void{data=data;for(;;){PC_DispChar(70,15,'|',DISP_FGND_WHITE+PC_DispChar(70,15,'/',DISP_FGND_WHITE+PC_DispChar(70,15,'-',DISP_FGND_WHITE+PC_DispChar(70,15,'\\',DISP_FGND_WHITE+}}任务3(程序L1.15)也显示了与任务2相同的一个旋转指针,但是旋转的方向不同。任务3在堆栈voidTask3(void{ INT16Ui;data=for(I=0;i<499;i++)dummy[i]=}for(;;)PC_DispChar(70,16,'|',DISP_FGND_WHITE+PC_DispChar(70,16,'\\',DISP_FGND_WHITE+PC_DispChar(70,16,'-',DISP_FGND_WHITE+PC_DispChar(70,16,'/',DISP_FGND_WHITE+}}任务4(程序L1.16)向任务5发送消息并等待确认[程序L1.16(1)]。发送的消息是一个指向字符的指针。每当任务4从任务5收到确认[程序L1.16(2)],就将传递的ASCII码加1再发送[程序L1.16(3)],结果是不断的传送“ABCDEFG.”。voidTask4(void{ INT8Uerr;data=data;txmsg='A';for(;;){while(txmsg<='Z'){OSMboxPost(TxMbox,(void*)&txmsg);OSMboxPend(AckMbox,0,}txmsg=}}当任务5[程序L1.17]接收消息后[程序L1.17(1)](发送的字符),就将消息显示到屏幕上voidTask5(void{charINT8Udata=data;for(;;){rxmsg=(char*)OSMboxPend(TxMbox,0,&err);PC_DispChar(70,18,*rxmsg,OSTimeDlyHMSM(0,0,1,OSMboxPost(AckMbox,(void*)1);}}voidTaskClk(void{Structtimenow;Structdatetoday; data=data;for(;;){PC_DispStr(0,24,s,DISP_FGND_BLUE+DISP_BGND_CYAN);}}例3中使用了许多uCOSI提供的附加功能。任务3使用了OSTakCreateE()中CB的扩展数据结构,用户定义的任务切换对外接口函数(OSskSwHo()),用户定义的统计任务statisictask)的对外接口函数OSTaskSttHoo()以及消息队列。例3的磁盘文件是\SOFTWAE\uCOSI\E_x86L,它包括9个任务。除了空闲任务idle)和统计任务(staistictask),还有7个任务。与例,例2一,Taskrmai(main()函数[程序L1.19]和例2中的相不多,不同的是在用户定义的TCB扩展数据结构中可以保存每个任务的名称[程序L1.19(1)](扩展结构的声明在INCLUDES.H中定义,也可参看程序L1.20)。笔者定义了30个字节来存放任务名(包括空格)[程序L1.20(1)]。本例中没有用到堆栈检voidmain{PC_VectSet(uCOS,OSCtxSw);(void*)0,}typedefstruct INT16UTaskCtr;INT16UTaskExecTime;INT32U}

voidTaskStart(void{Preventcompilerwarningbyassigning‘data’toitself; yabannerandnon-changingtext;InstalluC/OS-II’stickhandler;Changethetickrateto200Hz;Initializethestatisticstask;Createamessagequeue;Createataskthatwill ythedateandtimeontheCreate5applicationtaskswithanamestoredintheTCBfor(;;) y#tasksrunning; yCPUusagein y#contextswitchesperseconds;Clearthecontextswitchcounter; yuC/OS-II’sIf(Keywaspressed)if(KeypressedwastheESCAPE{Returnto}}Delayfor1}}voidTask1(void{charone='1';chartwo='2';charthree='3';data=data;for(;;){OSQPost(MsgQueue,(void*)&one);OSTimeDlyHMSM(0,0, OSQPost(MsgQueue,(void*)&two);OSTimeDlyHMSM(0,0,0,500);OSQPost(MsgQueue,(void*)&three);OSTimeDlyHMSM(0,0,1, }}voidTask2(void{INT8Udata=data;for(;;){msg=(INT8U*)OSQPend(MsgQueue,0,&err);PC_DispChar(70,14,*msg,OSTimeDlyHMSM(0,0,0,}}任务3同样处于等待消息的挂起状态,但是它设定了等待结束时间250mS[程序L1.24(1)]。如果有消息来到,任务3将显示消息号[程序L1.24(3)],如果超过了等待时间,任务3就显示“T”(意为voidTask3(void{INT8Udata=data;for(;;){msg=(INT8U*)OSQPend(MsgQueue,OS_TICKS_PER_SEC/4,If(err=={}else}}}任务4的操作只是从邮箱发送[程序L1.25(1)]和接收[程序L1.25(2)],这使得用户可以测量voidTask4(void{ data=mbox=OSMboxCreate((void*)0);for(;;){OSMboxPost(mbox,(voidOSMboxPend(mbox,0,OSTimeDlyHMSM(0,0,0,}}任务5除了延时一个时钟节拍以外什么也不做[程序L1.26(1)]。注意所有的任务都应该调用uCOS-II的函数,等待延时结束或者的发生而让出CPU。如果始终占用CPU,这将使低优先级的任务无法得到voidTask5

温馨提示

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

评论

0/150

提交评论