版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第15章嵌入式操作系统μC/OS-II-2-本章目标了解嵌入式系统程序设计特点熟悉嵌入式系统程序结构掌握模块化程序设计方法掌握嵌入式操作系统概念了解μCOS-II特点掌握μCOS-II任务特点和优先级掌握μCOS-II运行机制掌握μCOS-II任务同步与通信-3-嵌入式系统程序设计
嵌入式微处理器的处理能力与PC机中的通用CPU相比存在很大差距,且嵌入式系统受功耗、成本和体积等因素的较多制约。所以嵌入式系统对程序运行的效率、存储空间和执行时间要求更为苛刻。嵌入式系统的程序设计与PC机的程序设计差别较大,有其自身的特点。本章将介绍嵌入式系统程序设计的特点和结构、嵌入式操作系统的概念和嵌入式操作系统μC/OS-II等内容。-4-嵌入式系统程序设计--嵌入式系统程序设计特点
嵌入式系统程序设计,就是针对嵌入式系统所设计的程序。嵌入式系统的程序设计,从某种意义上来说,与PC机的程序设计是不同的两个领域。嵌入式系统
从嵌入式系统的构成上来看,嵌入式系统是集软硬件于一体的、可独立工作的计算机系统。嵌入性、专用性和计算机系统是嵌入式系统的三个基本要素。-5-嵌入式系统程序设计--嵌入式系统程序设计特点
编程语言
嵌入式系统程序常用的编程语言有两类:汇编语言和C语言,不管用什么语言和编译器,最终生成的都是机器语言以便使微处理器识别和执行。-6-嵌入式系统程序设计--嵌入式系统程序设计特点
嵌入式系统程序特点嵌入式系统程序与PC机程序相比,有如下特点:受硬件的限制,嵌入式程序的规模一般都不大,代码也比较紧凑。较多的直接对硬件进行操作,硬件相关度比较高。在中断处理等应用中,对实时性要求较高。嵌入式系统一般是面向专业领域的应用,所以程序的专用性较高,通用性不强。嵌入式程序需要有专用的开发工具和环境。嵌入式程序需要考虑程序的可靠性和健壮性。-7-嵌入式系统程序设计--嵌入式系统程序结构
所谓嵌入式系统程序的结构,就是程序基本的架构和组织形式。一个典型的嵌入式系统程序,如图15-1所示。此结构包括两部分:初始化程序和主程序。初始化程序:负责系统复位之后对系统时钟,相关功能模块进行初始化设置,以及主程序开始前的相关准备工作。典型的主程序是一个无限循环体:周而复始的轮流执行相关功能模块,或者有选择的执行某些功能,甚至干脆停在原地执行毫无意义的指令,等待中断或者定时的到来。
-8-嵌入式系统程序设计--嵌入式系统程序结构
一般情况下,嵌入式程序有三种结构:顺序调度结构、前后台结构、分时调度结构。1.顺序调度程序结构系统运行后按一个预定的顺序依次执行一系列的功能模块,循环不止。这种结构的程序常见于各类无人值守的MCU系统,一般只需要按照顺序按部就班的执行。功能模块的触发方式即主程序在什么条件下调用各个功能模块。大体可以分为三种:接力方式:上一个功能模块执行完成后,无条件的触发下一个功能模块。定时方式:预先安排好每个功能模块的运行时刻表,由系统时钟来顺序触发对应的功能模块。外部信息触发方式:当外部信息满足预定条件时触发相应的功能模块后两种方式可以称之为事件驱动机制,给每个功能模块安排“使能标志”,通过使能标志来触发该功能模块代表的事件。在每次进入功能模块时,先判断该模块是否满足执行条件,如果满足则执行,同时将使能标志清零,否则直接返回即可。-9-嵌入式系统程序设计--嵌入式系统程序结构
时间片与分时调度机制
在系统任务较多时,为了保证每个任务都能得到系统事件,可以使用分时调度机制。将整个系统时间分成若干个时间片,并用ID进行标识。每个时间片内执行一个功能模块。甚至可以将整个程序中的所有任务都纳入到分时调度机制中。-10-嵌入式系统程序设计--嵌入式系统程序结构
中断与前/后台程序结构
前述的在主程序中实现的事件驱动机制,应付一般的任务已经足够,但是遇到紧急突发事件,无法保证及时响应。此时可以将实时性要求高的事件放到中断(后台)中响应,把实时性要求较低的任务(按键扫描、显示刷新等)交给主程序(前台)去调度。这样就形成了前/后台的程序结构。-11-嵌入式系统程序设计--模块化程序设计
模块化程序设计方法,即面向“模块”的程序设计方法,其基本思想是,在结构上将软件系统划分为若干功能“模块”或实体。1.模块设计原则模块相对独立性:模块的功能单一,函数处理的任务明确,函数的定义相互独立,一个函数不从属于另一个函数,但可以相互调用。模块之间的关系简单:模块之间没有过多的相互作用,只通过数据传递发生联系,并且函数传递的数据个数越少越好。模块内数据的局部化:模块内使用的数据具有独立性,一个模块不允许使用其它模块的数据,且一个模块的数据也不能影响其它模块中的数据。模块大小适中。模块分解层次清楚。-12-嵌入式系统程序设计--模块化程序设计
嵌入式程序设计中常用模块如下:初始化模块:当系统上电或复位后对CPU本身以及系统外围电路进行一系列的设置。全局变量模块:用于定义系统需要的全局变量。延时模块:用于定义系统需要的延时函数。可以包含几个不同延时长度的函数。按键输入与处理模块:用来定义按键扫描函数和按键处理函数。显示模块:通常用来定义系统的显示函数。其它功能模块。-13-嵌入式系统程序设计--模块化程序设计
嵌入式操作系统
随着半导体技术的发展,嵌入式处理器的硬件资源逐渐丰富,运行速度不断提高,存储空间越来越大,应用环境变得复杂。此时,仅仅基于硬件直接编写程序变得越来越困难。在这个背景下,嵌入式操作系统应运而生。1.嵌入式操作系统运行在嵌入式系统硬件平台上,对整个系统硬件所操作的部件、装置等资源进行统一协调、指挥和控制的系统软件,就叫做嵌入式操作系统。其主要特点如下:微型化:嵌入式系统的存储器通常不会太大,硬件资源也有限。可裁剪性:嵌入式操作系统运行的环境是多种多样的,功能要求也各不相同实时性和可靠性:目前,嵌入式系统广泛应用于生产过程控制、数据采集、传输通信等场合,这些应用的共同特点就是要求系统能快速响应事件和具备一定的抗干扰能力。易移植性:为了适应多种多样的硬件平台,嵌入式操作系统应可以在不做大量修改的情况下稳定的运行在不同平台之上。-14-嵌入式系统程序设计--模块化程序设计
实时操作系统所谓实时,有立即、及时的意思。如果操作系统能使嵌入式系统及时响应外部事件的请求,并能及时控制所有硬件设备与实时任务协调运行,且能在一个规定(Deadline)的时间内完成对事件的处理这种操作系统就是一个实时操作系统(RealTimeOperationSystem,ROTS)。-15-嵌入式系统程序设计--模块化程序设计
嵌入式操作系统现状
目前比较常见的嵌入式操作系统有WindRiver公司的VxWorks、pSOS,微软公司的WindowsCE,QNX公司的QNXOS,在手持设备嵌入式操作系统中三足鼎立的Plam,WinCE、EPOC等,但使用这些商业操作系统需要高昂的费用。面对这种情况,一些组织和个人也开发了一些免费的、源码开放的操作系统在互联网发布,比较有名的有μCLinux和FreeRTOS。-16-μC/OS-II简介
μC/OS-II是由JeanJ.Labrosse于1992年编写的一个嵌入式多任务实时操作系统。最早这个系统叫做μC/OS1999年JeanJ.Labrosse推出了μC/OS-II,并在2000年得到了美国联邦航空管理局对用于商业飞机的、符合RTCADO-178B标准的认证,从而证明μC/OS-II具有足够的稳定性和安全性。μC/OS-II是一种可移植的,可植入ROM的,可裁剪的,抢占式的实施多任务操作系统内核,采用C语言和汇编语言来编写。其中绝大部分代码都是用C语言编写,只有极少部分与处理器密切相关的部分代码是用汇编语言编写。所以,用户只要做很少的工作就可以把它移植到各类8位、16位和32位嵌入式微处理器上。-17-μC/OS-II简介
注意,μC/OS-II不是免费软件,也不是开放源码的软件。μC/OS-II源代码仅可以作为个人和学校学习使用,所有和μC/OS-II直接和间接相关的商业目的行为,必须购买使用μC/OS-II及系列产品的商业授权。尽管如此,由于μC/OS-II的构思巧妙,结构简洁精练,可读性很强,同时又具备了实时操作系统的大部分功能,所以虽然它只是一个内核,但是通过对这个内核的学习,可以深入了解嵌入式实时操作系统的运行机制。μC/OS-II的文件结构并不复杂,为了便于移植,其绝大部分代码使用C语言编写,与硬件无关。只有少数文件需要根据移植环境进行编写。-18-μC/OS-II简介
μC/OS-II的文件结构以及与硬件的关系-19-μC/OS-II的时钟和中断
任何操作系统都要提供一个周期性的信号源,以供系统处理诸如延时、超时等与事件有关的事件,这个周期性的信号源叫做时钟。而任务在运行过程中,应内部或外部异步事件的请求终止当前任务,而去处理异步事件所要求的任务的过程,叫做中断。具体到STM32F107通常使用内置的SysTick(滴答时钟)定时器的定时中断,来产生这个时钟。-20-μC/OS-II的时钟和中断
--μC/OS-II的时钟μC/OS-II和大多数计算机系统一样,用硬件定时器(一般为SysTick)产生一个周期为毫秒的系统时钟。最小的时钟单位就是两次中断之间相间隔的时间,这个最小时钟单位叫做时钟节拍。硬件定时器以时钟节拍为周期定时的产生中断,该中断的服务程序通过调用函数OSTimeTick()来完成系统在每个时钟节拍是需要做的工作。-21-μC/OS-II的时钟和中断
--μC/OS-II的时钟以STM32F107为例要配置SYSTICK定时器每1ms产生一次中断,可以使用如下代码。【示例15-1】配置SYSTICK定时器
/*声明时钟类型变量*/ RCC_ClocksTypeDefRCC_Clocks; /*获取时钟频率*/ RCC_GetClocksFreq(&RCC_Clocks); /*设置SysTick定时器溢出值,每秒1000次溢出,即1ms一次*/ SysTick_Config(RCC_Clocks.HCLK_Frequency/1000);【函数15-1】SysTick_HandlervoidSysTick_Handler(void){ OSTimeTick();}-22-μC/OS-II的时钟和中断
--μC/OS-II的时间管理由于嵌入式系统的任务是一个无限循环,并且μC/OS-II还是一个抢占式内核所以为了使高优先级别的任务不至于独占CPU,可以给其它任务优先级别较低的任务获得CPU使用权的机会。μC/OS-II规定,除了空闲任务之外的所有任务,必须在任务中合适的位置调用系统提供的函数OSTimeDly()。以便使当前任务的运行延时(暂停)一段时间让出CPU的使用权,并进行一次任务调度。-23-μC/OS-II的时钟和中断
--μC/OS-II的时间管理函数OSTimeDly()的函数原型如下:该函数需要一个16位无符号的参数ticks,该参数是以时钟节拍数为单位的延时时间。μC/OS-II还提供了一个可以用时、分、秒为参数的任务的延时函数OSTimeDlyHMSM(),该函数原型如下。【函数15-2】OSTimeDly()/*系统延时函数,以节拍为单位*/VoidOSTimeDly(INT16Uticks);
【函数15-3】OSTimeDlyHMSM()/*系统延时函数,以时分秒毫秒为单位*/INT8UOSTimeDlyHMSM(INT8Uhours,INT8Uminutes,INT8Useconds,INT16Ums)
-24-μC/OS-II的时钟和中断
--μC/OS-II的时间管理临界段
在μC/OS-II中,那些不希望被中断的代码段叫做临界段。从代码上来看,处在关中断和开中断之间的代码段就是临界段。由于各厂商生产的CPU和C编译器的关中断和开中断的方法及指令不尽相同,为了增加μC/OS-II的可移植性,采用了OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()两个宏封装了与系统硬件相关的关中断和开中断指令。特别需要注意,不要在临界段中调用μC/OS-II提供的功能函数,以免系统崩溃。【示例15-2】临界段代码OS_ENTER_CRITICAL();……/*临界段内的程序代码*/OS_EXIT_CRITICAL();
-25-μC/OS-II的时钟和中断
--μC/OS-II的时间管理μC/OS-II的中断过程
μC/OS-II系统也能够响应各种中断,其响应中断的过程如下:系统接收到中断请求后,如果这时CPU处于中断允许状态,系统就会终止正在运行的当前任务,而按照中断向量的指向转而去运行中断服务子程序。当终端服务子程序的运行结束后,系统会根据情况返回到被终止的任务继续运行,或者转向运行另一个具有更高优先级别的就绪任务。注意,中断服务子程序运行结束后,并不一定要继续运行被中断的任务。-26-μC/OS-II的时钟和中断
--μC/OS-II的时间管理μC/OS-II系统中断嵌套为了记录中断嵌套的层数,μC/OS-II定义了一个全局变量OSIntNesting。在编写μC/OS-II的中断服务程序时,全局变量OSIntNesting加1,用来记录中断嵌套的层数。而这个过程需要在临界段中完成。另一个函数OSIntExit()叫做退出中断服务函数,这个函数在从任务就绪表中查找到的最高就绪任务又不是被中断的任务的条件下将要进行任务调度,否则就返回被中断的服务子程序。
-27-μC/OS-II的时钟和中断
--μC/OS-II的时间管理综上所述,SysTick时钟节拍的中断服务函数,便是一个典型μC/OS-II的中断服务函数,其完整源码如下。【示例15-3】SysTick_Handler()/*系统节拍中断*/voidSysTick_Handler(void){ OS_CPU_SR cpu_sr; /*进临界段*/ OS_ENTER_CRITICAL(); /*统计中断嵌套*/ OSIntNesting++; /*出临界段*/ OS_EXIT_CRITICAL(); /*更新系统节拍*/ OSTimeTick(); /*结束中断服务函数,并调用一次中断级任务调度*/ OSIntExit();}
-28-μC/OS-II的任务
在设计一个较为复杂的应用程序时,通常把一个大型的任务分解成为多个小型任务在嵌入式系统中通过运行这些小型任务,最终达到完成大型任务的目的。这样可以有效降低开发难度,并且小型任务还可以封装、模块化以便移植和重复使用。-29-μC/OS-II的任务
--任务在μC/OS-II中,与上述小任务对应的程序就叫做“任务”,而μC/OS-II就是一个能对这些小任务的运行进行管理和调度的多任务操作系统。从代码上看,μC/OS-II的任务就是一个死循环函数。例如一个LED灯闪烁任务,源码如下。【示例15-4】led_task()staticvoidled_task(void*p_arg){ p_arg=p_arg; while(1) { /*低电平点亮LED4*/ GPIO_ResetBits(GPIOC,GPIO_Pin_9); /*调用系统函数,延时1秒*/ OSTimeDlyHMSM(0,0,1,0); /*高电平点亮LED4*/ GPIO_SetBits(GPIOC,GPIO_Pin_9); /*调用系统函数,延时1秒*/ OSTimeDlyHMSM(0,0,1,0); }}
-30-μC/OS-II的任务
--任务从任务的存储结构上看,μC/OS-II的任务由三部分组成:任务程序代码(函数):任务程序代码就是任务的执行部分。任务堆栈:任务堆栈则用来保存任务的工作环境。任务控制块:任务控制块就是关联了任务代码的程序控制块,他记录了任务的各个属性。μC/OS-II的任务有两种:系统任务:由操作系统系统提供的任务叫做系统任务,是为应用程序提供服务或为系统本身提供服务。用户任务:由应用程序设计者编写的任务。目前,μC/OS-II中最多可以含有64个任务(包括用户任务和系统任务)。
-31-μC/OS-II的任务
--任务的优先级μC/OS-II采用了按优先级抢占式规则,所以优先级的概念在μC/OS-II非常重要由于可以在μC/OS-II中创建最多64个任务,所以任务的优先级别最多有64级,每个级别都用一个整数数字来表示,即0、1、2、3……63。数字越小,优先级别越高。由于大多数应用程序中的任务小于64个,所以为了使用户可以设置所需要任务的实际数目,μC/OS-II在系统配置文件OS_CFG.H中定义了一个用来表示最低优先级别的常数OS_LOWEST_PRIO。另外,为了用户的方便,系统总是把最低优先级别OS_LOWEST_PRIO自动赋给空闲任务。把OS_LOWEST_PRIO-1优先级赋给统计任务。因此,用户可以使用的的任务总数减少两个,即OS_LOWEST_PRIO-1个。另外,μC/OS-II保留了最高和最低的四个优先级(包括空闲和统计任务),以便将来升级时使用。所以,用户实际最多使用的任务数量为64-8=56个。
-32-μC/OS-II的任务
--任务的状态μC/OS-II是按照系统中只有一个CPU来设计的。在这种系统中,一个具体时刻只会有一个任务占用CPU处在运行状态,而其它任务只能处在其它状态。μC/OS-II中的任务共有五种状态,根据具体情况如表15-1所示。任务的状态说明睡眠状态任务在没有被配置任务控制块或者被剥夺了任务控制块时的状态,叫做任务的睡眠状态就绪状态如果系统为任务配置了任务控制块且在任务就绪表中进行了就绪登记,则任务就具备了运行的充分条件,这是任务的状态叫做就绪状态。运行状态处于就绪状态的任务如果经调度器判断获得了CPU的使用权,则任务就进入了运行状态。任何时刻只能有一个任务处于运行状态,就绪的任务只有当所有优先级高于本任务的任务都转为等待状态时,才能进入运行状态。等待状态装在运行的任务,需要等待一段时间或需要等待一个事件发生在运行时,该任务就会把CPU的使用权让给其它任务而使任务进入等待状态。中断服务状态一个正在运行的任务一旦响应中断申请,就会终止运行二区执行中断服务程序,这时任务的状态叫做中断服务状态。-33-μC/OS-II的任务
--任务的状态在μCOS-II系统的管理下,一个任务可以在五个不同的状态之间发生转换。其转换关系如图15-3所示。-34-μC/OS-II的任务
--任务堆栈任务堆栈是任务的重要组成部分。所谓堆栈,就是在存储器中按数据“后进先出(LIFO)”的原则组织的连续存储空间。为了满足任务切换和响应中断时保存CPU寄存器中的内容及任务调用其它函数时的需要,每个任务都应该配有自己的堆栈。所有μC/OS-II任务的任务控制块中都含有一个指向该任务堆栈的指针。为了定义任务堆栈的方便,在文件OS_CPU.H中专门定义了一个数据类型OS_STK:typedefunsignedintOS_STK;//该类型长度为32位-35-μC/OS-II的任务
--任务控制块μC/OS-II用来记录任务堆栈指针、任务的当前状态、任务的优先级别等一些与任务管理有关的属性的表就叫做任务控制块。任务控制块相当于一个任务的身份证,系统就是通过任务控制块来感知和管理任务的,没有任务控制块的任务不能被系统承认和管理。μC/OS-II把系统所有任务的控制块链接为两条链表,并通过他们管理各个任务。-36-μC/OS-II的任务
--任务控制块任务控制块是一个结构类型数据。当用户应用程序调用OSTaskCreate()函数创建一个用户任务时,该函数就会对任务控制块中的所有成员赋予与该任务相关的数据。给用户任务分配任务控制块及对其进行初始化也是操作系统的职责。当应用程序调用函数OSTaskCreate()创建一个任务时,这个函数会调用系统函数OSTCBInit()来为任务控制块来进行初始化。该函数的主要任务如下:为被创建任务从空任务控制块链表获取一个任务控制块。用任务的属性对任务控制块各个成员进行赋值。把这个任务控制块连接入任务控制块链表。-37-μC/OS-II的任务
--任务调度μC/OS-II的任务调度思想是“近似的每时每刻让优先级最高的就绪任务处于运行状态”。在具体做法上,它在系统或用户任务调用系统函数及执行中断服务程序结束时来调用调度器,以确定应该运行的任务并运行它。在μC/OS-II中,任务调度由任务调度器来完成。任务调度器的主要工作有两项:一是在任务就绪表中查找具有最高优先级别的就绪任务;二是实现任务的切换。μC/OS-II有两种调度器:一种是任务级的调度器,另一种是中断级的调度器。任务级的调度器由函数OSSched()来实现,而中断级的调度器由函数OSIntExt()来实现。-38-μC/OS-II的任务
--任务建立根据嵌入式系统任务的工作特点,任务的执行代码通常是一个无限循环结构,并且在这个循环中可以响应中断,这种结构也叫做超循环结构。从程序设计的角度来看,一个μC/OS-II任务的代码就是一个C语言函数。为了可以传递各种不同类型的数据甚至函数,μC/OS-II把任务的参数定义成了一个void类型的指针。从程序代码的形式上来看,用户任务就是一个C语言函数,但这个函数不是由主函数main()调用的函数,在系统中它与main()处于平等的地位,它们何时被运行以及何时被终止是由操作系统来调度的。但要注意,main()毕竟是一个应用程序的主函数,是程序运行的入口点,所以虽然main()不用调用任务,但是要负责任务的创建并将这些任务交给系统,至于何时运行它们,则与主函数无关。-39-μC/OS-II的任务
--任务建立任务示例如下:其中,OSTaskCreate()是μC/OS-II提供的用来创建任务的函数,OSStart()是启动μC/OS-II的函数。系统被启动以后,任务就由操作系统来管理和调度。【示例15-6】任务voidMyTask1(boid*pdata){ while(1){…… /*用户任务1的具体代码*/}}voidMyTask2(boid*pdata){ while(1){…… /*用户任务2的具体代码*/}}voidMyTask3(boid*pdata){ while(1){…… /*用户任务3的具体代码*/}}voidmain(){ /*初始化μC/OS-II*/ OSInit(); // /*创建用户任务*/ OSTaskCreate(MyTask1,……); OSTaskCreate(MyTask2,……);OSTaskCreate(MyTask3,……); /*启动μC/OS-II*/ OSStart(); }-40-μC/OS-II的任务
--任务建立综上所述,在μC/OS-II中要建立一个任务,需要建立任务堆栈、分配任务优先级并且调用相应的任务建立函数,下述代码用于实现任务描述15.D.1,建立一个LED闪烁任务,源码如下。【描述15.D.1】任务建立/*声明80个字节的内存用于任务堆栈*/staticOS_STKled_task_stk[80];/*声明任务函数*/staticvoidled_task(void*p_arg); /*调用任务建立函数,任务堆栈栈顶指针为最后数组一个成员,优先级为6*/ OSTaskCreate(led_task, (void*)0, &led_task_stk[79], 6 );
-41-μC/OS-II任务的同步与通信
应用程序中的各个任务,必须通过彼此之间的有效合作,才能完成一项大规模的工作。因为这些任务经常需要互相无冲突地访问同一个共享资源,或者需要互相支持和依赖,甚至有时还要互相加以必要的制约,才能保证任务的顺利运行。因此,操作系统必须具有对任务的运行进行协调的能力,从而使任务之间可以无冲突、流畅的同步运行,而不至于导致灾难性的后果。-42-μC/OS-II任务的同步与通信
--任务间的同步为了实现各个任务之间的合作和无冲突的运行,在各任务之间建立一些制约关系。在多任务合作工作的过程中,操作系统应该解决两个问题:一个是任务间应该具有一种互斥关系,即对于某个共享资源,如果一个任务正在使用,则其它任务只能等待,等到该任务释放该资源后,等待的任务之一才能使用它;二是相关的任务在执行上要有先后次序,一个任务要等其它伙伴发来通知,或建立了某个条件后才能继续执行,否则只能等待。任务之间的这种制约性的合作运行机制叫做任务间的同步。-43-μC/OS-II任务的同步与通信
--事件μC/OS-II使用信号量、邮箱和消息队列这些中间环节来实现任务之间的通信。这些中间环节都统一被称作“事件”。μC/OS-II把任务发送事件、请求事件以及其它对事件的操作都定义成为全局函数,以供应用程序的所有任务来调用。事件的处理和存储依赖于事件控制块,事件控制块的数据结构是一个结构体,源码如下。【结构体15-1】OS_EVENTtypedefstructos_event{ /*事件类型*/INT8UOSEventType;/消息或消息队列指针*/void*OSEventPtr;/*信号量计数器*/INT16UOSEventCnt;/*等待事件的任务组*/INT8UOSEventGrp;/*等待事件表*/INT8UOSEventTbl[OS_EVENT_TBL_SIZE];}OS_EVENT;
-44-μC/OS-II任务的同步与通信
--事件其中,参数OSEventType可取的值,如表15-2所示。OSEventType的值说明OS_EVENT_TYPE_UNUSED未使用OS_EVENT_TYPE_MBOX消息邮箱OS_EVENT_TYPE_Q消息队列OS_EVENT_TYPE_SEM信号量OS_EVENT_TYPE_MUTEX互斥型信号量OS_EVENT_TYPE_FLAG标志-45-μC/OS-II任务的同步与通信
--信号量信号量是一种事件。使用信号量的最初目的,是为了给共享资源设立一个标志,该标志表示该共享资源被占用的情况。信号量由信号量计数器和等待任务表两部分组成,遵循以下规则:每当有任务申请信号量时,如果信号量计数器OSEventCnt的值大于0,则把OSEventCnt减1,并使任务继续运行。如果OSEventCnt的值为0,则会将任务列入任务等待表OSEventTbl[],而使任务处于等待状态。如果有正在使用信号量的任务释放了该信号量,则会在任务等待表中找出优先级别最高的的等待任务,并在使它就绪后调用调度器引发一次调度。如果任务等待表中已没有等待任务,则信号量计数器就只简单的加1。-46-μC/OS-II任务的同步与通信
--信号量1.创建信号量
在使用信号量之前,应用程序必须调用函数OSSemCreate()来创建一个信号量。函数OSSemCreate()的原型如下。参数cnt为信号量初值,函数的返回值为已创建的信号量的指针。【函数15-4】OSSemCreate()OS_EVENT*OSSemCreate(INT16Ucnt);
-47-μC/OS-II任务的同步与通信
--信号量2.请求信号量任务通过调用函数OSSemPend()请求信号量。函数OSSemPend()的原型如下。参数pevent是被请求信号量的指针,timeout为等待时限,设置为0时,则表明任务的等待无限长。如果函数调用成功,err为OS_NO_ERR。当一个任务请求信号量时,如果希望在信号量无效时不进入等待状态,而继续运行,则可以调用函数OSSemAccept()来请求信号量,函数OSSemAccept()原型如下:调用函数成功后,函数返回值为OS_NO_ERR。
【函数15-5】OSSemPend()voidOSSemPend(OS_EVENT*pevent,INT16Utimeout,INT8U*err);
【函数15-6】OSSemAccept()INT16UOSSemAccept(OS_EVENT*pevent);
-48-μC/OS-II任务的同步与通信
--信号量3.发送信号量
任务获得信号量,并在访问共享资源结束后,必须释放信号量。释放信号量也叫发送信号量,需要调用函数OSSemPost()。函数OSSemPost()原型如下。调用函数成功后,函数返回值为OS_NO_ERR。函数15-7】OSSemPost()INT8UOSSemPost(OS_EVENT*pevent);
-49-μC/OS-II任务的同步与通信
--信号量4.删除信号量如果应用程序不需要某个信号量,那么可以调用函数OSSemDel()来删除该信号量。该函数原型如下。其中参数opt用来指明信号量的删除条件,有两个参数值可选:OS_DEL_NO_PEND,当等待任务表中已没有等待任务时才删除信号量。OS_DEL_ALLWAYS,无论任务表中是否有等待任务都立即删除信号量。注意,信号量只能在任务中删除,而不能在中断服务程序中删除。
【函数15-8】OSSemDel()OS_EVENT*OSSemDel(OS_EVENT*pevent,INT8Uopt,INT8U*perr);
-50-μC/OS-II任务的同步与通信
--信号量5.查询信号量
任务可以调用函数OSSemQuery()随时查询信号量的当前状态,该函数原型如下。该函数第二个参数pdata是一个OS_SEM_DATA结构的指针,里面存储信号量的相关信息。因此,调用函数OSSemQuery()之前,需要定义一个OS_SEM_DATA结构类型的变量。【函数15-9】OSSemQuery()NT8UOSSemQuery(OS_EVENT*pevent,OS_SEM_DATA*p_sem_data);
-51-μC/OS-II任务的同步与通信
--信号量6.信号量实例下述程序代码用于实现任务描述15.D.2,创建一个信号量,在任务task1中每1秒发送一次,task2中等待信号量,一旦信号量有效则闪烁一次LED4。源码如下。
描述15.D.2】信号量实例/*声明信号量*/OS_EVENT*led_sem; /*初始化信号量*/led_sem=OSSemCreate(0);staticvoidtask1(void*p_arg){ p_arg=p_arg;
/*任务代码*/ while(1) { /*每秒发送一次信号量*/ OSTimeDlyHMSM(0,0,1,0); OSSemPost(led_sem); }}staticvoidbeep_task(void*p_arg){ INT8Uerr; p_arg=p_arg; while(1) { /*请求信号量有效后,点亮LED4300毫秒*/ OSSemPend(led_sem,0,&err); GPIO_ResetBits(GPIOC,GPIO_Pin_9); OSTimeDlyHMSM(0,0,0,300); GPIO_SetBits(GPIOC,GPIO_Pin_9); }}
-52-μC/OS-II任务的同步与通信
--互斥型信号量互斥型信号量是一个二值信号量。任务可以用互斥信号量来实现对共享资源的独占式处理。互斥型信号量除了具有普通信号量的机制外,还可以解决任务在使用独占资源出现的优先级翻转问题。1.创建互斥型信号量在使用互斥型信号量之前,应用程序必须调用函数OSMutexCreate()来创建一个互斥型信号量,该函数的原型如下。【函数15-10】OSMutexCreate()OS_EVENT*OSMutexCreate(INT8Uprio,INT8U*err);
-53-μC/OS-II任务的同步与通信
--互斥型信号量2.请求互斥型信号量任务通过调用函数OSMutexPend()请求互斥型信号量,该函数的原型如下。当一个任务请求互斥型信号量时,如果希望在互斥型信号量无效时不进入等待状态,而继续运行,则可以调用函数OSMutexAccept()来请求互斥型信号量,该函数的原型如下。【函数15-11】OSMutexPend()voidOSMutexPend(OS_EVENT*pevent,INT16Utimeout,INT8U*err);函数15-12】OSMutexAccept()INT16UOSMutexAccept(OS_EVENT*pevent);
-54-μC/OS-II任务的同步与通信
--互斥型信号量3.发送互斥型信号量
释放互斥型信号量也叫发送互斥型信号量,需要调用函数OSMutexPost(),该函数的原型如下。【函数15-13】OSMutexPost()INT8UOSMutexPost(OS_EVENT*pevent);
-55-μC/OS-II任务的同步与通信
--互斥型信号量4.删除互斥型信号量如果应用程序不需要某个互斥型信号量,那么可以调用函数OSMutexDel()来删除该互斥型信号量,该函数原型如下。函数15-14】OSMutexDel()OS_EVENT*OSMutexDel(OS_EVENT*pevent,INT8Uopt,INT8U*perr);
-56-μC/OS-II任务的同步与通信
--互斥型信号量5.查询互斥型信号量
任务可以调用函数OSMutexQuery()随时查询互斥型信号量的当前状态,该函数的原型如下。【函数15-15】OSMutexQuery()NT8UOSMutexQuery(OS_EVENT*pevent,OS_MUTEX_DATA*p_data);-57-μC/OS-II任务的同步与通信
--消息邮箱在多任务操作系统中,常常需要在任务与任务之间通过传递一个或多个数据的方式来进行通信,这种数据就叫做消息。可以在内存中创建一个存储空间作为该数据的缓冲区。如果把这个缓冲区叫做消息缓冲区,那么在任务间传递数据的一个简单的方法就是传递消息缓冲区的指针。因此,用来传递消息缓冲区指针的数据结构,就叫做消息邮箱。-58-μC/OS-II任务的同步与通信
--消息邮箱1.创建消息邮箱
在使用消息邮箱之前,应用程序必须调用函数OSMboxCreate()来创建一个消息邮箱,该函数的原型如下。函数的返回值为已创建的消息邮箱的指针,msg为消息的指针。调用函数OSMboxCreate()需先定义msg的初始值。在一般的情况下,这个初始值为NULL,但也可以事先定义一个邮箱,然后把这个邮箱的指针作为参数传递给函数OSMboxCreate(),从而使其一开始就指向一个邮箱。【函数15-16】OSMboxCreate()OS_EVENT*OSMboxCreate(void*msg);-59-μC/OS-II任务的同步与通信
--消息邮箱2.请求消息邮箱任务通过调用函数OSMboxPend()请求消息邮箱,该函数的原型如下。当一个任务请求消息邮箱时,如果希望在消息邮箱无效时不进入等待状态,而继续运行,则可以调用函数OSMboxAccept()来请求消息邮箱,该函数的原型如下。【函数15-17】OSMboxPend()voidOSMboxPend(OS_EVENT*pevent,INT16Utimeout,INT8U*err);函数15-18】OSMboxAccept()INT16UOSMboxAccept(OS_EVENT*pevent);
-60-μC/OS-II任务的同步与通信
--消息邮箱3.发送消息邮箱
任务获得消息邮箱,并在访问共享资源结束后,必须释放消息邮箱。释放消息邮箱也叫发送消息邮箱,需要调用函数OSMboxPost(),该函数的原型如下。【函数15-19】OSMboxPost()INT8UOSMboxPost(OS_EVENT*pevent,void*msg);
-61-μC/OS-II任务的同步与通信
--消息邮箱4.删除消息邮箱如果应用程序不需要某个消息邮箱,那么可以调用函数OSMboxDel()来删除该消息邮箱,该函数原型如下。【函数15-20】OSMboxDel()OS_EVENT*OSMboxDel(OS_EVENT*pevent,INT8Uopt,INT8U*perr);-62-μC/OS-II任务的同步与通信
--消息邮箱5.查询消息邮箱任务可以调用函数OSMboxQuery()随时查询消息邮箱的当前状态,该函数原型如下。【函数15-21】OSMboxQuery()NT8UOSMboxQuery(OS_EVENT*pevent,OS_MBOX_DATA*p_data);
-63-μC/OS-II任务的同步与通信
--消息邮箱6.消息邮箱实例下述程序代码用于实现任务描述15.D.3,实现创建一个消息邮箱,在UART4中断中将收到的数据放进消息邮箱,task3中等待消息邮箱,一旦消息邮箱数据,则将该数据通过USART2发送。源码如下。【描述15.D.3】消息邮箱实例/*声明消息邮箱*/OS_EVENT*com_Mbox;/*初始化消息邮箱*/com_mbox=OSMboxCreate((void*)0);staticvoidtask3(void*p_arg){ INT8Uerr; chartemp; p_arg=p_arg; while(1) { /*请求消息邮箱有效后,通过USAR2将数据发出*/ temp=OSMboxPend(com_mbox,0,&err); USART_SendData(USART2,temp); }}
-64-μC/OS-II任务的同步与通信
--消息邮箱6.消息邮箱实例UART4中断服务函数在stm32f10x_it.c文件中,源码如下。【示例15-7】stm32f10x_it.cexternOS_EVENTcom_mbox;externchartemp;voidUART4_IRQHandler(void){ OS_CPU_SRcpu_sr; OS_ENTER_CRITICAL(); OSIntNesting++; OS_EXIT_CRITICAL(); if(USART_GetFlagStatus(UART4,USART_FLAG_RXNE)!=RESET) { temp=USART_ReceiveData(UART4); /*通过消息邮箱传递数据temp*/ OSMboxPost(com_mbox,temp); USART_ClearFlag(UART4,USART_FLAG_RXNE); } OSIntExit();}-65-μC/OS-II任务的同步与通信
--消息队列上述消息邮箱不仅可以用来传递一个消息,也可以定义一个指针数组。让数组的每个元素都存放一个消息缓冲区指针,那么任务就可通过传递这个指针数组的方法来传递多个消息了。这种可以传递多个消息的数据结构叫做消息队列。限于篇幅,本章不对消息队列和信号量集做详细介绍,请参考相关资料。-66-μC/OS-II任务的同步与通信
--信号量集在实际应用中,任务常常需要根据多个信号量组合作用的结果来决定任务的运行方式,为此,μC/OS-II提供了信号量集。信号量集实质上就是一个多输入、多输出的组合逻
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年减肥指导场景智能音箱多轮对话实践
- 护理人文关怀与健康教育
- 护理护理专业素养
- 护理课件曲线图:患者睡眠质量改善趋势
- 6.1 友谊的真谛 教学课件(共23张)+内嵌视频 2024-2025学年统编版(2024)初中道德与法治七年级上册
- 水泥混凝土制品养护工操作规程知识考核试卷含答案
- 水生动物病害防治员操作评优考核试卷含答案
- 2026年新科教版高中高一生物上册第三单元细胞器分工协作关系卷含答案
- 城市管理网格员岗前安全培训效果考核试卷含答案
- 2026年新科教版高中高二物理上册洛伦兹力圆周运动卷含答案
- 《建筑施工花篮拉杆附着式钢管脚手架安全技术标准》(发布版)
- 2025版CSCO恶性血液病诊疗指南更新要点(全文)
- 2025多环境下的 LLM Agent 应用与增强
- 《中华人民共和国农产品质量安全法》培训与解读课件
- 团员入团知识培训课件
- 机械挖树根施工方案
- 小牛串焊机培训
- 老年人手机课件
- 2025年甘肃省甘南州农林牧草科学院高层次人才引进13人备考练习题库及答案解析
- 政务礼仪培训课件模板
- 黑龙江省绥棱县2025年上半年事业单位公开招聘试题含答案分析
评论
0/150
提交评论