uC/OS-II 中的事件标志组相当于一群互斥型信号量,通过这些信号量占用情况的逻辑函数求值确定任务的执行与挂起。事件标志组在一些中文参考资料中又称信号量集,以下采用前一种名称。本文使用的是 uC/OS-II V2.91 的源码。
OS_FLAG_GRP
与事件 OS_EVENT
不同的是,uC/OS 使用另一种数据结构 OS_FLAG_GRP
来表示事件标志组。
typedef struct os_flag_grp {
INT8U OSFlagType; //表示数据类型的标识,一定为 OS_EVENT_TYPE_FLAG
void *OSFlagWaitList; //指向等待任务列表
OS_FLAGS OSFlagFlags; //8, 16 or 32 bit flags
#if OS_FLAG_NAME_EN > 0u
INT8U *OSFlagName;
#endif
} OS_FLAG_GRP;
结构体中的 OSFlagFlags
成员可以是一个 8 位、16 位、32 位的变量,变量的每个位代表一个事件标志(又称信号量,对应于信号量集),变量的大小根据 os_cfg.h 中的 OS_FLAGS_NBITS
条件编译而得
#if OS_FLAGS_NBITS == 8u
typedef INT8U OS_FLAGS;
#endif
#if OS_FLAGS_NBITS == 16u
typedef INT16U OS_FLAGS;
#endif
#if OS_FLAGS_NBITS == 32u
typedef INT32U OS_FLAGS;
#endif
OSFlagWaitList
指向等待任务列表的首地址,等待任务列表是一个双向链表,节点名为 OS_FLAG_NODE
,每个节点对应一个等待任务。
typedef struct os_flag_node {
void *OSFlagNodeNext; //指向等待链表中的后一个节点
void *OSFlagNodePrev; //指向等待链表中的前一个节点
void *OSFlagNodeTCB; //指向本节点任务的任务控制块 TCB
void *OSFlagNodeFlagGrp; //反向指向事件标志组
OS_FLAGS OSFlagNodeFlags; //获取事件标志的掩码(对应位为 1 的时候,信号对当前任务有影响)
INT8U OSFlagNodeWaitType;//逻辑函数(下文提及)
} OS_FLAG_NODE;
对等待任务列表的操作 OS_FlagBlock()
, OS_FlagInit()
, OS_FlagTaskRdy()
, OS_FlagUnlink()
作为底层函数被事件标志组函数封装。
OSFlagCreate()
使用事件标志组之前需要调用 OSFlagCreate()
函数从空标志组链表中取出一个元素。
OS_FLAG_GRP *OSFlagCreate(OS_FLAGS flags, INT8U *perr)
flags
原样赋值给 OSFlagFlags
,由于后者是无符号整型数,以防万一传递参数时可用强制类型转换。这里是要给所有事件标志初始化,赋值 0 即可。
perr
照旧是存储输出信息的地方。
OS_ERR_CREATE_ISR //无法在中断服务函数中创建事件标志组
OS_ERR_FLAG_GRP_DEPLETED //空的事件标志组节点已经用完了
这两种情况都会返回空指针,OS_ERR_NONE
表示创建成功,返回事件标志组指针。
OSFlagDel()
删除事件标志组的方式与其他事件相同。
OS_FLAG_GRP *OSFlagDel(OS_FLAG_GRP *pgrp, INT8U opt, INT8U *perr)
perr
, opt
行为一致,删除成功后返回空指针
OSFlagPend()
& OSFlagAccept()
任务请求一个事件标志组事件的某些标志位,在触发或超时后继续执行。
OS_FLAGS OSFlagPend(OS_FLAG_GRP *pgrp,
OS_FLAGS flags,
INT8U wait_type,
INT32U timeout, INT8U *perr)
flags
是即将写入等待任务列表节点的掩码 OSFlagNodeFlags
。wait_type
是触发条件设置,写入等待任务列表节点的 OSFlagNodeWaitType
。
#define OS_FLAG_WAIT_CLR_ALL 0u //等待所有有效位清零
#define OS_FLAG_WAIT_CLR_AND 0u
#define OS_FLAG_WAIT_CLR_ANY 1u //等待任意有效位清零
#define OS_FLAG_WAIT_CLR_OR 1u
#define OS_FLAG_WAIT_SET_ALL 2u //等待任意有效位置位
#define OS_FLAG_WAIT_SET_AND 2u
#define OS_FLAG_WAIT_SET_ANY 3u //等待所有有效位置位
#define OS_FLAG_WAIT_SET_OR 3u
需要不挂起请求的,使用 OSFlagAccept()
,用法基本相同
OS_FLAGS OSFlagAccept (OS_FLAG_GRP *pgrp,
OS_FLAGS flags,
INT8U wait_type,
INT8U *perr)
需要注意的是,如果对应的事件标志没有发生,函数返回值为 0;如果对应事件标志发生,函数返回事件标志组结构中的 OSFlagFlags
成员,即当前事件标志组的所有事件标志。因此在判断时还需参考 perr
的值。
OSFlagPost()
用于发送事件标志。
OS_FLAGS OSFlagPost(OS_FLAG_GRP *pgrp,
OS_FLAGS flags,
INT8U opt,
INT8U *perr)
flags
仍是用于掩码,判断哪些位被操作;opt
则描述对有效位的操作。
switch (opt) {
case OS_FLAG_CLR: //将有效位清零
pgrp->OSFlagFlags &= (OS_FLAGS)~flags;break;
case OS_FLAG_SET: //将有效位置位
pgrp->OSFlagFlags |= flags;break;
default: //除此之外的其他值将返回出错信息
OS_EXIT_CRITICAL();
*perr = OS_ERR_FLAG_INVALID_OPT;
return ((OS_FLAGS)0);
}
OSFlagQuery()
OSFlagQuery()
可以直接返回事件标志组结构中的 OSFlagFlags
成员。
OS_FLAGS OSFlagQuery(OS_FLAG_GRP *pgrp, INT8U *perr)
与其他 OS_EVENT
不同的是,访问事件标志组不会对内部的事件标志造成影响。一旦有任务就绪,等待任务列表里只会移除对应任务的节点。