嵌入式系统教程参考课件第5、8章ucos的应用_第1页
嵌入式系统教程参考课件第5、8章ucos的应用_第2页
嵌入式系统教程参考课件第5、8章ucos的应用_第3页
嵌入式系统教程参考课件第5、8章ucos的应用_第4页
嵌入式系统教程参考课件第5、8章ucos的应用_第5页
已阅读5页,还剩51页未读 继续免费阅读

下载本文档

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

文档简介

1、1第五章 C/OS-II在ARM系统中的应用与开发5.1 C/OS-II系统的特点及结构 COS-是一个免费的、源代码公开的实时嵌入式内核,其内核提供了实时系统所需要的一些基本功能。其中包含全部功能的核心部分代码占用8.3 KB,全部的源代码约5500行,结构合理、清晰易懂,且注解详尽,非常适合初学者进行学习分析。而且由于COS-是可裁剪的,所以用户系统中实际的代码最少可达2.7 KB,可谓短小精悍。COS-不仅使用户得到廉价的解决方案,而且由于COS-的开放源代码特性,还使用户可针对自己的硬件优化代码,获得更好的性能。 COS-是在PC机上开发的,C编辑器使用的是Borland C/C+3.

2、1版。从早期使用的COS到现在的COS- V2.52版,应用的实例也进一步说明了该内核的实用性和可靠性。25.1.1 COS-系统的特点1有源代码,COS-源代码是开放的,用户可登录COS-的网站(wwwuCOS-IIcom)下载针对不同微处理器的移植代码。这极大地方便了实时嵌入式系统COS-的开发,降低了开发成本。2可移植(Portable),COS-的源代码中,除了与微处理器硬件相关的部分是使用汇编语言编写的,其绝大部分是使用移植性很强的ANSI C来编写的。并且把用汇编语言编写的部分已经压缩到最低的限度,以使COS-更方便于移植到其他微处理器上使用。如Intel公司、Zilog公司、Mo

3、torola公司的微控制器和TI公司的DSP,以及包括ARM公司、Analog Device公司、三菱公司、日立公司、飞利浦公司和西门子公司的各种微处理器。 33可固化(ROMable),COS-是为嵌入式应用而设计的操作系统,只要具备有合适的软硬件工具,就可将COS-嵌入到产品中去,从而成为产品的一部分。 4可裁剪(Scalable),COS-可根据实际用户的应用需要使用条件编译来完成对操作系统的裁剪,这样就可以减少COS-对代码空间和数据空间的占用。5可剥夺型(Preemptive),COS-是完全可剥夺型的实时内核,运行就绪条件下优先级最高的任务。6多任务,COS-可管理64个任务。一般

4、情况下,建议用户保留8个任务给COS-。这样,留给用户应用程序的任务最多可有56个。系统赋给每个任务的优先级必须不同,这意味着COS-不支持时间片轮转调度法(Round-robin Scheduling)。 7可确定性,绝大多数COS-的函数调用和服务的执行时间具有确定性。在任何时候用户都能知道COS-的函数调用与服务的执行时间。48任务栈,COS-的每个任务都有自己单独的栈和栈空间。使用COS-的栈空间校验函数可确定每个任务到底需要多少栈空间。 9系统服务,提供了例如信号量、互斥信号量、消息邮箱、事件标志、数据队列、块大小固定的内存的申请与释放及时间管理函数等。 10中断管理,中断可使正在执

5、行的任务暂时挂起,如果优先级更高的任务被中断唤醒,则高优先级的任务在中断嵌套全部退出后立即执行。中断嵌套层数可达255层。 11稳定性与可靠性,2000年7月,COS-在一个航空项目中得到了美国联邦航空管理局对商用飞机的符合RTCA DO-178B标准的认证。可以说,COS-的每一种功能、每一个函数及每一行代码都经过了考验与测试。55.1.2 COS-系统的内核结构与其他操作系统不同,COS-其实只有一个内核,提供任务调度、任务间的通信与同步、任务管理、时间管理和内存管理等基本功能。 任务在COS-中,一个任务通常是一个无限的循环。一个任务看起来像其他c语言的函数一样,有函数返回类型,有形式参

6、数变量,但任务是决不会返回的。故返回参数必须定义成void,例如:Void YourTask(void *pdata)for(;)*用户代码*调用COS-II的某种系统服务:* *用户代码*6 任务调度COS-II可以管理多达64个任务,其优先级可以从0到OS_LOWEST_PRIO,优先级号越低,其任务的优先级就越高。但目前版本的COS-II有两个任务已经被系统占用了,而且保留了优先级0、1、2、3、和OS_LOWEST_PRIO-3、OS_LOWEST_PRIO-2、0S_LOWEST_PRIO-1以及OS_LOWEST_PRIO这8个任务已备将来使用。OS_LOWEST_PRIO是作为常

7、数在OS_CFG.H文件中用定义常数语句#define constant来定义的。因此用户可以使用多达56个应用任务,但首先要给每个任务赋以不同的优先级。COS-II总是运行进入就绪态的优先级最高的任务。目前版本的COS-II中,任务的优先级号就是任务编号(ID)。优先级号(或任务的ID号)也可以被一些内核服务函数调用,比如改变优先级函数OSTaskChangePrio()或者OSTaskDel()。 为了使COS-II能管理用户任务,用户必须在建立一个任务的时候,将任务的起始地址与其他参数一起传给OSTaskCreate()或者OSTaskCreateExt()这两个函数中的任何一个函数。图

8、5-1是COS-II控制下的任务状态转换图,在任一时刻,任务的状态一定是这五种状态之一。7由于,COS-II总是运行进入就绪态任务中优先级最高的那个任务。那么确定哪一个任务优先级最高、该哪个任务将要运行,这样的工作是由调度器完成的。COS-II任务调度所花的时间是常数,与应用程序中建立的任务数无关。任务切换很简单,一般由以下两步完成:首先将被挂起任务的微处理器寄存器推入堆栈;然后将较高优先级的任务的寄存器值从堆栈中恢复到寄存器中。在COS-II中,就绪任务的栈结构总是看起来跟刚刚发生过中断一样,所有微处理器的寄存器都保存在栈中。换句话说,COS-II运行就绪态的任务所要做的一切,只是恢复所有的

9、CPU寄存器并运行中断返回指令。 85.1.3 主要模块介绍1内存管理在ANSI C中,一般采用内存分配函数malloc()和内存释放函数free()两个函数动态地分配和释放内存。为了消除多次动态分配与释放内存所引起的内存碎片和分配、释放函数执行时间的不确定性的现象,COS-把连续的大块内存按分区来进行管理。每个分区中都包含若干个存储容量大小相同的内存块,但不同分区之间的内存块容量大小是可以不同的。在需要动态分配内存时,可选择一个适当的分区,按块来分配内存。在释放内存时,将该块放回它以前所属的分区。这样,就能有效解决内存碎片问题。而且每次调用malloc()和free()分配和释放的都是整数倍

10、的固定内存块长,这样执行时间就是确定的了。 9(1)内存管理控制块OS_MEM为便于内存的管理,COS-II中使用内存控制块(Memory Control Blocks)的数据结构跟踪每一个内存分区系统,每个分区都有属于自己的内存控制块,系统是通过内存控制块数据结构OS_MEM来管理内存的。 (2)内存管理内存管理主要通过以下4个函数来实现: OSMemCreate()函数,用于建立一个内存分区。该函数共有4个参数:内存分区的起始地址、分区内的内存块数、每个内存块的字节数和一个指向错误信息代码的指针。 OSMemGet()函数,用于分配一个内存块。当调度某任务执行时,必须先从已建立的内存分区中

11、为该任务申请一个内存块。 OSMemPut()函数,释放一个内存块。当某一任务不再使用一个内存块时,必须及时地把它放回到相应的内存分区中,以便下一次的分配操作。 OSMemQuery()函数,用于查询一个特定内存分区的状态。如查询某内存分区中内存块的大小、可用内存块数和正在使用的内存块数等信息。10(3)时间管理与大部分内核一样,COS-要求提供定时中断,以实现延时与超时控制等功能。这个定时中断也可以被叫作为时钟节拍。在相关的内容中,介绍了时钟的中断服务子程序(ISR)和时钟节拍函数OSTimeTick()。时钟节拍函数的作用是用于通知COS-发生了时钟节拍中断,下面再介绍几个可以处理时间问题

12、的函数。任务延时函数OSTimeDIy() 调用该函数会使COS-进行一次任务调度,并且执行下一个优先级最高的就绪态任务。任务调用OSTimeDly()后,一旦规定的时间期满或者有其他任务通过调用OSTimeDlyResume()取消了延时,它就会立即进入就绪状态。只有当该任务在所有就绪任务中具有最高的优先级时,它才会立即运行。恢复延时的任务函数OSTimeDlyResume() COS-II具有允许结束正处于延时期的任务的功能。具体方法是通过调用OSTimeDlyResume()和指定要恢复的任务的优先级的方式,这样延时的任务就可以不用等待延时期满,而是通过其他任务取消延时来使自己处于就绪态

13、。实际上,OSTimeDlyResume()也可唤醒正在等待事件的任务。11按时、分、秒、毫秒延时函数OSTimeDlyHMSM() OSTimeDly()是一个非常有用的函数,但用户的应用程序须要知道延时时间所对应的时钟节拍的数目。增加了OSTimeDlyHMSM()函数后,就可按时、分、秒和毫秒来定义时间了,这样会显得更加方便。与OSTimeDly()一样,调用OSTimeDIyHMSM()函数也会使COS-II进行一次任务调度,并且执行下一个优先级最高的就绪态任务。任务调用OSTimeDlyHMSM()后,一旦规定的时间期满或有其他任务通过调用OSTimeDlyResume()取消了延时

14、,它就会立即处于就绪态。同样,只有当该任务在所有就绪态任务中具有最高的优先级时,它才会立即运行。系统时间函数OSTimeGet()和OSTimeSet() 无论时钟节拍何时发生,COS-II都会将一个32位的计数器加1。这个计数器在调用OSStart()初始化多任务和 个节拍执行完一遍后,从0开始计数。在时钟节拍频率等于100Hz时,这个32位的计数器每隔497天就重新开始计数。在执行的过程中可以通过调用OSTimeGet()函数来获得该计数器的当前值,也可以通过调用OSTimeSet()函数来改变该计数器的值。122、任务的管理COS-II提过大量的API函数实现对任务的管理,主要的任务有:

15、(1)建立任务COS-II要管理用户的任务,就必须先建立任务。通过将任务的地址和其他参数传递给以下两个函数来建立任务。OSTaskCreate()和带有扩展附加功能的OSTaskCreateExt()函数。在main()函数内开始多任务调度(OSStart()前,必须至少建立一个任务,而且任务不能由中断服务程序(ISR)建立。创建一个任务控制块,并通过任务控制块把任务代码和任务堆栈关联起来形成一个完整的任务。还有使刚创建的任务进入就绪状态,并引发一次任务调度(取决于任务是否处于多多任务工作状态)。13两个函数OSTaskCreate()和OSTaskCreateExt()原型如下:INT8U

16、OSTaskCreate( void (*task)(void *pd); /指向任务的指针 void * pdata; /传递给任务的参数 OS_STK * ptos;/指向任务堆栈栈顶的指针 INT8U prio /任务的优先级)14INT8U OSTaskCreateExt( void (*task)(void *pd); /指向任务的指针 void * pdata; /传递给任务的参数 OS_STK * ptos;/指向任务堆栈栈顶的指针 INT8U prio /任务的优先级 INT16U id /任务的标识 OS_STK * pbos;/指向任务堆栈栈低的指针 INT32U stk_

17、siaze; /任务堆栈容量 void * pext; /指向附加数据域的指针 INT16U opt /用于设定操作选项 ) 在调用任务建立函数后, COS-II内核会首先从TCB空闲列表内申请一个空的TCB指针;然后根据用户给出的参数初始化任务堆栈,并在内部的任务就绪表中标记该任务为就绪状态;最后返回。这样就建立了一个任务。15(2)任务堆栈在COS-II中,每个任务都有自己的堆栈空间。堆栈必须声明为OS_STK类型,并且由连续的内存空间组成。可以静态分配堆栈空间(在编译时分配),也可以动态分配堆栈空间(在运行时分配),这两种声明方式都应放置在函数外面。任务所需堆栈的容量由应用程序确定。但必

18、须考虑到任务调用的所有函数的嵌套情况、任务调用的所有函数为局部变量分配的所有内存的数目,以及所有可能的中断服务子程序嵌套对堆栈的需求。此外,堆栈必须能够保存CPU所有的寄存器。COS-II提供了堆栈检验函数OSTaskStkChk(),用来确定任务实际需要的堆栈空间的大小。这样能够避免为任务分配过多的堆栈空间,从而减少应用程序代码所需的RAM数量。调用堆栈检验函数后,所得到的只是一个大致的堆栈使用情况,并不能说明堆栈使用的全部实际情况。为了适应系统以后的升级和扩展,应该多分配10100的堆栈空间。16(3)任务的挂起和恢复挂起一个任务,就是停止这个任务的运行。在uC/OS-II中,用户任务可以

19、通过调用系统提供的函数OSTaskSuspend()来挂起自身或者处空闲任务之外的其他任务。挂起的任务,只能在其他任务中通过调用恢复函数OSTaskResume()使其恢复为就绪状态。该函数并不要求和挂起函数OSTaskSuspend()成对使用。但是,如果任务在被挂起的同时还在等待延迟时间到,则需要对任务取消挂起操作,并且要继续等待延迟时间到,任务才能转入就绪状态。17(4)任务的删除 删除一个任务,就是把该任务置于睡眠状态,任务的代码不再被uC/OS-II使用,而并不是说任务的代码被删除了。调用OSTaskDel()后,先进行条件判断,当所有的条件都满足后,就会从所有可能的uC/OS-II

20、的数据结构中去除任务的任务控制块OS_TCB,这样就不会被其他的任务或中断服务子程序置于就绪态,即任务置于休眠状态。函数原型如下: INT8U OSTaskDel(INT8U prio) 可删除任务自身或者除了空闲任务之外的其他任务。删除自己参数为: OS_PRIO_SELF 直接调用这样的删除任务,可能出现某些问题,如果任务拥有一些动态的内存或者信号量之类的资源,那么如果它被删除了,它的资源就不会被释放而丢失,会造成同样使用资源的其他任务进入死等待,出现错误情况。要慎重使用。提供了一个可以在请求删除方和被删除方通信完成删除的函数。原型如下: INT8U OSTaskDelReq(INT8U

21、prio) 返回是否被删除和是否有要删除自己的要求。被删除方调用得知要删除自己,释放资源后,在删除自己。18(5)其他任务管理函数任务优先级别修改 任务运行过程中,用户可以根据需要来改变任务的优先级别。调用的函数原型如下: INT8U OSTaskChangePrio( INT8U oldprio; /任务现在的优先级别 INT8U newprio /要修改的优先级别 ) 查询任务的信息 查询一些任务中的信息,函数原型如下: INT8U OSTaskQuery( INT8U prio; OS_TCB * pdata )193、任务间同步与通信的管理uC/OS-II中,使用信号量、邮箱(消息邮箱

22、)和消息队列来实现任务相互同步或相互之间的通信。 uC/OS-II把关于它们的操作都定义为全局函数,以供应用程序的所有任务来调用等待任务列表 采用INT8U类型的数组OSEventTbl作为记录等待事件任务的记录表,叫做等待任务表,每个任务占1位,为1表示是等待任务。任务的等待时限,记录在等待任务的任务控制块TCB的成员OSTCBDly中20(1)事件控制块 uC/OS-II使用叫做事件控制块ECB的数据结构来描述诸如信号量、邮箱和消息队列这些事件。事件控制块包含包括等待任务表在内的所有有关事件的数据。21操作事件控制块的函数 uC/OS-II有4个对时间控制块进行基本操作的函数(定义在OS_

23、CORE.C中)。事件控制块的初始化函数 void OS_EventWaitListInit(OS_ENENT * pevent ) 把变量OSEventGrp及任务等待表中的每一位都清0,即令事件的任务等待表中不含有任何等待任务。该函数被OSXXXCreate()创建时所调用。 XXX Sem 信号量 Mutex 互斥信号量 Mbox 消息邮箱 Q 消息队列22使一个任务进入等待状态的函数 void OS_EventTaskWait( OS_ENENT * pevent) 将在任务调用函数OSXXXPend()请求一个事件时调用。使一个正在等待任务进入就绪状态的函数 INT8U OS_Eve

24、ntTaskRdy( OS_EVENT * pevent, void *msg , INT8U msk) 作用:把调用这个函数的任务在任务等待表中的位置清0后,再把任务在任务就绪表中的对应的位置1,然后引发一次任务调度 将在任务调用函数OSXXXPost()发送一个事件时,被调用。使一个等待超时的任务仅需就绪状态的函数 void OS_EventTo(OS_EVENT *pevent) 作用:当任务已经超过了等待的时间,却要使它进入就绪状态。 将在任务调用函数OSXXXPend()请求一个事 件时,被调用23(2)信号量管理使用信号量可以在任务间传递信息,实现任务与任务或中断服务子程序的同步。

25、 uC/OS-II中的信号量由两部分组成:16位的无符号整数信号量的计数值(065535);另一部分是由等待该信号量的任务组成的等待任务列表。 uC/OS-II提供了以下6个函数对信号量进行操作。操作创建信号量OSSemCreat(INT16U cnt)创建,返回已创建信号量的指针。请求信号量OSSemPend(OS_EVENT *pevent INT16U timeout INT8U *err)24 time为0,则表示无限等待。 不等待调用的函数为OSSemAccept(OS_EVENT * pevent)。发送信号量 INT8U OSSemPost(OS_EVENT * pevent)

26、当获得信号量,访问共享资源结束以后,释放信号量,调用该函数。先检查是否有等待该信号量的任务。没有,信号量计数器加1,有,则调用调度器OS_Sched()。25删除信号量 OS_EVENT *OSSemDel( OS_EVENT * pevent, INT8U opt, INT8U *err) opt OS_DEL_NO_PEND 没有等待任务删除 OS_DEL_ALLWAYS 立即删除 只能任务执行,不能在中断服务程序中删除查询信号量的状态 INT8U OSSemQuery(OS_EVENT * pevent OS_SEM_DATA *pdata) pdata是一个结构指针,存储信号量的状态。

27、26(3)消息邮箱管理消息邮箱是uC/OS-II中的一种通信机制,通常使用时要先定义一个指针型的变量该指针指向一个包含了消息内容的特定数据结构。发送消息的任务或中断服务子程序把这个变量送往邮箱,接收消息的任务从邮箱中取出该指针变量,完成信息交换。 uC/OS-II提供6种对消息邮箱的操作,它们通过以下函数实现:创建OS_EVENT * OSMoxCreate( void * msg)Msg为消息指针,一般初始为NULL。27向消息邮箱发送消息INT8U OSMboxPost(OS_EVENT * pevent, void * msg)发送广播消息INT8U OSMboxPostOpt(OS_E

28、VENT * pevent, void * msg, INT8U opt)opt: OS_POST_OPT_BROADCAST 广播消息 OS_POST_OPT_NONE 最高优先级28请求消息邮箱void * OSMboxPend(OS_EVENT * pevent, INT16U timeout, INT8U *err)无等待请求void * OSMboxAccept( OS_EVENT * pevent)查询邮箱状态INT8U OSMboxQuery( OS_EVENT * pevent, OS_MBOX_DATA *pdata)删除邮箱OS_EVENT *OSMboxDel( OS_E

29、VENT * pevent, INT8U opt, INT8U *err)29(4)消息队列管理消息队列是uC/OS-II的另一种通信机制,它可以使一个任务或中断服务子程序向另一个任务发送以指针定义的变量。 uC/OS-II提供了9个对消息队列进行操作的函数。创建 先创建一个指针数组,然后用该数组来创建消息队列 OS_EVENT OSQCreate( void * start,INT16U size)请求消息队列 void* OSQPend(OS_EVENT * pevent, INT16U timeout,INT8U *err)无等待请求void OSQAccept( OS_EVENT *

30、pevent)30向消息队列发送消息INT8U OSQPost(OS_EVENT * pevent,void * msg) 工作方式FIFOINT8U OSQPostFront(OS_EVENT * pevent,void * msg) 工作方式LIFO发送广播消息INT8U OSQPostOpt(OS_EVENT * pevent,void * msg, INT8U opt)31清空消息队列INT8U OSQFlush ( OS_EVENT * pevent )删除消息队列OS_EVENT * OSQDel( OS_EVENT * pevent )查询消息队列INT8U OSQQuery(

31、OS_EVENT * pevent, OS_Q_DATA *pdata)324COS-II操作系统的文件系统COS-II操作系统的文件体系结构如图5-2所示,其核心主要可分为以下3部分:(1)应用软件层,指的是基于COS-II的应用程序代码。(2)内核的核心代码层,主要包括8个源代码文件。这8个源代码文件为OS_CORE.C、OS_MBOX.C、OS_MEM.C、OS_SEM.C、OS_TIME.C、uCOS_II.C、OS_Q.C和OS_TASK.C,其主要实现的功能分别是核心管理、事件管理、存储管理、消息队列管理、定时管理、信号量处理、消息管理和任务调度等,这部分代码与处理器无关。(3)系

32、统设置与移植层。系统设置部分的代码由两个头文件OS_CFG.H和INCLUDES.H组成。其主要功能是用来配置事件控制块的数目以及是否包含消息管理的相关代码等。与处理器相关的移植代码部分包括:一个头文件OS_CPU.H、一个汇编文件OS_CPU_A.ASM和一个C代码文件OS_CPU_C.C。系统设置与移植层与具体应用和处理器相关,在随后的COS-II的移植和开发过程中,用户所需要关注的就是这部分文件。33图5-2 COS-II文件体系结构345.1.4 COS-II 操作系统的初始化在调用COS-II操作系统的其他服务之前,COS-II操作系统要求用户首先调用系统初始化函数OSInit()。

33、执行OSInit()函数后将初始化COS-II所有的变量和数据结构,另外OSInit()会建立空闲任务,并且这个任务总是处于就绪状态的。空闲任务OSTaskldle()函数的优先级总是设置成为最低级别,即OS_LOWEST_PRIO。多任务的启动是用户通过调用OSStart()函数来实现的。然而,在启动COS-II之前,用户至少要建立一个应用任务,例如:void main() OSInit();.通过OSTaskCreate()或OSTaskCreateExt()创建至少一个任务.OSStart(); /*开始多任务调度,OSStart()永远都不会返回*/355.2 COS-II系统在ARM

34、系统中的移植所谓移植,就是指使一个实时操作系统能够在其他的微处理器平台上进行运行。由于COS-II的主要代码都是由标准的C语言写成的,所以,一般来说移植过程并不复杂。5.2.1 COS-II移植条件虽然COS-II的大部分源代码是用C语言写成的,但是,仍需要用汇编语言完成一些与微处理器相关的代码。例如,COS-II在读写微处理器、寄存器时只能通过汇编语言来实现。这是因为COS-II在设计的时候就已经充分考虑了可移植性。为了要使COS-II可以正常工作,处理器必须要满足如下要求:361)微处理器的C编译器能产生可重入代码可重入的代码指的是一段代码(如一个函数)可以被多个任务同时调用,而不必担心会

35、破坏其内部的数据。也就是说,可重入型函数在任何时候都可以被中断执行,也不会因为在函数中断的时候被其他的任务重新调用,影响函数中的数据。可重入代码或者只使用局部变量,即变量保存在CPU寄存器中或堆栈中;或者使用全局变量,则要对全局变量予以保护。通常的C编译器,把局部变量分配在栈中。所以,多次调用同一个函数,可以保证每次的局部变量互不受影响。而全局变量,在多次调用函数的时候,必然受到影响。代码的可重入性是保证完成多任务的基础,除了在C程序中使用局部变量以外,还需要C编译器的支持。基于ARM的SDT、ADS等集成开发环境,都可以生成可重入的代码。372)在程序中可以使用c语言打开或者关闭中断在COS

36、-II中,可以通过进入中断屏蔽的宏定义OS_ENTER_CRITICAL()或者退出中断屏蔽的宏定义OS EXIT_CRITICAL()来控制系统关闭中断或者打开中断,这需要微处理器的支持。在目前的ARM系列的微处理器上,都可以设置相应的寄存器来关闭或者打开系统的所有中断。3)微处理器支持中断,并且能产生定时中断(通常在10Hz-1000Hz之间)。COS-II是通过微处理器产生定时的中断来实现多任务之间的调度的。4)微处理器支持能够容纳一定量数据的硬件堆栈,并具有将堆栈指针和其他CPU寄存器读写到堆栈(或者内存)的指令。5)COS-II进行任务调度的时候,会把当前任务的CPU内部寄存器的内容

37、存放到此任务的堆栈中。然后,再从另一个任务的堆栈中恢复原来的工作寄存器,继续运行另一个任务。所以,寄存器中内容的入栈和出栈是COS-II多任务调度的基础。385.2.2 COS-II的移植步骤在的移植过程中,使用的是基于ARM公司架构的软件开发工具作为编译器,所值得关注的问题是与微处理器相关的代码,这部分主要包括一个头文件OS_CPU.H、一个汇编文件OS_CPU_A.ASM和一个C代码文件OS_CPU_C.C。1)设置头文件OS_CPU.H中与处理器和编译器相关的代码(1)与编译器相关的数据类型#define INT8U unsigned char #define INTl6U unsign

38、ed short #define INT32U unsigned long #define OS_STK unsigned long #define BOOLEAN int #define OS_CPU_SR unsigned long#define INT8S char因为不同的微处理器有不同的字长,所以COS-的移植包括了一系列的类型定义以确保其可移植性。 39用户必须将任务堆栈的数据类型定义到COS-II操作系统中,这个过程是通过为OS_STK声明正确的C语言数据类型来完成的。由于使用的微处理器上的堆栈成员是16位的,所以将OS_TSK声明为无符号整形数据类型。值得注意的是,所有的任务堆

39、栈都必须使用OS_TSK声明数据类型。(2)进入中断屏蔽的宏定义OS_ENTER_CRITICAL()和退出中断屏蔽的宏定义OS_EXIT_CRITICAL() extern int INTS_OFF(void); extern void INTS_ON(void); #define OS_ENTER_CRITICAL() cpu_sr = INTS_OFF(); #define OS_EXIT_CRITICAL() if(cpu_sr=0) INTS_ON(); 与所有的实时内核一样,COS-II操作系统在进行任务切换时需要先禁止中断在访问代码的临界区,并且在访问完毕后重新允许中断。这就使得

40、COS-II能够保护临界区代码免受多任务或中断服务例程(ISR)的破坏。在S3C44B0微处理器上是通过OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()两个函数来实现开、关中断的。40(3)栈增长方向标OS_STK_GROWTH#define OS_STK_GROWTH 1 #define STACKSIZE 256绝大多数的微处理器的堆栈是从高地址向低地址增长的,但是有些微处理器是采用相反方式工作的。鉴于这种情况COS-II操作系统被设计成为这两种情况都可以处理,只要在结构常量OS_STK_GROWTH中指定堆栈的生长方式就可以了。例如:设OS_STK_GROWT

41、H为0表示堆栈从下往上增长。设OS_STK_GROWTH为1表示堆栈从上往下增长。412). 用汇编语言在OS_CPU_A.ASM文件中编写4个与微处理器相关的函数(1) 调用优先级最高的就绪任务函数 OSStartHighRdy()(2)任务级的任务切换函数 OSCtxSw() (3)中断级的任务切换函数 OSIntCtxSw() (4)时钟节拍中断服务函数 OSTickISR()3)用C语言编写6个操作系统相关的函数(OS_CPU_C.C)这里主要涉及6个函数:OSTaskStkInit()、OSTaskCreateHook()、OSTaskDelHook()、OSTaskSwHook()

42、、OSTaskStatHook()及OSTimeTickHook()。 这些函数中,惟一必须移植的是任务堆栈初始化函数OSTaskStkInit()。这个函数在任务创建时被调用,负责初始化任务的堆栈结构并返回新堆栈的指针stk。在ARM体系结构下,任务堆栈空间由高至低依次保存着PC、LR、R12、R11、R10、R1、R0、CPSR及SPSR 。OSTaskStkInit()初始化后的堆栈内容如图5-3所示。堆栈初始化工作结束后,返回新的堆栈栈顶指针。 42以下5个Hook函数,又称为钩子函数,主要用来扩展COS-功能,使用前必须被声明,但并不一定要包含任何代码。 (1) 0STaskCrea

43、teHook()函数 当用OSTaskCreate()函数或OSTaskCreateExt()函数建立任务时,就会调用OSTaskCreateHook()函数。COS-设置完自己的内部结构后,会在调用任务调度程序之前调用OSTaskCreateHook()函数。该函数被调用时中断是禁止的,因此应尽量减少该函数中的代码,以缩短中断的响应时问。 (2) OSTaskDelHook()函数 当任务被删除时,就会调用OSTaskDelHook()函数。该函数在把任务从COS-的内部任务链表中解开之前被调用。当OSTaskDelHook()函数被调用时,会收到指向正被删除任务的OS_TCB的指针,这样它

44、就可访问所有的结构成员了。OSTaskDelHook()函数可用来检验TCB扩展是否被建立了(一个非空指针),并进行一些清除操作。注意,此函数不返回任何值。 43(3) OSTaskSwHook()函数 当发生任务切换时,调用OSTaskSwHook()函数。不管任务切换是通过OSCtxSw()函数,还是通过OSIntCtxSw()函数来执行的,都会调用该函数。OSTaskSwHook()函数可直接访问OSTCBCur和OSTCBHighRdy,这是因为它们都是全局变量。OSTCBCur指向被切换出去的任务的OS_TCB,而OSTCBHighRdy指向新任务的OS_TCB。在调用OSTaskS

45、wHook()函数期间,中断一直是被禁止的。这时因为代码的多少会影响到中断的响应时间,所以应尽量使代码简化。此函数没有任何参数,也不返回任何值。 (4) OSTaskStatHook()函数 OSTaskStatHook()函数每秒都会被OSTaskStat()函数调用一次,可用OSTaskStatHook()函数来扩展统计功能。该函数没有任何参数,也不返回任何值。 (5) OSTimeTickHook()函数 OSTimeTickHook()函数在每个时钟节拍都会被0STimeTick()函数调用。实际上,OSTimeTickHook()函数是在节拍被COS-处理,并在通知用户的移植实例或应

46、用程序之前被调用的。OSTimeTickHook()函数没有任何参数,也不返回任何值。 445.3 基于COS-的应用开发5.3.1 在COS-系统上的应用程序结构1任务一个任务通常是一个无限循环,也如同其他C的函数一样,具有函数返回类型,有形式参数变量,但是,任务是不会返回参数的。void YourTask(void *pdata)for(;)*用户代码*OSTaskDel(OS_PRIO_SELF);45形式参数变量是由用户代码在第一次执行的时候带入的,该变量的类型是一个指向void的指针。这是为了允许用户应用程序传递任何类型的数据给任务。不同的是,当任务完成以后,任务可以自我删除。任务代

47、码并非真的删除了,COS-II只是简单地不再理会这个任务了,这个任务的代码也不会再运行,如果任务调用了OSTaskDel()函数,这个任务绝不会返回什么。为了使COS-II能管理用户任务,用户必须在建立一个任务的时候,将任务的起始地址与其他参数一起传给任务创建函数OSTaskCreate()或任务创建扩展函数OSTaskCreateExt()这两个函数中的任一个。462COS-II的启动系统多任务的启动是用户通过调用OSStart()函数来实现的。然而,启动COS-II之前,用户至少要建立一个应用任务。void main(void) OSInit();*初始化COSII * 通过调用OSTas

48、kCreate()或OSTaskCreateExt()创建至少一个任务 OSStart();*开始多任务调度!OSStart()永远不会返回。475.3.2 COS-的API任何一个操作系统都会提供大量的应用程序接口API供开发者使用,COS-也不例外。由于COS-面向的是实时嵌入式系统开发,并不要求大而全。所以,内核提供的API也就大多与多任务相关。主要有如下几类:任务类、消息类、同步类、时间类及临界区与事件类。下面介绍几个比较重要的API函数。 1) OSTaskCreate()函数 该函数在使用前,至少应在主函数main()内被调用一次,同时要求是在调用OSInit()函数之后才可再调用

49、该函数,它的作用就是创建一个任务。OSTaskCreate()函数有4个参数,它们分别是任务的入口地址、任务的参数、任务堆栈的首地址和任务的优先级。在调用这个函数后,系统会首先从TCB空闲列表内申请一个空的TCB指针,然后根据用户给出的参数初始化任务堆栈,并在内部的任务就绪表内标记该任务为就绪状,然后返回。这样,一个任务就创建成功了。 482) OSTaskSuspend()函数 该函数可将指定的任务挂起。如果挂起的是当前任务,那么还会引发系统执行任务切换先导函数OSShed()来进行一次任务切换。实际上,这个函数只有一个指定任务优先级的参数。事实上在系统内部,优先级除了表示一个任务执行的先后

50、次序外,还起着区分每一个任务的作用。换句话说,优先级也就是任务的ID。所以,COS-不允许出现相同优先级的任务。 3) OSTaskResume()函数 该函数与OSTaskSuspend()函数的作用正好相反,它用于将指定的已经挂起的函数恢复为就绪状态。如果恢复任务的优先级高于当前任务,那么还将引发一次任务切换。其参数类似于OSTaskSuspend()函数,用来指定任务的优先级。需要特别说明的是,该函数并不要求和OSTaskSuspend()函数成对使用。 494) OS_ENTER_CRITICAL()宏 由OS_CPUH文件可知,OS_ENTER_CRITICAL()和下面要谈到的OS_EXIT_CRITI_CAL()都是宏,它们都与特定的CPU相关,一般都被替换为一条或者几条嵌入式汇编代码。由于系统希望向上层开发者隐藏内部实现,故一般都宣称执行此条指令后系统进入临界区。其实,它就是关个中断而已。这样,只要任务不主动放弃CPU使用权,别的任务就没有占用CPU的机会了。相对这个任务而言,它就是独占了,所以说进入临界区了。这个宏应尽量少用,因为它会破坏系统的一些服务,尤其是时间服务,并使系统对外界响应性能降低。 505) OS_EXIT_CRITICAL()宏 该宏与上面介绍的宏配套使用,在退出临界区时使用。其实它就是重新开中断。需要注意的是,它必须和上面的宏成对出现,

温馨提示

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

评论

0/150

提交评论