UC/OS II 事件标志组管理(二)

事件标志组管理由9个函数实现,下面的8函数加上之前的初始化函数,其代码定义在os_flag.c中。

1:创建事件标志组OSFlagCreate
创建事件标志组函数OSFlagCreate主要功能为从空闲链表中去一块时间标志组控制块,进行一些属性的设置。
其函数原型为:OS_FLAG_GRP  *OSFlagCreate (OS_FLAGS  flags,INT8U    *perr)  其中flags是事件组合的标志,是位掩码,表示事件要等待哪些位。如果应用程序等待任务组中事件标志中的0到2,则为0x05
除了参数检查之外,主要的功能的代码如下:
pgrp = OSFlagFreeList;                          
    if (pgrp != (OS_FLAG_GRP *)0) {                 /* See if we have event flag groups available      */
                                                    //从空闲链表表头取事件标志组控制块
        OSFlagFreeList       = (OS_FLAG_GRP *)OSFlagFreeList->OSFlagWaitList;
        pgrp->OSFlagType     = OS_EVENT_TYPE_FLAG;  //设置为标志组事件
        pgrp->OSFlagFlags    = flags;               //将形参的事件标志flag赋值给OSFlagFlags成员
        pgrp->OSFlagWaitList = (void *)0;           //首个事件标志节点指针设置为空

2:事件标志组阻塞函数OS_FlagBlock
事件标志组阻塞函数是将等待事件标志组的任务阻塞,知道请求的事件标志被设置
其函数原型如下:static  void  OS_FlagBlock (OS_FLAG_GRP  *pgrp, OS_FLAG_NODE  *pnode,OS_FLAGS  flags,INT8U  wait_type, INT32U  timeout)
wait_type:等待类型,说明是所有等待所有位都被置位还是只要任务位被置位。
函数功能实现代码如下:
 //更改当前任务的任务控制块控制信息
  OSTCBCur->OSTCBStat      |= OS_STAT_FLAG;
    OSTCBCur->OSTCBStatPend   = OS_STAT_PEND_OK;
    OSTCBCur->OSTCBDly        = timeout;              
#if OS_TASK_DEL_EN > 0u
    OSTCBCur->OSTCBFlagNode   = pnode;                //将任务控制块的事件标志节点指针指向该节点
#endif
//设置标志组节点
    pnode->OSFlagNodeFlags    = flags;                /* Save the flags that we need to wait for       */
    pnode->OSFlagNodeWaitType = wait_type;            /* Save the type of wait we are doing            */
    pnode->OSFlagNodeTCB      = (void *)OSTCBCur;     /* Link to task's TCB                            */
    pnode->OSFlagNodeNext     = pgrp->OSFlagWaitList; //将节点的指针指向节点链表的头部
    pnode->OSFlagNodePrev     = (void *)0;
    pnode->OSFlagNodeFlagGrp  = (void *)pgrp;      /* Link to Event Flag Group                      */
    pnode_next                = (OS_FLAG_NODE *)pgrp->OSFlagWaitList;
    if (pnode_next != (void *)0) {                    /*如果为空地址,只有一个节点(就是本节点),如果不为空地址,那么将该链表的头节点改为现在这个节点 */
        pnode_next->OSFlagNodePrev = pnode;           /将之前头节点指向现在这个节点/
    }
    pgrp->OSFlagWaitList = (void *)pnode; //将事件标志组指向该节点
//上面的的功能就是将pnode设置为链表的新的头节点
    y            =  OSTCBCur->OSTCBY;                 //取消该任务的就绪状态
    OSRdyTbl[y] &= (OS_PRIO)~OSTCBCur->OSTCBBitX;
    if (OSRdyTbl[y] == 0x00u) {
        OSRdyGrp &= (OS_PRIO)~OSTCBCur->OSTCBBitY;

3:请求事件标志OSFlagPend
该函数的主要功能是用于等待事件标志组中的组合条件。
函数的主要源码如下:
wait_type等待类型可以是下面说的四种情况之一加上或者不加OS_FLAG_CONSUME 。
#define  OS_FLAG_WAIT_CLR_ALL           0u  
#define  OS_FLAG_WAIT_CLR_ANY           1u 
#define  OS_FLAG_WAIT_SET_ALL           2u  
#define  OS_FLAG_WAIT_SET_ANY           3u  
#define  OS_FLAG_CONSUME             0x80u  
例如wait_type可以为OS_FLAG_WAIT_CLR_ALL,即0x01或者OS_FLAG_WAIT_CLR_ALL + OS_FLAG_CONSUME  即0x81。两个的区别在于是否是消耗类型的。
所谓的消耗与否,就是事件组满足条件后,获得事件标志组后。事件标志组控制块的对应事件标志清除,即消耗掉。
flags是事件组合的标志,是位掩码,表示事件要等待哪些位。如果应用程序等待任务组中事件标志中的0到2,即掩码为0x05。

result = (INT8U)(wait_type & OS_FLAG_CONSUME);  //将wiat_type与OS_FLAG_COUNSUME进行与,结果存放在result中。若是rusult为0,则说明wait_type是没有加上OS_FLAG_CONSUME的,也就是非消耗类型的,否则就是消耗类型的
    if (result != (INT8U)0) {                              /* See if we need to consume the flags      */
        wait_type &= (INT8U)~(INT8U)OS_FLAG_CONSUME; //消耗类型的,就将wait_type的高四位清零,保留低四位
        consume    = OS_TRUE;  //消耗类型的,conusme 设为ture
    } else {
        consume    = OS_FALSE; //非消耗类型,consume设为false
    }
下面就是根据不同的wait_type等待类型就是执行不同的代码
case OS_FLAG_WAIT_SET_ALL:                         //等待选择的所有位都置1
             flags_rdy = (OS_FLAGS)(pgrp->OSFlagFlags & flags);   //事件标志组中与掩码进行and运算,提取出事件标志位的位中所满足条件的位。即掩码和标志位都为1的位,即为请求的标志位
             if (flags_rdy == flags) {                     //判断是否所有请求的标志都被置位。如果是
                 if (consume == OS_TRUE) {                 //看是否为消耗类型
                     pgrp->OSFlagFlags &= (OS_FLAGS)~flags_rdy;   //清零请求事件组中对应请求标志位的位,消耗了事件
                 }
                 OSTCBCur->OSTCBFlagsRdy = flags_rdy;      //将当前的请求的标志位存在当前任务控制块的OSTCBFlagsRdy中
                 OS_EXIT_CRITICAL();                       /* Yes, condition met, return to caller     */
                 *perr                   = OS_ERR_NONE;
                 return (flags_rdy);
             } else {                                      //否则阻塞,等待事件标志组完成或者是超时
                 OS_FlagBlock(pgrp, &node, flags, wait_type, timeout);
                 OS_EXIT_CRITICAL();
             }
             break;

case OS_FLAG_WAIT_SET_ANY:               //等待选择的任何位置位1
             flags_rdy = (OS_FLAGS)(pgrp->OSFlagFlags & flags);    /* Extract only the bits we want    */
             if (flags_rdy != (OS_FLAGS)0) {               //是否有任何为被置为1
                 if (consume == OS_TRUE) {                 //消耗类型
                     pgrp->OSFlagFlags &= (OS_FLAGS)~flags_rdy;    /* Clear ONLY the flags that we got */
                 }
                 OSTCBCur->OSTCBFlagsRdy = flags_rdy;      /* Save flags that were ready               */
                 OS_EXIT_CRITICAL();                       /* Yes, condition met, return to caller     */
                 *perr                   = OS_ERR_NONE;
                 return (flags_rdy);
             } else {                                      //不满足条件,则阻塞
                 OS_FlagBlock(pgrp, &node, flags, wait_type, timeout);
                 OS_EXIT_CRITICAL();
             }
             break;

case OS_FLAG_WAIT_CLR_ALL:                         //等待选的所有位都为0
             flags_rdy = (OS_FLAGS)~pgrp->OSFlagFlags & flags;    //这跟前面有所不同,在前面用了一个取反,因为这里等待是要求所有位(对应的位)为0
             if (flags_rdy == flags) {                     //设置的等待的所有位都为0
                 if (consume == OS_TRUE) {                 //
                     pgrp->OSFlagFlags |= flags_rdy;       /* Set ONLY the flags that we wanted        */
                 }
                 OSTCBCur->OSTCBFlagsRdy = flags_rdy;      /* Save flags that were ready               */
                 OS_EXIT_CRITICAL();                       /* Yes, condition met, return to caller     */
                 *perr                   = OS_ERR_NONE;
                 return (flags_rdy);
             } else {                                      /* Block task until events occur or timeout */
                 OS_FlagBlock(pgrp, &node, flags, wait_type, timeout);
                 OS_EXIT_CRITICAL();
             }
             break;

case OS_FLAG_WAIT_CLR_ANY: //选择等待的位有任何一位为0
             flags_rdy = (OS_FLAGS)~pgrp->OSFlagFlags & flags;   /* Extract only the bits we want      */
             if (flags_rdy != (OS_FLAGS)0) {               //设置的等待位中有一位被置0
                 if (consume == OS_TRUE) {                 /* See if we need to consume the flags      */
                     pgrp->OSFlagFlags |= flags_rdy;       /* Set ONLY the flags that we got           */
                 }
                 OSTCBCur->OSTCBFlagsRdy = flags_rdy;      /* Save flags that were ready               */
                 OS_EXIT_CRITICAL();                       /* Yes, condition met, return to caller     */
                 *perr                   = OS_ERR_NONE;
                 return (flags_rdy);
             } else {                                      /* Block task until events occur or timeout */
                 OS_FlagBlock(pgrp, &node, flags, wait_type, timeout);
                 OS_EXIT_CRITICAL();
             }
             break;

OS_Sched();                                            //执行调度

当任务重新就绪(超时或者得到满足),任务从这里开始执行。
    OS_ENTER_CRITICAL();
    if (OSTCBCur->OSTCBStatPend != OS_STAT_PEND_OK) {      //回到任务就绪后的原因
        pend_stat                = OSTCBCur->OSTCBStatPend;
        OSTCBCur->OSTCBStatPend  = OS_STAT_PEND_OK;
        OS_FlagUnlink(&node);
        OSTCBCur->OSTCBStat      = OS_STAT_RDY;            //修改任务任务控制块的运行状态
        OS_EXIT_CRITICAL();
        flags_rdy                = (OS_FLAGS)0;
        switch (pend_stat) {
            case OS_STAT_PEND_ABORT:
                 *perr = OS_ERR_PEND_ABORT;                //异常
                 break;

            case OS_STAT_PEND_TO:
            default:
                 *perr = OS_ERR_TIMEOUT;                   //超时
                 break;
        }
        return (flags_rdy);//因为是超时或者异常而恢复到就绪状态,所以直接返回
    }
//下面是在时间内,条件得到满足,需要完成的
    flags_rdy = OSTCBCur->OSTCBFlagsRdy; //从任务控制块中取出等待事件组的标志
    if (consume == OS_TRUE) {                              //消耗类型,则需要根据不同的等待类型,对等待事件组进行清除。
        switch (wait_type) {
            case OS_FLAG_WAIT_SET_ALL:
            case OS_FLAG_WAIT_SET_ANY:                     
                 pgrp->OSFlagFlags &= (OS_FLAGS)~flags_rdy;
                 break;

#if OS_FLAG_WAIT_CLR_EN > 0u
            case OS_FLAG_WAIT_CLR_ALL:
            case OS_FLAG_WAIT_CLR_ANY:                     /* Set   ONLY the flags we got              */
                 pgrp->OSFlagFlags |=  flags_rdy;
                 break;
#endif
            default:
                 OS_EXIT_CRITICAL();
                 *perr = OS_ERR_FLAG_WAIT_TYPE;
                 return ((OS_FLAGS)0);
        }
    }
    OS_EXIT_CRITICAL();
    *perr = OS_ERR_NONE;                                   /* Event(s) must have occurred   */
    return (flags_rdy);

4:查询事件标志组组信息OSFlagQuery
函数原型为OS_FLAGS  OSFlagQuery (OS_FLAG_GRP  *pgrp, INT8U   *perr)
5:标志节点任务就绪OS_FlagTaskRdy
该函数的原型如下:BOOLEAN  OS_FlagTaskRdy (OS_FLAG_NODE *pnode,OS_FLAGS      flags_rdy)
其主要功能是将标志节点pnode指向的任务转为就绪态
代码如下:
OS_TCB   *ptcb;
    BOOLEAN   sched;
//获取任务控制块的地址,同时将任务控制块修改跟事件标志组相关的数据结构
    ptcb                 = (OS_TCB *)pnode->OSFlagNodeTCB; 
    ptcb->OSTCBDly       = 0u;
    ptcb->OSTCBFlagsRdy  = flags_rdy; 
    ptcb->OSTCBStat     &= (INT8U)~(INT8U)OS_STAT_FLAG;
    ptcb->OSTCBStatPend  = OS_STAT_PEND_OK;
    if (ptcb->OSTCBStat == OS_STAT_RDY) {                  //任务状态为就绪
        OSRdyGrp               |= ptcb->OSTCBBitY;         //更新就绪数组和就绪表
        OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
        sched                   = OS_TRUE;
    } else {
        sched                   = OS_FALSE;
    }
    OS_FlagUnlink(pnode);
    return (sched);
}
6:删除事件标志组OSFlagDel
该函数的原型如下:OS_FLAG_GRP  *OSFlagDel (OS_FLAG_GRP  *pgrp, INT8U  opt,INT8U *perr)
该函数的主要功能就是删除事件组,并且对相应的数据结构进行操作。
参数opt有两种情况,OS_DEL_NO_PEND和OS_DEL_ALWAYS。
OS_DEL_NO_PEND 在没有任务等待该事件组的的时候删除该事件标志组
OS_DEL_ALWAYS  不管什么情况,始终删除该标志组
其主要代码如下:
if (pgrp->OSFlagWaitList != (void *)0) {               //是否有事件标志节点,从而判断是否有任务等待该事件标志组
        tasks_waiting = OS_TRUE;                           /* Yes                                      */
    } else {
        tasks_waiting = OS_FALSE;                          /* No                                       */
    }
  switch (opt) {
case OS_DEL_NO_PEND:                               //在没有任务等待的情况下删除事件标志组
             if (tasks_waiting == OS_FALSE) { //判断是否有任务等待该事件标志组,如果是有就进行删除操作
                 pgrp->OSFlagName     = (INT8U *)(void *)"?";
                 pgrp->OSFlagType     = OS_EVENT_TYPE_UNUSED; //将事件标志组类型改为OS_EVENT_TYPE_UNUSED,即未被使用
                 pgrp->OSFlagWaitList = (void *)OSFlagFreeList; //将该事件标志组加入到事件标志组空闲链表中
                 pgrp->OSFlagFlags    = (OS_FLAGS)0;
                 OSFlagFreeList       = pgrp;   
                 OS_EXIT_CRITICAL();
                 *perr                = OS_ERR_NONE;
                 pgrp_return          = (OS_FLAG_GRP *)0;  /* Event Flag Group has been deleted        */
             } else { //有任务在等待该事件标志组,不执行删除操作,返回
                 OS_EXIT_CRITICAL();
                 *perr                = OS_ERR_TASK_WAITING;
                 pgrp_return          = pgrp;
             }
             break;

        case OS_DEL_ALWAYS:                                //当为始终删除该等待事件组的时候
             pnode = (OS_FLAG_NODE *)pgrp->OSFlagWaitList;//指向等待事件组的节点链表
             while (pnode != (OS_FLAG_NODE *)0) {          //遍历该链表
                 (void)OS_FlagTaskRdy(pnode, (OS_FLAGS)0); //将该链表中的节点指向的任务转为就绪状态
                 pnode = (OS_FLAG_NODE *)pnode->OSFlagNodeNext;
             }
//下面就是该等待事件标志组控制块加入到空闲链表中
             pgrp->OSFlagName     = (INT8U *)(void *)"?"; 
#endif
             pgrp->OSFlagType     = OS_EVENT_TYPE_UNUSED;
             pgrp->OSFlagWaitList = (void *)OSFlagFreeList;/* Return group to free list                */
             pgrp->OSFlagFlags    = (OS_FLAGS)0;
             OSFlagFreeList       = pgrp;
             OS_EXIT_CRITICAL();
             if (tasks_waiting == OS_TRUE) {               //如果之前是有任务在等待事件标志组
                 OS_Sched();                               //执行调度
             }
             *perr = OS_ERR_NONE;
             pgrp_return          = (OS_FLAG_GRP *)0;      /* Event Flag Group has been deleted        */
             break;

        default:
             OS_EXIT_CRITICAL();
             *perr                = OS_ERR_INVALID_OPT;
             pgrp_return          = pgrp;
             break;
    }

7:提交事件标志组OSFlagPost
提交事件标志组函数将对事件标志组中的事件标志进行操作,并且根据事件等待的标志,恢复阻塞的任务就绪。
其函数原型如下:OS_FLAGS  OSFlagPost (OS_FLAG_GRP  *pgrp,OS_FLAGS      flags,INT8U         opt,  INT8U        *perr)
其参数中OPT 有两个选项OS_FLAG_SET和OS_FLAG_CLR,flags为掩码之前有解释很详细。
OS_FLAG_SET是将掩码对应的位置为1
OS_FLAG_CLR是将掩码对应的位置为0
其源码如下:

switch (opt) {
        case OS_FLAG_CLR: //掩码对应的位清零
             pgrp->OSFlagFlags &= (OS_FLAGS)~flags;  //掩码取反然后将,进行AND运算,就将掩码对应的位置清零
             break;

        case OS_FLAG_SET: //掩码对应的位置为1
             pgrp->OSFlagFlags |=  flags;            //直接进行OR运算,对应的位就为1
             break;

        default:
             OS_EXIT_CRITICAL();                     /* INVALID option                                 */
             *perr = OS_ERR_FLAG_INVALID_OPT;
             return ((OS_FLAGS)0);
    }
    sched = OS_FALSE;                                //sched取值表示该是否需要进行调度
    pnode = (OS_FLAG_NODE *)pgrp->OSFlagWaitList; //取得第一个事件标志节点的地址
    while (pnode != (OS_FLAG_NODE *)0) {             //若该地址不为空,则说说明标志节点链表不为空,同时说明有任务在等待该事件标志组,遍历该链表
        switch (pnode->OSFlagNodeWaitType) {  //事件标志节点的等待类型
            case OS_FLAG_WAIT_SET_ALL:               //所有等待事件对应的位设置为1
                 flags_rdy = (OS_FLAGS)(pgrp->OSFlagFlags & pnode->OSFlagNodeFlags);
                 if (flags_rdy == pnode->OSFlagNodeFlags) { //判断事情是全部否发生
                     rdy = OS_FlagTaskRdy(pnode, flags_rdy);  //若是,则将对应任务运行状态由阻塞转为就绪
                     if (rdy == OS_TRUE) {
                         sched = OS_TRUE;                     //同时sched赋值,表示可以调度
                     }
                 }
                 break;

            case OS_FLAG_WAIT_SET_ANY:               //任一等待事件的位设置为1
                 flags_rdy = (OS_FLAGS)(pgrp->OSFlagFlags & pnode->OSFlagNodeFlags);
                 if (flags_rdy != (OS_FLAGS)0) {
                     rdy = OS_FlagTaskRdy(pnode, flags_rdy);  /* Make task RTR, event(s) Rx'd*/
                     if (rdy == OS_TRUE) {
                         sched = OS_TRUE;                     /* When done we will reschedule          */
                     }
                 }
                 break;

#if OS_FLAG_WAIT_CLR_EN > 0u
            case OS_FLAG_WAIT_CLR_ALL:               //所有的等待事件的位都为0
                 flags_rdy = (OS_FLAGS)~pgrp->OSFlagFlags & pnode->OSFlagNodeFlags;
                 if (flags_rdy == pnode->OSFlagNodeFlags) { 
                     rdy = OS_FlagTaskRdy(pnode, flags_rdy);  /* Make task RTR, event(s) Rx'd          */
                     if (rdy == OS_TRUE) {
                         sched = OS_TRUE;                     /* When done we will reschedule          */
                     }
                 }
                 break;

            case OS_FLAG_WAIT_CLR_ANY:               //若有等待事件对应的位的任意位为0
                 flags_rdy = (OS_FLAGS)~pgrp->OSFlagFlags & pnode->OSFlagNodeFlags;
                 if (flags_rdy != (OS_FLAGS)0) {
                     rdy = OS_FlagTaskRdy(pnode, flags_rdy);  /* Make task RTR, event(s) Rx'd          */
                     if (rdy == OS_TRUE) {
                         sched = OS_TRUE;                     /* When done we will reschedule          */
                     }
                 }
                 break;
#endif
            default:
                 OS_EXIT_CRITICAL();
                 *perr = OS_ERR_FLAG_WAIT_TYPE;
                 return ((OS_FLAGS)0);
        }
        pnode = (OS_FLAG_NODE *)pnode->OSFlagNodeNext; /指向下一个节点/
    }
    OS_EXIT_CRITICAL();
    if (sched == OS_TRUE) { //调度
        OS_Sched();
    }
 
    OS_ENTER_CRITICAL();
    flags_cur = pgrp->OSFlagFlags; 
    OS_EXIT_CRITICAL();
    *perr     = OS_ERR_NONE;
    return (flags_cur);//返回事件标志组的事件标志
}
从这里可以看出,当有事件标志组提交的时候,事件标志组的管理以及获取不是根据优先级优先得到的,而是后申请(阻塞)的任务先得到,先申请(阻塞)任务后得到。
因为后面申请并阻塞的任务的时候,最近阻塞节点总是在链表的第一个节点,而唤醒等待任务的时候,是遍历的,第一个任务肯定是最早的。这是跟前面的信号量是有很大的不同
8:不等待请求事件标志组OSFlagAccept
该函数的主要功能就是请求事件标志组,若事件标志组的不可用,则不阻塞,转而执行其他。
其函数原型如下:OS_FLAGS  OSFlagAccept (OS_FLAG_GRP  *pgrp, OS_FLAGS      flags, INT8U         wait_type,NT8U        *perr)
该函数的实现跟请求事件标志组差不多。差别在于等待事件标志组不可用的时候。该函数的直接返回,并返回不可用状态。







  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值