分享
分享赚钱 收藏 举报 版权申诉 / 44

类型第6章 嵌入式实时操作系统UCOS-II(第2版).doc

  • 上传人:dzzj200808
  • 文档编号:2303587
  • 上传时间:2018-09-10
  • 格式:DOC
  • 页数:44
  • 大小:721.50KB
  • 配套讲稿:

    如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。

    特殊限制:

    部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。

    关 键  词:
    第6章 嵌入式实时操作系统UCOS-II(第2版).doc
    资源描述:

    1、I第 6 章 任务之间的通讯与同步 6.0 事件控制块 ECB.6.1 初始化一个 ECB 块,OSEVENTWAIT LISTINIT()6.2 使一个任务进入就绪状态,OSEVENT TASKRDY().6.3 使一个任务进入等待状态, OSEVENT TASKWAIT().6.4 由于等待超时将一个任务置为就绪状态, OSEVENTTO() 6.5 信号量 6.5.1 建立一个信号量, OSSemCreate() .6.5.2 等待一个信号量, OSSemPend() .6.5.3 发送一个信号量, OSSemPost() .6.5.4 无等待地请求一个信号量, OSSemAccept(

    2、) .6.5.5 查询一个信号量的当前状态, OSSemQuery() .6.6 邮箱 6.6.1 建立一个邮箱,OSMboxCreate() .6.6.2 等待一个邮箱中的消息,OSMboxPend() .6.6.3 发送一个消息到邮箱中,OSMboxPost() .6.6.4 无等待地从邮箱中得到一个消息, OSMboxAccept() .6.6.5 查询一个邮箱的状态, OSMboxQuery() .6.6.6 使用邮箱作为二值信号量 .6.6.7 使用邮箱实现延时,而不使用 OSTimeDly()6.7 消息队列 6.7.1 建立一个消息队列,OSQCreate() .6.7.2 等待

    3、一个消息队列中的消息,OSQPend() .6.7.3 向消息队列发送一个消息(FIFO),OSQPost() .6.7.4 向消息队列发送一个消息(LIFO),OSQPostFront() .6.7.5 无等待地从一个消息队列中取得消息, OSQAccept() .6.7.6 清空一个消息队列, OSQFlush() .6.7.7 查询一个消息队列的状态,OSQQuery() .6.7.8 使用消息队列读取模拟量的值 .6.7.9 使用一个消息队列作为计数信号量 .6-第 6 章 任务之间的通讯与同步在 C/OS-II 中,有多种方法可以保护任务之间的共享数据和提供任务之间的通讯。在前面的章

    4、节中,已经讲到了其中的两种:一是利用宏 OS_ENTER_CRITICAL()和 OS_EXIT_CRITICAL()来关闭中断和打开中断。当两个任务或者一个任务和一个中断服务子程序共享某些数据时,可以采用这种方法,详见 3.00 节 临界段、 8.03.02 节 OS_ENTER_CRITICAL() 和 OS_EXIT_CRITICAL()及 9.03.02 节 临界段,OS_CPU.H;二是利用函数 OSSchedLock()和 OSSchekUnlock()对 C/OS-II 中的任务调度函数上锁和开锁。用这种方法也可以实现数据的共享,详见 3.06 节 给调度器上锁和开锁。本章将介绍

    5、另外三种用于数据共享和任务通讯的方法:信号量、邮箱和消息队列。图 F6.1 介绍了任务和中断服务子程序之间是如何进行通讯的。一个任务或者中断服务子程序可以通过事件控制块 ECB(Event Control Blocks)来向另外的任务发信号F6.1A(1)。这里,所有的信号都被看成是事件(Event)。这也说明为什么上面把用于通讯的数据结构叫做事件控制块。一个任务还可以等待另一个任务或中断服务子程序给它发送信号F6.1A(2)。这里要注意的是,只有任务可以等待事件发生,中断服务子程序是不能这样做的。对于处于等待状态的任务,还可以给它指定一个最长等待时间,以此来防止因为等待的事件没有发生而无限期

    6、地等下去。多个任务可以同时等待同一个事件的发生F6.1B。在这种情况下,当该事件发生后,所有等待该事件的任务中,优先级最高的任务得到了该事件并进入就绪状态,准备执行。上面讲到的事件,可以是信号量、邮箱或者消息队列等。当事件控制块是一个信号量时,任务可以等待它,也可以给它发送消息。6-图 6.1 事件控制块的使用6.0 事件控制块 ECBC/OS-II 通过 uCOS_II.H 中定义的 OS_EVENT 数据结构来维护一个事件控制块的所有信息程序清单 L6.1,也就是本章开篇讲到的事件控制块 ECB。该结构中除了包含了事件本身的定义,如用于信号量的计数器,用于指向邮箱的指针,以及指向消息队列的

    7、指针数组等,还定义了等待该事件的所有任务的列表。程序清单 L6.1 是该数据结构的定义。6-程序清单 L6.1 ECB数据结构typedef struct void *OSEventPtr; /* 指向消息或者消息队列的指针 */INT8U OSEventTblOS_EVENT_TBL_SIZE; /* 等待任务列表 */INT16U OSEventCnt; /* 计数器(当事件是信号量时) */INT8U OSEventType; /* 时间类型 */INT8U OSEventGrp; /* 等待任务所在的组 */ OS_EVENT;.OSEventPtr 指针,只有在所定义的事件是邮箱或者

    8、消息队列时才使用。当所定义的事件是邮箱时,它指向一个消息,而当所定义的事件是消息队列时,它指向一个数据结构,详见6.06 节消息邮箱和 6.07 节消息队列。.OSEventTbl 和 .OSEventGrp 很像前面讲到的 OSRdyTbl和 OSRdyGrp,只不过前两者包含的是等待某事件的任务,而后两者包含的是系统中处于就绪状态的任务。(见 3.04 节 就绪表).OSEventCnt 当事件是一个信号量时,.OSEventCnt 是用于信号量的计数器,(见 6.05节信号量)。.OSEventType 定义了事件的具体类型。它可以是信号量(OS_EVENT_SEM)、邮箱(OS_EVE

    9、NT_TYPE_MBOX)或消息队列(OS_EVENT_TYPE_Q)中的一种。用户要根据该域的具体值来调用相应的系统函数,以保证对其进行的操作的正确性。每个等待事件发生的任务都被加入到该事件事件控制块中的等待任务列表中,该列表包括.OSEventGrp 和.OSEventTbl两个域。变量前面的.说明该变量是数据结构的一个域。在这里,所有的任务的优先级被分成 8 组(每组 8 个优先级),分别对应.OSEventGrp 中的 8 位。当某组中有任务处于等待该事件的状态时,.OSEventGrp 中对应的位就被置位。相应地,该任务在.OSEventTbl中的对应位也被置位。.OSEventTb

    10、l数组的大小由系统中任务的最低优先级决定,这个值由 uCOS_II.H 中的 OS_LOWEST_PRIO 常数定义。这样,在任务优先级比较少的情况下,减少 C/OS-II 对系统 RAM 的占用量。当一个事件发生后,该事件的等待事件列表中优先级最高的任务,也即在.OSEventTbl中,所有被置 1 的位中,优先级代码最小的任务得到该事件。图 F6.2 给出了.OSEventGrp 和.OSEventTbl之间的对应关系。该关系可以描述为:当.OSEventTbl0中的任何一位为 1 时,.OSEventGrp 中的第 0 位为 1。当.OSEventTbl1中的任何一位为 1 时,.OSE

    11、ventGrp 中的第 1 位为 1。6-当.OSEventTbl2中的任何一位为 1 时,.OSEventGrp 中的第 2 位为 1。当.OSEventTbl3中的任何一位为 1 时,.OSEventGrp 中的第 3 位为 1。当.OSEventTbl4中的任何一位为 1 时,.OSEventGrp 中的第 4 位为 1。当.OSEventTbl5中的任何一位为 1 时,.OSEventGrp 中的第 5 位为 1。当.OSEventTbl6中的任何一位为 1 时,.OSEventGrp 中的第 6 位为 1。当.OSEventTbl7中的任何一位为 1 时,.OSEventGrp 中的

    12、第 7 位为 1。图 F6.2 事件的等待任务列表下面的代码将一个任务放到事件的等待任务列表中。程序清单 L6.2将一个任务插入到事件的等待任务列表中pevent-OSEventGrp |= OSMapTblprio 3;pevent-OSEventTblprio 3 |= OSMapTblprio 其中,prio 是任务的优先级,pevent 是指向事件控制块的指针。6-从程序清单 L6.2 可以看出,插入一个任务到等待任务列表中所花的时间是相同的,和表中现有多少个任务无关。从图 F6.2 中可以看出该算法的原理:任务优先级的最低 3 位决定了该任务在相应的.OSEventTbl中的位置,紧

    13、接着的 3 位则决定了该任务优先级在.OSEventTbl中的字节索引。该算法中用到的查找表 OSMapTbl(定义在 OS_CORE.C 中)一般在 ROM 中实现。表 T6.1 OSMapTblIndex Bit Mask (Binary)0 000000011 000000102 000001003 000010004 000100005 001000006 010000007 10000000从等待任务列表中删除一个任务的算法则正好相反,如程序清单 L6.3 所示。程序清单 L6.3 从等待任务列表中删除一个任务if (pevent-OSEventTblprio 3 该代码清除了任务在

    14、.OSEventTbl中的相应位,并且,如果其所在的组中不再有处于等待该事件的任务时(即.OSEventTblprio3为 0),将.OSEventGrp 中的相应位也清除了。和上面的由任务优先级确定该任务在等待表中的位置的算法类似,从等待任务列表中查找处于等待状态的最高优先级任务的算法,也不是从.OSEventTbl0开始逐个查询,而是采用了查找另一个表 OSUnMapTbl256(见文件 OS_CORE.C)。这里,用于索引的 8 位分别代表对应的 8组中有任务处于等待状态,其中的最低位具有最高的优先级。用这个值索引,首先得到最高优先级任务所在的组的位置(07 之间的一个数)。然后利用.O

    15、SEventTbl中对应字节再在OSUnMapTbl中查找,就可以得到最高优先级任务在组中的位置(也是 07 之间的一个数)。这样,最终就可以得到处于等待该事件状态的最高优先级任务了。程序清单 L6.4 是该算法的具体实现代码。6-程序清单 L6.4 在等待任务列表中查找最高优先级的任务y = OSUnMapTblpevent-OSEventGrp;x = OSUnMapTblpevent-OSEventTbly;prio = (y OSEventGrp = 0x00;6-for (i = 0; i OSEventTbli = 0x00;6.2 使一个任务进入就绪态,OSEventTaskRd

    16、y()程序清单 L6.6 是函数 OSEventTaskRdy()的源代码。当发生了某个事件,该事件等待任务列表中的最高优先级任务(Highest Priority Task HPT)要置于就绪态时,该事件对应的 OSSemPost(),OSMboxPost(),OSQPost(),和 OSQPostFront()函数调用 OSEventTaskRdy()实现该操作。换句话说,该函数从等待任务队列中删除 HPT 任务(Highest Priority Task),并把该任务置于就绪态。图 F6.4 给出了 OSEventTaskRdy()函数最开始的 4 个动作。该函数首先计算 HPT 任务在

    17、.OSEventTbl中的字节索引L6.6/F6.4(1),其结果是一个从 0 到 OS_LOWEST_PRIO/8+1 之间的数,并利用该索引得到该优先级任务在.OSEventGrp 中的位屏蔽码L6.6/F6.4(2)(从表 T6.1 可以得到该值)。然后,OSEventTaskRdy()函数判断 HPT任务在.OSEventTbl中相应位的位置L6.6/F6.4(3),其结果是一个从 0 到OS_LOWEST_PRIO/8+1 之间的数,以及相应的位屏蔽码L6.6/F6.4(4)。根据以上结果,OSEventTaskRdy()函数计算出 HPT 任务的优先级L6.6(5),然后就可以从等

    18、待任务列表中删除该任务了L6.6(6)。任务的任务控制块中包含有需要改变的信息。知道了 HPT 任务的优先级,就可以得到指向该任务的任务控制块的指针L6.6(7)。因为最高优先级任务运行条件已经得到满足,必须停止 OSTimeTick()函数对.OSTCBDly 域的递减操作,所以 OSEventTaskRdy()直接将该域清澈0L6.6(8)。因为该任务不再等待该事件的发生,所以 OSEventTaskRdy()函数将其任务控制块中指向事件控制块的指针指向 NULLL6.6(9)。如果 OSEventTaskRdy()是由 OSMboxPost()或者OSQPost()调用的,该函数还要将相

    19、应的消息传递给 HPT,放在它的任务控制块中L6.6(10)。另外,当 OSEventTaskRdy()被调用时,位屏蔽码 msk 作为参数传递给它。该参数是用于对任务控制块中的位清零的位屏蔽码,和所发生事件的类型相对应L6.6(11)。最后,根据.OSTCBStat 判断该任务是否已处于就绪状态L6.6(12)。如果是, 则将 HPT 插入到 C/OS-II 的就绪任务列表中L6.6(13)。注意,HPT 任务得到该事件后不一定进入就绪状态,也许该任务已经由于其它原因挂起了。见 4.07 节,挂起一个任务,OSTaskSuspend(),和 4.08 节,恢复一个任务,OSTaskResum

    20、e()。另外,.OSEventTaskRdy()函数要在中断禁止的情况下调用。6-程序清单 L6.6 使一个任务进入就绪状态void OSEventTaskRdy (OS_EVENT *pevent, void *msg, INT8U msk)OS_TCB *ptcb;INT8U x;INT8U y;INT8U bitx;INT8U bity;INT8U prio;y = OSUnMapTblpevent-OSEventGrp; (1)bity = OSMapTbly; (2)x = OSUnMapTblpevent-OSEventTbly; (3)bitx = OSMapTblx; (4)p

    21、rio = (INT8U)(y OSEventTbly ptcb = OSTCBPrioTblprio; (7)ptcb-OSTCBDly = 0; (8)ptcb-OSTCBEventPtr = (OS_EVENT *)0; (9)#if (OS_Q_EN (10)#elsemsg = msg;#endifptcb-OSTCBStat (11)if (ptcb-OSTCBStat = OS_STAT_RDY) (12)OSRdyGrp |= bity; (13)OSRdyTbly |= bitx;6-图 F6.4 使一个任务进入就绪状态Figure 6.46.3 使一个任务进入等待某事件发生

    22、状态, OSEventTaskWait()程序清单 L6.7 是 OSEventTaskWait()函数的源代码。当某个任务要等待一个事件的发生时,相应事件的 OSSemPend(),OSMboxPend()或者 OSQPend()函数会调用该函数将当前任务从就绪任务表中删除,并放到相应事件的事件控制块的等待任务表中。程序清单 L6.7 使一个任务进入等待状态void OSEventTaskWait (OS_EVENT *pevent)OSTCBCur-OSTCBEventPtr = pevent; (1)if (OSRdyTblOSTCBCur-OSTCBY pevent-OSEventTb

    23、lOSTCBCur-OSTCBY |= OSTCBCur-OSTCBBitX; (3)pevent-OSEventGrp |= OSTCBCur-OSTCBBitY;6-在该函数中,首先将指向事件控制块的指针放到任务的任务控制块中 L6.7(1),接着将任务从就绪任务表中删除L6.7(2),并把该任务放到事件控制块的等待任务表中L6.7(3)。6.4 由于等待超时而将任务置为就绪态, OSEventTO()程序清单 L6.8 是 OSEventTO()函数的源代码。当在预先指定的时间内任务等待的事件没有发生时,OSTimeTick()函数会因为等待超时而将任务的状态置为就绪。在这种情况下,事件

    24、的 OSSemPend(),OSMboxPend()或者 OSQPend()函数会调用 OSEventTO()来完成这项工作。该函数负责从事件控制块中的等待任务列表里将任务删除L6.8(1),并把它置成就绪状态L6.8(2)。最后,从任务控制块中将指向事件控制块的指针删除L6.8(3)。用户应当注意,调用OSEventTO()也应当先关中断。程序清单 L6.8 因为等待超时将任务置为就绪状态void OSEventTO (OS_EVENT *pevent)if (pevent-OSEventTblOSTCBCur-OSTCBY OSTCBCur-OSTCBStat = OS_STAT_RDY;

    25、 (2)OSTCBCur-OSTCBEventPtr = (OS_EVENT *)0; (3)6.5 信号量C/OS-II 中的信号量由两部分组成:一个是信号量的计数值,它是一个 16 位的无符号整数(0 到 65,535 之间);另一个是由等待该信号量的任务组成的等待任务表。用户要在OS_CFG.H 中将 OS_SEM_EN 开关量常数置成 1,这样 C/OS-II 才能支持信号量。在使用一个信号量之前,首先要建立该信号量,也即调用 OSSemCreate()函数(见下一节),对信号量的初始计数值赋值。该初始值为 0 到 65,535 之间的一个数。如果信号量是用来表示一个或者多个事件的发生

    26、,那么该信号量的初始值应设为 0。如果信号量是用于对共享资源的访问,那么该信号量的初始值应设为 1(例如,把它当作二值信号量使用)。最后,如果该信号量是用来表示允许任务访问 n 个相同的资源,那么该初始值显然应该是 n,并把该信号量作为一个可计数的信号量使用。6-C/OS-II 提供了 5 个对信号量进行操作的函数。它们是:OSSemCreate(),OSSemPend(),OSSemPost(),OSSemAccept()和 OSSemQuery()函数。图 F6.5 说明了任务、中断服务子程序和信号量之间的关系。图中用钥匙或者旗帜的符号来表示信号量:如果信号量用于对共享资源的访问,那么信号

    27、量就用钥匙符号。符号旁边的数字 N 代表可用资源数。对于二值信号量,该值就是 1;如果信号量用于表示某事件的发生,那么就用旗帜符号。这时的数字 N 代表事件已经发生的次数。从图 F6.5 中可以看出 OSSemPost()函数可以由任务或者中断服务子程序调用,而 OSSemPend()和 OSSemQuery()函数只能有任务程序调用。图 F6.5 任务、中断服务子程序和信号量之间的关系Figure 6.56.5.1 建立一个信号量, OSSemCreate()程序清单 L6.9 是 OSSemCreate()函数的源代码。首先,它从空闲任务控制块链表中得到一个事件控制块L6.9(1),并对空

    28、闲事件控制链表的指针进行适当的调整,使它指向下一个空闲的事件控制块L6.9(2)。如果这时有任务控制块可用L6.9(3),就将该任务控制块的事件类型设置成信号量 OS_EVENT_TYPE_SEML6.9(4)。其它的信号量操作函数 OSSem?()通过检查该域来保证所操作的任务控制块类型的正确。例如,这可以防止调用 OSSemPost()函数对一个用作邮箱的任务控制块进行操作6.06 节,邮箱。接着,用信号量的初始值对任务控制块进行初始化L6.9(5),并调用 OSEventWaitListInit()函数对事件控制任务控制块的等待任务列表进行初始化见 6.01 节,初始化一个任务控制块,O

    29、SEventWaitListInit()L6.9(6)。因为信号量正在被初始化,所以这时没有任何任务等待该信号量。最后,OSSemCreate()返回给调用函数一个指向任务控制块的指针。以后对信号量的所有操作,如 OSSemPend(), 6-OSSemPost(), OSSemAccept()和 OSSemQuery()都是通过该指针完成的。因此,这个指针实际上就是该信号量的句柄。如果系统中没有可用的任务控制块,OSSemCreate()将返回一个 NULL 指针。值得注意的是,在 C/OS-II 中,信号量一旦建立就不能删除了,因此也就不可能将一个已分配的任务控制块再放回到空闲 ECB 链

    30、表中。如果有任务正在等待某个信号量,或者某任务的运行依赖于某信号量的出现时,删除该任务是很危险的。程序清单 L6.9 建立一个信号量OS_EVENT *OSSemCreate (INT16U cnt)OS_EVENT *pevent;OS_ENTER_CRITICAL();pevent = OSEventFreeList; (1)if (OSEventFreeList != (OS_EVENT *)0) (2)OSEventFreeList = (OS_EVENT *)OSEventFreeList-OSEventPtr;OS_EXIT_CRITICAL();if (pevent != (OS

    31、_EVENT *)0) (3)pevent-OSEventType = OS_EVENT_TYPE_SEM; (4)pevent-OSEventCnt = cnt; (5)OSEventWaitListInit(pevent); (6)return (pevent); (7)6.5.2 等待一个信号量, OSSemPend()程序清单 L6.10 是 OSSemPend()函数的源代码。它首先检查指针 pevent 所指的任务控制块是否是由 OSSemCreate()建立的L6.10(1)。如果信号量当前是可用的(信号量的计数值大于 0)L6.10(2),将信号量的计数值减 1L6.10(3)

    32、,然后函数将“无错”错误代码返回给它的调用函数。显然,如果正在等待信号量,这时的输出正是我们所希望的,也是运行OSSemPend()函数最快的路径。如果此时信号量无效(计数器的值是 0),OSSemPend()函数要进一步检查它的调用函数是不是中断服务子程序L6.10(4)。在正常情况下,中断服务子程序是不会调用 OSSemPend()6-函数的。这里加入这些代码,只是为了以防万一。当然,在信号量有效的情况下,即使是中断服务子程序调用的 OSSemPend(),函数也会成功返回,不会出任何错误。如果信号量的计数值为 0,而 OSSemPend()函数又不是由中断服务子程序调用的,则调用 OSS

    33、emPend()函数的任务要进入睡眠状态,等待另一个任务(或者中断服务子程序)发出该信号量(见下节)。OSSemPend()允许用户定义一个最长等待时间作为它的参数,这样可以避免该任务无休止地等待下去。如果该参数值是一个大于 0 的值,那么该任务将一直等到信号有效或者等待超时。如果该参数值为 0,该任务将一直等待下去。OSSemPend()函数通过将任务控制块中的状态标志.OSTCBStat 置 1,把任务置于睡眠状态L6.10(5),等待时间也同时置入任务控制块中L6.10(6),该值在 OSTimeTick()函数中被逐次递减。注意,OSTimeTick()函数对每个任务的任务控制块的.O

    34、STCBDly 域做递减操作(只要该域不为 0)见 3.10 节,时钟节拍。真正将任务置入睡眠状态的操作在 OSEventTaskWait()函数中执行 见 6.03 节,让一个任务等待某个事件,OSEventTaskWait()L6.10(7)。因为当前任务已经不是就绪态了,所以任务调度函数将下一个最高优先级的任务调入,准备运行L6.10(8)。当信号量有效或者等待时间到后,调用 OSSemPend()函数的任务将再一次成为最高优先级任务。这时 OSSched()函数返回。这之后,OSSemPend()要检查任务控制块中的状态标志,看该任务是否仍处于等待信号量的状态L6.10(9)。如果是,

    35、说明该任务还没有被 OSSemPost()函数发出的信号量唤醒。事实上,该任务是因为等待超时而由 TimeTick()函数把它置为就绪状态的。这种情况下,OSSemPend()函数调用 OSEventTO()函数将任务从等待任务列表中删除L6.10(10),并返回给它的调用任务一个“超时”的错误代码。如果任务的任务控制块中的 OS_STAT_SEM 标志位没有置位,就认为调用 OSSemPend()的任务已经得到了该信号量,将指向信号量 ECB 的指针从该任务的任务控制块中删除,并返回给调用函数一个“无错”的错误代码L6.10(11)。程序清单 L6.10 等待一个信号量void OSSemP

    36、end (OS_EVENT *pevent, INT16U timeout, INT8U *err)OS_ENTER_CRITICAL();if (pevent-OSEventType != OS_EVENT_TYPE_SEM) (1)OS_EXIT_CRITICAL();*err = OS_ERR_EVENT_TYPE;if (pevent-OSEventCnt 0) (2)pevent-OSEventCnt-; (3)OS_EXIT_CRITICAL();*err = OS_NO_ERR; else if (OSIntNesting 0) (4)6-OS_EXIT_CRITICAL();*

    37、err = OS_ERR_PEND_ISR; else OSTCBCur-OSTCBStat |= OS_STAT_SEM; (5)OSTCBCur-OSTCBDly = timeout; (6)OSEventTaskWait(pevent); (7)OS_EXIT_CRITICAL();OSSched(); (8)OS_ENTER_CRITICAL();if (OSTCBCur-OSTCBStat (10)OS_EXIT_CRITICAL();*err = OS_TIMEOUT; else OSTCBCur-OSTCBEventPtr = (OS_EVENT *)0; (11)OS_EXIT

    38、_CRITICAL();*err = OS_NO_ERR;6.5.3 发送一个信号量, OSSemPost()程序清单 L6.11 是 OSSemPost()函数的源代码。它首先检查参数指针 pevent 指向的任务控制块是否是 OSSemCreate()函数建立的L6.11(1),接着检查是否有任务在等待该信号量L6.11(2)。如果该任务控制块中的.OSEventGrp 域不是 0,说明有任务正在等待该信号量。这时,就要调用函数 OSEventTaskRdy()见 6.02 节,使一个任务进入就绪状态,OSEventTaskRdy(),把其中的最高优先级任务从等待任务列表中删除L6.11(

    39、3)并使它进入就绪状态。然后,调用 OSSched()任务调度函数检查该任务是否是系统中的最高优先级的就绪任务L6.11(4)。如果是,这时就要进行任务切换当 OSSemPost()函数是在任务中调用的,准备执行该就绪任务。如果不是,OSSched()直接返回,调用 OSSemPost()的任务得以继续执行。如果这时没有任务在等待该信号量,该信号量的计数值就简单地加 1L6.11(5)。上面是由任务调用 OSSemPost()时的情况。当中断服务子程序调用该函数时,不会发生上面的任务切换。如果需要,任务切换要等到中断嵌套的最外层中断服务子程序调用 OSIntExit()函数后才能进行(见 3.

    40、09 节,C/OS-II 中的中断)。6-程序清单 L6.11 发出一个信号量INT8U OSSemPost (OS_EVENT *pevent)OS_ENTER_CRITICAL();if (pevent-OSEventType != OS_EVENT_TYPE_SEM) (1)OS_EXIT_CRITICAL();return (OS_ERR_EVENT_TYPE);if (pevent-OSEventGrp) (2)OSEventTaskRdy(pevent, (void *)0, OS_STAT_SEM); (3)OS_EXIT_CRITICAL();OSSched(); (4)ret

    41、urn (OS_NO_ERR); else if (pevent-OSEventCnt OSEventCnt+; (5)OS_EXIT_CRITICAL();return (OS_NO_ERR); else OS_EXIT_CRITICAL();return (OS_SEM_OVF);6.5.4 无等待地请求一个信号量, OSSemAccept()当一个任务请求一个信号量时,如果该信号量暂时无效,也可以让该任务简单地返回,而不是进入睡眠等待状态。这种情况下的操作是由 OSSemAccept()函数完成的,其源代码见程序清单 L6.12。该函数在最开始也是检查参数指针 pevent 指向的事件控

    42、制块是否是由OSSemCreate()函数建立的L6.12(1),接着从该信号量的事件控制块中取出当前计数值L6.12(2),并检查该信号量是否有效(计数值是否为非 0 值)L6.12(3)。如果有效,则将信号量的计数值减 1L6.12(4),然后将信号量的原有计数值返回给调用函数L6.12(5)。调用函数需要对该返回值进行检查。如果该值是 0,说明该信号量无效。如果该值大于 0,说明该信号量有效,同时该值也暗示着该信号量当前可用的资源数。应该注意的是,这些可用资源中,已经被该调用函数自身占用了一个(该计数值已经被减 1)。中断服务子程序要请求信号量时,只能用 OSSemAccept()而不能

    43、用 OSSemPend(),因为中断服务子程序是不允许等待的。6-程序清单 L6.12 无等待地请求一个信号量INT16U OSSemAccept (OS_EVENT *pevent)INT16U cnt;OS_ENTER_CRITICAL();if (pevent-OSEventType != OS_EVENT_TYPE_SEM) (1)OS_EXIT_CRITICAL();return (0);cnt = pevent-OSEventCnt; (2)if (cnt 0) (3)pevent-OSEventCnt-; (4)OS_EXIT_CRITICAL();return (cnt); (

    44、5)6.5.5 查询一个信号量的当前状态, OSSemQuery()在应用程序中,用户随时可以调用函数 OSSemQuery()程序清单 L6.13来查询一个信号量的当前状态。该函数有两个参数:一个是指向信号量对应事件控制块的指针 pevent。该指针是在生产信号量时,由 OSSemCreate()函数返回的;另一个是指向用于记录信号量信息的数据结构 OS_SEM_DATA(见 uCOS_II.H)的指针 pdata。因此,调用该函数前,用户必须先定义该结构变量,用于存储信号量的有关信息。在这里,之所以使用一个新的数据结构的原因在于,调用函数应该只关心那些和特定信号量有关的信息,而不是象 OS

    45、_EVENT 数据结构包含的很全面的信息。该数据结构只包含信号量计数值.OSCnt 和等待任务列表.OSEventTbl、.OSEventGrp,而 OS_EVENT 中还包含了另外的两个域.OSEventType 和.OSEventPtr。和其它与信号量有关的函数一样,OSSemQuery()也是先检查 pevent 指向的事件控制块是否是 OSSemCreate()产生的L6.13(1),然后将等待任务列表L6.13(2)和计数值L6.13(3)从 OS_EVENT 结构拷贝到 OS_SEM_DATA 结构变量中去。程序清单 L6.13 查询一个信号量的状态INT8U OSSemQuery

    46、 (OS_EVENT *pevent, OS_SEM_DATA *pdata)6-INT8U i;INT8U *psrc;INT8U *pdest;OS_ENTER_CRITICAL();if (pevent-OSEventType != OS_EVENT_TYPE_SEM) (1)OS_EXIT_CRITICAL();return (OS_ERR_EVENT_TYPE);pdata-OSEventGrp = pevent-OSEventGrp; (2)psrc = pdest = for (i = 0; i OSCnt = pevent-OSEventCnt; (3)OS_EXIT_CRITICAL();return (OS_NO_ERR);6.6 邮箱邮箱是 C/OS-II 中另一种通讯机制,它可以使一个任务或者中断服务子程序向另一个任务发送一个指针型的变量。该指针指向一个包含了特定“消息”的数据结构。为了在 C/OS-II中使用邮箱,必须将 OS_CFG.H 中的 OS_MBOX_EN 常数置为 1。使用邮箱之前,必须先建立该邮箱。该

    展开阅读全文
    提示  道客多多所有资源均是用户自行上传分享,仅供网友学习交流,未经上传用户书面授权,请勿作他用。
    关于本文
    本文标题:第6章 嵌入式实时操作系统UCOS-II(第2版).doc
    链接地址:https://www.docduoduo.com/p-2303587.html
    关于我们 - 网站声明 - 网站地图 - 资源地图 - 友情链接 - 网站客服 - 联系我们

    道客多多用户QQ群:832276834  微博官方号:道客多多官方   知乎号:道客多多

    Copyright© 2025 道客多多 docduoduo.com 网站版权所有世界地图

    经营许可证编号:粤ICP备2021046453号    营业执照商标

    1.png 2.png 3.png 4.png 5.png 6.png 7.png 8.png 9.png 10.png



    收起
    展开