您的位置:首页 > 其它

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

2015-09-18 16:40 162 查看
事件标志组管理由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)
该函数的实现跟请求事件标志组差不多。差别在于等待事件标志组不可用的时候。该函数的直接返回,并返回不可用状态。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: