首页资源分类嵌入式系统RTOS > ucosii程序设计

ucosii程序设计

已有 445117个资源

下载专区

文档信息举报收藏

标    签:ucosii程序设计

分    享:

文档简介

μC/OS-II程序设计,在基于实时操作系统的应用程序设计中,任务设计是整个应用程序的基础,其它软件设计工作都是围绕任务设计来展开,任务设计就是设计“任务函数”(和相关的数据结构)。

文档预览

第1章 μC/OS-II程序设计 1.1 任务设计 在基于实时操作系统的应用程序设计中,任务设计是整个应用程序的基础,其它软件设计工作都是围绕任务设计来展开,任务设计就是设计“任务函数”(和相关的数据结构)。 任务的分类 在用户任务函数中,必须包含至少一次对操作系统服务函数的调用,否则比其优先级低的任务将无法得到运行机会,这是用户任务函数与普通函数的明显区别。事实上,对于非单次执行的任务来说,必须调用延时和/或等待事件的系统服务函数,否则优先级更低的任务将没有机会执行。 任务函数的结构按任务的执行方式可以分为3类:单次执行类、周期执行类和事件触发类,下面分别介绍其结构特点。 单次执行的任务 此类任务在创建后只执行一次,在执行结束时自己删除自己,单次执行的任务的结构见程序清单2.。 程序清单2. 单次执行任务函数的结构 void MyTask (void *pdata) /* 单次执行的任务函数 */ { 进行准备工作的代码; 任务实体代码; 调用任务删除函数; /* 调用OSTaskDel(OS_PRIO_SELF) */ } 单次执行的任务函数由3部分组成:第一部分是“进行准备工作的代码”,完成各项准备工作,如定义和初始化变量、初始化某些设备等等,这部分代码的多少根据实际需要来决定,也可能完全空缺。第二部分是“任务实体代码”,这部分代码完成该任务的具体功能,其中通常包含对若干系统函数的调用,除若干临界段代码(中断被关闭)外,任务的其它代码均可以被中断,以保证高优先级的就绪任务能够及时运行。第三部分是“调用任务删除函数”,该任务将自己删除,操作系统将不再管理它。 单次执行的任务采用“创建任务函数”来启动,当该任务被另外一个任务(或主函数)创建时,就进入就绪状态,等到比它优先级高的任务都被挂起来时便获得运行权,进入运行状态,任务完成后再自行删除。 周期性执行的任务 周期性执行的任务是按一个固定的周期来执行的任务,周期性执行的任务的结构见程序清单2.。 程序清单2. 周期性任务函数的结构 void MyTask (void *pdata) /* 周期性执行的任务函数 */ { 进行准备工作的代码; while (1) /* 无限循环 */ { 任务实体代码; 调用系统延时函数; /* 调用OSTimeDly( )或OSTimeDlyHMSM( ) */ } } 周期性执行的任务函数也由3部分组成:第一部分“进行准备工作的代码”和第二部分“任务实体代码”的含义与单次执行任务的含义相同,第三部分是“调用系统延时函数”,把CPU的控制权主动交给操作系统,使自己挂起,再由操作系统来启动其它已经就绪的任务。当延时时间到后,重新进入就绪状态,通常能够很快获得运行权。 事件触发执行的任务 事件触发执行的任务是平时处于等待状态,某个事件产生时才执行一次的任务。此类任务在创建后,虽然很快可以获得运行权,但任务实体代码的执行需要等待某种事件的发生,在相关事件发生之前,则被操作系统挂起。相关事件发生一次,该任务实体代码就执行一次,其任务函数的结构见程序清单2.。 程序清单2. 事件触发的任务函数的结构 void MyTask (void *pdata) /* 事件触发执行的任务函数 */ { 进行准备工作的代码; while (1) /* 无限循环 */ { 调用获取事件的函数; /* 如:等待信号量、等待邮箱中的消息等等 */ 任务实体代码; } } 事件触发执行的任务函数也由3部分组成:第一部分“进行准备工作的代码”和第三部分“任务实体代码”的含义与前面两种任务的含义相同,第二部分是“调用获取事件的函数”,使用了操作系统提供的某种通信机制,等待另外一个任务(或ISR)发出的信息(如信号量或邮箱中的消息),在取得这个信息之前处于等待状态(挂起状态),当另外一个任务(或ISR)发出相关信息时(调用了操作系统提供的通信函数),操作系统就使该任务进入就绪状态,通过任务调度,任务的实体代码获得运行权,完成该任务的实际功能。 任务的划分 任务划分的目标 在对一个具体的嵌入式应用系统进行任务划分时,可以有不同的任务划分方案。为了选择最佳划分方案,就必须知道任务划分的目标,目标可以总结为: 首要目标是满足“实时性”指标:即使在最坏的情况下,系统中所有对实时性有要求的功能都能够正常实现; 任务数目合理:对于同一个应用系统,任务划分的数目多时,每个任务需要实现的功能就简单一些,任务的设计也简单一些,但任务的调度操作和任务之间的通信活动增加,使系统运行效率下降,资源开销加大。任务划分的数目少时,每个任务需要实现的功能就繁杂一些,但可以免除不少通讯工作,减少共享资源的数量,减轻操作系统的负担,减少资源开销。合理的合并一些任务,使任务数目适当少一些还是比较有利; 简化软件系统:一个任务要实现其功能,除了需要操作系统的调度功能支持外,还需要操作系统的其它服务功能支持,如时间管理功能、任务之间的同步功能、任务之间的通信功能、内存管理功能等等。合理划分任务,可以减少对操作系统的服务要求,使操作系统的功能得到裁减,简化软件系统,减少软件代码的规模; 降低资源需求:合理划分任务,减少或简化任务之间的同步和通信需求,就可以减少相应数据结构的内存规模,从而降低对系统资源的需求。 任务划分的方法 至于任务的划分,请参考后面“电脑自动打铃器设计与实现”。 任务优先级安排 任务的优先级安排原则如下: 中断关联性:与中断服务程序(ISR)有关联的任务应该安排尽可能高的优先级,以便及时处理异步事件,提高系统的实时性。如果优先级安排得比较低,CPU有可能被优先级比较高的任务长期占用,以致于在第二次中断发生时连第一次中断还没有处理,产生信号丢失现象; 紧迫性:因为紧迫任务对响应时间有严格要求,在所有紧迫任务中,按响应时间要求排序,越紧迫的任务安排的优先级越高。紧迫任务通常与ISR关联; 关键性:任务越关键安排的优先级越高,以保障其执行机会; 频繁性:对于周期性任务,执行越频繁,则周期越短,允许耽误的时间也越短,故应该安排的优先级也越高,以保障及时得到执行; 快捷性:在前面各项条件相近时,越快捷(耗时短)的任务安排的优先级越高,以使其它就绪任务的延时缩短; 传递性:信息传递的上游任务的优先级高于下游任务的优先级。如信号采集任务的优先级高于数据处理任务的优先级。 1.2 系统函数使用概述 基本原则 配对性原则 对于μC/OS-II来说,大多数API都是成对的,而且一部分必须配对使用。当然,查询状态的系统函数一般不需要配对使用,而且部分API如延时,也不需要配对使用。配对的函数见表2.。 表2. 配对函数列表 函数1 功能 函数2 功能 备注 OSFlagCreate() 建立事件标志组 OSFlagDel() 删除事件标志组 动态使用事件时必须配对使用 OSMboxCreate() 建立消息邮箱 OSMboxDel() 删除消息邮箱 OSMutexCreate() 建立互斥信号量 OSMutexDel() 删除互斥信号量 OSQCreate() 建立消息队列 OSQDel() 删除消息队列 OSSemCreate() 建立信号量 OSSemDel() 删除信号量 OSFlagPend() 等待事件标志组的事件标志位 OSFlagPost() 置位或清0事件标志组中的标志 必须配对使用,但不在同一个任务中 OSMboxPend() 等待消息邮箱中的消息 OSMboxPost()或 OSMboxPostOpt() 以不同的方式向消息邮箱发送消息 OSMutexPend() 等待一个互斥信号量 OSMutexPost() 释放一个互斥信号量 OSQPend() 等待消息队列中的消息 OSQPost()或 OSQPostFront()或 OSQPostOpt() 以不同的方式向消息队列发送一条消息 OSSemPend() 等待一个信号量 OSSemPost() 发送一个信号量 OSMemGet() 分配一个内存块 OSMemPut() 释放一个内存块 必须配对使用 OSTaskCreate()或 OSTaskCreateExt() 建立任务 OSTaskDel() 删除任务 动态使用任务时必须配对使用 OSTaskSuspend() 挂起任务 OSTaskResume() 恢复任务 必须配对使用 OSTimeDly()或 OSTimeDlyHMSM() 延时 OSTimeDlyResume() 恢复延时的任务 不必配对使用。OSTimeDlyHMSM()可能需要多个OSTimeDlyResume()才能恢复 OSTimeGet() 获得系统时间 OSTimeSet() 设置系统时间 不必配对使用 OSIntEnter() 进入中断处理 OSIntExit() 退出中断处理 必须在中断服务程序中配对使用 OSSchedLock() 给调度器上锁 OSSchedUnlock() 给调度调度 必须在一个任务中配对使用 OS_ENTER_CRITICAL() 进入临界区 OS_EXIT_CRITICAL() 退出临界区 必须在一个任务或中断中配对使用 中断服务程序调用函数的限制 中断服务程序不能调用可能会导致任务调度的函数,它们主要是一些等待事件的函数,这些函数及其替代函数见表2.。 表2. 中断服务函数禁止使用的函数 禁止使用的函数 替代函数 功能 备注 OSFlagPend() OSFlagAccept() 无等待获得事件标志组的事件标志位 需要程序自己判断是否获得了相应的事件 OSMboxPend() OSMboxAccept() 无等待获得消息邮箱中的消息 OSMutexPend() OSMutexAccept() 无等待获得一个互斥信号量 OSQPend() OSQAccept() 无等待获得息队列中的消息 OSSemPend() OSSemAccept() 无等待获得一个信号量 一些函数虽然没有明确地规定不能被中断服务程序调用,但因为中断服务程序的特性,一般不会使用,它们主要是一些创建事件和删除事件的函数,主要有:OSFlagCreate()、OSFlagDel()、OSMboxCreate()、OSMboxDel()、OSMemCreate()、OSMutexCreate()、OSMutexDel、OSQCreate()、OSQDel()、OSSemCreate()、OSSemDel()、OSTaskDel()。 在中断服务程序中,与任务相关的函数一般都不会使用,除了以上提到的函数外,还有OSTaskChangePrio()、OSTaskStkChk()和OSTaskQuery()。至于函数OSSchedLock()和OSSchedUnlock(),在中断服务程序中使用没有任何意义。 注意:未列入表中的函数OSTaskCreate()、OSTaskCreateExt()、OSTaskDel()、OSTaskDelReq()、OSTaskSuspend()、OSTimeDly()、OSTimeDlyHMSM()都属于在中断服务程序中禁止调用的函数。 任务必须调用某个系统函数 因为μC/OS-II是完全基于优先级的操作系统,所以在一定的条件下必须出让CPU时间以便比自己优先级更低的任务能够运行,这是通过调用部分系统函数来实现的,这些函数见表2.。一般的任务必须调用表2.中至少一个函数,只有一种情况例外,就是单次执行的任务(参考任务的分类小节),因为任务删除后肯定出让CPU,所以可以不调用表2.中的函数。 表2. 出让CPU时间的函数 函数名 功能 函数名 功能 OSFlagPend 等待事件标志组的事件标志位 OSMutexPend 等待一个互斥信号量 OSQPend 等待消息队列中的消息 OSQPend 等待消息队列中的消息 OSSemPend 等待一个信号量 OSTaskSuspend 挂起任务 OSTimeDly 延时 OSTimeDlyHMSM 延时 系统函数的分类 根据功能不同,μC/OS-II的系统函数可以分为初始化函数、系统管理函数、任务管理函数、时间管理函数和事件管理函数。 初始化函数 μC/OS-II的初始化函数有2个:OSInit()和OSStart(),它们不能在任何任务和/或中断服务程序中使用,仅在main()函数中按照一定的规范被调用,其中OSInit()函数初始化μC/OS-II内部变量,OSStart()函数启动多任务环境。 系统管理函数 系统管理函数是一些与μC/OS-II内核或功能相关的一些函数,详见表2.。 表2. 系统管理函数列表 函数名 功能 备注 OSStatInit() 使能任务统计功能 复位一次只能调用一次,并且必须在任务中调用,在调用时其它用户任务不能处于就绪状态 OSIntEnter() 进入中断处理 必须由中断服务程序按照规范调用,使用本公司的模版就不需要调用它们 OSIntExit() 退出中断处理 OSSchedLock() 锁调度器 必须配对使用,一般情况不需要使用。事实上,μC/OS-II不推荐使用它们 OSSchedUnlock() 解锁调度器 OS_ENTER_CRITICAL() 进入临界区 必须配对使用,一般通过禁止中断和允许中断来实现的。对于一些移植代码来说,不能嵌套调用 OS_EXIT_CRITICAL() 退出临界区 任务管理函数 任务管理函数是操作与任务相关功能的函数,包括任务的建立与删除、任务优先级的改变、任务的挂起与恢复、任务堆栈的检查、任务的状态的查询等,详见表2.。 表2. 任务管理函数 函数名 功能 函数名 功能 OSTaskChangePrio() 改变任务优先级 OSTaskSuspend() 挂起任务 OSTaskCreate() 建立任务 OSTaskResume() 恢复任务 OSTaskCreateExt() 建立任务 OSTaskStkChk() 检查堆栈 OSTaskDel() 删除任务 OSTaskQuery() 获得任务信息 OSTaskDelReq() 请求删除任务 与OSTaskCreate()不同,OSTaskCreateExt()可以控制任务更多的属性,其次OSTaskSuspend()与OSTaskResume()必须配对使用,OSTaskDelReq()的使用有特殊之处。 时间管理函数 一般的操作系统都提供时间管理的函数,最基本的就是延时函数,μC/OS-II也不例外,μC/OS-II所具有的时间管理函数见表2.。 表2. 时间管理函数 函数名 功能 备注 OSTimeDly() 以时钟节拍为单位延时 OSTimeDlyHMSM() 以钟时分秒毫秒为单位延时 OSTimeDlyResume() 恢复延时的任务 OSTimeDlyHMSM()可能需要多次才能恢复 OSTimeGet() 获得系统时间 以时钟节拍为单位 OSTimeSet() 设置系统时间 以时钟节拍为单位 OSTimeTick() 时钟节拍处理函数 由时钟节拍中断处理程序调用,用户很少使用 事件管理函数 μC/OS-II把信号量等都称为事件,管理它们的就是事件管理函数。μC/OS-II V2.52具有的事件有普通信号量、互斥信号量、事件标志组、消息邮箱和消息队列,这些都是μC/OS-II用于同步与通讯的工具,本章后述的内容将会详细介绍。 动态内存管理函数 μC/OS-II具有简单的动态内存管理能力。μC/OS-II是以固定大小的块为单位动态分配内存的,无论需要多大的内存,一次只能分配一个块。如果用户需要大于一块的连续内存空间,则不能通过动态内存分配来实现。所幸的是,μC/OS-II可以管理多个堆(用于动态内存管理的空间),每个堆中块的大小可以不一样。μC/OS-II的动态内存管理函数见表2.。 表2. 动态内存管理函数 函数名 功能 OSMemCreate() 初始化一个堆 OSMemGet() 从指定堆中获得一个内存块 OSMemPut() 从指定堆中释放一个内存块 OSMemQuery() 查询指定堆的状态 1.3 系统函数的使用场合 时间管理 控制任务的执行周期 时间管理函数中使用率最高的是延时函数OSTimeDly()和OSTimeDlyHMSM(),其主要应用场合是控制周期性任务的执行周期,其程序结构见程序清单2.。 程序清单2. 用延时函数控制任务执行周期 void MyTask (void *pdata) /* 周期性执行的任务函数 */ { 进行准备工作的代码; while(1) /* 无限循环 */ { 任务实体代码; 调用系统延时函数; /* 调用OSTimeDly( )或OSTimeDlyHMSM( ) */ } } 延时函数OSTimeDly()是以系统节拍数为参数,而延时函数OSTimeDlyHMSM()是以实际时间值为参数,但在执行过程中仍然转换为系统节拍数。如果实际时间不是系统节拍的整数倍,将进行四舍五入处理。设系统节拍为50毫秒,调用OSTimeDly(20)的效果是延时1秒钟,调用OSTimeDlyHMSM(0,1,27,620)的实际时间是延时1分27秒600毫秒。 控制任务的运行节奏 在任务函数的代码中可以通过插入延时函数来控制任务的运行节奏,以便将空闲CPU时间利用起来,供其它任务使用。如某任务由3部分操作组成,相邻操作之间需要有一个时间间隔,则任务代码结构如见程序清单2.,各种时间顺序控制任务可以用这种结构的任务函数实现。 程序清单2. 用延时函数控制任务运行节奏 void MyTask (void *pdata) /* 任务函数 */ { 进行准备工作的代码; while(1) /* 无限循环 */ { 调用获取事件的函数; /* 如:等待信号量、等待邮箱中的消息等等 */ 第一部分操作代码; 调用系统延时函数; /* 调用OSTimeDly( )或OSTimeDlyHMSM( ) */ 第二部分操作代码; 调用系统延时函数; /* 调用OSTimeDly( )或OSTimeDlyHMSM( ) */ 第三部分操作代码; ... } } 状态查询 如果任务需要得到某种状态信息才能进行下一步的操作,通常采用等待信号量或消息的方法来处理。但有时这种状态消息不能通过具有行为同步功能的通信方法得到,必须由任务主动去查询。 查询过程是一个无限循环过程,只有当希望的状态出现以后才能退出这个无限循环,这种情况在实时操作系统管理下是不允许的,它将剥夺低优先级任务的运行机会。解决这个问题的办法是“用定时查询代替连续查询”,即在查询的过程中插入延时函数,不断地将CPU交出来,供其它任务使用。状态查询的函数结构可以参考程序清单2.。 程序清单2. 状态查询参考结构 void MyTask (void *pdata) /* 任务函数 */ { 进行准备工作的代码; while(1) /* 无限循环 */ { while (查询的条件不成立) { 调用系统延时函数; /* 调用OSTimeDly( )或OSTimeDlyHMSM( ) */ } 其它处理代码; } } 资源同步 被两个以上并发程序单元(任务或ISR)访问的资源称为共享资源,共享资源一定是全局资源。但不要以为全局资源就一定是共享资源,那些只为一个任务(或ISR)使用的全局资源并不是共享资源,而是这个任务(或ISR)的私有资源。对自己的私有资源进行读写操作是不受限制的,例如显示任务可以随时使用点阵字体数组(点阵字体数组常常定义为全局数组)。 任务对共享资源进行访问的代码称为临界区,各个任务访问同一共享资源的关键段落必须互斥,才能保障共享资源信息的可靠性和完整性,这种使得不同任务访问共享资源时能够确保共享资源信息可靠和完整的措施称为“资源同步”。“资源同步”可通过以下手段实现: 进入然后退出临界区 进入然后退出临界区是通过调用禁止中断函数OS_ENTER_CRITICAL()和允许中断函数OS_EXIT_CRITICAL()实现的。 禁止然后允许调度 禁止然后允许调度是通过调用函数OSSchedLock()和函数OSSchedUnlock()实现的,因为禁止调度违背了多任务的初衷,所以不建议用户使用。 使用信号量与互斥信号量 行为同步 一个任务的运行过程需要和其它任务的运行配合,才能达到预定的效果,任务之间的这种动作配合和协调关系称为“行为同步”。由于行为同步过程往往由某种条件来触发,故又称为“条件同步”。行为同步的结果体现为任务之间的运行按某种预定的顺序来进行,故又称为“顺序控制”。在每一次同步的过程中,其中一个任务(或ISR)为“控制方”,它使用操作系统提供的某种通信手段发出控制信息,另一个任务为“被控制方”,通过通信手段得到控制信息后即进入就绪状态,根据优先级高低,或者立即进入运行状态,或者随后某个时刻进入运行状态。被控制方的运行状态受到控制方发出的信息控制,即被控制方的运行状态由控制方发出的信息来“同步”。 为实现任务之间的“行为同步”,μC/OS-II提供了灵活多样的通信手段来适应不同场合的需要,它们分别是:信号量、事件标志组、消息邮箱、消息队列、任务之间的通讯。 在嵌入式系统的运行过程中,ISR与任务之间、任务与任务之间必然伴随数据通信。在μC/OS-II中,可以使用全局变量、消息邮箱与消息队列实现ISR与任务之间、任务与任务之间的通讯。 全局变量(包括全局数组和全局结构体)可以充当一种共享资源,用来在任务之间传输数据。提供数据的任务或ISR(生产者)对全局变量进行“写操作”,使用数据的任务或ISR(消费者)对全局变量进行“读操作”,从而实现了数据在任务或ISR之间的传输过程。这时全局变量是一种共享资源,对其进行的访问必须遵循“资源同步”的规则(如进入然后退出临界区)。因为全局变量访问速度很快,所以使用进入然后退出临界区(通常使用禁止、允许中断实现)是最适合的方法。事实上,所有的事件实现代码中都使用了这种方法访问自己的全局变量。 特别注意,尽管指针可能是局部变量,但只要指针指向的变量是全局变量,操作指针指向的变量时也需要当作全局变量来处理。 时间管理 μC/OS-II提供了若干个时间管理服务函数,可以满足任务在运行过程中对时间管理的需求。在使用时间管理服务函数时,必须十分清楚一个事实:时间管理服务函数是以系统节拍为处理单位的,实际的时间与希望的时间是有误差的,最坏的情况下误差接近一个系统节拍。因此时间管理服务函数只能用在对时间精度要求不高的场合,或者时间间隔较长的场合。 OSTimeDly()系统延时函数 μC/OS-Ⅱ提供了这样的一种系统服务:申请该服务的任务可以延时一段时间,这段时间的长短是由时钟节拍的数目来确定的,实现这个系统服务的函数就是OSTimeDly()。调用该函数会使μC/OS-Ⅱ进行一次任务调度,并且执行下一个优先级最高的处于就绪态的任务。 任务调用OSTimeDly()函数后,一旦达到规定的时间或者有其它的任务通过调用OSTimeDlyResume()取消了延时,它就会马上进入就绪状态。注意:只有当该任务在所有处于就绪态的任务中具有最高的优先级时,它才会立即运行。 OSTimeDlyHMSM()系统延时函数 OSTimeDly()虽然是一个非常有用的函数,但用户的应用程序需要知道延时时间对应的时钟节拍的数目。用户可以使用定义全局常数OS_TICKS_PER_SEC的方法将时间转换成时钟段,μC/OS-Ⅱ提供了OSTimeDlyHMSM()函数,这个函数是按小时(H)、分(M)、秒(S)和毫秒(m)来定义延时时间的。与OSTimeDly()一样,调用OSTimeDlyHMSM()函数也会使μC/OS-Ⅱ进行一次任务调度,并且执行下一个优先级最高的处于就绪态的任务。 任务调用OSTimeDlyHMSM()后,一旦达到规定的时间或者有其它的任务通过调用OSTimeDlyResume()取消了延时,它就会马上处于就绪态。同样,只有当该任务在所有处于就绪态任务的中具有最高的优先级时,它才会立即运行。OSTimeDlyHMSM()详细见表2.。 表2. OSTimeDlyHMSM函数 函数名称 OSTimeDlyHMSM 所属文件 OS_TIMC.C 函数原型 INT8U OSTimeDlyHMSM (INT8U hours, INT8U minutes, INT8U seconds, INT16U milli) 功能描述 延时,指定的延时时间为时、分、秒、毫秒 函数参数 hours:小时,minutes:分钟,seconds:秒,milli:毫秒 函数返回值 OS_TIME_INVALID_MINUTES:minutes参数错误 OS_TIME_INVALID_SECONDS:seconds参数错误 OS_TIME_INVALID_MILLI:milli参数错误 特殊说明 (1) 所有参数为0时不延时,函数直接返回 (2) 必须正确设置全局常数OS_TICKS_PER_SEC,否则延时时间是错误的 (3) 因为OSTimeDlyHMSM()是通过多次(或1次)调用OSTimeDly()实现的,所以延时分辨率为时钟节拍 (4) 因为OSTimeDlyHMSM()是通过多次(或1次)调用OSTimeDly()实现的,所以可能需要调用多次OSTimeDlyResume()才能恢复延时的任务 强制延时的任务结束延时 µC/OS-Ⅱ允许用户结束正处于延时期的任务,延时的任务可以不等待延时期满,而是通过取消其它任务的延时来使自己处于就绪态,可以通过调用OSTimeDlyResume()和指定要恢复的任务的优先级来完成。OSTimeDlyResume()的具体信息见表2.。 表2. OSTimeDlyResume函数 函数名称 OSTimeDlyResume 所属文件 OS_TIMC.C 函数原型 INT8U OSTimeDlyResume(INT8U prio) 功能描述 让延时的任务结束延时 函数参数 prio:任务优先级 函数返回值 OS_NO_ERR:成功,OS_PRIO_INVALID:prio错误 OS_TIME_NOT_DLY:任务没有延时,OS_TASK_NOT_EXIST:任务不存在 特殊说明 因为OSTimeDlyHMSM()是通过多次(或1次)调用OSTimeDly()实现的,所以可能需要调用多次OSTimeDlyResume()才能恢复延时的任务 获得系统时间(OSTimeGet)和设置系统时间(OSTimeSet) 无论时钟节拍何时发生,µC/OS-Ⅱ都会将一个32位的计数器加1,这个计数器在用户调用OSStart()初始化多任务和4,294,967,295个节拍执行完一遍的时候从0开始计数。在时钟节拍的频率等于100Hz的时候,这个32位的计数器每隔497天就重新开始计数。用户可以通过调用OSTimeGet()来获得该计数器的当前值,也可以通过调用OSTimeSet()来改变该计数器的值。OSTimeGet()的详细信息见表2.,OSTimeSet()的详细信息见表2.。 表2. OSTimeGet函数 函数名称 OSTimeGet 所属文件 OS_TIMC.C 函数原型 INT32U OSTimeGet(void) 功能描述 获得系统时间 函数参数 prio:任务优先级 函数返回值 系统时间 表2. OSTimeSet函数 函数名称 OSTimeSet 所属文件 OS_TIMC.C 函数原型 void OSTimeSet(INT32U ticks) 功能描述 设置系统时间 函数参数 ticks:需要设置的值 函数返回值 无 特殊说明 很少使用 系统管理 进入然后退出临界区 和其它内核一样,μC/OS-Ⅱ为了处理临界段代码需要禁止中断,处理完毕后再允许中断,这使得μC/OS-Ⅱ能够避免同时有其它任务或中断服务进入临界区代码。 微处理器一般都有禁止中断/允许中断指令,用户使用的C语言编译器必须有某种机制能够在C中直接实现禁止中断/允许中断地操作。μC/OS-Ⅱ定义了两个宏(macros)来禁止中断和允许中断,以便避开不同C编译器厂商选择不同的方法来处理禁止中断和允许中断,μC/OS-Ⅱ中的这两个宏调用分别是:OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()。 禁止然后允许调度 给调度器上锁OSSchedlock()函数用于禁止任务调度,直到任务完成后调用给调度器开锁OSSchedUnlock()函数为止。OSSchedlock()和OSSchedUnlock()必须成对使用,也可以嵌套使用。调用多少次OSSchedlock()就需要调用多少次OSSchedUnlock(),否则调度还是被禁止,μC/OS-Ⅱ允许嵌套深度达255层。 尽管有个优先级更高的任务进入了就绪态,调用OSSchedlock()的任务仍然保持对CPU的控制权。但调用OSSchedlock()不会对中断有任何影响,换句话说,此时如果允许中断,中断到来时还是会执行对应的中断服务程序。 使用OSSchedLock()和OSSchedUnlock()函数要非常谨慎,因为它们影响μC/OS-Ⅱ对任务的正常管理。因为调度器上了锁,即锁住了系统,任何其它任务都不能运行,所以调用OSSchedLock()以后,用户的应用程序不得使用任何能将现行任务挂起的系统调用。也就是说,用户程序不得调用OSMutexPend()、OSMboxPend()、OSQPend()、OSSemPend()、OSTaskSuspend(OS_PR1O_SELF)、OSTimeDly()或OSTimeDlyHMSM(),直到配对的OSSchedUnlock()调用为止。 对于用户来说,极少使用禁止然后允许调度的方法。因为这种方法有极大的缺陷,同时也可使用其它方法替代,所以几乎没有人使用它。不过,很多操作系统内部和驱动程序使用它来减少中断响应时间。 事件的一般使用规则 相似性 事件管理函数是μC/OS-II中最多的系统函数,在μC/OS-II V2.52中总共有34个,而且每种事件具有的管理函数数目不同。但是所有的事件都有类似的6个函数,分别对应建立、删除、等待、发送、无等待获取和查询状态。它们是所有事件的基本功能,其函数名类似,使用方法也类似,详细函数见表2.。 表2. 事件管理核心函数 功能 信号量 互斥信号量 事件标志组 消息邮箱 消息队列 建立事件 OSSemCreate OSMutexCreate OSFlagCreate OSMboxCreate OSQCreate 删除事件 OSSemDel OSMutexDel OSFlagDel OSMboxDel OSQDel 等待事件 OSSemPend OSMutexPend OSFlagPend OSMboxPend OSQPend 发送事件 OSSemPost OSMutexPost OSFlagPost OSMboxPost OSQPost 无等待获得事件 OSSemAccept OSMutexAccept OSFlagAccept OSMboxAccept OSQAccept 查询事件状态 OSSemQuery OSMutexQuery OSFlagQuery OSMboxQuery OSQQuery 先创建后使用 任何一个事件,必须先创建后使用。创建事件是通过调用函数OS??? Create()实现的,其中???为事件的类型。创建事件可以在main()函数中(OSStatInit()之后OSStart()之前),但更多的是在任务的初始化部分。一般来说,在嵌入式系统中,事件是静态使用的,也就是说,创建后永远不删除。但有时候需要根据需要创建和删除事件,此时创建事件就是在任务的事件执行代码中,此时特别要注意创建和删除要配对,同时删除事件后不要再使用它。 静态使用事件时创建任务的方法见程序清单2.,动态使用事件时创建任务的方法见程序清单2.。 程序清单2. 静态使用事件 OS_EVENT *event; void Task0(void *pdata) { pdata = pdata; event = OS???Create(...); while (1) { /* 其它代码 */ } } 程序清单2. 动态使用事件 OS_EVENT *event; void Task0(void *pdata) { pdata = pdata; while (1) { event = OS???Create(...); /* 其它代码 */ OS???Del(event,...); } } 配对使用 如果需要动态使用事件,那么建立事件和删除事件必须配对使用。如果建立事件比删除任务多,则会造成事件资源越来越少,最后不能创建事件,程序执行错误。如果删除事件比建立事件多,则可能误删事件,造成事件指针指向非法的数据(野指针),会出现莫名其妙的错误。 使用事件必须使用等待事件和发送事件功能,否则就没有使用事件的意义。无等待获得事件是等待事件的一种特殊形式,有事件时它与等待事件没有差别,没有事件时,它不等待,直接返回错误信息。事件的一般使用方法见程序清单2.,假设Task0为高优先级任务,而Task1为中断服务程序。 注意:一些事件有多个发送事件的函数。与此同时因为已经具有无等待获得事件的功能,所以很少使用查询功能。 程序清单2. 事件的一般使用方法 OS_EVENT *event; void Task0(void *pdata) { pdata = pdata; event = OS???Create(...); while (1) { OS???Pend(event,...) /* 其它代码 */ } } void Task1(void *pdata) { pdata = pdata; while (1) { /* 其它代码 */ OS???Post(event,...); /* 其它代码 */ } } 在中断服务程序中使用 中断的主要特点有:中断与所有的任务异步、中断服务程序总体是顺序结构、中断服务程序不能等待、中断服务程序需要尽快退出。所以,中断服务程序一般不会调用建立和删除事件函数,否则要么没有起到事件的作用,要么程序很复杂。特别地,中断服务程序不能调用等待事件的函数,否则可能造成程序崩溃。 事实上,在中断中调用无等待获得事件的情况都很少,在中断服务程序中使用事件的方法见程序清单2.。 程序清单2. 中断中事件的一般使用方法 OS_EVENT *event; void Task0(void *pdata) { pdata = pdata; event = OS???Create(...); while (1) { OS???Pend(event,...) /* 其它代码 */ } } void ISR(void) { /* 其它代码 */ OS???Post(event,...); /* 其它代码 */ }

Top_arrow
回到顶部
EEWORLD下载中心所有资源均来自网友分享,如有侵权,请发送举报邮件到客服邮箱bbs_service@eeworld.com.cn 或通过站内短信息或QQ:273568022联系管理员 高进,我们会尽快处理。