【UCOSii源码解析】事件控制块

系列文章

  1. UCOSii启动流程以及平台相关文件分析
  2. 优先级算法及内核源码分析
  3. 任务管理
  4. 时间管理
  5. 事件控制块
  6. 内存管理
  7. 任务间通讯与同步

在这里插入图片描述

(一)事件控制块

这个任务控制块指向的任务,用的是位图的数据结构,并非是链表。

typedef struct os_event {
    INT8U    OSEventType;                    /*事件类型 Type of event control block (see OS_EVENT_TYPE_xxxx)    */
    void    *OSEventPtr;                     /*指向消息或者消息队列的指针Pointer to message or queue structure                   */
    INT16U   OSEventCnt;                     /*计数器Semaphore Count (not used if other EVENT type)          */
    OS_PRIO  OSEventGrp;                     /*等待任务所在的组Group corresponding to tasks waiting for event to occur */
    OS_PRIO  OSEventTbl[OS_EVENT_TBL_SIZE];  /*等待任务列表List of tasks waiting for event to occur                */

#if OS_EVENT_NAME_EN > 0u
    INT8U   *OSEventName;
#endif
} OS_EVENT;

.OSEventPtr指针,只有在所定义的事件是邮箱或者消息队列时才使用。当所定义的事件是邮箱时,它指向一个消息,而当所定义的事件是消息队列时,它指向一个数据结构
.OSEventTbl[] 和 .OSEventGrp 很像前面讲到的OSRdyTbl[]和OSRdyGrp,只不过前两者包含的是等待某事件的任务,而后两者包含的是系统中处于就绪状态的任务。
.OSEventCnt 当事件是一个信号量时,.OSEventCnt是用于信号量的计数器,。
.OSEventType定义了事件的具体类型。它可以是信号量(OS_EVENT_SEM)、邮箱(OS_EVENT_TYPE_MBOX)或消息队列(OS_EVENT_TYPE_Q)中的一种。用户要根据该域的具体值来调用相应的系统函数,以保证对其进行的操作的正确性。

(二)事件控制块的工作模式

在这里插入图片描述

(1)任务或者中断服务程序可以给事件控制块ECB发送信号。
(2)多个任务会等待同一事件的发生,在事件发生以后,高优先级的任务会进入就绪态。

在这里插入图片描述

任务控制块中的 OS_PRIO OSEventGrp;
OS_PRIO OSEventTbl[OS_EVENT_TBL_SIZE];
与任务管理中 OSRdyGrp OSTCBPrioTbl是想同的道理。

将一个任务插入一个事件的等待队列中

程序清单 L6.2——将一个任务插入到事件的等待任务列表中
pevent->OSEventGrp |= OSMapTbl[prio >> 3];
pevent->OSEventTbl[prio >> 3] |= OSMapTbl[prio & 0x07];

其中,prio是任务的优先级,pevent是指向事件控制块的指针。

将一个任务从任务链表中删除

程序清单 L6.3 从等待任务列表中删除一个任务
if ((pevent->OSEventTbl[prio >> 3] &= ~OSMapTbl[prio & 0x07]) == 0) {
pevent->OSEventGrp &= ~OSMapTbl[prio >> 3];
}

在等待任务列表中查找最高优先级的任务

y    = OSUnMapTbl[pevent->OSEventGrp];
x    = OSUnMapTbl[pevent->OSEventTbl[y]];
prio = (y << 3) + x;

和任务管理中的处理方式几乎一毛一样。

(三)任务控制块的产生

初始化一个事件控制块,OSEventWaitListInit()

当建立一个信号量、邮箱或者消息队列时,相应的建立函数OSSemInit(),OSMboxCreate(),或者OSQCreate()通过调用OSEventWaitListInit()对事件控制块中的等待任务列表进行初始化。该函数初始化一个空的等待任务列表,其中没有任何任务。该函数的调用参数只有一个,就是指向需要初始化的事件控制块的指针pevent。

void OSEventWaitListInit (OS_EVENT *pevent)
{
    INT8U i;
    pevent->OSEventGrp = 0x00;
    for (i = 0; i < OS_EVENT_TBL_SIZE; i++) {
        pevent->OSEventTbl[i] = 0x00;
    }
}

使一个任务进入就绪态

597 **********************************************************************************************
598 * 使一个任务进入就绪态 MAKE TASK READY TO RUN BASED ON EVENT OCCURING
599 *
600 * 描述: 当发生了某个事件,该事件等待任务列表中的最高优先级任务(HPT)要置于就绪态时,该事件对应
601 *OSSemPost()OSMboxPost()OSQPost(),和OSQPostFront()函数调用OSEventTaskRdy()实现
602 * 该操作。换句话说,该函数从等待任务队列中删除HPT任务,并把该任务置于就绪态
603 *
604 * 参数: pevent is a pointer to the event control block corresponding to the event
608 * service functions.
609 *
610 * msk is a mask that is used to clear the status byte of the TCB. For example,
611 * OSSemPost() will pass OS_STAT_SEM, OSMboxPost() will pass OS_STAT_MBOX etc.
612 *
613 * 返回:614 *
615 * 注意: 这个函数是uC/OS-II内部函数,你不可以在应用程序调用它,调用此函数也应当关闭中断
616 **********************************************************************************************
617 */
618 #if OS_EVENT_EN > 0 //各类消息事件是否允许
619 //使一个任务进入就绪态
620 INT8U OS_EventTaskRdy (OS_EVENT *pevent, void *msg, INT8U msk)
621 {
622 OS_TCB *ptcb;
623 INT8U x;
624 INT8U y;
625 INT8U bitx;
626 INT8U bity;
627 INT8U prio;
628
629 //1)首先计算HPT任务在.OSEventTbl[]中的字节索引,其结果是一个从0到OS_LOWEST_PRIO/8+1之间的数
630 //2)并利用该索引得到该优先级任务在.OSEventGrp中的位屏蔽码
631 //3)判断HPT任务在.OSEventTbl[]中相应位的位置
632 //4)其结果是一个从0到OS_LOWEST_PRIO/8+1之间的数,以及相应的位屏蔽码
633 //5)根据以上结果,OSEventTaskRdy()函数计算出HPT任务的优先级
634 //6)然后就可以从等待任务列表中删除该任务了
635 y = OSUnMapTbl[pevent->OSEventGrp]; //1)
636 bity = OSMapTbl[y]; //2)
637 x = OSUnMapTbl[pevent->OSEventTbl[y]]; //3)
638 bitx = OSMapTbl[x]; //4)
639 prio = (INT8U)((y << 3) + x); //5)
640 if ((pevent->OSEventTbl[y] &= ~bitx) == 0x00) { //6)
641 pevent->OSEventGrp &= ~bity;
642 }
643 //7)任务的TCB中包含有需要改变的信息。知道了HPT任务的优先级,就可得到指向该任务的TCB的指针
644 //8)因为最高优先级任务运行条件已经得到满足,必须停止OSTimeTick()函数对.OSTCBDly域的递减操作,
645 // 所以OSEventTaskRdy()直接将该域清澈0
646 //9)因为该任务不再等待该事件的发生,所以本函数将其任务控制块中指向事件控制块的指针指向NULL
647 //10)如果OSEventTaskRdy()是由OSMboxPost()或者OSQPost()调用的,该函数还要将相应的消息传递给
648 // HPT,放在它的任务控制块中
649 ptcb = OSTCBPrioTbl[prio]; //7)
650 ptcb->OSTCBDly = 0; //8)
651 ptcb->OSTCBEventPtr = (OS_EVENT *)0; //9)
652 #if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0)
653 ptcb->OSTCBMsg = msg; //10)
654 #else
655 msg = msg;
656 #endif
657 //11)当OSEventTaskRdy()被调用时,位屏蔽码msk作为参数传递给它。该参数是用于对任务控制块中的
658 // 位清零的位屏蔽码,和所发生事件的类型相对应
659 //12)根据.OSTCBStat判断该任务是否已处于就绪状态
660 //13)如果是, 则将HPT插入到uC/OS-II的就绪任务列表中。注意,HPT任务得到该事件后不一定进入就绪
661 // 状态,也许该任务已经由于其它原因挂起了
662 ptcb->OSTCBStat &= ~msk; //11)
663 if (ptcb->OSTCBStat == OS_STAT_RDY) { //12)
664 OSRdyGrp |= bity; //13)
665 OSRdyTbl[y] |= bitx; //返回就绪态任务的优先级
666 }
667 return (prio);
668 }
669 #endif

这个函数主要完成两个操作,通过pevent->OSEventGrp,算出该任务控制块中最高任务的优先级,然后将这个任务变成就绪态。

使一个任务进入等待状态

672 **********************************************************************************************
673 * 使一个任务进入等待某事件发生状态 MAKE TASK WAIT FOR EVENT TO OCCUR
674 *
675 * 描述: 当某个任务须等待一个事件的发生时,信号量、互斥型信号量、邮箱以及消息队列会通过相应的
676 * PEND函数调用本函数,使当前任务从就绪任务表中脱离就绪态,并放到相应的事件控制块ECB的等
677 * 待任务表中
678 *
679 * 参数: pevent 分配给事件控制块的指针,为等待某事件发生的任务
680 *
681 * 返回:682 *
683 * 注意: 这个函数是uC/OS-II内部函数,你不可在应用程序中调用它,调用OS_EventTO()也应当关闭中断
684 **********************************************************************************************
685 */
686 #if OS_EVENT_EN > 0 //各类消息事件是否允许
687 void OS_EventTaskWait (OS_EVENT *pevent) //使一个任务进入等待某事件发生状态(ECB指针)
688 { //将指向事件控制块ECB的指针放到任务的任务控制块TCB中,建立任务与事件控制块ECB之间的链接
689 OSTCBCur->OSTCBEventPtr = pevent;
690 //将任务从就绪任务表中删除
691 if ((OSRdyTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0x00) {
692 OSRdyGrp &= ~OSTCBCur->OSTCBBitY;
693 }
694 //把该任务放到事件控制块ECB的等待事件的任务列表中
695 pevent->OSEventTbl[OSTCBCur->OSTCBY] |= OSTCBCur->OSTCBBitX;
696 pevent->OSEventGrp |= OSTCBCur->OSTCBBitY;
697 }
698 #endif
699 /*$PAGE*/

将任务从就绪表删除,将任务的优先级放入事件控制块等待事件的任务列表中。

701 **********************************************************************************************
702 * 由于超时而将任务置为就绪态 MAKE TASK READY TO RUN BASED ON EVENT TIMEOUT
703 *
704 * 描述: 如果在预先指定的等待时限内任务等待的事件没有发生,那么本函数会因为等待超时而将任务的
705 * 状态置为就绪态。在这种情况下,信号量、互斥型信号量、邮箱以及消息队列会通过PEND函数调
706 * 用本函数,以完成这项工作
707 *
708 * 参数: pevent 分配给事件控制块的指针,为超时就绪态的任务
709 *
710 * 返回:711 *
712 * 注意: 这个函数是uC/OS-II内部函数,你不可以在应用程序中调用它,调用OS_EventTO()也应当关闭中断
713 **********************************************************************************************
714 */
715 #if OS_EVENT_EN > 0 //消息事件是否 > 0
716 void OS_EventTO (OS_EVENT *pevent) //由于超时而将任务置为就绪态(ECB指针)
717 { //本函数必须从事件控制块ECB中等待任务列表中将该任务删除
718 if ((pevent->OSEventTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0x00) {
719 pevent->OSEventGrp &= ~OSTCBCur->OSTCBBitY;
720 }
721 OSTCBCur->OSTCBStat = OS_STAT_RDY; //该任务被置为就绪态
722 OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; //从任务控制块TCB中将事件控制块ECB的指针删除
723 }
724 #endif
725 /*$PAGE*/?
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

与光同程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值