这节总结操作系统UCOSIII的内核对象------->事件标志组
事件标志组的作用是:
1.用来做任务与任务之间的同步即一个任务发生了就触发了另一个任务的运行
2.用来任务与多个任务之间的同步,即多个任务都已经发生完了才会触发一个任务运行
下面转自一位网友的博客
比如,我现在用迅雷下载一部10集的连续剧,我打算10集全部下载完成之后,才开始正式看,现在3~10集因为种子原因,先早下完了,现在第1集下到了82%,第2集下到了97%,因为我的计划是10集全部下完才开始看,而第1集和第2集由于网络、种子等等各种原因,迟迟不能下载完成,进而导致我的计划被悬停,不能进行,已下载的8集,也因为前2集没能下完,而白白等待—这就等同于flag事件组。
1-10集,每一集都是一个事件,因为我内定,10个事件全部完成之后,才进入下一事件–“观看”,所以及早完成自己事件的第3-10集,将主动把自己通过flag事件组函数OSFlagPost()登记到事件组上,他们不关心,其他友邻事件完成否,只专注自己的事件是否完成,自己的事件一旦完成就登记到事件组上,最后3~10集,都把自己登记上去了,只剩下第1集和第2集,一旦某天的某个时刻,第2集下完了,那么第2集也把自己登记到事件组上,这样整个事件距离完成还剩下一个事件,就是第1集是否下载完成,只要第1集下载完成,那么我内定的“观看计划”开始启动,过了3分钟,由于网速提高,竟以300k的速度开始下载第1集,1分钟之后,第1集也下载完成了,第1集立即调用OSFlagPost事件组函数,将自己登记到事件组上,ok,OSFlagPost()检测到所有事件已经完成,OSFlagPost()将是"我"自动进入下一事件—“观看”,
那么事件标志组是什么
它是一个结构体 OS_FLAG_GRP(事件标志组)
typedef struct os_flag_grp OS_FLAG_GRP;
struct os_flag_grp { /* Event Flag Group /
OS_OBJ_TYPE Type; / 存储内核类型变量 */
CPU_CHAR NamePtr; / 存储内核的名字,即事件标志组的名字 /
OS_PEND_LIST PendList; / 等待列表结构体 /
OS_FLAGS Flags; / 8位、16位或32位标志 /
CPU_TS TS; / 时间戳 */
};
下面简要说明消息队列结构体的重要成员的作用
PendList 等待列表 的功能,等待列表是用来当有任务等待事件标志组的标志位,但是发现事件标志组的标志位没有达到预设值,只能将任务挂载在等待列表然后等待事件标志组的标志位满足条件就可以从等待列表中移出该等待任务,然后继续执行任务函数,本节等待列表也是挂载任务,只不过是在任务函数等待事件标志组的标志位
Flags 变量,它就是用来确定事件标志组的初始值,即设置好一个初始值作为标志位赋值在该变量上面,当任务满足一定条件,与这个初始值的标志位一样,就可以触发一个任务的运行
使用一个事件标志组要先创建它函数是 OSFlagCreate()
void OSFlagCreate (OS_FLAG_GRP p_grp, / 事件标志组指针 */
CPU_CHAR p_name, / 事件标志组名字 /
OS_FLAGS flags, / 事件标志组初始值 */
OS_ERR p_err) / 返回的错误值 */
初始化函数做了什么看代码
void OSFlagCreate (OS_FLAG_GRP *p_grp,
CPU_CHAR *p_name,
OS_FLAGS flags,
OS_ERR *p_err)
{
CPU_SR_ALLOC();
/*参数检查代码省略*/
OS_CRITICAL_ENTER();
p_grp->Type = OS_OBJ_TYPE_FLAG; /*标记创建内核对象为事件标志组*/
p_grp->NamePtr = p_name; //标记事件标志组的名称
p_grp->Flags = flags; /*设置标志初始值 */
p_grp->TS = (CPU_TS)0;
OS_PendListInit(&p_grp->PendList); //初始化该事件标志组的等待列表
/* 代码省略 */
OS_CRITICAL_EXIT_NO_SCHED();
*p_err = OS_ERR_NONE;
}
该函数主要是初始化 OS_FLAG_GRP(事件标志组)结构体成员
那么一个任务是如何等待事件标志组的到来 看函数 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) //返回错误值
根据 timeout+opt组合可以有不同的功能
opt | 功能 | 结果 |
---|---|---|
OS_OPT_PEND_FLAG_CLR_ALL | 等待 flags 指定位均被清 0即可 | 标志位使用1次之后等待标志位就 |
OS_OPT_PEND_FLAG_CLR_ANY | 等待 flags指定位有一位被清 0 即可 | |
OS_OPT_PEND_FLAG_SET_ALL | 等待 flags 指定位均被置 1 即可 | |
OS_OPT_PEND_FLAG_SET_ANY | 等待 flags指定位有一位被置 1 即可 | |
OS_OPT_PEND_FLAG_CLR_ALL + OS_OPT_PEND_FLAG_CONSUME | 等待 flags 指定位均被清 0;等到后把触发位取反。 | |
OS_OPT_PEND_FLAG_CLR_ANY+ OS_OPT_PEND_FLAG_CONSUME | 等待 flags指定位有一位被清 0 即可;等到后把触发位取反 | |
OS_OPT_PEND_FLAG_SET_ALL + OS_OPT_PEND_FLAG_CONSUME: | 等待 flags 指定位均被置 1;等到后把触发位取反 | |
OS_OPT_PEND_FLAG_SET_ANY + OS_OPT_PEND_FLAG_CONSUME | 等待 flags指定位有一位被置 1即可;等到后把触发位取反 |
这8种情况还设计如果没有获取标志位,是否进行任务切换问题,还可以在配合
OS_OPT_PEND_NON_BLOCKING 标志组不满足条件,继续执行任务函数
OS_OPT_PEND_BLOCKING 标志组不满足条件,挂起任务,等待标志位, 当然选择挂起任务timeout=0 表示无限等待标志位
看代码如何实现这些功能
CPU_BOOLEAN consume;
OS_FLAGS flags_rdy;
OS_OPT mode;
OS_PEND_DATA pend_data;
CPU_SR_ALLOC();
#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
if (OSIntNestingCtr > (OS_NESTING_CTR)0) {
*p_err = OS_ERR_PEND_ISR;
return ((OS_FLAGS)0);
}
#endif
#if OS_CFG_ARG_CHK_EN > 0u
if (p_grp == (OS_FLAG_GRP *)0) { /* Validate 'p_grp' */
*p_err = OS_ERR_OBJ_PTR_NULL;
return ((OS_FLAGS)0);
}
switch (opt) { /* Validate 'opt' */
case OS_OPT_PEND_FLAG_CLR_ALL:
case OS_OPT_PEND_FLAG_CLR_ANY:
case OS_OPT_PEND_FLAG_SET_ALL:
case OS_OPT_PEND_FLAG_SET_ANY:
case OS_OPT_PEND_FLAG_CLR_ALL | OS_OPT_PEND_FLAG_CONSUME:
case OS_OPT_PEND_FLAG_CLR_ANY | OS_OPT_PEND_FLAG_CONSUME:
case OS_OPT_PEND_FLAG_SET_ALL | OS_OPT_PEND_FLAG_CONSUME:
case OS_OPT_PEND_FLAG_SET_ANY | OS_OPT_PEND_FLAG_CONSUME:
case OS_OPT_PEND_FLAG_CLR_ALL | OS_OPT_PEND_NON_BLOCKING:
case OS_OPT_PEND_FLAG_CLR_ANY | OS_OPT_PEND_NON_BLOCKING:
case OS_OPT_PEND_FLAG_SET_ALL | OS_OPT_PEND_NON_BLOCKING:
case OS_OPT_PEND_FLAG_SET_ANY | OS_OPT_PEND_NON_BLOCKING:
case OS_OPT_PEND_FLAG_CLR_ALL | OS_OPT_PEND_FLAG_CONSUME | OS_OPT_PEND_NON_BLOCKING:
case OS_OPT_PEND_FLAG_CLR_ANY | OS_OPT_PEND_FLAG_CONSUME | OS_OPT_PEND_NON_BLOCKING:
case OS_OPT_PEND_FLAG_SET_ALL | OS_OPT_PEND_FLAG_CONSUME | OS_OPT_PEND_NON_BLOCKING:
case OS_OPT_PEND_FLAG_SET_ANY | OS_OPT_PEND_FLAG_CONSUME | OS_OPT_PEND_NON_BLOCKING:
break;
default:
*p_err = OS_ERR_OPT_INVALID;
return ((OS_OBJ_QTY)0);
}
#endif
#if OS_CFG_OBJ_TYPE_CHK_EN > 0u
if (p_grp->Type != OS_OBJ_TYPE_FLAG) { /* Validate that we are pointing at an event flag */
*p_err = OS_ERR_OBJ_TYPE;
return ((OS_FLAGS)0);
}
#endif
//判断p_grp->Flags是否清零
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; /* Initialize the returned timestamp */
}
mode = opt & OS_OPT_PEND_FLAG_MASK; //从选项中提取对标志位的要求
CPU_CRITICAL_ENTER();
switch (mode) {
case OS_OPT_PEND_FLAG_SET_ALL: /* 如果要求所有标志位均要置1 */
flags_rdy = (OS_FLAGS)(p_grp->Flags & flags); /*提取想要的标志位的值 */
if (flags_rdy == flags) { /*如果该值与期望值匹配 */
if (consume == DEF_TRUE) { /*如果要求将标志位匹配后取反 */
p_grp->Flags &= ~flags_rdy; /*清0标志组的相关标志位 */
}
OSTCBCurPtr->FlagsRdy = flags_rdy; /*保存让任务脱离等待的标志值*/
if (p_ts != (CPU_TS *)0) { //如果 p_ts 非空
*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) { /* 标志位的值不为0,表示标志组收到了标志位 */
if (consume == DEF_TRUE) {
p_grp->Flags &= ~flags_rdy; /* 清0相关标志位 */
}
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: /* 所有标志位置0 */
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: //有一个标志位清0
flags_rdy = (OS_FLAGS)(~p_grp->Flags & flags);/* 从事件标志组结构体获取标志位值*/
if (flags_rdy != (OS_FLAGS)0) { /* 从事件标志组结构体获取标志位值发现有一位被置0 */
if (consume == DEF_TRUE) { /* See if we need to consume the flags */
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: /*//如果等到了事件标志组 We got the vent flags */
if (p_ts != (CPU_TS *)0) {
*p_ts = OSTCBCurPtr->TS; //返回等到标志组时的时间戳
}
*p_err = OS_ERR_NONE; //错误类型为“无错误”
break; //跳出
case OS_STATUS_PEND_ABORT: /*//如果等待被中止 Indicate that we aborted */
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: /*//如果等待超时 Indicate that we didn't get semaphore within 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: /*//如果等待对象被删除 Indicate that object pended on has been deleted */
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) { /*//如果需要取反触发事件的标志位 See if we need to consume the flags */
switch (mode) { 根据事件触发模式分类处理
case OS_OPT_PEND_FLAG_SET_ALL:
case OS_OPT_PEND_FLAG_SET_ANY: /* Clear ONLY the flags we got */
p_grp->Flags &= ~flags_rdy; //清0标志组里触发事件的标志位
break;
#if OS_CFG_FLAG_MODE_CLR_EN > 0u
case OS_OPT_PEND_FLAG_CLR_ALL:
case OS_OPT_PEND_FLAG_CLR_ANY: /* Set ONLY the flags we got */
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; /*错误类型为“无错误” Event(s) must have occurred */
return (flags_rdy); //返回让任务脱离等待的标志值
}
void OS_FlagBlock (OS_PEND_DATA *p_pend_data, //等待列表元素
OS_FLAG_GRP *p_grp,
OS_FLAGS flags, //要操作的标志位
OS_OPT opt,
OS_TICK timeout)
{
OSTCBCurPtr->FlagsPend = flags; /* 保存需要等待的标志位 */
OSTCBCurPtr->FlagsOpt = opt; /*保存对标志位的要求 */
OSTCBCurPtr->FlagsRdy = (OS_FLAGS)0;
OS_Pend(p_pend_data, //阻塞任务,等待事件标志组
(OS_PEND_OBJ *)((void *)p_grp),
OS_TASK_PEND_ON_FLAG,
timeout);
}
简单将一下等待标志组函数的运行流程
就以函数选择了 OS_OPT_PEND_FLAG_SET_ALL | OS_OPT_PEND_FLAG_CONSUME 选项为例子
其他选项走向基本一致
函数一进来就是参数检查 其中一个看
#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u
if (OSIntNestingCtr > (OS_NESTING_CTR)0) {
p_err = OS_ERR_PEND_ISR;
return ((OS_FLAGS)0);
}
#endif
这个宏定义表示该函数是不能运用在中断里面的,否则直接退出该函数
然后就是根据等待标志位的类型做相对应的处理就是 switch (mode)
mode = opt & OS_OPT_PEND_FLAG_MASK; //从选项中提取对标志位的要求
switch (mode)
case OS_OPT_PEND_FLAG_SET_ALL: / 如果要求所有标志位均要置1 */
然后如果从事件标志组结构体中获取到的标志位符合自己预设的标志位值
如果有opt 有OS_OPT_PEND_FLAG_CONSUME,则将事件标志组结构体的标志位值取反
如果不取反,那么只要等待到一次标志位值,下一次就不需要等待标志位值就没有意义,因为不取反就是没有还原初始的标志位值,标志位值将一直不变
接着退出函数,返回标志位值 return (flags_rdy);
否则就要将任务插入等待列表通过函数 OS_FlagBlock() 最后进行任务切换
一旦等待列表的任务等到了有其他任务发送事件标志位,则等待标志组的任务函数有恢复运行从这里开始运行
switch (OSTCBCurPtr->PendStatus) { //根据运行任务的等待状态分类处理
case OS_STATUS_PEND_OK: /*如果等到了事件标志组
错误值,标记为无错误
接着要要将清0标志组里触发事件的标志位 p_grp->Flags &= ~flags_rdy;
退出任务函数
那么一个任务是如何向事件标志组的发送标志位 看函数 OSFlagPost()
OS_FLAGS OSFlagPost (OS_FLAG_GRP *p_grp, //指向标志组的指针
OS_FLAGS flags, //选定要操作的标志位
OS_OPT opt, //选项
OS_ERR *p_err)
opt | 功能 |
---|---|
OS_OPT_POST_FLAG_SET | 把选定的标志位置 1 |
OS_OPT_POST_FLAG_CLR | 把选定的标志位清 0 |
OS_OPT_POST_FLAG_SET+ OS_OPT_POST_NO_SCHED | 把消息发布到队列的末端,并且唤醒全部等待任务;不进行任务调度,继续运行当前任务 |
OS_OPT_POST_FLAG_CLR+OS_OPT_POST_NO_SCHED | 把消息发布到队列的前端,并且唤醒全部等待任务;不进行任务调度,继续运行当前任务 |
看代码如何实现这些功能
OS_FLAGS OSFlagPost (OS_FLAG_GRP *p_grp, //指向标志组的指针
OS_FLAGS flags, //选定要操作的标志位
OS_OPT opt, //选项
OS_ERR *p_err)
{
OS_FLAGS flags_cur;
CPU_TS ts;
//参数检查代码省略
#if OS_CFG_ISR_POST_DEFERRED_EN > 0u
if (OSIntNestingCtr > (OS_NESTING_CTR)0) {
OS_IntQPost((OS_OBJ_TYPE)OS_OBJ_TYPE_FLAG, //在中断用该函数发送标志位
(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
/***********improtance function****************/
flags_cur = OS_FlagPost(p_grp, //任务级的话 将标志组直接发布
flags,
opt,
ts,
p_err);
return (flags_cur);
}
在OSFlagPost ()函数中
我们发现它是可以在中断中使用该函数发送标志位
当然发送的主要流程都在 OS_FlagPost()
在解析该函数
OS_FLAGS OS_FlagPost (OS_FLAG_GRP *p_grp,
OS_FLAGS flags,
OS_OPT opt,
CPU_TS ts,
OS_ERR *p_err)
{
OS_FLAGS flags_cur;
OS_FLAGS flags_rdy;
OS_OPT mode;
OS_PEND_DATA *p_pend_data;
OS_PEND_DATA *p_pend_data_next;
OS_PEND_LIST *p_pend_list;
OS_TCB *p_tcb;
CPU_SR_ALLOC();
CPU_CRITICAL_ENTER();
switch (opt) { //根据选项分类处理
case OS_OPT_POST_FLAG_SET:
case OS_OPT_POST_FLAG_SET | OS_OPT_POST_NO_SCHED:
p_grp->Flags |= flags; /*将选定位置1 这里很关键已经将p_grp->Flags即将p_grp 里面的成员Flags给改变了 */
break;
case OS_OPT_POST_FLAG_CLR:
case OS_OPT_POST_FLAG_CLR | OS_OPT_POST_NO_SCHED:
p_grp->Flags &= ~flags; /*将选定位置0 */
break;
default:
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_OPT_INVALID;
return ((OS_FLAGS)0);
}
p_grp->TS = ts;
p_pend_list = &p_grp->PendList; //获取标志组的等待列表
if (p_pend_list->NbrEntries == 0u) { /*如果没有任务在等待标志组 */
CPU_CRITICAL_EXIT(); /*开中断 */
*p_err = OS_ERR_NONE; // 错误类型为“无错误”
return (p_grp->Flags); //返回事件标志组的标志值 其实就是初始值在初始化的时候p_grp->Flags=0
}
// 如果有任务在等待标志组
OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT();
p_pend_data = p_pend_list->HeadPtr; //获取等待列表第一个等待任务
p_tcb = p_pend_data->TCBPtr;
while (p_tcb != (OS_TCB *)0) { /*从头至尾遍历等待列表的所有任务 */
p_pend_data_next = p_pend_data->NextPtr;
mode = p_tcb->FlagsOpt & OS_OPT_PEND_FLAG_MASK; //获取任务的标志选项 p_tcb->FlagsOpt这个成员在请求函数里的这个函数 OS_FlagBlock()---->STCBCurPtr->FlagsOpt = opt;附了值
switch (mode) { //根据任务的标志选项分类处理
case OS_OPT_PEND_FLAG_SET_ALL: /*如果要求任务等待的标志位都得置1 */
flags_rdy = (OS_FLAGS)(p_grp->Flags & p_tcb->FlagsPend); //注意他是在请求不到信号量就会走这个OS_FlagBlock()函数 p_tcb->FlagsPend这个成员在请求函数里的这个函数 OS_FlagBlock()---->OSTCBCurPtr->FlagsPend = flags;
if (flags_rdy == p_tcb->FlagsPend) { //如果任务等待的标志位都置1了
OS_FlagTaskRdy(p_tcb, /*让该任务准备运行 */
flags_rdy,
ts);
}
break;
case OS_OPT_PEND_FLAG_SET_ANY:
flags_rdy = (OS_FLAGS)(p_grp->Flags & p_tcb->FlagsPend);
if (flags_rdy != (OS_FLAGS)0) {
OS_FlagTaskRdy(p_tcb,
flags_rdy,
ts);
}
break;
#if OS_CFG_FLAG_MODE_CLR_EN > 0u
case OS_OPT_PEND_FLAG_CLR_ALL:
flags_rdy = (OS_FLAGS)(~p_grp->Flags & p_tcb->FlagsPend);
if (flags_rdy == p_tcb->FlagsPend) {
OS_FlagTaskRdy(p_tcb,
flags_rdy,
ts);
}
break;
case OS_OPT_PEND_FLAG_CLR_ANY:
flags_rdy = (OS_FLAGS)(~p_grp->Flags & p_tcb->FlagsPend);
if (flags_rdy != (OS_FLAGS)0) {
OS_FlagTaskRdy(p_tcb,
flags_rdy,
ts);
}
break;
#endif
default:
OS_CRITICAL_EXIT();
*p_err = OS_ERR_FLAG_PEND_OPT;
return ((OS_FLAGS)0);
}
p_pend_data = p_pend_data_next; /*准备处理下一个等待任务*/
if (p_pend_data != (OS_PEND_DATA *)0) { //如果该任务存在
p_tcb = p_pend_data->TCBPtr; //获取该任务的任务控制块
} else { //如果该任务不存在
p_tcb = (OS_TCB *)0; //清空 p_tcb,退出 while 循环
}
}
OS_CRITICAL_EXIT_NO_SCHED();
if ((opt & OS_OPT_POST_NO_SCHED) == (OS_OPT)0) { //如果 opt 没选择“发布时不调度任务”
OSSched(); //任务调度
}
CPU_CRITICAL_ENTER();
flags_cur = p_grp->Flags; //获取事件标志组的标志值
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_NONE;
return (flags_cur); //返回事件标志组的当前标志值
简单解析发送事件标志组函数的运行流程
首先根据发送的选项 OS_OPT_POST_FLAG_SET或者OS_OPT_POST_FLAG_CLR 对事件标志组结构体成员 Flags 进行操作,接着如果等待列表有任务在等待事件标志组,那么历遍整个等待列表,判断操作完事件标志组结构体成员 Flags是否与自己在创建标志组函数是预设值一样,一样就从等待列表中移出,然后将任务插入任务就绪列表,最后在根据选项 OS_OPT_POST_NO_SCHED 如果有就暂时不进行任务切换,等当前任务函数运行完在任务切换到等待标志组的任务函数