(1)UCOSII的事件标志组由2部分组成:
一是用来保存当前事件组中各事件状态的一些标志位。
二是等待这些标志位置位或者清除的任务列表。
(2)UCOSII提供了6个函数,完成事件标志组的各种功能:
OSFlagAccept(),OSFlagCreate(),OSFlagDel(),
OSFlagPend(),OSFlagPost()以及OSFlagQuery();
(1)任务或者中断都可以调用函数OSFlagAccept(),OSFlagPost()或者OSFlagQuery();但是只有任务才可以调用函数OSFlagCreate(),OSFlagDel()以及OSFlagPend();
(1)参数1:OSFlagType变量用来检测指针的类型是否是指向事件标志组的指针,这个变量是事件标志组数据结构的第一个成员。
注意:事件控制块ECB的第一个字节也是一个标志该事件控制块的类型:
信号量,互斥型信号量,邮箱或者消息队列的变量
(2)参数2:OSFlagWaitList包含了一个等待事件标志组的任务列表。
(3)OSFlagFlags包含了一系列表明当前事件标志状态的位,这些位的数目在编译时由OS_CFG.h中的OS_FLAGS常量决定。
typedef struct
{
INT8U OSFlagType; //定义一个类型,用来放置到事件标志组中
void *OSFlagWaitList; //定义一个事件标志链表
OS_FLAGS OSFlagFlags;
}OS_FLAG_GRP;
这里使用的等待事件标志组的任务列表和UCOSII里面用到的其他的等待任务列表不同。这里的任务列表是一个双向链表,使用了3个数据结构。
当一个任务开始等待某些事件标志位时,就建立一个OS_FLAG_NODE数据结构,当这些等待的事件标志位发生后,这个数据结构被删除,就是换句话说,当调用OSFlagPend()时,建立OS_FLAG_NODE.
(4)OS_FLAG_NODE的结构定义
OSFlagNodeNext和OSFlagNodePrev用来构建双向OS_FLAG_NODE数据结构链表、这种双向链表可以很方便地向链表中插入或者从链表中删除一个OS_FLAG_NODE的结构。
OSFlagNodeTCB指针指向某个等待事件标志组中的事件标志的任务控制块。
换句话说,通过这个指针,可以知道哪一个任务在等待事件标志组中的事件。
typedef struct
{
void *OSFlagNodeNext;
void *OSFlagNodePrev;
void *OSFlagNodeTCB;
void *OSFlagNodeFlagGrp;
OS_FLAGS OSFlagNodeFlags;
INT8U OSFlagNodeWaitType;
}OS_FLAG_NODE;
实现事件标志组的链表:
根据图中可以详细的知道一个事件控制组的列表是由三个部分组成的:
(1)首先是定义事件控制块的类型:OSFlagType
其实是定义事件标志组的标志位,用来判断是否执行这些请求了这些标志位的任务。
最后是一个链表的结构:这个链表的一个单元是OS_FLAG_NODE
(2)这个链表结点的成员包括:
连接这些结点的两个链表指针:OSFlagNodeNext,OSFlagNodePrev;
(3)有一个OSFlagNodeTCB,指针指向某个等待事件标志组中的事件标志的任务控制块,换句话说,通过这个指针可以知道哪一个任务在等待事件标志组中的事件。
(4)OS_FlagNodeFlagGrp是一个反向指向事件标志组的指针,在删除一个OS_FLAG_NODE数据结构或者通过OSTaskDel()删除一个仍然在等待事件标志的任务时候,将会用到这个指针。
(5)OSFlagNodeFlags用来指明任务等待事件标志组中的哪些事件标志。
任务可能调用OSFlagPend(),并且指明等待事件标志组中的事件标志。
在这种情况下面,OSFlagNodeFlags的值为0xD1,根据由OS_FLAGS指定的事件标志组的位数。OS_FLAGS可能是8位或者16位或者32位。
可以很方便的根据实际产品的需要更改事件标志组的位数。使用8位的事件标志组,可以减少程序占用的RAM和ROM空间。但是为了程序的可移植性,我们一般定义为32位。
(6)最后一个变量是OSFlagNodeWaitType(),这个变量指明任务是等待事件标志组中的所有事件标志都发生(与型),还是任何的一个事件标志发生或型。
(1)建立一个事件标志组OSFlagCreate()
OS_FLAG_GRP *OSFlagCreate(OS_FLAGS flags,INT8U *err)
{
OS_FLAG_GRP *pgrp;
OS_ENTER_CRITICAL();
pgrp = OSFlagFreeList;
if(pgrp != (OS_FLAG_GRP *)0)
{
OSFlagFreeList = (OS_FLAG_GRP *)OSFlagFreeList->OSFlagWaitList;
pgrp->OSFlagType = OS_EVENT_TYPE_FLAG;
pgrp->OSFlagFlags = flags;
pgrp->OSFlagWaitList = (void *)0;
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
}
else
{
OS_EXIT_CRITICAL();
*err = OS_FLAG_GRP_DEPLETED;
}
return (pgrp);
}
其实就是要在OS_FLAG_GRP填入相应的值:
填入flags参数值。
(2)删除事件标志组:
OS_FLAG_GRP *OSFlagDel(OS_FLAG_GRP * pgrp, INT8U opt,INT8U *err)
{
/*异常处理*/
OS_ENTER_CRITICAL();
if(pgrp->OSFlagWaitList != (void *)0)
{
tasks_waiting = TRUE;
}
else
{
tasks_waiting = FALSE;
}
switch(opt)
{
case OS_DEL_NO_PEND:
if(tasking_waiting == FALSE)
{
pgrp->OSFlagType = OS_EVENT_TYPE_UNUSED;
pgrp->OSFlagWaitList = (void *)OSFlagFreeList;
OSFlagFreeList = pgrp;
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
return ((OS_FLAG_GRP *)0);
}
else
{
OS_EXIT_CRITICAL();
*err = OS_ERR_TASK_WAITING;
return (pgrp);
}
case OS_DEL_ALWAYS:
pnode = pgrp->OSFlagWaitList;
while(pnode != (OS_FLAG_NODE *)0)
{
OS_FlagTaskRdy(pnode, (OS_FLAGS)0);
pnode = pnode->OSFlagNodeNext;
}
pgrp->OSFlagType = OS_EVENT_TYPE_UNUSED;
pgrp->OSFlagwaitList = (void *)OSFlagFreeList;
OSFlagFreeList = pgrp;
OS_EXIT_CRITICAL();
if(tasks_waiting == TRUE)
{
OS_sched();
}
*err = OS_NO_ERR;
return ((OS_FLAG_GRP *)0);
default:
OS_EXIT_CRITICAL();
*err = OS_ERR_INVALID_OPT;
return (pgrp);
}
}
PS:使用这个函数需要注意的是,多任务系统中可能出现一个任务试图,访问已经被删除的事件标志组,一般来说,在删除一个事件标志组之前,必须先删除所有的可能用到这个事件标志组的任务。
当调用选项:OS_DEL_ALWAYS时,所有等待这个事件标志组的任务都会被标志为就绪态,认为任务等待的事件都发生了,将在讨论OSFlagPOST()函数时候,进一步的了解这里用到的OS_FlagTaskRdy()函数。
(3)等待事件标志组的事件标志位OSFlagPend()
参数1:pgrp是事件标志组
参数2:事件标志位,比如哪些可以将其恢复到就绪状态中
参数3:等待的类型,与或者或关系
参数4:超时时间
参数5:返回的输出型参数:错误类型
OS_FLAGS OSFlagPend (OS_FLAG_GRP *pgrp,
OS_FLAGS flags,
INT8U wait_type,
INT32U timeout,
INT8U *perr)
{
OS_FLAG_NODE node;
OS_FLAGS flags_rdy;
INT8U result;
INT8U pend_stat;
BOOLEAN consume;
/*异常处理*/
if (pgrp->OSFlagType != OS_EVENT_TYPE_FLAG)
{
/* 错误的事件标志组类型 */
*perr = OS_ERR_EVENT_TYPE;
return ((OS_FLAGS)0);
}
result = (INT8U)(wait_type & OS_FLAG_CONSUME);
if (result != (INT8U)0)
{
/* 我们判断等待的类型 */
wait_type &= (INT8U)~(INT8U)OS_FLAG_CONSUME;
consume = OS_TRUE;
}
else
{
consume = OS_FALSE;
}
/*$PAGE*/
OS_ENTER_CRITICAL();
switch (wait_type) {
case OS_FLAG_WAIT_SET_ALL:
/* See if all required flags are set */
flags_rdy = (OS_FLAGS)(pgrp->OSFlagFlags & flags);
/* Extract only the bits we want */
if (flags_rdy == flags)
{
/* Must match ALL the bits that we want */
if (consume == OS_TRUE)
{
/* See if we need to consume the flags */
pgrp->OSFlagFlags &= (OS_FLAGS)~flags_rdy;
/* Clear ONLY the flags 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_SET_ANY:
flags_rdy = (OS_FLAGS)(pgrp->OSFlagFlags & flags); /* Extract only the bits we want */
if (flags_rdy != (OS_FLAGS)0) { /* See if any flag set */
if (consume == OS_TRUE) { /* See if we need to consume the flags */
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 { /* Block task until events occur or timeout */
OS_FlagBlock(pgrp, &node, flags, wait_type, timeout);
OS_EXIT_CRITICAL();
}
break;
#if OS_FLAG_WAIT_CLR_EN > 0u
case OS_FLAG_WAIT_CLR_ALL: /* See if all required flags are cleared */
flags_rdy = (OS_FLAGS)~pgrp->OSFlagFlags & flags; /* Extract only the bits we want */
if (flags_rdy == flags) { /* Must match ALL the bits that we want */
if (consume == OS_TRUE) { /* See if we need to consume the flags */
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:
flags_rdy = (OS_FLAGS)~pgrp->OSFlagFlags & flags; /* Extract only the bits we want */
if (flags_rdy != (OS_FLAGS)0) { /* See if any flag cleared */
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;
#endif
default:
OS_EXIT_CRITICAL();
flags_rdy = (OS_FLAGS)0;
*perr = OS_ERR_FLAG_WAIT_TYPE;
return (flags_rdy);
}
/*$PAGE*/
OS_Sched(); /* Find next HPT ready to run */
OS_ENTER_CRITICAL();
if (OSTCBCur->OSTCBStatPend != OS_STAT_PEND_OK) { /* Have we timed-out or aborted? */
pend_stat = OSTCBCur->OSTCBStatPend;
OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK;
OS_FlagUnlink(&node);
OSTCBCur->OSTCBStat = OS_STAT_RDY; /* Yes, make task ready-to-run */
OS_EXIT_CRITICAL();
flags_rdy = (OS_FLAGS)0;
switch (pend_stat) {
case OS_STAT_PEND_ABORT:
*perr = OS_ERR_PEND_ABORT; /* Indicate that we aborted waiting */
break;
case OS_STAT_PEND_TO:
default:
*perr = OS_ERR_TIMEOUT; /* Indicate that we timed-out waiting */
break;
}
return (flags_rdy);
}
flags_rdy = OSTCBCur->OSTCBFlagsRdy;
if (consume == OS_TRUE) { /* See if we need to consume the flags */
switch (wait_type) {
case OS_FLAG_WAIT_SET_ALL:
case OS_FLAG_WAIT_SET_ANY: /* Clear ONLY the flags we got */
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);
}
中间函数:添加一个任务到事件标志组等待任务链表中
static void OS_FlagBlock(
OS_FLAG_GRP *pgrp,
OS_FLAG_NODE *pnode,
OS_FLAGS flags,
INT8U wait_type,
INT16U timeout
)
{
OS_FLAG_NODE *pnode_next;
OSTCBCur->OSTCBStat |= OS_STAT_FLAG;
OSTCBCur->OSTCBDly = timeout;
pnode->OSFlagNodeFlags = flags;
pnode->OSFlagNodeWaitType = wait_type;
pnode->OSFlagNodeTCB = (void *)OSTCBCur;
pnode->OSFlagNodeNext = pgrp->OSFlagWaitList;
pnode->OSFlagNodePrev = (void *)0;
pnode_next = pgrp->OSFlagWaitList;
if(pnode_next != (void *)0)
{
pnode_next->OSFlagNodePrev = pnode;
}
pgrp->OSFlagWaitList = (void *)pnode;
...
}