第4章基于ARM9及μCOSII嵌入式系统创新设计_第1页
第4章基于ARM9及μCOSII嵌入式系统创新设计_第2页
第4章基于ARM9及μCOSII嵌入式系统创新设计_第3页
第4章基于ARM9及μCOSII嵌入式系统创新设计_第4页
第4章基于ARM9及μCOSII嵌入式系统创新设计_第5页
已阅读5页,还剩124页未读 继续免费阅读

下载本文档

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

文档简介

第4章基于ARM9及μCOSII嵌入式系统创新设计4.1µC/OS-II的内核多任务系统中,内核负责管理各个任务,或者说为每个任务分配CPU时间,并且负责任务之间的通讯。内核提供的基本服务是任务切换。之所以使用实时内核可以大大简化应用系统的设计,是因为实时内核允许将应用分成若干个任务,由实时内核来管理它们。内核本身也增加了应用程序的额外负荷,代码空间增加ROM的用量,内核本身的数据结构增加了RAM的用量。但更主要的是,每个任务要有自己的栈空间,这一块吃起内存来是相当厉害的。内核本身对CPU的占用时间一般在2到5个百分点之间。UC/OS-II有一个精巧的内核调度算法,实时内核精小,执行效率高,算法巧妙,代码空间很少。4.1.1µC/OS-II内核调度特点µC/OS-II内核调度主要有如下特点:●只支持基于优先级的抢占式调度算法,不支持时间片轮训。●64个优先级,只能创建64个任务,用户只能创建56个任务。●每个任务优先级都不相同。●不支持优先级逆转。●READY队列通过内存映射表实现快速查询。效率非常高。●支持时钟节拍。●支持信号量,消息队列,事件控制块,事件标志组,消息邮箱任务通讯机制。●支持中断嵌套,中断嵌套层数可达255层,中断使用当前任务的堆栈保存上下文。●每个任务有自己的堆栈,堆栈大小用户自己设定。●支持动态修改任务优先级。●任务TCB为静态数组,建立任务只是从中获得一个TCB,不用动态分配,释放内存。●任务堆栈为用户静态或者动态创建,在任务创建外完成,任务创建本身不进行动态内存分配。●任务的总个数(OS_MAX_TASKS)由用户决定。●0优先级最高,63优先级最低;●有一个优先级最低的空闲任务,在没有用户任务运行时运行。4.1.2任务控制块OS_TCB描述UC/OS-II的TCB数据结构简单,内容容易理解,保存最基本的任务信息,同时还支持裁减来减小内存消耗,TCB是事先根据用户配置,静态分配内存的结构数组,通过优先级序号进行添加,查找,删除等功能。减少动态内存分配和释放。因为依靠优先级进行TCB分配,每个任务必须有自己的优先级,不能和其他任务具有相同的优先级。typedefstructos_tcb{OS_STK*OSTCBStkPtr;#ifOS_TASK_CREATE_EXT_EN>0void*OSTCBExtPtr;OS_STK*OSTCBStkBottom;INT32UOSTCBStkSize;INT16UOSTCBOpt;INT16UOSTCBId;#endifstructos_tcb*OSTCBNext;structos_tcb*OSTCBPrev;

#if((OS_Q_EN&&(OS_MAX_QS>=2))||OS_MBOX_EN||OS_SEM_EN)OS_EVENT*OSTCBEventPtr;#endif#if((OS_Q_EN&&(OS_MAX_QS>=2))||OS_MBOX_EN)void*OSTCBMsg;#endifINT16UOSTCBDly;INT8UOSTCBStat;INT8UOSTCBPrio;INT8UOSTCBX;INT8UOSTCBY;INT8UOSTCBBitX;INT8UOSTCBBitY;#ifOS_TASK_DEL_ENBOOLEANOSTCBDelReq;#endif}OS_TCB;其中:OSTCBStkPtr是指向当前任务栈顶的指针。*OSTCBExtPtr是任务扩展模块使用;*OSTCBStkBottom指向任务堆栈栈底的指针;OSTCBStkSize存有栈中可容纳的指针元数目;OSTCBOpt把“选择项”传给函数OSTashCreaktExt()。只有当用户将OS_CFG.H文件中的OS_TASK_CREATE_EXT设为1时,这个变量才有效;OSTCBId用于存储任务的识别码(ID)。这个变量现在没有用,保留给将来扩展用;OSTCBNext和OSTCBPrev用于任务控制块OS_TCBs的双向链表的前后链接,该链表在时钟节拍函数OSTimerTick()中使用;OSTCBEventPtr是指向事件控制块的指针;OSTCBMsg是指向传给任务的消息的指针;OSTCBDly当需要把任务延时若干时钟节拍时要用到这个变量,或者需要把任务挂起一段时间以等待某事件的发生;OSTCBStat是任务的状态字;OSTCBPrio是任务优先级,高优先级任务的OSTCBPrio值小;OSTCBDelReq是一个布尔量,用于表示该任务是否需要删除;OSTCBX,OSTCBY,OSTCBBitX和OSTCBBitY用于加速任务进入就绪态的过程或进入等待事件发生状态的过程。这些值是在任务建立时算好的,或者是在改变任务优先级时算出的。这些值的算法可由下面程序实现。OSTCBY=priority>>3;OSTCBBitY=OSMapTbl[priority>>3];OSTCBX=priority&0x07;OSTCBBitX=OSMapTbl[priority&0x07];4.1.3就绪表(ReadyList)

UC/OS-II采用内存映射的方式来实现READY队列的加入,查找,删除功能,效率非常高。但是也因此只能支持64个任务,每个任务都有自己的优先级,不能和其他任务优先级相同。每个任务的就绪态标志都放入就绪表中的,就绪表中有两个变量OSRdyGrp和OSRdyTbl[]。在OSRdyGrp中,任务按优先级分组,8个任务为一组。OSRdyGrp中的每一位表示8组任务中每一组中是否有进入就绪态的任务。任务进入就绪态时,就绪表OSRdyTbl[]中的相应元素的相应位也置为1。就绪表OSRdyTbl[]数组的大小取决于OS_LOWEST_PRIO(见文件OS_CFG..H)。为确定下次该哪个优先级的任务运行了,UC/OS-II中的内核调度器总是将最低优先级的任务在就绪表中相应字节的相应位置1,即OS_LOWEST_PRIO=1。OSRdyGrp和OSRdyTbl[]的关系是按以下规则给出的:当OSRdyTbl[i]中的任何一位是1时,OSRdyGrp的第i位置1。i从0到7。可用下面句使任务进入就绪态:OSRdyGrp|=OSMapTbl[prio>>3];OSRdyTbl[prio>>3]|=OSMapTbl[prio&0x07];任务优先级的低三位用于确定任务在总就绪表OSRdyTbl[]中的所在位。接下去的三位用于确定是在OSRdyTbl[]数组的第几个元素。OSMapTbl[]是在ROM中的(见文件OS_CORE.C)屏蔽字,用于限制OSRdyTbl[]数组的元素下标在0到7之间。下面程序从就绪表中删除一个任务。if((OSRdyTbl[prio>>3]&=~OSMapTbl[prio&0x07])==0)OSRdyGrp&=~OSMapTbl[prio>>3];以上代码将就绪任务表数组OSRdyTbl[]中相应元素的相应位清0,而对于OSRdyGrp,只有当被删除任务所在任务组中全组任务一个都没有进入就绪态时,才将相应位清零。也就是说OSRdyTbl[prio>>3]所有的位都是0时,OSRdyGrp的相应位才清零。为了找到那个进入就绪态的优先级最高的任务,并不需要从OSRdyTbl[0]开始扫描整个就绪任务表,只需要查另外一张表,即优先级判定表OSUnMapTbl([256])(见文件OS_CORE.C)。OSRdyTbl[]中每个字节的8位代表这一组的8个任务哪些进入就绪态了,低位的优先级高于高位。利用这个字节为下标来查OSUnMapTbl这张表,返回的字节就是该组任务中就绪态任务中优先级最高的那个任务所在的位置。这个返回值在0到7之间。确定进入就绪态的优先级最高的任务是用以下代码完成的。y=OSUnMapTbl[OSRdyGrp];x=OSUnMapTbl[OSRdyTbl[y]];prio=(y<<3)+x;4.1.4任务状态

UC/OS-II主要有五种任务状态,睡眠态就是挂起态,阻塞态和延时态这里统一为等待状态。增加了一个被中断状态。UC/OS-Ⅱ总是建立一个空闲任务,这个任务在没有其它任务进入就绪态时投入运行。这个空闲任务[OSTaskIdle()]永远设为最低优先级空闲任务OSTaskIdle()什么也不做,只是在不停地给一个32位的名叫OSIdleCtr的计数器加1,统计任务使用这个计数器以确定现行应用软件实际消耗的CPU时间。空闲任务不可能被应用软件删除。睡眠态(DORMANT)指任务驻留在程序空间之中,还没有交给μC/OS-Ⅱ管理,把任务交给μC/OS-Ⅱ是通过调用下述两个函数之一:OSTaskCreate()或OSTaskCreateExt()。当任务一旦建立,这个任务就进入就绪态准备运行。任务的建立可以是在多任务运行开始之前,也可以是动态地被一个运行着的任务建立。如果一个任务是被另一个任务建立的,而这个任务的优先级高于建立它的那个任务,则这个刚刚建立的任务将立即得到CPU的控制权。一个任务可以通过调用OSTaskDel()返回到睡眠态,或通过调用该函数让另一个任务进入睡眠态。调用OSStart()可以启动多任务。OSStart()函数运行进入就绪态的优先级最高的任务。就绪的任务只有当所有优先级高于这个任务的任务转为等待状态,或者是被删除了,才能进入运行态。正在运行的任务可以通过调用两个函数之一将自身延迟一段时间,这两个函数是OSTimeDly()或OSTimeDlyHMSM()。这个任务于是进入等待状态,等待这段时间过去,下一个优先级最高的、并进入了就绪态的任务立刻被赋予了CPU的控制权。等待的时间过去以后,系统服务函数OSTimeTick()使延迟了的任务进入就绪态(详见时钟节拍)。正在运行的任务期待某一事件的发生时也要等待,手段是调用以下3个函数之一:OSSemPend(),OSMboxPend(),或OSQPend()。调用后任务进入了等待状态(WAITING)。当任务因等待事件被挂起(Pend),下一个优先级最高的任务立即得到了CPU的控制权。当事件发生了,被挂起的任务进入就绪态。事件发生的报告可能来自另一个任务,也可能来自中断服务子程序。正在运行的任务是可以被中断的,除非该任务将中断关了,或者μC/OS-Ⅱ将中断关了。被中断了的任务就进入了中断服务态(ISR)。响应中断时,正在执行的任务被挂起,中断服务子程序控制了CPU的使用权。中断服务子程序可能会报告一个或多个事件的发生,而使一个或多个任务进入就绪态。在这种情况下,从中断服务子程序返回之前,μC/OS-Ⅱ要判定,被中断的任务是否还是就绪态任务中优先级最高的。如果中断服务子程序使一个优先级更高的任务进入了就绪态,则新进入就绪态的这个优先级更高的任务将得以运行,否则原来被中断了的任务才能继续运行。当所有的任务都在等待事件发生或等待延迟时间结束,μC/OS-Ⅱ执行空闲任务(idletask),执行OSTaskIdle()函数。4.1.5任务切换

任务切换(ontextSwitch)又称上下文切换,或CPU寄存器内容切换。当多任务内核决定运行另外的任务时,它保存正在运行任务的当前状态(Context),即CPU寄存器中的全部内容。这些内容保存在任务的当前状况保存区(Task’sContextStoragearea),即任务自己的栈区之中。把将要运行的任务的当前状况从该任务的栈中重新装入CPU的寄存器,并开始下一个任务的运行。这个过程叫做任务切换。4.1.6任务调度分析

μC/OS-Ⅱ只支持优先级抢占任务调度,不支持时间片轮训调度算法,也不支持优先级逆转。μC/OS-Ⅱ总是运行进入就绪态任务中优先级最高的那一个。确定哪个任务优先级最高,下面该哪个任务运行的工作是由调度器(Scheduler)完成的。任务级的调度函数:OSSched()中断级的调度函数:OSIntExt()OSTCBHighRdy指向优先级最高的那个任务控制块OS_TCB,是将以OSPrioHighRdy为下标的OSTCBPrioTbl[]数组中的那个元素赋给OSTCBHighRdy来实现的。调用OS_TASK_SW()来完成任务切换。任务切换由以下两步完成:将被挂起任务的微处理器寄存器推入堆栈,然后将较高优先级的任务的寄存器值从栈中恢复到寄存器中。在μC/OS-Ⅱ中,就绪任务的栈结构总是看起来跟刚刚发生过中断一样,所有微处理器的寄存器都保存在栈中。换句话说,μC/OS-Ⅱ运行就绪态的任务所要做的一切,只是恢复所有的CPU寄存器并运行中断返回指令。为了做到任务切换,运行OS_TASK_SW(),人为模仿了一次中断。多数微处理器有软中断指令或者陷阱指令TRAP来实现上述操作。中断服务子程序或陷阱处理(Traphardler),也称作事故处理(exceptionhandler),必须提供中断向量给汇编语言函数OSCtxSw()。OSCtxSw()除了需要OS_TCBHighRdy指向即将被挂起的任务,还需要让当前任务控制块OSTCBCur指向即将被挂起的任务。OSSched()的所有代码都属临界段代码。在寻找进入就绪态的优先级最高的任务过程中,为防止中断服务子程序把一个或几个任务的就绪位置位,中断是被关掉的。为缩短切换时间,OSSched()的代码可用汇编语言写。为增加可读性,可移植性和将汇编语言代码最少化,OSSched()是用C写的。任务切换的相关函数是与CPU体系相关,用汇编语言编写完成。任务切换的相关函数如下:

●OSStartHighRdy()执行优先级最高的任务●OSCtxSw()完成任务的上下文切换●OSIntCtxSw()中断后的上下文切换●OSTickISR()中断服务程序启动4.1.7UC/OS-II的初始化

OSInit()建立空闲任务idletask,这个任务总是处于就绪态的。空闲任务OSTaskIdle()的优先级总是设成最低。这两个任务的任务控制块(OS_TCBs)是用双向链表链接在一起的。OSTCBList指向这个链表的起始处。当建立一个任务时,这个任务总是被放在这个链表的起始处。换句话说,OSTCBList总是指向最后建立的那个任务。链的终点指向空字符NULL(也就是零)。因为这两个任务都处在就绪态,在就绪任务表OSRdyTbl[]中的相应位是设为1的。还有,因为这两个任务的相应位是在OSRdyTbl[]的同一行上,即属同一组,故OSRdyGrp中只有1位是设为1的。μC/OS-Ⅱ还初始化了4个空数据结构缓冲区,每个缓冲区都是单向链表,允许μC/OS-Ⅱ从缓冲区中迅速得到或释放一个缓冲区中的元素。控制块OS_TCB的数目也就自动确定了。当然,包括足够的任务控制块分配给统计任务和空闲任务。

4.2µC/OS-II的API函数

任何一个操作系统都会提供大量的API供程序员使用,uC/OS-II也不例外。由于uC/OS-II面向的是嵌入式开发,并不要求大而全,所以内核提供的API也就大多和多任务息息相关。主要的有以下几类:1)任务类2)消息类3)同步类4)时间类5)临界区与内存类4.2.1任务类1)OSTaskCreate()在OSInit函数调用之后调用。作用是创建一个任务。有四个参数:任务的入口地址,任务的参数,任务堆栈的首地址、任务的优先级。调用本函数后,系统会首先从TCB空闲列表内申请一个空的TCB指针,然后将会根据用户给出参数初始化任务堆栈,并在内部的任务就绪表内标记该任务为就绪状态。最后返回,即可成功创建一个任务。2)OSTaskSuspend()

这个函数可以将指定的任务挂起。如果挂起的是当前任务将会引发系统执行任务切换先导函数OSShed来进行一次任务切换。这个函数只有一个参数,是指定任务的优先级。事实上在µC/OS-II内部,优先级除了表示一个任务执行的先后次序外,还起着分辨每一个任务的作用,换句话说,优先级也就是任务的ID。所以uC/OS-II不允许出现相同优先级的任务。3)OSTaskResume()

这个函数和上面的函数作用相反,它用于将指定的已经挂起的函数恢复成就绪状态。如果恢复任务的优先级高于当前任务将会引发一次任务切换。其参数类似OSTaskSuspend函数,为指定任务的优先级。需要特别说明是,本函数并不要求和OSTaskSuspend函数成对使用。4.2.2消息类4)OSMboxCreate()这个函数用于创建消息邮箱,消息邮箱是操作系统的任务间通信的一种方式,一个任务可以阻塞或者不阻塞地等待另外一个任务发送到它的邮箱的邮箱消息,而根据消息的内容进行动作。5)OSMboxPost()这个函数用于一个任务向另一个任务的邮箱发邮箱消息,消息的内容在参数中指定。6)OSMboxPend()这个函数用于一个任务获取本任务邮箱中的消息,如果邮箱中没有消息,则等待,任务处于阻塞状态。4.2.3同步类7)OSSemCreate()这个函数用于创建信号量,信号量是操作系统的任务间同步的一种方式,两个或者多个任务可以获知信号量的状态并根据之进行动作从而实现同步。8)OSSemPost()这个函数用于一个任务对一个信号量进行设置,设置的内容在参数中指定。9)OSSemPend()这个函数用于一个任务获取本信号量的状态,如果信号量不为零则成功获取信号量并将信号量减去1,如果信号量为零,则等待,任务处于阻塞状态。4.2.4时间类10)OSTimeDly()

这应该是程序员们调用最多的一个函数了,这个函数完成功能很简单,就是先挂起当起当前任务,然后进行任务切换,在指定的时间到来之后,将当前任务恢复为就绪状态,但是并不一定运行,如果恢复后是优先级最高就绪任务的话,那么运行之。简单点说,就是可以任务延时一定时间后再次执行它,或者说,暂时放弃CPU的使用权。一个任务可以不显式的调用这些可以导致放弃CPU使用权的API,但那样多任务性能会大大降低,因为此时仅仅依靠时钟机制在进行任务切换。4.2.5内存操作类11)OSMemCreate()这个函数用于创建一个内存分区,µC/OS-II对内存进行统一管理,这样利于消除简单采用malloc函数和free函数对内存操作产生的内存碎片。12)OSMemGet()从一个指定的的内存区中分配一个内存块。13)OSMemPut()释放一个内存块。µC/OS-II具有约的50个API函数,除了上述列出的外,其他的在本章后面以附录的方式给出。4.3µC/OS-II的应用程序开发应用uC/OS-II,自然要为它开发应用程序,下面论述基于uC/OS-II的应用程序的基本结构以及注意事项。每一个uC/OS-II应用至少要有一个任务。而每一个任务必须被写成无限循环的形式。以下是推荐的结构:voidtask(void*pdata){INT8Uerr;InitTimer();//可选For(;;){//你的应用程序代码…………OSTimeDly(1);//可选}}以上就是基本结构,至于为什么要写成无限循环的形式呢?那是因为系统会为每一个任务保留一个堆栈空间,由系统在任务切换的时候恢复上下文,并执行一条reti指令返回。如果允许任务执行到最后一个花括号(那一般都意味着一条ret指令)的话,很可能会破坏系统堆栈空间从而使应用程序的执行不确定。所以,每一个任务必须被写成无限循环的形式。程序中的InitTimer()函数由系统提供,需要在优先级最高的任务内调用它,不能在for循环内调用。注意:这个函数是和所使用的CPU相关的。在uC/OS–II中,不能在OSInit()或OSStart()内调用Timer初始化程序,那样会破坏系统的可移植性同时带来性能上的损失。4.4µC/OS-II在S3C2410X上的移植4.4.1移植原理所谓移植,指的是一个操作系统可以在某个微处理器或者微控制器上运行。uCOS-II的大部分源代码是用C语言写成的,仍需要用C语言和汇编语言完成一些与处理器相关的代码。比如:uCOS-II在读写处理器、寄存器时只能通过汇编语言来实现。因为uCOS-II在设计的时候就已经充分考虑了可移植性,所以,uCOS-II的移植还是比较容易的。

要使uCOS-II可以正常工作,处理器必须满足以下要求:1.处理器的C编译器能产生可重入代码可重入的代码指的是一段代码(如一个函数)可以被多个任务同时调用,而不必担心会破坏数据。例:可以比较可重入型函数和非可重入型函数:程序1:可重入型函数voidswap(int*x,int*y){inttemp;temp=*x;*x=*y;*y=temp;}程序2:非可重入型函数inttemp;voidswap(int*x,int*y){temp=*x;*x=*y;*y=temp;}程序1中使用的是局部变量temp作为变量。通常的C编译器,把局部变量分配在栈中。所以,多次调用同一个函数,可以保证每次的temp互不受影响。而程序2中temp定义的是全局变量,多次调用函数的时候,必然受到影响。代码的可重入性是保证完成多任务的基础,除了在C程序中使用局部变量以外,还需要C编译器的支持。笔者使用的是ARMADS的集成开发环境,均可以生成可重入的代码。2.在程序中可以打开或者关闭中断OS_ENTER_CRITICAL()关闭中断OS_EXIT_CRITICAL()打开中断3.处理器支持中断,并且能产生定时中断(通常在10Hz~1000Hz之间)。uCOS-II是通过处理器产生的定时器的中断来实现多任务之间的调度的。在ARM920T的处理器上可以产生定时器中断。4.处理器支持能够容纳一定量数据的硬件堆栈。5.处理器有将堆栈指针和其它CPU寄存器存储和读出到堆栈(或者内存)的指令。

uCOS-II进行任务调度的时候,会把当前任务的CPU寄存器存放到此任务的堆栈中,然后,再从另一个任务的堆栈中恢复原来的工作寄存器,继续运行另一个任务。所以,寄存器的入栈和出栈是uCOS-II多任务调度的基础。图4-1说明了uC/OS的结构以及它与硬件的关系。图4.1Ucos-II硬件和软件体系结构

4.4.2移植实现

本书以uCOS-II在博创实验系统上的例程为中心进行移植的说明。移植相关的文件分为两类,其一是STARTUP目录下的系统初始化、配置等文件,其二是uCOS-II的全部源码,arch目录下的3个文件是和处理器架构相关的。下面对移植步骤进行描述:1.设置os_cpu.h中与处理器和编译器相关的代码typedefunsignedcharBOOLEAN;typedefunsignedcharINT8U;typedefsignedcharINT8S;typedefunsignedintINT16U;typedefsignedintINT16S;typedefunsignedlongINT32U;typedefsignedlongINT32S;typedeffloatFP32;typedefdoubleFP64;typedefunsignedintOS_STK;typedefunsignedintOS_CPU_SR;externintINTS_OFF(void);externvoidINTS_ON(void);#defineOS_ENTER_CRITICAL(){cpu_sr=INTS_OFF();}#defineOS_EXIT_CRITICAL(){if(cpu_sr==0)INTS_ON();}#defineOS_STK_GROWTH11)与编译器相关的数据类型

因为不同的微处理器有不同的字长,所以uCOS-II的移植包括了一系列的类型定义以确保其可移植性。尤其是uCOS-II代码从不使用C的short,int和long等数据类型,因为它们是与编译器相关的,不可移植。例如,INT16U数据类型总是代表16位的无符号整数。现在,uCOS-II和用户的应用程序就可以估计出声明为该数据类型的变量的取值范围是0~65535。将uCOS-II移植到32位的处理器上也就意味着INT16U实际被声明为无符号短整型数据结构而不是无符号整数数据结构。但是,uCOS-II所处理的仍然是INT16U。2)OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()

与所有的实时内核一样,uCOS-II需要先禁止中断再访问代码的临界区,并且在访问完毕后重新允许中断。这就使得uCOS-II能够保护临界区代码免受多任务或中断服务例程(ISR)的破坏。在S3C2410X上是通过两个函数(OS_CPU_A.S)实现开关中断的。INTS_OFFmrsr0,cpsr;当前CSRmovr1,r0;复制屏蔽orrr1,r1,#0xC0;屏蔽中断位msrCPSR,r1;关中断(IRQandFIQ)andr0,r0,#0x80;从初始CSR返回FIQ位movpc,lr;返回INTS_ONmrsr0,cpsr;当前CSRbicr0,r0,#0xC0;屏蔽中断msrCPSR,r0;开中断(IRQandFIQ)movpc,lr;返回3)OS_STK_GROWTH

绝大多数的微处理器和微控制器的堆栈是从上往下长的。但是某些处理器是用另外一种方式工作的。uCOS-II被设计成两种情况都可以处理,只要在结构常量OS_STK_GROWTH中指定堆栈的生长方式就可以了。置OS_STK_GROWTH为0表示堆栈从下往上长。置OS_STK_GROWTH为1表示堆栈从上往下长。2.用C语言编写6个操作系统相关的函数(OS_CPU_C.C)1)OSTaskStkInit

OSTaskCreate()和OSTaskCreateExt()通过调用OSTaskStkInit()来初始化任务的堆栈结构。因此,堆栈看起来就像刚发生过中断并将所有的寄存器保存到堆栈中的情形一样。图4.2显示了OSTaskStkInt()放到正被建立的任务堆栈中的东西。这里我们定义了堆栈是从上往下长的。在用户建立任务的时候,用户传递任务的地址,pdata指针,任务的堆栈栈顶和任务的优先级给OSTaskCreate()和OSTaskCreateExt()。一旦用户初始化了堆栈,OSTaskStkInit()就需要返回堆栈指针所指的地址。OSTaskCreate()和OSTaskCreateExt()会获得该地址并将它保存到任务控制块(OS_TCB)中。图4.2堆栈的初始化OS_STK*OSTaskStkInit(void(*task)(void*pd),void*pdata,OS_STK*ptos,INT16Uopt){unsignedint*stk;stk=(unsignedint*)ptos;/*装载堆栈指针*/opt++;/*为新任务建立堆栈*/*--stk=(unsignedint)task;/*pc*/*--stk=(unsignedint)task;/*lr*/*--stk=12;/*r12*/*--stk=11;/*r11*/*--stk=10;/*r10*/*--stk=9;/*r9*/*--stk=8;/*r8*/*--stk=7;/*r7*/*--stk=6;/*r6*/*--stk=5;/*r5*/*--stk=4;/*r4*/*--stk=3;/*r3*/*--stk=2;/*r2*/*--stk=1;/*r1*/*--stk=(unsignedint)pdata;/*r0*/*--stk=(SUPMODE);/*cpsr*/*--stk=(SUPMODE);/*spsr*/return((OS_STK*)stk);}2)OSTaskCreateHook

当用OSTaskCreate()和OSTaskCreateExt()建立任务的时候就会调用OSTaskCreateHook()。该函数允许用户或使用移植实例的用户扩展uCOS-II功能。当uCOS-II设置完了自己的内部结构后,会在调用任务调度程序之前调用OSTaskCreateHook()。该函数被调用的时候中断是禁止的。因此用户应尽量减少该函数中的代码以缩短中断的响应时间。当OSTaskCreateHook()被调用的时候,它会收到指向已建立任务的OS_TCB的指针,这样它就可以访问所有的结构成员了。函数原型:voidOSTaskCreateHook(OS_TCB*ptcb)3)OSTaskDelHook

当任务被删除的时候就会调用OSTaskDelHook()。该函数在把任务从uCOS-II的内部任务链表中解开之前被调用。当OSTaskDelHook()被调用的时候,它会收到指向正被删除任务的OS_TCB的指针,这样它就可以访问所有的结构成员了。OSTaskDelHook()可以来检验TCB扩展是否被建立(一个非空指针)并进行一些清除操作。函数原型:voidOSTaskDelHook(OS_TCB*ptcb)4)OSTaskSwHook

当发生任务切换的时候就会调用OSTaskSwHook()。OSTaskSwHook()可以直接访问OSTCBCur和OSTCBHighRdy,因为它们是全局变量。OSTCBCur指向被切换出去的任务OS_TCB,而OSTCBHighRdy指向新任务OS_TCB。注意在调用OSTaskSwHook()期间中断一直是被禁止的。因此用户应尽量减少该函数中的代码以缩短中断的响应时间。函数原型:voidOSTaskSwHook(void)5)OSTaskStatHook

OSTaskStatHook()每秒钟都会被OSTaskStat()调用一次。用户可以用OSTaskStatHook()来扩展统计功能。例如,用户可以保持并显示每个任务的执行时间,每个任务所用的CPU份额,以及每个任务执行的频率等。函数原型:voidOSTaskStatHook(void)6)OSTimeTickHook

OSTimeTickHook()在每个时钟节拍都会被OSTaskTick()调用。实际上,OSTimeTickHook()是在节拍被uCOS-II真正处理,并通知用户的移植实例或应用程序之前被调用的。

函数原型:voidOSTimeTickHook(void)

后5个函数为钩子函数,可以不加代码。只有当OS_CFG.H中的OS_CPU_HOOKS_EN被置为1时才会产生这些函数的代码。3.用汇编语言编写4个与处理器相关的函数(OS_CPU.ASM)1)OSStartHighRdy();运行优先级最高的就绪任务OSStartHighRdyLDRr4,addr_OSTCBCur;得到当前任务TCB地址LDRr5,addr_OSTCBHighRdy;得到最高优先级任务TCB地址LDRr5,[r5];获得堆栈指针LDRsp,[r5];转移到新的堆栈中STRr5,[r4];设置新的当前任务TCB地址LDMFDsp!,{r4};MSRSPSR,r4LDMFDsp!,{r4};从栈顶获得新的状态MSRCPSR,r4;CPSR处于SVC32Mode摸式LDMFDsp!,{r0-r12,lr,pc};运行新的任务2)OS_TASK_SW();任务级的任务切换函数

STMFDsp!,{lr};保存pcSTMFDsp!,{lr};保存lrSTMFDsp!,{r0-r12};保存寄存器和返回地址MRSr4,CPSRSTMFDsp!,{r4};保存当前的PSRMRSr4,SPSRSTMFDsp!,{r4};保存SPSR;OSPrioCur=OSPrioHighRdyLDRr4,addr_OSPrioCurLDRr5,addr_OSPrioHighRdyLDRBr6,[r5]STRBr6,[r4];得到当前任务TCB地址LDRr4,addr_OSTCBCurLDRr5,[r4]STRsp,[r5];保存sp在被占先的任务的TCB;得到最高优先级任务TCB地址LDRr6,addr_OSTCBHighRdyLDRr6,[r6]LDRsp,[r6];得到新任务堆栈指针;OSTCBCur=OSTCBHighRdySTRr6,[r4];设置新的当前任务的TCB地址;保存任务方式寄存器LDMFDsp!,{r4}MSRSPSR,r4LDMFDsp!,{r4}MSRCPSR,r4;返回到新任务的上下文LDMFDsp!,{r0-r12,lr,pc}3)OSIntCtxSw();中断级的任务切换函数OSIntCtxSwAddr7,sp,#16;保存寄存器指针LDRsp,=IRQStack;FIQ_STACKmrsr1,SPSR;得到暂停的PSRorrr1,r1,#0xC0;关闭IRQ,FIQ。MsrCPSR_cxsf,r1;转换模式(应该是SVC_MODE)ldrr0,[r7,#52];从IRQ堆栈中得到IRQ'sLR(任务PC)subr0,r0,#4;当前PC地址是(saved_LR-4)STMFDsp!,{r0};保存任务PCSTMFDsp!,{lr};保存LRmovlr,r7;保存FIQ堆栈ptrinLR(转到nuker7)ldmfdlr!,{r0-r12};从FIQ堆栈中得到保存的寄存器STMFDsp!,{r0-r12};在任务堆栈中保存寄存器;在任务堆栈上保存PSR和任务PSRMRSr4,CPSRbicr4,r4,#0xC0;使中断位处于使能态STMFDsp!,{r4};保存任务当前PSRMRSr4,SPSRSTMFDsp!,{r4};SPSR;OSPrioCur=OSPrioHighRdy//改变当前程序LDRr4,addr_OSPrioCurLDRr5,addr_OSPrioHighRdyLDRBr6,[r5]STRBr6,[r4];得到被占先的任务TCBLDRr4,addr_OSTCBCurLDRr5,[r4]STRsp,[r5];保存sp在被占先的任务的TCB;得到新任务TCB地址LDRr6,addr_OSTCBHighRdyLDRr6,[r6]LDRsp,[r6];得到新任务堆栈指针;OSTCBCur=OSTCBHighRdySTRr6,[r4];设置新的当前任务的TCB地址LDMFDsp!,{r4}MSRSPSR,r4LDMFDsp!,{r4}BICr4,r4,#0xC0;必须退出新任务通过允许中断MSRCPSR,r4LDMFDsp!,{r0-r12,lr,pc}4)OSTickISR();时钟节拍中断

多任务操作系统的任务调度是基于时钟节拍中断的,uCOS-II也需要处理器提供一个定时器中断来产生节拍,借以实现时间的延时和期满功能。但在博创实验系统移植uCOS-II时,时钟节拍中断的服务函数并非uCOS-II文献中提到的OSTickISR(),而直接是C语言编写的OSTimeTick()。博创实验系统uCOS-II移植时占用的时钟资源是TIMER1。在平台初始化函数ARMTargetInit()中,调用uHALr_InitTimers()函数初始化TIMER4相关寄存器;调用uHALr_InstallSystemTimer(void)开始系统时钟,其中通过语句SetISR_Interrupt(IRQ_TIMER4,TimerTickHandle,NULL)将TimerTickHandle函数设置为TIMER4的中断服务函数。这些函数在文件UHAL.C以及ISR.C中。

程序中必须在开始多任务调度之后再允许时钟节拍中断,即在OSStart()调用过后,uCOS-II运行的第一个任务中启动节拍中断。如果在调用OSStart()启动多任务调度之前就启动时钟节拍中断,uCOS-II运行状态可能不确定而导致崩溃。博创实验系统是在系统任务SYS_Task中调用uHALr_InstallSystemTimer()函数设置TIMER4的IRQ中断的,从而启动时钟节拍。SYS_Task()在文件OSAddTask.C中定义,用户不必创建。4.编写一个简单的多任务程序来测试一下移植是否成功。

为了使uCOS-II可以正常运行,除了上述必须的移植工作外,硬件初始化和配置文件也是必须的。STARTUP目录下的文件还包括中断处理,时钟,串口通信等基本功能函数。在文件main.c中给出了应用程序的基本框架,包括初始化和多任务的创建,启动等。任务创建方法如下:1)在程序开头定义任务堆栈,任务函数声明和任务优先级:OS_STKTaskName_Stack[STACKSIZE]={0,};//任务堆栈voidTaskName(void*Id);//任务函数#defineTaskName_PrioN//任务优先级2)在main()函数中调用OSStart()函数之前用下列语句创建任务:OSTaskCreate(TaskName,(void*)0,(OS_STK*)&TaskName_Stack[STACKSIZE-1],TaskName_Prio);OSTaskCreate()函数的原型是:INT8UOSTaskCreate(void(*task)(void*pd),void*p_arg,OS_STK*ptos,INT8Uprio);需要将任务函数TaskName,任务堆栈TaskName_Stack,任务优先级TaskName_Prio三个参数传给OSTaskCreate()函数。根据任务函数的内容决定堆栈大小,宏STACKSIZE定义为4KB,可以在此基数上乘倍。任务优先级越高,TaskName_Prio值越小;uCOS-II可以管理64个任务,由OSInit()创建的空闲任务的优先级最低为63;uCOS-II保留4个最高和4个最低优先级,用户任务可以使用其余56个优先级值。3)编写任务函数内容:voidTaskName(void*Id){//添入任务初始化语句for(;;){//添入任务循环内容OSTimeDly(SusPendTime);//挂起一定时间,以使其他任务可以占用CPU}}uCOS-II至少要有一个任务,这里已经创建一个系统任务SYS_Task,启动系统时钟和多任务切换。为了验证uCOS-II多任务切换的进行,再编写两个简单的任务,分别在超级终端上输出runtask1和runtask2。可以参考main.c的结构创建多个不同功能的任务,观察个任务的切换。5.编译并下载移植后的uCOS-II

所有的源代码都准备好后就可以进行编译了。在ADS环境下需要设置工程的访问路径。从菜单Edit|DebugSettings进入设置对话框,在Target|AccessPaths中选择UserPaths并选上Alwayssearchuserpaths。然后点Add按钮添加路径ucos-ii和arch。这主要是设置编译器处理文件包含时的搜索范围。按照映象文件下载方法实验中的操作方法将编译后的代码下载到平台的flash中。这个实验从结构上看和其他的实验没有多大区别,同样生成可执行文件system.bin。将system.bin装载到Flash中,重启平台,然后在超级终端上观察结果。4.5µC/OS-II的API应用本节将以一些简单的例子针对µC/OS-II的API应用进行描述。4.5.1任务相关函数的使用任务相关函数主要包括:任务创建,任务的挂起和恢复,任务的优先级改变,任务删除和任务查询等。在这里我们以一个最简单的“helloworld”移动的程序为例,对任务的概念和相关的API的应用进行描述。例4-1#include"includes.h"#include"debug.h"#defineTASK_STK_SIZE512OS_STKTask_1_Stk[TASK_STK_SIZE];voidTask(void*data);voidmain(void){PC_DispClrScr(DISP_FGND_WHITE+DISP_BGND_BLACK);/*清屏幕*/OSInit();/*初始化uC/OS-II*/PC_DOSSaveReturn();PC_VectSet(uCOS,OSCtxSw);OSTaskCreate(Task,(void*)0,&Task_1_Stk[TASK_STK_SIZE-1],0);/*创建任务*/OSStart();/*多任务启动*/}voidTask(void*pdata){INT8Ux=1;INT8Uy=1;INT8Ujudge;INT8Uerr;INT16Skey;OS_ENTER_CRITICAL();PC_VectSet(0x08,OSTickISR);PC_SetTickRate(OS_TICKS_PER_SEC);OS_EXIT_CRITICAL();for(;;){ PC_DispClrScr(DISP_FGND_WHITE+DISP_BGND_BLACK);/*清屏幕*/PC_DispStr(x,y,"helloworld!!",DISP_FGND_WHITE+DISP_BGND_RED+DISP_BLINK); x=(x+1)%(80-12);y=(y+1)%(25-1);

if(PC_GetKey(&key)==TRUE){if(key==0x1B){PC_DOSReturn();}}OSCtxSwCtr=0;OSTimeDlyHMSM(0,0,0,350);}}OSTaskCreate(Task,(void*)0,&Task_1_Stk[TASK_STK_SIZE-1],0);是创建任务语句,其中Task是任务的实现函数,(void*)0是传递给任务的参数,Task_1_Stk是任务的堆栈。该程序实现的功能是将“helloworld”沿着屏幕的对角线移动,并且在右上方打印出其所处在的位置。图4.3例4-1应用程序的运行结果

例4-2任务挂起及恢复例子#include"includes.h"#defineTASK_STK_SIZE512OS_STKTaskStartStk[TASK_STK_SIZE];OS_STKTask_1_Stk[TASK_STK_SIZE];OS_STKTask_2_Stk[TASK_STK_SIZE];OS_EVENT*RandomSem;charTaskData[2][2]={'a','b','c','d'};INT16Uschetime1,schetime2;INT8Ucount=0;voidTaskStart(void*data);voidTask1(void*data);voidTask2(void*data);staticvoidTaskStartCreateTasks(void);voidmain(void){PC_DispClrScr(DISP_FGND_WHITE+DISP_BGND_BLACK);OSInit();PC_DOSSaveReturn();PC_VectSet(uCOS,OSCtxSw);RandomSem=OSSemCreate(1);schetime1=0;schetime2=0;OSTaskCreate(TaskStart,(void*)0,&TaskStartStk[TASK_STK_SIZE-1],0);OSStart();}voidTaskStart(void*pdata){#ifOS_CRITICAL_METHOD==3OS_CPU_SRcpu_sr;#endifchars[100];INT16Skey;

pdata=pdata;OS_ENTER_CRITICAL();PC_VectSet(0x08,OSTickISR);PC_SetTickRate(OS_TICKS_PER_SEC);OS_EXIT_CRITICAL();

TaskStartCreateTasks();

for(;;){

if(PC_GetKey(&key)==TRUE){if(key==0x1B){PC_DOSReturn();}}OSCtxSwCtr=0;OSTimeDlyHMSM(0,0,1,0);}}staticvoidTaskStartCreateTasks(void){OSTaskCreate(Task1,(void*)&TaskData[0],&Task_1_Stk[TASK_STK_SIZE-1],5);OSTaskCreate(Task2,(void*)&TaskData[1],&Task_2_Stk[TASK_STK_SIZE-1],8);}voidTask1(void*pdata){INT8Uerr; INT16Skey;charDispData[2];DispData[0]=*(char*)pdata; DispData[1]=*((char*)pdata+1);

for(;;){ count++; OSSemPend(RandomSem,0,&err); PC_DispClrScr(DISP_FGND_WHITE+DISP_BGND_BLACK);if(count==2) { err=OSTaskSuspend(8); if(err==OS_NO_ERR)PC_DispStr(2,2,"Task2wassuspended!!",DISP_FGND_WHITE+DISP_BGND_RED+DISP_BLINK);OSTimeDlyHMSM(0,0,0,800); } if(count==5) { err=OSTaskResume(8); if(err==OS_NO_ERR)PC_DispStr(2,2,"Task2wasresumed!!",DISP_FGND_WHITE+DISP_BGND_RED+DISP_BLINK); OSTimeDlyHMSM(0,0,0,800); }schetime1++; if((schetime1%2)==1)PC_DispChar(20,10,DispData[0],DISP_FGND_WHITE+DISP_BGND_RED+DISP_BLINK); elsePC_DispChar(20,10,DispData[1],DISP_FGND_WHITE+DISP_BGND_RED+DISP_BLINK);

OSTimeDlyHMSM(0,0,0,800); OSSemPost(RandomSem);

if(PC_GetKey(&key)==TRUE){ if(key==0x1B){ PC_DOSReturn(); }}OSTimeDlyHMSM(0,0,0,850);}}voidTask2(void*pdata){INT8Uerr;INT16Skey;charDispData[2];DispData[0]=*(char*)pdata; DispData[1]=*((char*)pdata+1);

for(;;){ OSSemPend(RandomSem,0,&err);PC_DispClrScr(DISP_FGND_WHITE+DISP_BGND_BLACK);schetime2++;if((schetime2%2)==1)PC_DispChar(60,10,DispData[0],DISP_FGND_WHITE+DISP_BGND_RED+DISP_BLINK);elsePC_DispChar(60,10,DispData[1],DISP_FGND_WHITE+DISP_BGND_RED+DISP_BLINK);OSTimeDlyHMSM(0,0,0,800);OSSemPost(RandomSem);

if(PC_GetKey(&key)==TRUE){if(key==0x1B){PC_DOSReturn();}}OSTimeDlyHMSM(0,0,0,850);}}

该程序实现了对任务挂起和恢复的操作。Task1优先级为5,对优先级为8的Task2进行控制,err=OSTaskSuspend(8);和err=OSTaskResume(8);分别是挂起和恢复的语句。4.5.2任务间同步和通信相关函数的使用本小节内容将对任务间同步和通信的API的应用进行描述,µC/OS-II的任务间同步和通信主要有五种机制,包括信号量,互斥型信号量,消息邮箱,消息队列,信号量集等,下面通过例子说明这些机制及相应API函数的使用。例4-3信号量的应用#include"includes.h"#defineTASK_STK_SIZE512OS_STKTaskStartStk[TASK_STK_SIZE];OS_STKTask_1_Stk[TASK_STK_SIZE];OS_STKTask_2_Stk[TASK_STK_SIZE];OS_EVENT*RandomSem;charTaskData[2][2]={'a','b','c','d'};INT16Uschetime1,schetime2;voidTaskStart(void*data);voidTask1(void*data);voidTask2(void*data);staticvoidTaskStartCreateTasks(void);voidmain(void){PC_DispClrScr(DISP_FGND_WHITE+DISP_BGND_BLACK);OSInit();PC_DOSSaveReturn();PC_VectSet(uCOS,OSCtxSw);RandomSem=OSSemCreate(1);schetime1=0;schetime2=0;OSTaskCreate(TaskStart,(void*)0,&TaskStartStk[TASK_STK_SIZE-1],0);OSStart();}voidTaskStart(void*pdata){#ifOS_CRITICAL_METHOD==3OS_CPU_SRcpu_sr;#endifchars[100];INT16Skey;pdata=pdata;OS_ENTER_CRITICAL();PC_VectSet(0x08,OSTickISR);PC_SetTickRate(OS_TICKS_PER_SEC);OS_EXIT_CRITICAL();TaskStartCreateTasks();

for(;;){

if(PC_GetKey(&key)==TRUE){if(key==0x1B){PC_DOSReturn();}}OSCtxSwCtr=0;

温馨提示

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

评论

0/150

提交评论