1.当任务需要与多个事件的发生同步时,可以使用事件标志组。
等待多个事件时,任何一个事件发生,任务都被同步,这种同步机制称为“或”同步(逻辑“或运算”);
当所有事件都发生时,任务才被同步,这种同步机制被称为“与”同步(逻辑“与”运算)。
2.使用事件标志组前需要先配置事件标志组相关的宏定义,它们的定义位于os_cfg.h中
#define OS_CFG_FLAG_EN 1u // 事件标志组使能标志
#define OS_CFG_FLAG_DEL_EN 1u // 包含函数OSFlagDel()的代码
#define OS_CFG_FLAG_MODE_CLR_EN 1u //
#define OS_CFG_FLAG_PEND_ABORT_EN 1u // 包含函数OSFlagPendAbort()的代码
3.事件标志组的使用
(1)当任务或ISR向事件标志组发布事件标志时,所有满足等待条件的任务都会进入就绪态。
(2)事件标志组的创建最好放在启动代码中,也就是main函数中。
(3)事件标志通常有两种用途:状态信息监控和瞬时事件监控。
状态信息监控一般通过非阻塞调用来监控相关的事件标志;瞬时事件监控则是通过阻塞等待的方式来实现。
4.事件标志组的类型为OS_FLAG_GRP,它的定义位于os.h中
typedef struct os_flag_grp OS_FLAG_GRP; // 617行
struct os_flag_grp { // 742行 - 753行,事件标志组
OS_OBJ_TYPE Type; // 类型必须设置成OS_OBJ_TYPE_FLAG
CPU_CHAR *NamePtr; // 名称
OS_PEND_LIST PendList; // 挂起标
#if OS_CFG_DBG_EN > 0u // 调试相关
OS_FLAG_GRP *DbgPrevPtr;
OS_FLAG_GRP *DbgNextPtr;
CPU_CHAR *DbgNamePtr;
#endif
OS_FLAGS Flags; // 事件标志
CPU_TS TS; // 最近一次发布事件标志的时间戳
};
5.事件标志组的创建使用函数OSTaskCreate(),它的定义位于os_flag.c中。
void OSFlagCreate (OS_FLAG_GRP *p_grp, // 69行 - 120行,事件标志组的地址
CPU_CHAR *p_name, // 名称
OS_FLAGS flags, // 标志初始值
OS_ERR *p_err) // 返回的错误码
{
CPU_SR_ALLOC(); // 声明变量cpu_sr,用来临时存储CPU的状态寄存器
#ifdef OS_SAFETY_CRITICAL // 允许进行系统安全性检查
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
#ifdef OS_SAFETY_CRITICAL_IEC61508 // 允许进行系统安全性检查--IEC61508
if (OSSafetyCriticalStartFlag == DEF_TRUE) {
*p_err = OS_ERR_ILLEGAL_CREATE_RUN_TIME;
return;
}
#endif
#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u // 允许进行ISR调用检查
if (OSIntNestingCtr > (OS_NESTING_CTR)0) { // 查看是否是在ISR中调用该函数
*p_err = OS_ERR_CREATE_ISR; // 不能再ISR中创建
return;
}
#endif
#if OS_CFG_ARG_CHK_EN > 0u // 允许进行参数检查
if (p_grp == (OS_FLAG_GRP *)0) { // 无效的事件标志组
*p_err = OS_ERR_OBJ_PTR_NULL;
return;
}
#endif
OS_CRITICAL_ENTER(); // 进入临界区
p_grp->Type = OS_OBJ_TYPE_FLAG; // 设置类型为OS_OBJ_TYPE_FLAG
p_grp->NamePtr = p_name;
p_grp->Flags = flags; // 设置事件标志初始值
p_grp->TS = (CPU_TS)0;
OS_PendListInit(&p_grp->PendList); // 创建挂起表
#if OS_CFG_DBG_EN > 0u // 调试相关
OS_FlagDbgListAdd(p_grp);
#endif
OSFlagQty++; // 系统事件标志组的数目加1
OS_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_NONE;
}
6.等待事件标志组使用OSFlagPend(),它的定义位于os_flag.c中。
OS_FLAGS OSFlagPend (OS_FLAG_GRP *p_grp, // 313行 - 585行
OS_FLAGS flags, // 等待的标志
OS_TICK timeout, // 超时时间
OS_OPT opt, // 等待选项
CPU_TS *p_ts, // 返回的时间戳
OS_ERR *p_err) // 返回的调用结果
{
CPU_BOOLEAN consume;
OS_FLAGS flags_rdy;
OS_OPT mode;
OS_PEND_DATA pend_data;
CPU_SR_ALLOC(); // 声明变量cpu_sr,用来临时存储CPU的状态寄存器
#ifdef OS_SAFETY_CRITICAL // 允许进行系统安全性检查
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return ((OS_FLAGS)0);
}
#endif
#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u // 允许进行ISR调用检查
if (OSIntNestingCtr > (OS_NESTING_CTR)0) { // 查看是否在ISR中调用该函数
*p_err = OS_ERR_PEND_ISR; // 不能在ISR中调用
return ((OS_FLAGS)0);
}
#endif
#if OS_CFG_ARG_CHK_EN > 0u // 允许进行参数检查
if (p_grp == (OS_FLAG_GRP *)0) { // 无效的参数
*p_err = OS_ERR_OBJ_PTR_NULL;
return ((OS_FLAGS)0);
}
#endif
#if OS_CFG_OBJ_TYPE_CHK_EN > 0u // 允许进行对象类型检查
if (p_grp->Type != OS_OBJ_TYPE_FLAG) { // 无效的指向
*p_err = OS_ERR_OBJ_TYPE;
return ((OS_FLAGS)0);
}
#endif
if ((opt & OS_OPT_PEND_FLAG_CONSUME) != (OS_OPT)0) { // 查看我们是否需要消耗标志
consume = DEF_TRUE;
} else {
consume = DEF_FALSE;
}
if (p_ts != (CPU_TS *)0) {
*p_ts = (CPU_TS)0; // 初始化要返回的时间戳
}
mode = opt & OS_OPT_PEND_FLAG_MASK;
CPU_CRITICAL_ENTER(); // 进入临界区
switch (mode) {
case OS_OPT_PEND_FLAG_SET_ALL: // 所有的标志置位
flags_rdy = (OS_FLAGS)(p_grp->Flags & flags); // 提取出需要事件标志
if (flags_rdy == flags) { // 所有的事件都已发生
if (consume == DEF_TRUE) { // 查看是否需要消耗掉这些标志
p_grp->Flags &= ~flags_rdy; // 仅仅清理掉那些我们需要的位
}
OSTCBCurPtr->FlagsRdy = flags_rdy; // 保存已经准备就绪的位
if (p_ts != (CPU_TS *)0) {
*p_ts = p_grp->TS;
}
CPU_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_NONE;
return (flags_rdy);
} else { // 所有的事件没有全部发生
if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) { // 不挂起任务
CPU_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_PEND_WOULD_BLOCK;
return ((OS_FLAGS)0);
} else { // 挂起任务
if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { // 查看调度器是否已锁定
CPU_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_SCHED_LOCKED;
return ((OS_FLAGS)0);
}
}
OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT(); // 进入临界区,使能中断
OS_FlagBlock(&pend_data, // 将任务挂起
p_grp,
flags,
opt,
timeout);
OS_CRITICAL_EXIT_NO_SCHED(); // 退出临界区,不调度
}
break;
case OS_OPT_PEND_FLAG_SET_ANY: // 任意一个标志置位
flags_rdy = (OS_FLAGS)(p_grp->Flags & flags); // 提取需要的事件标志
if (flags_rdy != (OS_FLAGS)0) { // 有事件发生
if (consume == DEF_TRUE) { // 是否需要消耗掉这些位
p_grp->Flags &= ~flags_rdy; // 清空掉这些标志
}
OSTCBCurPtr->FlagsRdy = flags_rdy; // 保存已发生的事件标志
if (p_ts != (CPU_TS *)0) {
*p_ts = p_grp->TS;
}
CPU_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_NONE;
return (flags_rdy);
} else { // 没有任何事件发生
if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) { // 不挂起
CPU_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_PEND_WOULD_BLOCK;
return ((OS_FLAGS)0);
} else { // 挂起
if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { // 查看任务调度器是否锁定
CPU_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_SCHED_LOCKED;
return ((OS_FLAGS)0);
}
}
OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT(); // 进入临界区,使能中断
OS_FlagBlock(&pend_data,
p_grp,
flags,
opt,
timeout);
OS_CRITICAL_EXIT_NO_SCHED(); // 退出临界区,不调度
}
break;
#if OS_CFG_FLAG_MODE_CLR_EN > 0u
case OS_OPT_PEND_FLAG_CLR_ALL: // 所有的标志清零
flags_rdy = (OS_FLAGS)(~p_grp->Flags & flags); // 提取需要的事件标志位
if (flags_rdy == flags) { // 所有的事件都已发生
if (consume == DEF_TRUE) { // 查看是否需要消耗这些事件标志位
p_grp->Flags |= flags_rdy; // 仅仅设置需要的事件标志位
}
OSTCBCurPtr->FlagsRdy = flags_rdy; // 保存这些事件标志位
if (p_ts != (CPU_TS *)0) {
*p_ts = p_grp->TS;
}
CPU_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_NONE;
return (flags_rdy);
} else { // 所有事件未全部发生
if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) { // 不挂起
CPU_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_PEND_WOULD_BLOCK;
return ((OS_FLAGS)0);
} else { // 挂起
if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { // 查看调度器是否锁定
CPU_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_SCHED_LOCKED;
return ((OS_FLAGS)0);
}
}
OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT(); // 进入临界区,使能中断
OS_FlagBlock(&pend_data, // 挂起任务
p_grp,
flags,
opt,
timeout);
OS_CRITICAL_EXIT_NO_SCHED(); // 退出临界区,不调度
}
break;
case OS_OPT_PEND_FLAG_CLR_ANY: // 某一个标志清零
flags_rdy = (OS_FLAGS)(~p_grp->Flags & flags); // 提取出需要的事件标志位
if (flags_rdy != (OS_FLAGS)0) { // 有事件发生
if (consume == DEF_TRUE) { // 查看是否需要消耗掉这些标志位
p_grp->Flags |= flags_rdy; // 仅仅设置需要的标志位
}
OSTCBCurPtr->FlagsRdy = flags_rdy; // 保存事件标志位
if (p_ts != (CPU_TS *)0) {
*p_ts = p_grp->TS;
}
CPU_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_NONE;
return (flags_rdy);
} else { // 没有任何事情发生
if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) { // 不挂起
CPU_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_PEND_WOULD_BLOCK;
return ((OS_FLAGS)0);
} else { // 挂起
if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { // 查看调度器是否锁定
CPU_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_SCHED_LOCKED;
return ((OS_FLAGS)0);
}
}
OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT(); // 进入临界区,使能中断
OS_FlagBlock(&pend_data, // 挂起任务
p_grp,
flags,
opt,
timeout);
OS_CRITICAL_EXIT_NO_SCHED(); // 退出临界区,不调度
}
break;
#endif
default:
CPU_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_OPT_INVALID;
return ((OS_FLAGS)0);
}
OSSched(); // 执行调度程序
CPU_CRITICAL_ENTER(); // 进入临界区
switch (OSTCBCurPtr->PendStatus) {
case OS_STATUS_PEND_OK: // 目标事件标志位被置位(清零)
if (p_ts != (CPU_TS *)0) {
*p_ts = OSTCBCurPtr->TS;
}
*p_err = OS_ERR_NONE;
break;
case OS_STATUS_PEND_ABORT: // 等待操作被其他任务取消了
if (p_ts != (CPU_TS *)0) {
*p_ts = OSTCBCurPtr->TS;
}
CPU_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_PEND_ABORT;
return ((OS_FLAGS)0);
case OS_STATUS_PEND_TIMEOUT: // 等待超时
if (p_ts != (CPU_TS *)0) {
*p_ts = (CPU_TS )0;
}
CPU_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_TIMEOUT;
return ((OS_FLAGS)0);
case OS_STATUS_PEND_DEL: // 事件标志组被删除了
if (p_ts != (CPU_TS *)0) {
*p_ts = OSTCBCurPtr->TS;
}
CPU_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_OBJ_DEL;
return ((OS_FLAGS)0);
default:
CPU_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_STATUS_INVALID;
return ((OS_FLAGS)0);
}
flags_rdy = OSTCBCurPtr->FlagsRdy;
if (consume == DEF_TRUE) { // 查看是否需要消耗掉这些标志
switch (mode) {
case OS_OPT_PEND_FLAG_SET_ALL:
case OS_OPT_PEND_FLAG_SET_ANY: // 清空事件标志位
p_grp->Flags &= ~flags_rdy;
break;
#if OS_CFG_FLAG_MODE_CLR_EN > 0u
case OS_OPT_PEND_FLAG_CLR_ALL:
case OS_OPT_PEND_FLAG_CLR_ANY: // 置位事件标志位
p_grp->Flags |= flags_rdy;
break;
#endif
default:
CPU_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_OPT_INVALID;
return ((OS_FLAGS)0);
}
}
CPU_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_NONE;
return (flags_rdy);
}
6.发送事件标志组使用OSFlagPost(),它的定义位于os_flag.c中。
OS_FLAGS OSFlagPost (OS_FLAG_GRP *p_grp, // 790行 - 856行
OS_FLAGS flags, // 事件标志
OS_OPT opt, // 事件标志的选项
OS_ERR *p_err) // 调用的结果
{
OS_FLAGS flags_cur;
CPU_TS ts;
#ifdef OS_SAFETY_CRITICAL // 允许执行系统安全性检查
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return ((OS_FLAGS)0);
}
#endif
#if OS_CFG_ARG_CHK_EN > 0u // 允许进行参数检查
if (p_grp == (OS_FLAG_GRP *)0) { // 检查p_grp
*p_err = OS_ERR_OBJ_PTR_NULL;
return ((OS_FLAGS)0);
}
switch (opt) { // 检查opt
case OS_OPT_POST_FLAG_SET:
case OS_OPT_POST_FLAG_CLR:
case OS_OPT_POST_FLAG_SET | OS_OPT_POST_NO_SCHED:
case OS_OPT_POST_FLAG_CLR | OS_OPT_POST_NO_SCHED:
break;
default:
*p_err = OS_ERR_OPT_INVALID;
return ((OS_FLAGS)0);
}
#endif
#if OS_CFG_OBJ_TYPE_CHK_EN > 0u // 允许进行对象类型检查
if (p_grp->Type != OS_OBJ_TYPE_FLAG) { // 确保是事件标志组
*p_err = OS_ERR_OBJ_TYPE;
return ((OS_FLAGS)0);
}
#endif
ts = OS_TS_GET(); // 获取时间戳
#if OS_CFG_ISR_POST_DEFERRED_EN > 0u // 延时发布模式
if (OSIntNestingCtr > (OS_NESTING_CTR)0) { // 查看是否是在ISR中调用
OS_IntQPost((OS_OBJ_TYPE)OS_OBJ_TYPE_FLAG, // 发布到ISR队列中
(void *)p_grp,
(void *)0,
(OS_MSG_SIZE)0,
(OS_FLAGS )flags,
(OS_OPT )opt,
(CPU_TS )ts,
(OS_ERR *)p_err);
return ((OS_FLAGS)0);
}
#endif
flags_cur = OS_FlagPost(p_grp, // 发布事件标志组
flags,
opt,
ts,
p_err);
return (flags_cur);
}