首页 [信息与通信]ucOS简要教程

[信息与通信]ucOS简要教程

举报
开通vip

[信息与通信]ucOS简要教程[信息与通信]ucOS简要教程 第一篇 UCOS介绍 第一篇 UCOS介绍 这个大家都知道。呵呵。考虑到咱们学习的完整性还是在这里唠叨一下。让大家再熟悉一下。高手们忍耐一下吧~ uC/OS II(Micro Control Operation System Two)是一个可以基于ROM运行的、可裁减的、抢占式、实时多任务内核,具有高度可移植性,特别适合于微处理器和控制器,是和很多商业操作系统性能相当的实时操作系统(RTOS)。为了提供最好的移植性能,uC/OS II最大程度上使用ANSI C语言进行开发,并且...

[信息与通信]ucOS简要教程
[信息与通信]ucOS简要教程 第一篇 UCOS介绍 第一篇 UCOS介绍 这个大家都知道。呵呵。考虑到咱们学习的完整性还是在这里唠叨一下。让大家再熟悉一下。高手们忍耐一下吧~ uC/OS II(Micro Control Operation System Two)是一个可以基于ROM运行的、可裁减的、抢占式、实时多任务内核,具有高度可移植性,特别适合于微处理器和控制器,是和很多商业操作系统性能相当的实时操作系统(RTOS)。为了提供最好的移植性能,uC/OS II最大程度上使用ANSI C语言进行开发,并且已经移植到近40多种处理器体系上,涵盖了从8位到64位各种CPU(包括DSP)。 uC/OS II可以简单的视为一个多任务调度器,在这个任务调度器之上完善并添加了和多任务操作系统相关的系统服务,如信号量、邮箱等。其主要特点有公开源代码,代码结构清晰、明了,注释详尽,组织有条理,可移植性好,可裁剪,可固化。内核属于抢占式,最多可以管理60个任务。 μC/OS-II 的前身是μC/OS,最早出自于1992 年美国嵌入式系统专家Jean J.Labrosse 在《嵌入式系统编程》杂志的5 月和6 月刊上刊登的文章连载,并把μC/OS 的源码发布在该杂志的B B S 上。 μC/OS 和μC/OS-II 是专门为计算机的嵌入式应用设计的, 绝大部分代码是用C语言编写的。CPU 硬件相关部分是用汇编语言编写的、总量约200行的汇编语言部分被压缩到最低限度,为的是便于移植到任何一种其它的CPU 上。用户只要有 标准 excel标准偏差excel标准偏差函数exl标准差函数国标检验抽样标准表免费下载红头文件格式标准下载 的ANSI 的C交叉编译器,有汇编器、连接器等软件工具,就可以将μC/OS-II嵌人到开发的产品中。μC/OS-II 具有执行效率高、占用空间小、实时性能优良和可扩展性强等特点, 最小内核可编译至 2KB 。μC/OS-II 已经移植到了几乎所有知名的CPU 上。 严格地说uC/OS-II只是一个实时操作系统内核,它仅仅包含了任务调度,任务管理,时间管理,内存管理和任务间的通信和同步等基本功能。没有提供输入输出管理,文件系统,网络等额外的服务。但由于uC/OS-II良好的可扩展性和源码开放,这些非必须的功能完全可以由用户自己根据需要分别实现。 uC/OS-II目标是实现一个基于优先级调度的抢占式的实时内核,并在这个内核之上提供最基本的系统服务,如信号量,邮箱,消息队列,内存管理,中断管理等。 uC/OS-II以源代码的形式发布,但并不意味着它是开源软件。你可以将其用于教学和私下研究(peaceful research);但是如果你将其用于商业用途,那么你必须通过Micrium获得商用许可。 虽然uCOS-II在商业上使用时需要的得到授权并且费用也是一笔不小的数字,但是他的开源毕竟带领我们走入了内核的世界。在此我代 关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf 嵌入式 工程 路基工程安全技术交底工程项目施工成本控制工程量增项单年度零星工程技术标正投影法基本原理 师向Mr Jean J.Labrosse 致谢。 任务管理 uC/OS-II 中最多可以支持64 个任务,分别对应优先级0,63,其中0 为最高优先级。63为最低级,系统保留了4个最高优先级的任务和4个最低优先级的任务,所有用户可以使用的任务数有56个。 uC/OS-II提供了任务管理的各种函数调用,包括创建任务,删除任务,改变任务的优先级,任务挂起和恢复等。 系统初始化时会自动产生两个任务:一个是空闲任务,它的优先级最低,该任务仅给一个整形变量做累加运算;另一个是系统任务,它的优先级为次低,该任务负责统计当前cpu的利用率。 在系统初始化完毕后启动任务时必须创建一份用户任务,也就是说必须有一个应用程序(用户任务,使用应用程序对于我们经常使用Windows用户容易接受一些。呵呵),否则系统会崩溃。当然还有一些其他的要求,咱们后续再说,下面简要概述一下任务管理相关的函数 1:建立任务OSTaskCreat()/OSTaskCreatExt() 如果想让UCOS管理用户的任务,必须先建立任务。可以通过将任务的地址和其他参数传递到以下两个函数之一来建立任务。当调用OSTaskCreat()时,需要四个参数: OSTaskCreate(void(*task)(void*pd),void*pdata,OS_STK*ptos,INTU prio) Task:是指向任务代码的指针,pdata:是任务开始执行是,传递给任务的参数的指针,ptos:是分配给任务的堆栈的栈顶指针,prio是分配给任务的优先级。 也可以用OSTaskCreatExt(),不过该函数需要9个参数,前四个参数与OSTaskCreat()一样,例如: INT8U OSTaskCreateExt(void(*task)(void *pd),void *pdata,OS_STK *ptos, INT8U prio, INT16U id, OS_STK *pbos, OS_STK *pbos, OS_STK *pbos, INT16U opt) id参数为要建立的任务创建一个特殊的标识符。pbos是指向任务的堆栈栈底的指针,用于堆栈的检验。stk _size用于指定堆栈成员数目的容量。pext是指向用户附加的数据域的 指针,用来扩展任务的OS_TCB。opt用于设定OSTaskCreateExt()的选项,指定是否允许堆栈检验,是否将堆栈清零,任务是否要进行浮点操作等等。 2:任务堆栈OS_STK() 每个任务都有自己的堆栈,堆栈必须申明为OS_STK类型,并且由连续的内存空间组成。可以静态分配堆栈空间,也可以动态分配堆栈空间。 3:堆栈检验OSTaskStkChk() 有时确定任务实际需要的堆栈空间的大小是很有必要的,因为这样就可以避免为任务分配过多的堆栈空间,从而减少应用程序代码所需的RAM空间。 4:删除任务OSTaskDel() 有时需要删除任务,删除任务,是说任务返回并处于休眠态,并不是说任务的代码被删除了,只是任务的代码不再被UCOS调用。删除任务前应保证所删任务并非空闲任务。 5:请求删除任务OSTaskDelReq() 有时,任务会占用一些内存缓冲或信号量一类的资源。这时,假如另一个任务试图删除该任务,这些被占用的资源就会因为没有被释放而丢失。在这种情况下,需想办法拥有这些资源的任务在使用完资源后先释放资源,再删除自己。 6:改变任务的优先级OSTaskChangePrio() 在建立任务时,会分配给任务一个优先级。在程序运行期间,可以通过调用该函数改变任务的优先级。也就是说,UCOS允许动态的改变任务的优先级。 7:挂起任务OSTaskSuspend() 任务挂起是一个附加功能,也就是说,如果任务在被挂起的同时也在等待延迟时间到,那么,需要对任务做取消挂起的操作,并且等待延迟时间到,任务才能转让就绪状态。任务可以挂起自己或者其他任务。 8:恢复任务OSTaskResume() 挂起的任务只有通过该函数才能被恢复。 9:获得任务的信息OSTaskQuery() 通过调用该函数,来获得自身或其他应用任务的信息 时间管理 uC/OS-II的时间管理是通过定时中断来实现的,该定时中断一般为10毫秒或100毫秒发生一次(这个时间片段是OS的作者推荐的,大家可以参考邵贝贝翻译的《嵌入式实时操作系统ucos-II》这本书),时间频率取决于用户对硬件系统的定时器编程来实现。中断发生的时间间隔是固定不变的,该中断也成为一个时钟节拍。这里隐含的意思就是你选择的芯片如果想使用UCOS系统,前提条件一定要有一个Timer。 uC/OS-II要求用户在定时中断的服务程序中,调用系统提供的与时钟节拍相关的系统函数, 例如中断级的任务切换函数,系统时间函数。 uCOS时间管理的相关函数 1:任务延迟函数OSTimeDly() Ucos提供一个可以被任务调用而将任务延时一段特定时间的功能函数,即OSTimeDly().任务调用OSTimeDly()后,一旦规定的时间期满或者有其他的任务通过调用OSTimeDlyResume() 取消了延时,他就会进入就绪状态。只有当该任务在所有就绪态任务中具有最高的优先级,它才会立即运行。 2:按时,分,秒延时函数OSRimeDLyHMSM() 与OSTimeDly()一样,调用OSRimeDlyHMSM()函数也会是UCOS进行一次任务调度,并且执行下一个优先级最高的就绪任务。当OSTimeDlyHMSM()后,一旦规定的时间期满,或者有OSTimeDlyResume(),它就会马上处于就绪态。同样,只有当该任务在所有就绪态任务中具有最高的优先级,他才开始运行。 3:恢复延时的任务OSTimeDlyResume() 延时的任务可以不等待延时的期满,而是通过其他任务取消延时而使自己处于就绪态,可以通过该函数来实现,实际上,OSTimeDlyResume()也可以唤醒正在等待的事件。 4:系统时间OSTimeGet()和OSTimeSet() 内存管理 在ANSI C中是使用malloc和free两个函数来动态分配和释放内存。例如在Linux系统中就是这样。但在嵌入式实时系统中,多次这样的操作会导致内存碎片,因为嵌入式系统尤其是uCOS是实地址模式,这种模式在分配任务堆栈时需要整块连续的空间,否则任务无法正确运行。且由于内存管理算法的原因,malloc和free的执行时间也是不确定。这点是实时内核最大的矛盾。 基于以上的原因uC/OS-II中把连续的大块内存按分区管理。每个分区中包含整数个大小相同的内存块,但不同分区之间的内存快大小可以不同。用户需要动态分配内存时,系统选择一个适当的分区,按块来分配内存。释放内存时将该块放回它以前所属的分区,这样能有效解决碎片问 快递公司问题件快递公司问题件货款处理关于圆的周长面积重点题型关于解方程组的题及答案关于南海问题 ,同时执行时间也是固定的。 同时uCOS-II根据以上的处理封装了适合于自己的动态内存分配函数OSMemGet()和OSMemPut(),但是使用这两个函数动态分配内存前需要先创建内存空间,也就是第二段咱们介绍的内存分块。呵呵,不罗嗦了,具体的关于内存管理的函数如下: 内存控制块的数据结构 Typedef struct {void *osmemaddr ;指向内存分区起始地址的指针。 Void *osmemfreelist ;指向下一个空余内存控制块或者下一个空余内存块的指针, Int32u osmemblksize ;内存分区中内存块的大小,是建立内存分区时定义的。 Int32u osmemnblks ;内存分区中总的内存块数量,也是建立该内存分区时定义的。 Int32u osmemnfree ;内存分区块中当前获得的空余块数量。 }os_mem; 1;建立一个内存分区,OSMemCreate() 2:分配一个内存块,OSMemGet() 应用程序通过调用该函数,从已经建立的内存分区中申请一个内存块。该函数唯一的参数是指向特定内存分区的指针。 3:释放一个内存块,OSMemPut() 当应用程序不再使用一个内存块时,必须及时的把它释放,并放回到相应的内存分区中,这个操作就是通过调用该函数实现的。 4:查询一个内存分区的状态,OSQMemQuery()。 任务间通信与同步 对一个多任务的操作系统来说,任务间的通信和同步是必不可少的。uC/OS-II中提供了4种同步对象,分别是信号量,邮箱,消息队列和事件。所有这些同步对象都有创建,等待,发送,查询的接口用于实现进程间的通信和同步。 对于这4种同步对象将在后面一一讨论。 任务调度 uC/OS-II 采用的是可剥夺型实时多任务内核。可剥夺型的实时内核在任何时候都运行就绪了的最高优先级的任务。 uC/os-II的任务调度是完全基于任务优先级的抢占式调度,也就是最高优先级的任务一旦处于就绪状态,则立即抢占正在运行的低优先级任务的处理器资源。为了简化系统设计,uC/OS-II规定所有任务的优先级不同,因为任务的优先级也同时唯一标志了该任务本身。 UCOS的任务调度在一下情况下发生: 1) 高优先级的任务因为需要某种临界资源,主动请求挂起,让出处理器,此时将调度就绪状态的低优先级任务获得执行,这种调度也称为任务级的上下文切换。 2) 高优先级的任务因为时钟节拍到来,在时钟中断的处理程序中,内核发现高优先级任务获得了执行条件(如休眠的时钟到时),则在中断态直接切换到高优先级任务执行。这种调度也称为中断级的上下文切换。 这两种调度方式在uC/OS-II的执行过程中非常普遍,一般来说前者发生在系统服务中,后 者发生在时钟中断的服务程序中。 调度工作的内容可以分为两部分:最高优先级任务的寻找和任务切换。其最高优先级任务的寻找是通过建立就绪任务表来实现的。u C / O S 中的每一个任务都有独立的堆栈空间,并有一个称为任务控制块TCB(Task Control Block)的数据结构,其中第一个成员变量就是保存的任务堆栈指针。任务调度模块首先用变量OSTCBHighRdy 记录当前最高级就绪任务的TCB 地址,然后调用OS_TASK_SW()函数来进行任务切换。 第二章 搭建UCOS-II 2.52版的调试平台 在这一章中我们主要讨论UCOSII的源码调试环境,为了给大家一个共同的学习平台,我搜集整理了一写资料,就是以X86为平台,使用BC31(这个堪称骨灰级的编译器)来调试UCOSII源码。当然你也可以用BC45或更高版本的编译器,具体方法大同小异,我在此就不再啰嗦。 本章节的主要内容包括四点: 1、下载并安装BC31编译器 2、下载并安装UCOS-II2.52版本源代码 3、使用BC31编译UCOS-II源码 4、让OS的第一个任务RUN起来 接下来会在每个帖子中讨论一点。耐心等待哦~ 下载并安装BC31编译器 我在这里提供给大家这个骨灰级的编译器BC31.需要的可以下载。见附件(骨灰级编译器BC31)由于这个软件的比较大,分成两个压缩包。下班了,先到这里,回家再传附件~ 上传了好几次都没有上传成功。可能是我的网速太慢。如果有需要的坛友把邮箱留给我吧,我发邮箱去~ 让自己的第一个任务Run起来 前面已经给大家介绍了如何在PC机上调试UCOS,方法和需要的软件都介绍给大家了,相信有兴趣的朋友已经安装调试了,下面咱们就让自己的第一个任务在PC上Run起来。 OK,下面我就分步介绍建立自己的第一个任务 第一步:CopyC:\SOFTWARE\uCOS-II 目录 工贸企业有限空间作业目录特种设备作业人员作业种类与目录特种设备作业人员目录1类医疗器械目录高值医用耗材参考目录 下的EX1_x86L文件夹。作为我们的工程模板 第二步:修改工程模板的名字为:HelloEEWorld 第三部:按照咱们前面的《使用 BC31 工具编译 UCOS‐II 的源码过程 》修改配置文件; 第四步:修改Test.c文件,建立自己的第一个任务 具体的内容我就不再帖子上写了。大家可以参考附件HelloEEWorld.rar里面的Test.c文件。然后编译 OK,第一个任务就Run起来了,显示如下界面 关于UCOS任务的理解 UCOS的运行是基于任务运行的,为了能够好的使用UCOS我们先要对UCOS的任务的概念做一个理解 在学习,,,,任务前我们先对我们以前使用的模式做一个回顾,,前后台模式。 这种系统可称为前后台系统或超循环系统(Super-Loops)。应用程序是一个无限的循环,循环中调用相应的函数完成相应的操作,这部分可以看成后台行为(background)。中断服务程序处理异步事件,这部分可以看成前台行 foreground。后台也可以叫做任务级。前台也叫中断级。时间相关性很强的关键操作(Critical operation)一定是靠中断服务来保证的。因为中断服务提供的信息一直要等到后台程序走到该处理这个信息这一步时才能得到处理,这种系统在处理信息的及时性上,比实际可以做到的要差。这个指标称作任务级响应时间。最坏情况下的任务级响应时间取决于整个循环的执行时间。因为循环的执行时间不是常数,程序经过某一特定部分的准确时间也是不能确定的。进而,如果程序修改了,循环的时序也会受到影响。 这种系统是在我们上学时和做小项目时经常用到,很多工程师称这种方式为“裸奔”。哈哈~我大学毕业后的钱三年写的项目都是在裸奔。 UCOS-II是基于任务运行的。一个任务,也称作一个线程,是一个简单的程序,该程序可以认为 CPU 完全只属该程序自己。实时应用程序的设计过程,包括如何把问题分割成多个任务,每个任务都是整个应用的某一部分,每个任务被赋予一定的优先级,有它自己的一套 CPU 寄存器和自己的栈空间(如下图所示)。 可以这么理解,UCOS-II的每一个任务都有一个CPU,任务在运行时占用CPU的全部资源,同时拥有自己的一套寄存器,当任务执行完毕后(时间片到),他把自己的CPU寄存器所有内容保存到自己的堆栈中,同时把CPU让给别的任务,那么得到CPU使用权的任务把自己的CPU寄存器从自己的堆栈中放到真正的CPU寄存器中开始运行,就这样周而复始。 大家一定不要把任务的运行当成是函数的调用,这完全是两回事。这个我们到后面的任务调度时在细说。每个任务都是一个无限的循环。每个任务都处在以下 5种状态之一的状态下,这5种状态是休眠态, 就绪态、 运行态、 挂起态(等待某一事件发生)和被中断态(参见下图) 休眠态相当于该任务驻留在内存中,但并不被多任务内核所调度。就绪意味着该任务已经准备好, 可以运行了, 但由于该任务的优先级比正在运行的任务的优先级低, 还暂时不能运行。运行态的任务是指该任务掌握了 CPU 的控制权,正在运行中。挂起状态也可以叫做等待事件态WAITING,指该任务在等待,等待某一事件的发生, (例如等待某外设的 I/O 操作,等待某共享资源由暂不能使用变成能使用状态, 等待定时脉冲的到来或等待超时信号的到来以结束目前的等待,等等) 。最后,发生中断时,CPU提供相应的中断 服务,原来正在运行的任务暂不能运行,就进入了被中断状态。如下图表示μC/OS-?中一些函数提供的服务,这些函数使任务从一种状态变到另一种状态。 简单的我们可以把每一次任务的切换当成一次中断,这个中断不同于我们在使用前后台模式时的中断,那个中断是硬件中断,中断时需要保存的CPU寄存器是由硬件实现的,而在UCOS中的任务切换是软中断,CPU保存了必要的寄存器后在切换时系统会在保存任务使用的寄存器。 补充知识-可剥夺型内核和不可剥夺型内核 不可剥夺型内核 不可剥夺型内核要求每个任务自我放弃CPU 的所有权。 不可剥夺型调度法也称作合作型多任务,各个任务彼此合作共享一个 CPU。异步事件还是由中断服务来处理。中断服务可以使一个高优先级的任务由挂起状态变为就绪状态。 但中断服务以后控制权还是回到原来被中断了的那个任务,直到该任务主动放弃 CPU 的使用权时,那个高优先级的任务才能获得 CPU 的使用权。 不可剥夺型内核允许每个任务运行,直到该任务自愿放弃 CPU的控制权。中断可以打入运行着的任务。 中断服务完成以后将 CPU 控制权还给被中断了的任务。任务级响应时间要大 大好于前后系统,但仍是不可知的,商业软件几乎没有不可剥夺型内核。 不可剥夺型内核的工作过程见下图: 可剥夺型内核 当系统响应时间很重要时,要使用可剥夺型内核。因此,μC/OS-?以及绝大多数商业上销售的实时内核都是可剥夺型内核。 最高优先级的任务一旦就绪, 总能得到CPU 的控制权。当一个运行着的任务使一个比它优先级高的任务进入了就绪态, 当前任务的CPU使用权就被剥夺了,或者说被挂起了,那个高优先级的任务立刻得到了 CPU的控制权。如果是中断服务子程序使一个高优先级的任务进入就绪态,中断完成时,中断了的任务被挂起,优先级高的那个任务开始运行。使用可剥夺型内核,最高优先级的任务什么时候可以执行,可以得 到 CPU的控制权是可知的。使用可剥夺型内核使得任务级响应时间得以最优化。 可剥夺型内核的工作过程是这样的: UCOS-II 任务调度 任务调度是内核的主要职责之一,就是要决定该轮到哪个任务运行了。多数实时内核是基于优先级调度法的,UCOS也不例外。每个任务根据其重要程度的不同被赋予一定的优先级。基于优先级的调度法指,CPU总是让处在就绪态的优先级最高的任务先运行。然而,究竟何时让高优先级任务掌握CPU 的使用权,有两种不同的情况,这要看用的是什么类型的内核, 是不可剥夺型的还是可剥夺型内核。 上一次咱们已经介绍了可剥夺型内核和不可剥夺型内核的工作过程了。在此不再赘述~ 当多任务内核决定运行另外的任务时,它保存正在运行任务的当前状态,即CPU寄存器中的全部内容。这些内容保存在任务的当前状况保存区,也就是任务自己的栈区之中,上一次讨论的内容中有这个图示。入栈工作完成以后,就是把下一个将要运行的任务的当前状况从该任务的栈中重新装入 CPU 的寄存器,并开始下一个任务的运行。这个过程叫做任务切换。任务切换过程增加了应用程序的额外负荷。CPU的内部寄存器越多,额外负荷就越重。做任务切换所需要的时间取决于CPU有多少寄存器要入栈。实时内核的性能不应该以每秒钟能做多少次任务切换来评价。而是要看OS总的关中断时间。总的关中断时间越短说明这个内核的实时性越好。这个问题在前面一个坛友的问题中我做了详细的描述,有兴趣的朋友可以在 UCOS这个版块找找这个帖子。 任务调度的算法有很多种。一种是基于优先级的。一种是基于时间片的。这两种算法在邵贝贝教授翻译的《UCOS-II内核详解》这本书中有详细解释。我就不再重复。如果坛子里有朋 友对此有什么不明白。可以在这里留言。咱们再讨论。 在这里给大家推荐三本学习ucos的必看书籍 1.(比较难买)嵌入式实时操作系统uc/os-II教程 西安电子科技大学出版 -----这本书 对UCOS的源代码分析的非常清楚 比作者原著 在某种程度上要好,这本书对关键的代码都给出了流程图~ 2.(比较好买)嵌入式实时操作系统uc/os-II原理与应用(第二版)任哲 北航出版的 这本书告诉你UCOS怎么用 对源代码分析的比较少 看完这本书你可以简单 应用UCOS 3.(比较难买) 这本书是绝对重量级的 基于嵌入式实时操作系统的程序设计 周慈航 北航 这本书教你UCOS的高级程序设计 是你迈向UCOS高手的必经之 路~~ 当然了 有人说 作者原著才是最经典的 但是我觉得 在原著之上的理解也许要更好吧 作者 的原著也必须要参考~~ UCOS-II的文件结构 前面我们对UCOS的基础知识做了了解,其中有些地方由于邵贝贝翻译的树上讲解的很少我就没有班门弄斧,大家可以结合那本书来看。有问题或不明白的在这里讨论,欢迎大家剔除 问题。 这次我们主要了解UCOS-II的文件结构。等对UCOS文件结构了解以后,我们就逐一的去讲 解其各章的重点和难点,达到在短时间内学会使用UCOS。 我们利用这张图片把UCOS的内部做一个解剖,我们可以清楚的看到UCOS内核的结构及层次,在这个图的最下面是我们使用的硬件,就是我们的移植平台,比如STM32F103XX系列的最小系统版、51最小系统版。呵呵,我本人觉得把UCOS移植到51上的意义不大。只是学习可以,使用我就不建议了~从图中我们可以知道,要想移植UCOS你的硬件平台必须具备一个定时器,也就是上图中的TIMER。这个TIMER是用来给UCOS提供时钟节拍的,相当于我们 人的心跳。如果没有这个TIMER,统统就无法运行。 再往上就是软件了,软件的第一层是我们移植的重点,这三个文件内主要包括一些与处理器 相关的代码,在后面我们我们再讲解移植过程的时候会详细的讨论到这三个文件。 在往上左侧就是系统内核源码的各个文件。有兴趣的坛友可以参考邵贝贝教授翻译的书进行深入学习,由于我在这里的主要任务是告诉大家如何使用UCOS,故不再过多的讲解源码部分,只是告诉大家如何使用即可。当然,如果你在研究过程中遇到问题可以拿出来和大家共 同讨论,右侧是系统的配置文件,相对比较简单,主要涉及到一些功能的裁剪。 最上层是我们的应用软件,相当于我们在电脑上使用的Office软件等,当然这里是你自己 的任务代码。 UCOS的任务及状态 任务的资源主要包括以下几部分,ECB控制块、任务堆栈、任务代码及与CPU共用的寄存器 和CPU的使用权 关于UCOS信号量 一:信号量的理解: (1)信号量可以分为两种:一种是二值信号量(0和1),一种是N值信号量(计数式信号 量)。 二值信号量的意思是可以有多少任务同时享用这个信号量。比如二值信号,就是只有1个任务可以使用。当有一个任务使用该信号量的时候,那么其他需要使用该信号量的任务就必须 等待,直到该任务释放该信号量。这种信号量可以看作一把钥匙。 对于N值信号量(计数式信号量),就是说可以同时有N-1个任务同时使用该信号量。对于 二值信号量,N=1。 (2)建立信号量的工作必须在任务级代码中或者多任务启动之前完成。 二:任务如何得到信号量的问题: 想得到信号量的任务,必须执行等待操作(pend)。在信号量的建立的时候,我们首先确定了该信号量可以被共享的资源数(N),并将其赋值给pevent->OSEventCnt。如果信号量有效(非0),即pevent->OSEventCnt>0,则信号量减1,任务得以继续运行。如果信号量无效,即pevent->OSEventCnt==0,则等待信号量的任务就被列入等待信号量的任务表中。许多内核允许定义等待超时,当等待时间超过了设定值,该信号量还是无效,则等待该信号量的任 务进入就绪态,准备运行,并返回出错代码(等待超时错误)。 三:任务对信号量的释放问题: 任务执行发信号(post)操作来释放信号量。如果没有任务等待信号量,那么信号量的值仅是简单的加1(则信号量大于0,有效);如果有任务等待该信号量,那么就会有另一个任 务进入就绪态,信号量的值就不加1。 之后,这个释放的信号量给那个等待中的任务,要看内核如何调度的。收到信号量的任务可 能是如下两者之一: ?等待任务中,优先级最高的;(uc/os-ii仅支持这种方式)。 ?最早开始等待信号量的任务(如果是按先进先出FIFO原则)。 四:信号量的有效与无效的问题: 信号量有效:信号量的计算器非0(.OSEventCnt~=0)。信号量有效表示任务对资源可用。 信号量无效:信号量的计算器为0。信号量无效表示任务对目前资源不可用,需要等待其他 另一个任务(或者中断服务子程序)发出该信号量(OSSemPost)。 五:关于信号量的三个重要函数: ?OSSemCreate() 创建一个信号量 (注:由任务或启动代码操作) 创建工作必须在任务级代码中或者多任务启动之前完成。功能只要是先获取一个事件控制块ECB,写入一些参数。其中调用了OS_EeventWaitListInt()函数,对事件控制块的等待任务 列表进行初始化。完成初始化工作后,返回一个该信号量的句柄(Handle)。 ?OSSemPend() 等待一个信号量 (注:只能由任务操作) 本函数应用于任务试图获得共享资源的使用权、任务需要与其他任务或中断同步及任务需要 等待特定事件发生的场合。 如果任务Task_A调用OSSemPend(),且信号量的值有效(非0),那么OSSemPend()递减信号量计数器(.OSEventCnt),并返回该值。换句话说,Task_A获取到共享资源的使用权了, 之后就执行该资源。 如果如果任务Task_A调用OSSemPend(),信号量无效(为0),那么OSSemPend()调用 OS_EventTaskWait()函数,把Task_A放入等待列表中。(等待到什么时候呢,要看 OSSemPost()(或者等待超时情况),由它释放信号量并检查任务执行权,见下资料) ?OSSemPost() 发出(释放)一个信号量 (注:由任务或中断操作) 本函数其中调用OS_EventTaskRdy()函数,把优先级最高的任务Task_A(在这假如是Task_A,另外假设当前调用OSSemPost()的任务是Task_B)从等待任务列表中去除,并使它进入就绪 态。然后调用OSSched()进行任务调度。如果Task_A是当前就绪态中优先级最高的任务, 则内核执行Task_A;否则,OSSched()直接返回,Task_B继续执行。 UCOS另类信号量--互斥信号量 在UCOS的信号量使用过程中,我们经常会用的是二值信号量,而在二值信号两种用的醉的情况就是互斥信号量。互斥信号是本身是一种二进制信号,具有超出uCOS,II提供的一般信号机制的特性。由于其特殊性,UCOS的作者将其独立成章,单独对待。组织了一套对于互斥信号量管理的单独函数。互斥信号量具有以下特点: 1) 降解优先级反转。 2) 实现 对资源的独占式访问(二值信号量)。 在应用程序中使用互斥信号是为了减少优先级翻转问题,当一个高优先级的任务需要的资源被一个低优先级的任务使用时,就会发生优先级翻转问题。为了减少优先级翻转问题,内核 可以提高的优先级任务的优先级,先于高优先级的任务运行,释放占用的资源。 为了实现互斥,实时内核需要具有支持在同一优先级具有多个任务的能力。不幸的是,UC/OS-II不允许在相同的优先级有多个任务,必须只有一个任务。但是我们有另外的方法解决这个问题。可以把需要资源的高优先级任务上面的一个任务使用Mutex保留,允许提高 的优先级任务的优先级。 举一个mutexes信号工作的例子,如l下面的程序所示。 其中有三个任务可以使用共同的资源,为了访问这个资源,每个任务必须在互斥信号 ResourceMutex上等待(pend),任务,1有最高优先级10,任务,2优先级为15,任务,3优先级为20,一个没有使用的正好在最高优先级之上的优先级,9用来作为优先级继承优先级。如main()所示,代码中(1)进行uC/OS-II初始化,并通过调用OSMutexCreate()代码中(2)创建了一个互斥信号。需要注意的是,OSMutexCreate()函数使用PIP最为参数。然后创 建三个任务代码中(3),启动uC/OS-II 代码中(4). 假设任务运行了一段时间,在某个时间点,任务#3最先访问了共同的资源,并得到了互斥信号,任务,3运行了一段时间后被任务,1抢占。任务,1需要使用这个资源,并通过调用OSMutexPend()企图获得互斥信号,这种情况下,OSMutexPend()会发现一个高优先级的任务需要这个资源,就会把任务,3的优先级提高到9,同时强迫进行上下文切换退回到任 务,3执行。 任务,3可以继续执行然后释放占用的共同资源。任务,3通过调用OSMutexPost()释放占用的mutex信号,OSMutexPost()会发现mutex被一个优先级提升的低优先级的任务占有,就会把任务,3的优先级返回到20。把资源释放给任务,1使用,执行 上下文切换到任务,1 ----------------------------------------------------------------- OS_EVENT *ResourceMutex; OS_STK TaskPrio10Stk[1000]; OS_STK TaskPrio15Stk[1000]; OS_STK TaskPrio20Stk[1000]; void main (void) { INT8U err; OSInit(); /* (1) */ /* ---------- 应用程序初始化 ---------- */ OSMutexCreate(9, &err); /* (2) */ OSTaskCreate(TaskPrio10, (void *)0, &TaskPrio10Stk[999], 10); /* (3) */ OSTaskCreate(TaskPrio15, (void *)0, &TaskPrio15Stk[999], 15); OSTaskCreate(TaskPrio20, (void *)0, &TaskPrio20Stk[999], 20); /* ---------- Application Initialization ---------- */ OSStart(); /* (4) */ } void TaskPrio10 (void *pdata) { INT8U err; pdata = pdata; while (1) { /* --------- 应用程序代码 ---------- */ OSMutexPend(ResourceMutex, 0, &err); /* ------- 访问贡献资源 ------ */ OSMutexPost(ResourceMutex); /* --------- 应用程序代码 ---------- */ } } void TaskPrio15 (void *pdata) { INT8U err; pdata = pdata; while (1) { /* ---------应用程序代码 ---------- */ OSMutexPend(ResourceMutex, 0, &err); /* ------- 访问共享资源 ------ */ OSMutexPost(ResourceMutex); /* --------- 应用程序代码 ---------- */ } } void TaskPrio20 (void *pdata) { INT8U err; pdata = pdata; while (1) { /* ---------应用程序代码---------- */ OSMutexPend(ResourceMutex, 0, &err); /* -------访问共享资源------ */ OSMutexPost(ResourceMutex); /* ---------应用程序代码---------- */ } } 上面代码为互斥信号使用示例 uC/OS-II'互斥信号包含三个元素,一个flag表示当前mutex是否能够获得(0或1);一个priority表示使用这个mutex的任务,以防一个高优先级的任务需要访问mutex;还包 括一个等待这个mutex的任务列表。 为了启动uC/OS-II’s mutex服务,应该在OS_CFG.H中设置OS_MUTEX_EN=1。在使用一个互斥信号之前应该首先创建它,创建一个mutex信号通过调用OSMutexCreate()完成,mutex 的初始值总是设置为1,表示资源可以获得。 uC/OS-II提供了六种访问互斥信号量的操作 OSMutexCreate(), OSMutexDel(),OSMutexPend(), OSMutexPost(), OSMutexAccept() and OSMutexQuery(). 展示了任务和互斥信号量的关系。一个互斥信号量只能被任务访问。在以后的资料中使用钥匙符号表示互斥信号。钥匙符号表明互斥信号用来访问共享资源。没有钥匙就无法访问。只 有得到钥匙的任务才有资格访问共享资源 UCOS互斥信号量操作函数分析 //建立并初始化一个互斥型信号量(优先级继承优先级(PIP)、出错代码指针) OS_EVENT *OSMutexCreate (INT8U prio, INT8U *err) { #if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */ OS_CPU_SR cpu_sr; #endif OS_EVENT *pevent; if (OSIntNesting > 0) { /* See if called from ISR ... */ *err = OS_ERR_CREATE_ISR; /* can''t CREATE mutex from an ISR */ return ((OS_EVENT *)0); }//不能从ISR中建立,不允许在ISR中调用此函数 #if OS_ARG_CHK_EN > 0 if (prio >= OS_LOWEST_PRIO) { /* Validate PIP */ *err = OS_PRIO_INVALID; return ((OS_EVENT *)0); }//不合理的PIP #endif OS_ENTER_CRITICAL(); if (OSTCBPrioTbl[prio] != (OS_TCB *)0) { /* Mutex priority must nalready exist*/ //确认PIP没有被任何任务占用。OSTCBPrioTbl[ ]中的一个指向NULL的空指针指示//PIP 有效 OS_EXIT_CRITICAL(); /* Task already exist at priority ... */ *err = OS_PRIO_EXIST; /* ... inheritance priority */ //如果优先级存在 ,则出错。 return ((OS_EVENT *)0); } OSTCBPrioTbl[prio] = (OS_TCB *)1; /* Reserve the table entry */ //置非空指针,将这个优先级保留下来。 pevent = OSEventFreeList; /* Get next free event control block */ //从空余ECB中得到一块空的ECB。 if (pevent == (OS_EVENT *)0) { /* See if an ECB was available */ //看ECB是否可用 OSTCBPrioTbl[prio] = (OS_TCB *)0; /* No, Release the table entry */ //如果不可用,释放此优先级表入口 OS_EXIT_CRITICAL(); *err = OS_ERR_PEVENT_NULL; /* No more event control blocks */ return (pevent); } OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr; /* Adjust the free list //如果可用,重新调整事件控制块的表头 OS_EXIT_CRITICAL(); pevent->OSEventType = OS_EVENT_TYPE_MUTEX; //将其标记为互斥型信号量 pevent->OSEventCnt = (prio << 8) | OS_MUTEX_AVAILABLE;/* Resource is available */ // (#define OS_MUTEX_AVAILABLE 0x00FF) //mutex为有效值,同时将PIP保存起来。值得注意的是,事件计数器.OSEventCnt //在此处的用法不同,高八位用于保存PIP的值,低侂位在资源无任务占用 //时的值为0xff,有任务占用时为占用mutex任务的优先级。这个避免了增加额 //外的空间,节约对RAM的占用量 pevent->OSEventPtr = (void *)0; /* No task owning the mutex */ //消息正在初始化,所以没有等待这个mutex的任务 OS_EventWaitListInit(pevent);//初始化事件等待列表 *err = OS_NO_ERR; return (pevent); } PIP是该函数的参数,指定优先级继承优先级。当发生优先级反转时,将占用该mutex的任 务的优先级太高到PIP。 UCOS互斥信号量操作函数分析 等待(申请)一个互斥信号量:OSMutexPend() 关键代码剖析: void OSMutexPend (OS_EVENT *pevent, INT16U timeout, INT8U *err) { //不得在中断中调用该函数 if (OSIntNesting > 0) { *err = OS_ERR_PEND_ISR; return; } OS_ENTER_CRITICAL(); //信号量可用 if ((INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8) == OS_MUTEX_AVAILABLE) { pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8; //将计数器低8为置成占用该mutex的任务(当前任务)的优先级。 pevent->OSEventCnt |= OSTCBCur->OSTCBPrio; //在mutex中保存占用信号量的任务:修改该mutex的OSEventPtr ,使其指向当前 任务 pevent->OSEventPtr = (void *)OSTCBCur; OS_EXIT_CRITICAL(); //信号量可用,正常返回。 *err = OS_NO_ERR; return; } //信号量不可用:即已被占用 //从该信号量中获得PIP pip = (INT8U)(pevent->OSEventCnt >> 8); //从该信号量中获得占用该信号量的任务的优先级。 mprio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8); //从信号量中获得占用该信号量的任务 ptcb = (OS_TCB *)(pevent->OSEventPtr); /* 如果原先占用该mutex的优先级比提出申请该mutex的任务的优先级低 (mprio > OSTCBCur->OSTCBPrio),则提升原任务的优先级至PIP */ if (ptcb->OSTCBPrio != pip && mprio > OSTCBCur->OSTCBPrio) { if ((OSRdyTbl[ptcb->OSTCBY] & ptcb->OSTCBBitX) != 0x00) { if ((OSRdyTbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBitX) == 0x00) { OSRdyGrp &= ~ptcb->OSTCBBitY; } //若原任务已就绪,则将其从就绪表中删除,并置就绪标志rdy rdy = TRUE; } else { rdy = FALSE; } //修改优先级,及相关参数 ptcb->OSTCBPrio = pip; ptcb->OSTCBY = ptcb->OSTCBPrio >> 3; ptcb->OSTCBBitY = OSMapTbl[ptcb->OSTCBY]; ptcb->OSTCBX = ptcb->OSTCBPrio & 0x07; ptcb->OSTCBBitX = OSMapTbl[ptcb->OSTCBX]; //如果原任务是就绪的,则继续让新的优先级就绪 if (rdy == TRUE) { OSRdyGrp |= ptcb->OSTCBBitY; OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX; } OSTCBPrioTbl[pip] = (OS_TCB *)ptcb; } //让提出申请的任务先等待(从就绪表中删除,如mutex的等待队列)„„„ OSTCBCur->OSTCBStat |= OS_STAT_MUTEX; OSTCBCur->OSTCBDly = timeout; OS_EventTaskWait(pevent); OS_EXIT_CRITICAL(); //执行任务切换(如果原来低优先级的任务优先级被抬高了,则该任务将被执行) OS_Sched(); OS_ENTER_CRITICAL(); //提出申请的任务被唤醒继续执行 if (OSTCBCur->OSTCBStat & OS_STAT_MUTEX) { //1)由于等待超时被定时器唤醒 OS_EventTO(pevent); OS_EXIT_CRITICAL(); *err = OS_TIMEOUT; return; } /* 2)原先占用mutex的任务执行完成释放了mutex 并唤醒了等待该mutex的最高优先级的任务 */ OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; OS_EXIT_CRITICAL(); *err = OS_NO_ERR; } TOP? 释放一个互斥信号量 释放一个互斥信号量:OSMutexPost() 关键代码剖析: INT8U OSMutexPost (OS_EVENT *pevent) { //不得在中断中调用该函数 if (OSIntNesting > 0) { return (OS_ERR_POST_ISR); } OS_ENTER_CRITICAL(); //从该信号量中获得PIP pip = (INT8U)(pevent->OSEventCnt >> 8); /* 从该信号量中获得占用该信号量的任务的优先级。 在OSEventCnt中的低8位保存占用mutex的任务的原始优先级, 不随优先级的提高而改变。 */ prio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8); /* 确认释放mutex的任务确实是占用mutex的任务自身。 占用/申请mutex的任务的优先级可能是pip(被提高),也可能是原先任务的优先级。 */ if (OSTCBCur->OSTCBPrio != pip && OSTCBCur->OSTCBPrio != prio) { OS_EXIT_CRITICAL(); //若释放mutex的任务非占用/申请的任务,则返回错误信息。 return (OS_ERR_NOT_MUTEX_OWNER); } //若当前释放mutex的任务的优先级为pip,则需将该任务的优先级降到原来水平 if (OSTCBCur->OSTCBPrio == pip) { //首先将pip从就绪表删除 if ((OSRdyTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0) { OSRdyGrp &= ~OSTCBCur->OSTCBBitY; } //将任务优先级修改为原始优先级,并修改相关参数 OSTCBCur->OSTCBPrio = prio; OSTCBCur->OSTCBY = prio >> 3; OSTCBCur->OSTCBBitY = OSMapTbl[OSTCBCur->OSTCBY]; OSTCBCur->OSTCBX = prio & 0x07; OSTCBCur->OSTCBBitX = OSMapTbl[OSTCBCur->OSTCBX]; //将修改优先级后的任务重新如就绪表 OSRdyGrp |= OSTCBCur->OSTCBBitY; OSRdyTbl[OSTCBCur->OSTCBY] |= OSTCBCur->OSTCBBitX; OSTCBPrioTbl[prio] = (OS_TCB *)OSTCBCur; } OSTCBPrioTbl[pip] = (OS_TCB *)1; //若mutex的等待列表不空,唤醒等待列表中最高优先级的任务,并将mutex分配给它 if (pevent->OSEventGrp != 0x00) { /* 唤醒等待列表中最高优先级的任务(从mutex等待列表中删除,使其入就绪表), 清除等待任务的OS_STAT_MUTEX标志,并返回其优先级prio */ prio = OS_EventTaskRdy(pevent, (void *)0, OS_STAT_MUTEX); pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8; //将mutex分配给新任务:置OSEventCnt为prio pevent->OSEventCnt |= prio; //在mutex中保存占用信号量的任务 pevent->OSEventPtr = OSTCBPrioTbl[prio]; OS_EXIT_CRITICAL(); //任务切换(如果唤醒的任务优先级比当前任务高,则使唤醒的任务得到运行) OS_Sched(); return (OS_NO_ERR); } //mutex的等待列表为空,即该mutex可用:置mutex可用标志及占用任务指针。 pevent->OSEventCnt |= OS_MUTEX_AVAILABLE; pevent->OSEventPtr = (void *)0; OS_EXIT_CRITICAL(); return (OS_NO_ERR); }
本文档为【[信息与通信]ucOS简要教程】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_963767
暂无简介~
格式:doc
大小:237KB
软件:Word
页数:0
分类:生活休闲
上传时间:2017-10-16
浏览量:25