




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
嵌入式实时操作系统原理参考教材:《嵌入式实时操作系统μC/OS—II》
北航出版社,邵贝贝等译《嵌入式实时操作系统μC/OS—II原理及应用》
北航出版社,任哲编《嵌入式实时操作系统μC/OS原理与实践》电子工业出版社,卢有亮编著概论嵌入式实时操作系统(RTOS)是嵌入式系统设计的一个平台。其最重要的优点就是将系统开发人员从繁杂的硬件及时序管理中解放出来,而更多地关注于问题的解决。现在流行的商业嵌入式实时操作系统种类繁多,但源码开放的并不多,只有少数几个,如:
Linus1、C/OS——MicroControllerOS,微控制器操作系统2、C/OS简介美国人JeanLabrosse1992年完成应用面覆盖了诸多领域,如照相机、医疗器械、音响设备、发动机控制、高速公路电话系统、自动提款机等1998年C/OS-II,目前的版本C/OS-IIV2.912000年,得到美国航空管理局(FAA)的认证,可以用于飞行器中uC/OS的商业应用全世界有数百种产品在应用:AvionicsMedicalCellphonesRoutersandswitchesHigh-endaudioequipmentWashingmachinesanddryersUPS(UninterruptiblePowerSupplies)IndustrialcontrollersGPSNavigationSystemsMicrowaveRadiosInstrumentationPoint-of-saleterminals更多公开源代码可移植性:主要内核由ANSIC编写,只有极少的部分采用了汇编。可固化:只要有合适的开发调试工具,即可实现固化可裁剪:可在应用程序中使用必要的uC/OS-II系统服务可剥夺:即总是运行优先级最高的任务。多任务:可以管理64个不同的任务,留给用户的可达56个可确定性:即任务执行时间的可确定性任务栈管理:各任务具有自己独立的任务栈系统服务:提供了丰富的系统服务例如邮箱、消息队列、信号量、块大小固定的内存的申请与释放、时间相关函数等。中断管理:嵌套层数可达255层稳定性与可靠性的特点:第一章实时系统概念分类软实时:要求各任务尽可能快的执行,但不限定某一任务在特定时间内完成。硬实时:各任务要求做到准时、无误地执行现在流行的实时操作系统多为二者的结合。操作系统的功能:管理计算机,提供对计算机底层硬件的管理(驱动)与接口(系统服务)在操作系统的支持下,用户开发程序的重点放在任务的实现上。嵌入式实时系统:嵌入到计算机(如ARM、DSP、MCU)内部本章主要内容:嵌入式实时操作系统的基本概念1.00前后台系统前后台系统多用在简单的以MCU为核心的较为简单的应用系统中,如:空调控制器等家电控制器中。前台系统:多为一个死循环(超循环supper-loops),或是带有休眠态(halt)的一段主程序,往往是完成界面处理。后台系统:又称为任务级程序或是服务程序,完成某些具体的任务处理,通常为中断服务程序(ISR)。前后台系统示例前后台系统的特点:系统较为简单,可用来完成一些简单的任务,但往往任务响应时效性较差;可维护性差。1.01代码的临界段
指处理时不可分割的代码,即不允许中断的代码。如一条指令,一段对数据的完整性有要求的程序代码。进入临界段之前必须关中断,而临界段代码执行完后,要立即开中断。1.02资源任何为任务所占用的实体都可称为资源。资源可以是硬件实体,亦可以是一个变量、数组或数据结构。1.03共享资源被一个以上的任务使用的资源叫做共享资源。
为防止对数据的破坏,每个任务在与共享资源打交道时必须独占该资源,这称为互斥(mutualexclusion)。1.04任务
任务:完成某一特定(单一)功能的程序代码;又称为线程。嵌入式程序设计的任务之一就是如何把问题分割成多个任务。在uC/OS-II中,每个任务有自己的任务栈。一个典型的任务:就是一个无限循环,其可能处在以下5种状态之一:休眠态:任务已经驻留内存,但不被内核调度就绪态:任务已准备好运行。但由于有高优先级的任务而尚未执行。运行态:已获得CPU的使用权。挂起态:又称等待事件态(waiting)。被中断态:被某个中断的ISR暂时中止了运行。CPU中内部资源的映像典型的任务一个无限循环。voidmytask(void*pdata){for(;;){dosomething;waitingforevent;dosomething;}}C/OS–II2.5以上版本支持64个任务,每个任务一个特定的优先级。优先级越高,数字越小。系统占用了8个任务,保留优先级为0、1、2、3、OS_LOWEST_PRIO-3、OS_LOWEST_PRIO-2、OS_LOWEST_PRIO-1、OS_LOWEST_PRIO-0。voidYourTask(void*pdata) {for(;;){ //用户代码
//调用uC/OS-II的某种系统服务:OSMboxPend();OSQPend();OSSemPend();OSTaskDel(OS_PRIO_SELF);OSTaskSuspend(OS_PRIO_SELF);OSTimeDly();OSTimeDlyHMSM();//用户代码}}1.05多任务
依靠CPU在不同的任务之间转换和调度。多任务与前后台系统的区别是:前后台系统中,一次只有一个后台任务在运行;而多任务系统可以有许多后台任务“同时”被执行。多任务的特点:允许同时运行多个任务,提高了CPU的利用率。一个复杂的任务(程序)可以按照任务性质分成多个“子”任务。提高了程序的模块化水平。有利于系统维护。1.06任务的切换任务切换(contextswitch):当多个任务交换CPU的使用权时,需要对寄存器等资源的当前状态(context)(类似中断现场)进行压栈保存,需保存的内容一般存在任务自己的栈区内的当前状态保存区(task’scontextstoragearea)。入栈完成后,则将下一个将要运行的任务的当前状态装入CPU中的寄存器,然后运行下一个任务。任务切换增加了CPU的额外开销,CPU内部寄存器越多,额外开销越大。1.07内核(kernel)实时操作系统的核心。主要负责任务的管理、任务间的通信、共享资源的管理以及时间的管理等。
实时内核在带来底层管理方便的同时,也会消耗CPU宝贵的硬件资源(如ROM和RAM)。所以在MCU中,尤其是在中低端的MCU中,很少能够看到实用化的实时内核的影子。MCU中因为RAM资源有限,更多地采用前、后台系统。1.08调度调度(schedulers,dispatcher):基于某种特定的规则来决定那个任务运行。常用的调度规则是基于优先级(priority)调度。即优先级最高的任务先运行。何时高优先级的任务获得CPU的使用权?问题对该问题的不同解决方法就形成了两种不同的内核:不可剥夺型内核与可剥夺型内核1.09不可剥夺型内核不可剥夺型内核(non-preemptivekernel):要求每个任务主动放弃CPU的使用权。见左图:不可剥夺型内核又称合作型多任务内核。异步事件还是由中断服务程序来处理,中断服务程序可使一个高优先级的任务由挂起态变为就绪态,ISR结束后,使用权仍返回原程序,直到原程序结束或主动放弃CPU的使用权。不可剥夺型内核的特点:优点:中断响应速度快,通常高于前后台系统几乎无需使用信号量来保护共享资源可以使用不可重入函数(本身中途不得放弃CPU使用权)缺点:任务级的响应时间不确定商业化的软件很少有采用不可剥夺型内核的1.10可剥夺型内核可剥夺型内核(preemptivekernel):最高优先级的任务一旦准备就绪,总能得到CPU的使用权。见左图:可剥夺型内核的突出优点就是可以保证响应时间。当一个运行的任务(或中断)使一个高优先级的任务进入就绪态时,当前任务的CPU的使用权即被剥夺而进入挂起态,而高优先级的任务得到CPU的使用权。可剥夺型内核的特点:优点:任务级的响应时间最优化,即最高优先级的任务何时被执行是可以预知的缺点:不可直接使用不可重入函数对于互斥型的共享资源,需使用信号量来保证互斥条件uC/OS属于可剥夺型内核1.11可重入函数可重入函数:可重入函数在任何时间可以被中断,一段时间后又可以运行。特性:可以被一个以上的任务调用,而不必担心数据被破环注意事项:
可重入函数使用的局部变量保存在CPU的寄存器或堆栈中,如使用全局变量,则在中断时应予保护。例1.1可重入函数voidstrcpy(char*dest,char*src)//‘dest’and‘src’islocalvariable{
;
while(*dest
++=*src++){;}}例1.2不可重入函数intTemp;//Tempisaglobalvariablevoidswap(int*x,int*y)//‘x’and‘y’islocalvariable{Temp=*x;*x=*y;*y=Temp;}如果swap()在执行过程中被中断,则可发生如下情况:解决的办法:在写函数时候尽量使用局部变量(例如寄存器、堆栈中的变量),如把Temp定义为局部变量。调用swap()函数之前关中断,调用之后再开中断采用信号量(semaphores)来控制共享资源的使用权(满足互斥条件)注意:不可重入函数引起的错误在测试中往往很难发现。1.12时间片轮番调度法时间片或称时间额度(timequantum)
任务运行事先确定的一个时间段。时间片轮番调度法:当两个以上的同优先级任务同时就绪时,则CPU的使用权交给下一个任务的条件是:当前任务已经空闲当前任务已经完成,但时间额度(时间片)尚未结束时间片已结束
uC/OS不支持时间片轮番调度法,各任务的优先级必须互不相同。1.13任务的优先级任务越重要,被赋予的优先级越高。对大多数内核而言,任务的优先级由用户决定对uC/OS而言,理论上支持64个优先级,0级最高,63级最低。为节省内存,用户可根据需要,通过给OS_CFG.H中的OS_LOWEST_PRIO来说明应用程序中任务优先级别的数目,其值为0,1,2,……,OS_LOWEST_PRIO+1。最低的两个优先级的任务保留为:空闲任务和CPU统计任务。1.14静态优先级与动态优先级静态优先级:
在执行过程中,任务的优先级不变。动态优先级:在执行过程中,任务的优先级可由用户改变。uC/OS支持动态优先级。但动态优先级在使用过程中应注意优先级的反转问题。1.15优先级的反转注意:任务1的优先级实际上已经降低到任务3的水平。并且由于任务2剥夺了任务3的CPU使用权,使任务1增加了额外的延迟时间。优先级的反转的解决方案—优先级继承优先级继承的核心是:内核自动变换任务的优先级。注意:1.mutex是互斥型信号量。2.uC/OS不支持优先级的继承。1.16任务优先级分配一个基本的原则就是根据任务的重要性来确定任务的优先级。一般应综合考虑如下两种需求:软实时:要求任务执行的尽可能快,但并没有特定的时间要求。硬实时:任务应准时、无误地执行。实用的优先级分配方法:单调执行率调度法RMS(RateMonotonicScheduling)基本假定:所有任务都是周期性的。任务间不需要同步,没有共享资源,没有数据交换。CPU总是执行最高优先级且处于就绪态的任务,即必须使用可剥夺型调度法。但该方法应满足如下的所谓RMS定理优先级分配方法:根据在一个周期内任务执行的次数(任务执行率)来分配任务的优先级,即执行最频繁的任务优先级最高。RMS定理假定:n为系统的任务数;Ei为任务i的最长执行时间;Ti是任务i的执行周期。则:Ei/Ti是任务i所需的CPU时间的占用率,一般在实际应用中,该值应小于70%。采用RMS方法,欲满足硬实时的条件,应满足如下不等式:n为系统的任务数说明:实际应用中,执行率最高的任务未必是最重要的任务。因此RMS方法仅仅是一个基础。分配任务的优先级有许多不同的算法,可参阅相关文献。在许多MCU中,硬中断(任务)的优先级是硬件设定的,软中断的优先级可用户设定。1.17互斥条件问题:如何在不同的任务间实现通讯及数据交换。解决的方法:通常使用的是共享的数据结构、消息邮箱或消息队列。存在的问题:不同任务在处理共享数据时如何实现“独占”,以避免竞争和数据的破坏。结论:应使不同的任务在使用共享数据时满足“互斥条件”使用“互斥条件”的一般方法:在进入数据处理前关中断,处理完毕后立即开中断。使用测试位,利用测试位来检测是否有其他任务正在使用共享数据。禁止任务切换;利用信号量。例1.3关中断与开中断例1.4利用uC/OS-II宏调用关中断与开中断voidfunction(void){OS_ENTER_CRITICAL();accessthesharedresource;OS_EXIT_CRITICAL();}disableinterrupts;accessthesharedresource;reenableinterrupts;例1.5利用测试位处理共享资源disableinterrupts;//closetheinterruptsif(access_variable==0)//testingthesharedresource{setaccess_variableto1;//lockedthesharedresourcereenableinterrupts;//opentheinterruptsaccesstheresource;disableinterrupts;settheaccess_variablebackto0;//releasesharedresourcereenableinterrupts;}else{reenableinterrupts;}
例1.6用给任务切换上锁,然后开锁的方法实现数据共享voidfunction(void){OSSchedLock();;;//处理共享数据,但注意中断是开着的
;OSSchedUnlock();}注意:如果任务不与ISR共享变量或数据结构,可以采用上述方法。但因该方法禁止了任务切换,与内核的主要功能“任务调度与协调”矛盾,所以应尽量少采用。信号量(semaphores)及其应用信号量在多任务内核中普遍采用,主要用于:控制共享资源的使用权(满足互斥条件)标志某事件的发生保证两个任务的同步信号量可以是二值量(逻辑量),也可以是8位、16位、32位的整型量。说明:信号量是各任务取得共享资源使用权的“钥匙”。一个信号量可以管理多个对象,例如管理10个缓冲区,则可使用信号量的10个不同状态(即计数式信号量)。一个二值的信号量可用测试位代替,且测试位的操作更简单。信号量可以实现任务间的相互同步。信号量所定义的主要操作:建立(create),亦可称之为初始化(initialize)(初始化的值为1或0)。对应的系统函数:
OSSemCreate()挂起(pend),或称等信号(wait)。对应的系统函数为:OSSemPend()发信号(post),或称给信号(signal)。对应的系统函数:
OSSemPost()另外还有无等待请求一个信号量
OSSemAccept()查询信号量的当前状态
OSSemQuery()OS_SEM_EN=1(OS_CFG.H中定义)信号量的使用:信号量的使用原则有以下两种:等待信号量的任务中优先级最高的任务最早开始等待信号量的任务
信号量的使用过程:发出等待请求;如果该信号量有效(其值大于0),则信号量值减1,任务执行。如果该信号量无效(其值等于0),则任务列入等待信号量的任务表,多数内核支持定义等待超时。信号量的删除、等待只能由任务程序调用。uC/OS仅支持基于优先级的方法例1.7通过获得信号量来处理共享数据OS_EVENT*SharedDataSem;voidFunction(void){INT8Uerr;OSSemPend(SharedDataSem,0,&err);//发出一个等待请求,等待时间
//为0
;;//共享数据处理(中断是开着的);
OSSempost(SharedDataSem);//释放信号量}例1.8隐含的信号量使用INT8UCommSendCmd(char*cmd,char*response,INT16Utimeout){acquireport’ssemphore;sendcommandtodevice;waitforresponse(withtimeout);if(timeout){releasesemphore;return(errorcode);}else{releasesemphore;return(errorcode);}}计数式信号量的使用例1.9上图的程序示例1.19死锁
死锁又称抱死(deadlock或deadlyembrace),是指2个任务无限期地互相等待对方控制的资源。解决方法:首先得到全部需要的资源,再做下一步的工作;用特定的顺序申请多个资源;释放资源时,采用相反的顺序;多数内核还允许采用等待超时。死锁在大型多任务系统中容易发生,嵌入式系统中要少得多。1.20同步利用信号量来保证任务与ISR,或者是两个不同的任务(互相之间无数据交换)的运行同步。注意:此时的信号量已经不再是保证互斥条件的钥匙。用来实现同步的信号量初始化为0。用一个信号量实现的单向同步(unilateralrendezvous):用2个信号量实现的双向同步(bilateralrendezvous):双向同步只能是在任务与任务之间,而不可能在任务与ISR之间。例1.10双向同步范例task1(){for(;;){performoperation;Initsignaltask#2;waitforsignalfromtask#2;continueoperation;}}task2(){for(;;){performoperation;Initsignaltask#1;waitforsignalfromtask#1;continueoperation;}}1.20事件标志(eventflag)当某任务需要与其他事件同步时,需使用事件标志。
上图表示某任务需与另一些事件或条件之一发生同步,称为独立型同步,亦即逻辑“或”关系。
上图表示某任务需与另一些事件或条件都发生同步,称为关联型同步,亦即逻辑“与”关系。
上图表示多个事件的组合事件发信号给多个任务。uC/OS支持事件标志置位、事件标志清0和等待事件标志。1.21任务间通信
指任务间或中断服务与任务间的通信(intertaskcommunication)。这种通信的方式有两种:通过全局变量使用全局变量时,必须保证每个任务或是ISR独享该变量。在中断服务中保证独享的唯一手段就是“关中断”,而任务欲独享该变量时,可以采用“关中断”或者是使用“信号量”。利用消息传递亦可使用消息队列或者邮箱。1.22消息邮箱利用内核服务可以给任务发送消息或者交换消息。典型的手段可以使用消息邮箱(messagemailbox),在消息邮箱中放的是一个指向待传送消息的指针型变量。一个或多个任务及ISR一个或多个任务及ISR等待超时计时器
在uC/OS中,内核总是把消息传递给等待消息任务列表中优先级最高的任务。内核提供的邮箱服务邮箱的创建(初始化)及撤销,邮箱里最初可以有(或没有)消息。相关函数为:
OSMboxCreat()OSMboxDel()
将消息放入邮箱(post)。
OSMboxPost()OSMboxPostOpt()
//新函数,可实现消息广播等待消息进入邮箱(pend)。
OSMboxPend()查询邮箱中的状态。
OSMboxQuery()
无等待从邮箱中得到消息。如果邮箱里没有消息,则任务并不被挂起,如果邮箱内有消息,则接受消息。
OSMboxAccept()邮箱里的消息可当作二值信号量来使用。亦可使用邮箱来实现延时。说明:1.23消息队列
消息队列(messagequeue)实际上就是邮箱队列。
任务或者是ISR可以将消息放入队列,同样一个任务或者是多个任务可以从队列中得到消息。得到消息的方式既可以是FIFO,亦可是LIFO。每个邮箱均有一个等待消息的列表,如果邮箱为空,则相应的任务被挂起。
与邮箱的使用相同,每个消息队列均有一个任务等待列表(waitinglist)。且如果消息队列为空,等待消息的任务挂起。并进入等待消息任务列表。如果等待任务定义了超时,则在规定的时间内没有收到消息的任务进入运行态,并返回错误代码。消息总是传递给等待消息任务列表中优先级最高的任务(uC/OS规定)。
内核提供的消息队列服务如下:消息队列的创建(初始化)及撤销,消息队列的初始化为空。相关函数为:
OSQcCreat();OSQDel()OSQFlush()//清空消息队列向消息队列发送消息(post)(FIFO)。
OSQPost()向消息队列发送消息(post)(FIFO或LIFO)
OSQPostOpt()//这是一个新函数,可实现消息广播等待消息队列中的消息(pend)。
OSQPend()获取消息队列的状态。
OSQQuery()无等待从消息队列中得到消息。如果邮箱里没有消息,则任务并不被挂起,如果邮箱内有消息,则接受消息。
OSQAccept()1.24中断中断是CPU提供的一种处理异步事件的硬件机制,中断一旦被识别,CPU将保存部分(或是全部)现场(context,寄存器内容),跳到ISR,当ISR处理完事件后,程序回到:在前后台系统中,回到后台程序;对不可剥夺型内核,回到被中断的任务;对可剥夺型内核,让进入就绪态的优先级最高的任务开始运行。中断所提供的机制无需CPU不断地查询(pollimg)异步事件。内核提供的与中断相关的服务:关中断(disableinterrupt)
OS_ENTER_CRITICAL();开中断(enableinterrupt)
OS_EXIT_CRITICAL();注意:在实时环境中,关中断的时间应尽可能短,也就是说ISR应尽可能短。不同的CPU对中断的嵌套有不同的规定。1.25中断延迟与中断响应中断延迟:CPU关中断的时间。所有的实时系统在进入临界区代码之前,总是要关中断,执行完临界区代码后再开中断。因此,关中断的时间长短是实时内核的重要指标。
中断延迟=关中断的最长时间+开始执行ISR第一条指令的时间中断响应:从中断发生到开始执行用户的ISR代码之间的时间。这个时间包含中断延迟时间与CPU保存现场的时间:中断响应=中断延迟+CPU保存现场的时间一般系统提供的中断相应时间应该是在最坏情况下的中断响应时间。对于可剥夺型内核:uC/OS-II在处理中断时需要首先调用OSIntEnter(),该函数通知内核即将开始中断服务,以使内核可以跟踪中断嵌套。
中断响应=中断延迟+CPU保存现场时间+内核进入ISR的执行时间1.26中断处理时间从原则上讲,中断处理时间应该尽可能得短。但由于ISR的重要性,我们无法给出一个具体的时间限制。ISR通常所做的工作是:识别中断源;从中断请求的外设读取状态或数据;“通知”负责对该事件处理的任务。
尤其是最后一条,在uC/OS-II中,“通知”意味着使用信号量、邮箱或是消息队列,需要花费较多的时间。因此对于一些简单的中断服务,应考虑由ISR来完成中断的处理以及开/关中断的管理。1.27中断延迟、响应及恢复前后台系统的中断延迟、响应及恢复可剥夺型内核的中断延迟、响应及恢复进入就绪态的最高级任务不可剥夺型内核的中断延迟、响应及恢复1.28非屏蔽中断(NMI)对于像复位、断电保存、看门狗溢出等异常事件的处理,内核引起的服务延迟是不可忍受的。此时应使用NMI,几乎所应的CPU均设有NMI。综合考虑各种中断的情况,则有以下关系:中断延迟时间=最长的指令执行时间+开始NMI服务的时间中断响应时间=中断延迟时间+CPU保存现场的时间中断恢复时间=恢复CPU寄存器的时间+执行中断返回指令的时间NMI的ISR不能使用内核服务,故不能处理临界区代码。但是向NMI的ISR传递参数或者得到参数是允许的。参数的传递必须使用全局变量,且应该是一次即可读写完毕的数据类型,而不应该是2个分离的字(节)。一个使用可屏蔽中断的处理机制处理NMI的例子1.29时钟节拍时钟节拍(时钟滴答ClockTick)是一种定时器中断,可通过编程方式实现。主要用来提供任务(消息)等待计时、任务延时计时等应用的依据。时钟节拍依据软硬件系统、任务的不同而设置不同。一般来讲,时钟节拍中断越快,时间分辨率越高,但系统的额外开销也就越大。如果在实时内核中要对任务进行n个时钟节拍的延时,应注意这种延时仅仅是在每个时钟节拍到来时做一次裁决,而绝不是精确地延时n个时钟节拍的时间。时钟节拍是操作系统的心脏。首先32位的整数OSTime加一。对任务列表进行扫描,判断是否有延时任务应该处于准备就绪状态,最后进行上下文切换。任务的延时与抖动voidOSTimeTick(void){OS_TCB*ptcb;OSTimeTickHook(); (1)ptcb=OSTCBList; (2)while(ptcb->OSTCBPrio!=OS_IDLE_PRIO){ (3)OS_ENTER_CRITICAL();if(ptcb->OSTCBDly!=0){if(--ptcb->OSTCBDly==0){if(!(ptcb->OSTCBStat&OS_STAT_SUSPEND)){ (4)OSRdyGrp|=ptcb->OSTCBBitY; (5)OSRdyTbl[ptcb->OSTCBY]|=ptcb->OSTCBBitX;}else{ptcb->OSTCBDly=1;}}}ptcb=ptcb->OSTCBNext;OS_EXIT_CRITICAL();}OS_ENTER_CRITICAL(); (6)OSTime++; (7)OS_EXIT_CRITICAL();}出现抖动的原因及解决方法原因:
与CPU的负荷有关,也可能与系统设计的缺陷(bug)有关。解决方法:增加CPU的时钟频率,或者是在同一家族中做系统升级;增加时钟节拍的频率;重新安排任务的优先级;尽量避免浮点运算(如必须使用,尽量采用单精度);使用优化功能强的编译器;时间要求苛刻的地方,采用汇编语言;1.30对存储器的需求系统对于存储器的需求是一个十分复杂的问题,主要需要考虑如下几个方面的需求:RAM的需求:应用程序的变量、数组、数据结构等;任务栈(uC/OS中,每个任务有独立的任务栈)及系统栈(根据需要设定);中断嵌套的层数以及每个中断所需采用的栈空间大小;最大任务数;RAM总需求=应用程序的RAM需求+内核数据区的RAM需求+任务栈×任务数
+最大中断嵌套栈的需求ROM的需求如果采用RTOS,则对ROM的需求要考虑应用程序代码及内核的大小。如果仅仅是一个前后台系统,则只需考虑应用程序的代码长度(含表格)。内核的大小取决于内核服务(功能)的数量。对于特定的CPU及特定的项目,可对内核的服务进行裁减。例如:对一个8位CPU,如果仅使用uC/OS-II的任务调度、任务切换、信号量处理、延时及超时服务,大约需要1~3KB的代码空间。RAM使用需注意的事项:除ARM或DSP这类的CPU,一般MCU的RAM仅有数Kbyte,因此对于任务栈空间的分配与使用要十分谨慎,尤其应注意:函数和ISR中的局部变量、数据结构,特别是大型数组和数据结构的定义;函数(子程序)的嵌套以及中断的嵌套;库函数需要的栈空间;多变元的函数调用。1.31使用实时内核的优缺点优点:通过将实时应用程序分割成若干任务,减少了开发周期;借助于内核提供的各种服务,有效地利用了各种资源,减少了设计难度;提高了程序的可维护性。使用可剥夺型内核,对于时间要求苛刻的任务,尽可能得到快捷、有效的处理。缺点:一般来说,采用RTOS,系统要增加大约2%~4%的CPU开销。另外,需增加部分的RAM及ROM容量。如果不熟悉RTOS,或对于特定的芯片,无合适的内核,需要增加学习或是移植的时间。费用。uC/OS-II的不足固定的基于优先级的调度,不支持时间片,使用起来不方便。一个任务的基础上增加一个基于时间片的微型调度核。系统时钟中断,没有提供用户使用定时器。可以利用C语言的特点,自己编制函数。在对临界资源的访问上使用关闭中断实现,没有使用CPU提供的硬件指令,例如测试并置位。只是一个实时多任务内核,没有图形用户接口(GUI)、文件系统(FS)和TCP/IP协议栈。本章小结嵌入式操作系统
uC/OS第二章μC/OS-II的任务处理本章主要内容:什么是任务uC/OS-II是如何得到用户任务的;任务是如何调度的;uC/OS-II是如何知道应用程序CPU利用率的;uC/OS-II是如何完成初始化的;怎样启动多任务;uC/OS-II的文件结构及使用2.0临界段,OS_ENTER_CRITICAL()和
OS_EXIT_CRITICAL()临界段处理的一个主要问题就是关中断,处理完毕后再开中断。开关中断的时间是内核的一个重要的指标。取决于微处理器的结构或是编译器所生成的代码质量。通常借助于插入汇编指令来实现开/关中断效率最高。uC/OS使用两个预定义的宏(OS_CPU.H)来实现开关中断:
OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()2.0.1.临界段的使用…{…OS_ENTER_CRITICAL();
//临界段代码
OS_EXIT_CRITICAL();
…}注意:在临界段不应调用uC/OS-II的功能函数,尤其是与时间相关的函数。或者说在必须调用系统的功能函数时,应该开中断。2.0.2OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()
的不同实现方法:使用在OS_CPU.H中预定义(#define)的常数OS_CRITICAL_METHOD可用来选择具体的实现方法:方法一,OS_CRITICAL_METHOD==1;采用处理器指令开/关中断。优点:简单、快捷,对某些处理器也许是唯一的选择。缺点:需要调用系统功能函数时,函数返回时会自动开中断。示意性代码:
#defineOS_ENTER_CRITICAL()asm(“DI”)
#defineOS_EXIT_CRITICAL()asm(“EI”)方法二,OS_CRITICAL_METHOD==2;采用在堆栈中保存开/关中断的状态,然后用处理器指令开/关中断。优点:在调用系统功能函数前后,中断标志在临界段的前后均不发生改变。缺点:稍微复杂一些。示意性代码:
#defineOS_ENTER_CRITICAL()asm(“PUSHPSW”)
asm(“DI”)
#defineOS_EXIT_CRITICAL()asm(“POPPSW”)
asm(“EI”)
方法三,OS_CRITICAL_METHOD==3;如果编译器提供了扩展功能,则利用OS_CPU.H中预定义的变量来保存和恢复PSW。优点:适用于各种微处理器。缺点:需要编译器提供支持。示意性代码:
OS_CPU_SRcpu_sr;……#defineOS_ENTER_CRITICAL()cpu_sr=get_processor_psw();disable_interrupts();//依赖于编译器
#defineOS_EXIT_CRITICAL()set_processor_psw(cpu_sr);//依赖于编译器
2.1uC/OS中的任务
将一个大的、复杂的应用程序分解成多个小程序,可简化解决问题的过程。这些小的程序实体就称其为“任务”,或称为“线程”。
uC/OS的一个主要功能就是对任务的管理、调度。从程序设计的角度看,任务就是为解决特定问题而建立的C语言函数及相关联的数据结构。
从存储结构来看任务的构成为:
任务代码、任务堆栈和任务控制块。从功能分:系统任务和用户任务2.1.0任务的组织形式2.1用户任务代码的一般结构
任务的代码通常是一个无限循环结构,或者说是一个超循环结构。在该循环中可以响应中断。voidMyTask(void*pdata)//形参必须定义成void的指针,以便参数传递{for(;;){
可被中断的代码;
OS_ENTER_CRITICAL();//
不可被中断的代码;//临界区
OS_EXIT_CRITICAL();//
可被中断的代码;
}}说明:在多任务系统中,用户任务看起来就是一个C语言函数,但该函数并不是被主函数或是其他函数调用的,而是由主函数创建和启动,然后交由操作系统负责调度和运行。
任务可以自我删除,但删除的代码仅仅是操作系统不再理会该代码,或者说操作系统“看不到”该代码。uC/OS提供了两个用来创建用户任务的函数:
OSTaskCreate()和OSTaskCreateExt()
其功能就是将任务的起始地址与任务控制块联系起来。其中后者提供了一些附加的功能。OSTaskCreateExt()的函数头;INT8UOSTaskCreateExt( void(*task)(void*pd),//指向任务的指针
void*pdata,//传递给任务的参数
OS_STK*ptos,//指向任务堆栈栈顶的指针
INT8Uprio//任务的优先级
INT16Uid//任务的标识符
OS_STK*pbos,//指向任务堆栈栈底的指针
INT32Ustk_size,//任务堆栈的容量
void*pext,//指向附加数据域的指针
INT16Uopt//用于设定操作选项
)OSTaskCreateExt()创建任务更为灵活,但会增加一些开销。//********************************************************************************************//CREATETASKS//********************************************************************************************/staticvoidTaskStartCreateTasks(void){INT8Ui;for(i=4;i<N_TASKS;i++)//CreateN_TASKSidenticaltasks{TaskData[i]='0'+i;//EachtaskwilldisplayitsownletterOSTaskCreate(Task,(void*)&TaskData[i],&TaskStk[i][TASK_STK_SIZE-1],i+1);}}睡眠态:用户任务代码驻留程序空间,但操作系统“看不见”。需“创建”后即入就绪态。创建需给出起始地址、优先级、栈空间。2.2任务状态就绪态:可在多任务运行之前通过“创建”进入,亦可由运行的任务建立。运行态:OSStart()可以启动多任务,但只能启动一次。OSStart()使最高级的任务进入运行态。等待状态:处于运行态的任务可由OSTimeDly()或OSTimeDlyHMSM()函数“带入”等待状态。亦可通过OSFlagPend(),OSSemPend(),
OSMutexPend(),
OSMboxPend()或OSQPend()来等待某事件的发生。OSTaskDel()可使一个任务由就绪态回到睡眠态。中断服务态:被ISR中断运行,除非该任务将中断关闭或者是操作系统关闭中断。2.3任务控制块
(OS_TCB)任务控制块的功能是:当一个任务的CPU使用权被剥夺时,操作系统用它来保存任务的状态。OS_TCB永远驻留内存。2.3.1任务控制块的数据结构(注意各成员之间的逻辑分组):typedefstructos_tcb{OS_STK*OSTCBStkPtr;//当前任务的栈顶指针,唯一的可用汇编处理的变量
#ifOS_TASK_CREATE_EXT_EN>0void*OSTCBEXTPtr;//指向用户定义的任务控制块扩展
OS_STK*OSTCBStkBottom;//指向任务堆栈的栈底指针,注意栈生长方向
INT32UOSTCBStkSize;//栈中可容纳的指针元数量,而非字节数
INT16UOSTCBOpt;//控制块扩展所定义的操作
INT16UOSTCBId;//任务识别码,目前未用
#endif任务控制块在任务建立时由创建函数建立structos_tcb*OSTCBNext;structos_tcb*OSTCBPrev;#if((OS_Q_EN>0)&&(OS_MAX_QS>0))||(OS_MBOX_EN>0)||(OS_SEM_EN>0)||(OS_MUTEX_EN>0)OS_EVENT*OSTCBEventPtr;#endif#if((OS_Q_EN>0)&&(OS_MAX_QS>0))||(OS_MBOX_EN>0)OS_EVENT*OSTCBMsg;#endif#if(OS_VERSION>=251)&&(OS_FLAG_EN>0)&&(OS_MAX_FLAGS>0)#ifOS_TASK_DEL_EN>0OS_FLAG_NODE*OSTCBFlagNode;#endifOS_FLAGSOSTCFlagsRdy;#endif
...…}OS_TCB;2.3.2任务控制块链表
uC/OS-II用两个链表来管理任务控制块,一条是空链表(缓冲池),在用户调用函数OSInit()对uC/OS-II初始化时建立;另一条是任务块链表(所有控制块均已分配给任务),是由创建函数OSTaskCreate()创建。
OSInit()对uC/OS-II初始化时,首先在RAM中创建OSTCB结构的OSTCBTbl[]数组,然后连结成下图所示的链表。初始化的空链表数=用户任务数+系统任务数
每当使用OSTaskCreate()或OSTaskCreateExt()创建一个任务时,系统会将OSTCBFreeList指向的任务快分配给该任务,在对各成员赋值后,按照任务控制块链表的头指针OSTCBList将其加入任务控制链表。下图是任务控制块链表的建立过程:uC/OS允许用函数OSTaskDel()删除一个任务。即将其从任务控制块链表中删掉,并归还空任务控制块链表。2.3.3任务控制块的初始化
当使用OSTaskCreate()或OSTaskCreateExt()创建一个任务时,该函数会自动调用系统函数OSTCBInit()来为任务控制块初始化。OSTCBInit()的函数原形为:2.4就绪表在OSRdyTbl[]中的“位”任务的优先级(prio):在OSRdyGrp中的“位”这是一个为加快运算速度预先建立起来的表表内填写的数字是任务的优先级号,其实际值是“0/1”使优先级别为prio的任务进入就绪态的操作:OSRdyGrp|=OSMapTbl[prio>>3];OSRdyTbl[prio>>3]|=OSMapTbl[prio&0x07];使优先级别为prio的任务脱离就绪态的操作:If((OSRdyTbl[prio>>3]&=-OSMapTbl[prio&0x7])==0)OSRdyGrp&=-OSMapTbl[prio>>3];应用实例:优先级判定表(OSUnMapTbl[256]):为加快运算速度在OS_CORE.H中定义的表假定OSRdyGrp==0x64,查表OSUnMapTbl[OSRdyGrp]=2即OSRdyTbl第二组对应的任务的优先级最高;假定OSRdyTbl[2]的值为0xE4,则查表OSUnMapTbl[0xE4]=2,得到任务的优先级prio=(2<<3)+2=18为当前的最高级中断。获得最高优先级任务运算:y=OSUnMapTbl[OSRdyGrp];x=OSUnMapTbl[OSRdyTbl[y]];prio=(y<<3)+x;2.5任务调度调度:即确定哪个任务的优先级最高,则该任务进入运行态。调度由调度器(scheduler)来完成;任务级的调度:OSSched()中断级的调度:OSIntExt()以下为任务调度器(taskscheduler)代码清单:voidOS_Sched(void){#ifOS_CRITICAL_METHOD==3OS_CPU_SRcpu_sr;#endifINT8Uy;OS_ENTER_CRITICAL();if((OSIntNesting==0&&(OSLockNesting==0)){y=OSUnMapTbl[OSRdyGrp];OSPrioHighRdy=(INT8U)((y<<3)+OSUnMapTbl[OSRdyTbl[y]);if(OSPrioHighRdy!=OSPrioCur{OSTCBHighRdy=OSTCBPrioTbl[OSPrioHighRdy];
OSCtxSwCtr++;
OS_TASK_SW();}}OS_Exit_CRITICAL();}任务调度所执行的任务切换为:1)将被挂起任务的处理器、寄存器推入堆栈。然后将高优先级任务的寄存器从栈中恢复到寄存器中。2)在uC/OS中,就绪任务的栈结构总是看起来跟刚刚发生过中断一样,所有寄存器都保存在栈中。即其运行只是恢复所有的CPU寄存器并运行中断返回指令。3)OS_TASK_SW()为模拟一次中断,从而挂起当前正在执行的任务,执行更重要的任务。4)OS_Sched()的所有代码均为临界段代码。2.6任务级的任务切换OS_TASK_SW()
当有更高优先级的任务进入就绪态时,调用OS_TASK_SW(),完成任务切换(context–switch)(具体函数是OSCtxSw(),也就是CPU中的全部寄存器内容。OS_TASK_SW()是一个宏调用,通常包含有软中断指令,一般来讲是移植时需要修改的内容。我们虚构一个如下的CPU:1个堆栈指针(SP)1个程序寄存器(PC)1个处理器状态寄存器4个通用寄存器(R1、R2、R3及R4)切换前的状态:1)调用OS_TASK_SW(),强制处理器保存PC、PSW
2)软中断指令保存R1~R4重新装入要运行的任务任务切换函数OSCtxSw()的示意性代码(通常由汇编完成):voidOSCtxSw(void){asm(“pushR1”);asm(“pushR2”);asm(“pushR3”);asm(“pushR4”);OSTCBCur->OSTCBStkPtr=SP;OSTCBCur=OSTCBHighRdy;SP=OSTCBCur->OSTCBStkPtr;
asm(“popR1”);asm(“popR2”);asm(“popR3”);asm(“popR4”);asm(“reti”);}2.7给调度器上锁和开锁
给调度器上锁(OSSchedLock())用于禁止任务调度,直到任务完成后,调用给调度器开锁函数(OSSchedUnlock())。
调度器上锁后,任务保持对CPU的使用权,而不管是否有更高级的任务进入就绪态。但如果此时是开中断状态,则中断依然可以被识别,且可以进入中断服务。给调度器上锁函数程序清单1给调度器开锁函数程序清单2给调度器上锁和开锁函数的应用应十分慎重uC/OS-II允许嵌套的最大深度为255。
变量OSLockNesting用来跟踪OSSchedLock()
被调用的次数,uC/OS-II只有在OSLockNesting==0时方允许任务调度打开。2.8空闲任务(ideltask)
空闲任务永远是最低级任务,即OS_LOWEST_PRIO。空闲任务不可能被应用软件删除。voidOS_TaskIdel(void*pdata){#ifOS_CRITICAL_METHED==3OS_CPU_SRcpu_sr;#endifpdata=pdata;for(;;){OS_ENTER_CRITICAL();OSIdelCtr++;//32bit,许多低端CPU需多条指令完成
OS_EXIT_CRITICAL();OSTaskIdelHook();//可执行使CPU进入低功耗模式等命令
}}3.9统计任务OSTaskStat()(在OS_CORE.C中)
如果系统配置常数OS_TASK_STAT_EN(在OS_CFG.H)设为1,该任务即可建立。一旦得到允许,则每秒运行一次。该任务的输出:CPU的使用率(OSCPUUage),用百分比表示,精度1%。说明:如果需要使用统计任务,则必须在初始化时建立的第一个任务中调用统计任务初始化函数OSStatInit(),即在启动OSStart()开始多任务调度之前先建立一个任务来调用统计任务的初始化函数OSStatInit(),然后再建立应用程序中的其他任务。3.10uC/OS中的中断ISR的结构用户中断服务子程序结构:中断服务程序的执行过程voidOSIntEnter(void){if(OSRunning==TRUE)if(OSIntNesting<255)OSIntNesting++;}voidOSIntExit(void){#ifOS_CRITICAL_METHED==3OS_CPU_SRcpu_sr;#endifOS_ENTER_CRITICAL();if(OSRunning==TRUE)if(OSIntNesting>0)OSLockNesting--;if((OSIntNesting==0)&&(OSLockNesting==0)){OSIntExitY=OSUnMapTbl[OSRdyGrp];OSPrioHighRdy=(INT8U)((OSIntExitY<<3))+OsunMapTbl[OSRdyTbl[OSIntExitY];if(OSPrioHighRdy!=OSPrioCur){OSTCBHighRdy=OSPrioTbl[OSPrioHighRdy];OSCtxSwCtr++;OSIntCtxSw();//与OS_TASK_SW()不同,ISR已将寄存器压栈
}}OS_EXIT_CRITICAL();}任务级的任务切换和中断级的任务切换:OSCtxSw():PUSH将当前CPU各寄存器值压入栈OSTCBCur->OSTCBStkPtr=sp在当前任务的TCB中保存当前任务的sp指针OSTCBCur=OSTCBHighRdy将指向当前任务的指针指向新任务OSPrioCur=OSPrioHighRdy将新任务的优先级复制给当前任务的优先级sp=OSTCBCur->OSTCBStkPtr将新任务的堆栈指针赋值给CPU的spPOP从新任务堆栈中恢复CPU各寄存器IRET执行中断返回,弹出程序指针PC和状态寄存器PSWOSIntSw():OSTCBCur=OSTCBHighRdy将指向当前任务的指针指向新任务OSPrioCur=OSPrioHighRdy将新任务的优先级复制给当前任务的优先级sp=OSTCBCur->OSTCBStkPtr将新任务的堆栈指针赋值给CPU的spPOP从新任务堆栈中恢复CPU各寄存器IRET执行中断返回,弹出程序指针PC和状态寄存器PSW2.11时钟节拍
许多任务需要时钟来实现延时或确认超时,uC/OS的节拍为10~100Hz。其节拍源可以是专门的硬件定时器,或是来自工频交流电。注意:定时器中断必须放在多任务系统启动之后,即在OSStart()之后要做的第一件事就是初始化定时器中断。而不应该放到系统初始化OSInit()之后,OSStart()之前。
uC/OS-II的时钟节拍服务是通过在中断服务子程序中调用OSTimeTick()实现的。OSTimeTick()跟踪所有任务的定时器及超时时限。时钟节拍中断服务子程序的一个示意性代码:voidOSTickISR(void){
保存全部CPU寄存器;
OSIntNesting++;//或用OSIntEnter()通知操作系统在做中断服务
if(OSIntNesting==1)
OSTCBCur->OSTCBStkPtr=SP;OSTimeTick();清发出中断的设备的中断标志;重新允许中断以及其他需要的服务;
OSIntExit();//通知操作系统已退出中断服务恢复所有CPU寄存器;执行中断返回指令;}2.12uC/OS-II初始化
在使用系统的所有服务之前,必须调用OSInt()对uC/OS
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 园艺主题酒店创新创业项目商业计划书
- 农产品期货交易咨询创新创业项目商业计划书
- 动物皮毛艺术品制作创新创业项目商业计划书
- 农产鲜品乐创新创业项目商业计划书
- 现场急救知识培训包扎课件
- 2025年教育行业数字化教材开发与多语言支持策略研究
- 2025年新能源汽车废旧电池回收利用产业链技术创新与产业竞争力研究报告
- 2025年城市轨道交通智慧运维系统在智慧城市建设中的关键作用报告
- 河南省三门峡市陕州区2022-2023学年大象版五年级上学期科学期中考试试题(含答案)
- 2026届云南省永德县第一中学化学高一第一学期期末学业质量监测模拟试题含解析
- 2025浙江宁波市海曙开发建设投资集团限公司国企业招聘26人易考易错模拟试题(共500题)试卷后附参考答案
- 国民经济行业分类代码(2024年版)
- 孕妇宫颈机能不全课件
- 2025至2030中国微流控芯片行业发展态势与投资规划研究报告
- 电子商务平台技术入股合同书7篇
- 房屋市政工程施工现场安全风险分级管控与防范措施清单
- 装配式预制场管理制度
- 更换纸尿裤的操作流程
- GB/T 37133-2025电动汽车用高压连接系统
- EPC项目设计管理方案
- 视觉文化影响下的非遗文化旅游游客忠诚度设计研究
评论
0/150
提交评论