事件标志组
事件标志组是进行任务同步的另外一种方法,事件标志组是为了解决一个任务与多个事件的同步。
或同步、与同步
按照“与”和“或”的逻辑,当任何一个事件发生都会进行任务同步的机制叫做“或”同步,当所有时间发生才会同步的机制叫做“与”同步。
API函数
UCOSIII中的事件标志组为:OS_FLAG_GRP
,定义在os.h中:
typedef struct os_flag_grp OS_FLAG_GRP;
struct os_flag_grp
内容如下:
struct os_flag_grp { /* Event Flag Group */
/* ------------------ GENERIC MEMBERS ------------------ */
OS_OBJ_TYPE Type; /* Should be set to OS_OBJ_TYPE_FLAG */
CPU_CHAR *NamePtr; /* Pointer to Event Flag Name (NUL terminated ASCII) */
OS_PEND_LIST PendList; /* List of tasks waiting on event flag group */
#if OS_CFG_DBG_EN > 0u
OS_FLAG_GRP *DbgPrevPtr;
OS_FLAG_GRP *DbgNextPtr;
CPU_CHAR *DbgNamePtr;
#endif
/* ------------------ SPECIFIC MEMBERS ------------------ */
OS_FLAGS Flags; /* 8, 16 or 32 bit flags */
CPU_TS TS; /* Timestamp of when last post occurred */
};
OSFlagCreate()
OSFlagCreate()即为创建事件标志组函数:
void OSFlagCreate (OS_FLAG_GRP *p_grp,
CPU_CHAR *p_name,
OS_FLAGS flags,
OS_ERR *p_err)
参数:
*p_grp
:创建的事件标志组
*p_name
:事件标志组的名字,就是字符串
flags
:事件标志组的初始值
*p_err
:用于保存错误
OSFlagPend()
等待事件标志组函数:
OS_FLAGS OSFlagPend (OS_FLAG_GRP *p_grp,
OS_FLAGS flags,
OS_TICK timeout,
OS_OPT opt,
CPU_TS *p_ts,
OS_ERR *p_err)
参数:
*p_grp
:等待的事件标志组
flags
:等待的事件标志组序列位,类似寄存器的位。
timeout
:等待超时时间,设置0表示一直等待直到发生标志置位。
opt
:选项,
*p_ts
:时间戳,设置NULL则不使用。
*p_err
:用于保存错误
opt
是这个函数的重要参数,可以选择如下选项进行搭配:
#define OS_OPT_PEND_FLAG_MASK (OS_OPT)(0x000Fu)
#define OS_OPT_PEND_FLAG_CLR_ALL (OS_OPT)(0x0001u) /* Wait for ALL the bits specified to be CLR */
#define OS_OPT_PEND_FLAG_CLR_AND (OS_OPT)(0x0001u)
#define OS_OPT_PEND_FLAG_CLR_ANY (OS_OPT)(0x0002u) /* Wait for ANY of the bits specified to be CLR */
#define OS_OPT_PEND_FLAG_CLR_OR (OS_OPT)(0x0002u)
#define OS_OPT_PEND_FLAG_SET_ALL (OS_OPT)(0x0004u) /* Wait for ALL the bits specified to be SET */
#define OS_OPT_PEND_FLAG_SET_AND (OS_OPT)(0x0004u)
#define OS_OPT_PEND_FLAG_SET_ANY (OS_OPT)(0x0008u) /* Wait for ANY of the bits specified to be SET */
#define OS_OPT_PEND_FLAG_SET_OR (OS_OPT)(0x0008u)
#define OS_OPT_PEND_FLAG_CONSUME (OS_OPT)(0x0100u) /* Consume the flags if condition(s) satisfied */
#define OS_OPT_PEND_BLOCKING (OS_OPT)(0x0000u)
#define OS_OPT_PEND_NON_BLOCKING (OS_OPT)(0x8000u)
与同步和或同步就是在这里通过选项进行设置的,opt分为:
1)等待所有的标志位清除:
#define OS_OPT_PEND_FLAG_CLR_ALL (OS_OPT)(0x0001u) /* Wait for ALL the bits specified to be CLR */
#define OS_OPT_PEND_FLAG_CLR_AND (OS_OPT)(0x0001u)
2)等待任意标志位清除
#define OS_OPT_PEND_FLAG_CLR_ANY (OS_OPT)(0x0002u) /* Wait for ANY of the bits specified to be CLR */
#define OS_OPT_PEND_FLAG_CLR_OR (OS_OPT)(0x0002u)
3)等待所有标志位置位
#define OS_OPT_PEND_FLAG_SET_ALL (OS_OPT)(0x0004u) /* Wait for ALL the bits specified to be SET */
#define OS_OPT_PEND_FLAG_SET_AND (OS_OPT)(0x0004u)
4)等待任意标志位置位
#define OS_OPT_PEND_FLAG_SET_ANY (OS_OPT)(0x0008u) /* Wait for ANY of the bits specified to be SET */
#define OS_OPT_PEND_FLAG_SET_OR (OS_OPT)(0x0008u)
上面提到的8个选项两两一组,每组的2个选项宏定义的值其实是一样的,使用名字含有AND和ALL的就是与同步机制,含有OR和ANY的就是或同步机制。
一般上面四种选项还会搭配下面三个选项进行使用:
#define OS_OPT_PEND_FLAG_CONSUME (OS_OPT)(0x0100u) /* Consume the flags if condition(s) satisfied */
#define OS_OPT_PEND_BLOCKING (OS_OPT)(0x0000u)
#define OS_OPT_PEND_NON_BLOCKING (OS_OPT)(0x8000u)
OS_OPT_PEND_FLAG_CONSUME
的意思是请求到事件组的标志位后即清除相应标志位,这样做其实是为了下次请求时可以使用该标志位。
OS_OPT_PEND_BLOCKING
和OS_OPT_PEND_NON_BLOCKING
就是选择是否进行挂起当前任务等待事件标志组标志位。
OSFlagPend()使用示例:
OSFlagPend((OS_FLAG_GRP*)&EventFlags,
(OS_FLAGS )KEY0_FLAG+KEY1_FLAG,
(OS_TICK )0,
(OS_OPT )OS_OPT_PEND_FLAG_SET_ALL+OS_OPT_PEND_FLAG_CONSUME,
(CPU_TS* )0,
(OS_ERR* )&err);
其中
#define KEY0_FLAG 0x01
#define KEY1_FLAG 0x02
即请求bit0和bit1都置1,请求到标志位后会进行清除。
OSFlagPost()
此函数发布事件标志组的标志位,即对事件标志组的相应位进行操作的函数,通过opt选项进行设置,可以对标志位置1或者清0。
OS_FLAGS OSFlagPost (OS_FLAG_GRP *p_grp,
OS_FLAGS flags,
OS_OPT opt,
OS_ERR *p_err)
参数:
*p_grp
:要发布的事件标志组
flags
:等待的事件标志组序列位,类似寄存器的位。
opt
:有如下两个选择:
#define OS_OPT_POST_FLAG_SET (OS_OPT)(0x0000u)
#define OS_OPT_POST_FLAG_CLR (OS_OPT)(0x0001u)
OS_OPT_POST_FLAG_SET 为置位标志位,置1
OS_OPT_POST_FLAG_CLR 为清除标志位,置0
*p_err
:用于保存错误
还有三个API没有列出:
上面的这些函数中,只有OSFlagPost()
可以在ISR中断服务函数中使用,也就是说在中断中只能发布事件标志组,类似于消息队列。
总结:
使用事件标志组是为了解决多个事件与任务的同步问题,满足所有的标志位置位时才可以请求到事件标志组就可以很好的说明这个问题。事件标志组不像消息队列,消息队列更多的是进行一些数据的交互、任务间信息的传递。感觉更像多个信号量组成的内核对象。