事件标志组
---恢复内容开始---
事件标志组,顾名思义,就是若干个事件标志的组合,代表若干个事件是否发生,通常用于集合两个或两个以上事件的状态。
如果想要使用事件标志组,就必须事先使能事件标志组。消息队列的使能位于“os_cfg.h”
/* ----------------------------- EVENT FLAGS --------------------------- */ #define OS_CFG_FLAG_EN 1u //使能/禁用事件标志组 #define OS_CFG_FLAG_DEL_EN 1u //使能/禁用 OSFlagDel() 函数 #define OS_CFG_FLAG_MODE_CLR_EN 1u //使能/禁用标志位清0触发模式 #define OS_CFG_FLAG_PEND_ABORT_EN 1u //使能/禁用 OSFlagPendAbort() 函数
OSFlagCreate ()
要使用 uC/OS 的事件标志组必须先声明和创建事件标志组,调用 OSFlagCreate () 函数可以创建一个事件标志组。OSFlagCreate () 函数的信息如下表所示。
OSFlagCreate () 函数的定义位于“os_flag.c” :
void OSFlagCreate (OS_FLAG_GRP *p_grp, //事件标志组指针 CPU_CHAR *p_name, //命名事件标志组 OS_FLAGS flags, //标志初始值 OS_ERR *p_err) //返回错误类型 { CPU_SR_ALLOC(); //使用到临界段(在关/开中断时)时必需该宏,该宏声明和 //定义一个局部变量,用于保存关中断前的 CPU 状态寄存器 // SR(临界段关中断只需保存SR),开中断时将该值还原。 #ifdef OS_SAFETY_CRITICAL //如果使能了安全检测 if (p_err == (OS_ERR *)0) { //如果错误类型实参为空 OS_SAFETY_CRITICAL_EXCEPTION(); //执行安全检测异常函数 return; //返回,停止执行 } #endif #ifdef OS_SAFETY_CRITICAL_IEC61508 //如果使能了安全关键 if (OSSafetyCriticalStartFlag == DEF_TRUE) { //如果在调用OSSafetyCriticalStart()后创建 *p_err = OS_ERR_ILLEGAL_CREATE_RUN_TIME; //错误类型为“非法创建内核对象” return; //返回,停止执行 } #endif #if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u //如果使能了中断中非法调用检测 if (OSIntNestingCtr > (OS_NESTING_CTR)0) { //如果该函数是在中断中被调用 *p_err = OS_ERR_CREATE_ISR; //错误类型为“在中断中创建对象” return; //返回,停止执行 } #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; //返回,停止执行 } #endif 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); //初始化该事件标志组的等待列表 #if OS_CFG_DBG_EN > 0u //如果使能了调试代码和变量 OS_FlagDbgListAdd(p_grp); //将该标志组添加到事件标志组双向调试链表 #endif OSFlagQty++; //事件标志组个数加1 OS_CRITICAL_EXIT_NO_SCHED(); //退出临界段(无调度) *p_err = OS_ERR_NONE; //错误类型为“无错误” }
其中,OSFlagCreate () 函数调用了 OS_PendListInit() 函数初始化了事件标志组的等待列表。每个事件标志组都有一个等待列表,凡是等待该事件标志组的的任务都会被插入到这个等待列表,方便高效管理。
OS_PendListInit() 函数的定义位于“os_core.c”:
void OS_PendListInit (OS_PEND_LIST *p_pend_list) { p_pend_list->HeadPtr = (OS_PEND_DATA *)0; //复位等待列表的所有成员 p_pend_list->TailPtr = (OS_PEND_DATA *)0; p_pend_list->NbrEntries = (OS_OBJ_QTY )0; }
如果使能了 OS_CFG_DBG_EN (位于“ os_cfg.h”),创建事件标志组时还会调用OS_FlagDbgListAdd() 函数将该事件标志组插入到一个事件标志组调试列表,是为方便调试所设。
OS_FlagDbgListAdd () 函数的定义位于“os_flag.c”:
#if OS_CFG_DBG_EN > 0u //如果使能(默认使能)了调试代码和变量 void OS_FlagDbgListAdd (OS_FLAG_GRP *p_grp) //将事件标志组插入到事件标志组调试列表的最前端 { p_grp->DbgNamePtr = (CPU_CHAR *)((void *)" "); //先不指向任何任务的名称 p_grp->DbgPrevPtr = (OS_FLAG_GRP *)0; //将该标志组作为列表的最前端 if (OSFlagDbgListPtr == (OS_FLAG_GRP *)0) { //如果列表为空 p_grp->DbgNextPtr = (OS_FLAG_GRP *)0; //该队列的下一个元素也为空 } else { //如果列表非空 p_grp->DbgNextPtr = OSFlagDbgListPtr; //列表原来的首元素作为该队列的下一个元素 OSFlagDbgListPtr->DbgPrevPtr = p_grp; //原来的首元素的前面变为了该队列 } OSFlagDbgListPtr = p_grp; //该标志组成为列表的新首元素 }
OSFlagPost ()
OSFlagPost () 函数用于发布一个事件标志组。OSFlagPost () 函数的信息如下表所示。
OSFlagPost () 函数的定义也位于“os_flag.c
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; #ifdef OS_SAFETY_CRITICAL //如果使能(默认禁用)了安全检测 if (p_err == (OS_ERR *)0) { //如果错误类型实参为空 OS_SAFETY_CRITICAL_EXCEPTION(); //执行安全检测异常函数 return ((OS_FLAGS)0); //返回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); //返回0,停止执行 } switch (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); //返回0,停止执行 } #endif #if OS_CFG_OBJ_TYPE_CHK_EN > 0u //如果使能了对象类型检测 if (p_grp->Type != OS_OBJ_TYPE_FLAG) { //如果 p_grp 不是事件标志组类型 *p_err = OS_ERR_OBJ_TYPE; //错误类型“对象类型有误” return ((OS_FLAGS)0); //返回0,停止执行 } #endif ts = OS_TS_GET(); //获取时间戳 #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); //返回0,停止执行 } #endif /* 如果没有使能中断延迟发布 */ flags_cur = OS_FlagPost(p_grp, //将标志组直接发布 flags, opt, ts, p_err); return (flags_cur); //返回当前标志位的值 }
其实,不管是否使能了中断延迟发布,最终都是调用 OS_FlagPost() 函数进行发布事件标志组。只是使能了中断延迟发布的发布过程会比较曲折,中间会有许多插曲,这是中断管理范畴的内容,留到后面再作介绍。
OS_FlagPost() 函数的定义位于“os_flag.c”。
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 状态寄存器 // SR(临界段关中断只需保存SR),开中断时将该值还原。 CPU_CRITICAL_ENTER(); //关中断 switch (opt) { //根据选项分类处理 case OS_OPT_POST_FLAG_SET: //如果要求将选定位置1 case OS_OPT_POST_FLAG_SET | OS_OPT_POST_NO_SCHED: p_grp->Flags |= flags; //将选定位置1 break; //跳出 case OS_OPT_POST_FLAG_CLR: //如果要求将选定位请0 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); //返回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); //返回事件标志组的标志值 } /* 如果有任务在等待标志组 */ OS_CRITICAL_ENTER_CPU_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; //获取任务的标志选项 switch (mode) { //根据任务的标志选项分类处理 case OS_OPT_PEND_FLAG_SET_ALL: //如果要求任务等待的标志位都得置1 flags_rdy = (OS_FLAGS)(p_grp->Flags & p_tcb->FlagsPend); if (flags_rdy == p_tcb->FlagsPend) { //如果任务等待的标志位都置1了 OS_FlagTaskRdy(p_tcb, //让该任务准备运行 flags_rdy, ts); } break; //跳出 case OS_OPT_PEND_FLAG_SET_ANY: //如果要求任务等待的标志位有1位置1即可 flags_rdy = (OS_FLAGS)(p_grp->Flags & p_tcb->FlagsPend); if (flags_rdy != (OS_FLAGS)0) { //如果任务等待的标志位有置1的 OS_FlagTaskRdy(p_tcb, //让该任务准备运行 flags_rdy, ts); } break; //跳出 #if OS_CFG_FLAG_MODE_CLR_EN > 0u //如果使能了标志位清0触发模式 case OS_OPT_PEND_FLAG_CLR_ALL: //如果要求任务等待的标志位都得请0 flags_rdy = (OS_FLAGS)(~p_grp->Flags & p_tcb->FlagsPend); if (flags_rdy == p_tcb->FlagsPend) {//如果任务等待的标志位都请0了 OS_FlagTaskRdy(p_tcb, //让该任务准备运行 flags_rdy, ts); } break; //跳出 case OS_OPT_PEND_FLAG_CLR_ANY: //如果要求任务等待的标志位有1位请0即可 flags_rdy = (OS_FLAGS)(~p_grp->Flags & p_tcb->FlagsPend); if (flags_rdy != (OS_FLAGS)0) { //如果任务等待的标志位有请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); //返回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_FlagPost() 函数中,会逐个遍历事件标志组的等待列表里的等待任务。如果已有任务在等待正在发布的事件标志组,而且该发布刚好吻合任务等待的条件,就会调用OS_FlagTaskRdy() 函 数 让 等 待 任 务 脱 离 等 待 列 表 , 去 除 该 等 待 任 务 的 等 待 状 态 。
OS_FlagTaskRdy() 函数的定义位于“os_flag.c”:
void OS_FlagTaskRdy (OS_TCB *p_tcb, //任务控制块指针 OS_FLAGS flags_rdy, //让任务就绪的标志位 CPU_TS ts) //事件标志组被发布时的时间戳 { p_tcb->FlagsRdy = flags_rdy; //标记让任务就绪的事件标志位 p_tcb->PendStatus = OS_STATUS_PEND_OK; //清除任务的等待状态 p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING; //标记任务没有等待任何对象 p_tcb->TS = ts; //记录任务脱离等待时的时间戳 switch (p_tcb->TaskState) { //根据任务的任务状态分类处理 case OS_TASK_STATE_RDY: //如果任务是就绪状态 case OS_TASK_STATE_DLY: //如果任务是延时状态 case OS_TASK_STATE_DLY_SUSPENDED: //如果任务是延时中被挂起状态 case OS_TASK_STATE_SUSPENDED: //如果任务是被挂起状态 break; //直接跳出,不需处理 case OS_TASK_STATE_PEND: //如果任务是无期限等待状态 case OS_TASK_STATE_PEND_TIMEOUT: //如果任务是有期限等待状态 OS_TaskRdy(p_tcb); //让任务进入就绪状态 p_tcb->TaskState = OS_TASK_STATE_RDY; //修改任务的状态为就绪状态 break; //跳出 case OS_TASK_STATE_PEND_SUSPENDED: //如果任务是无期限等待中被挂起状态 case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED: //如果任务是有期限等待中被挂起状态 p_tcb->TaskState = OS_TASK_STATE_SUSPENDED;//修改任务的状态为被挂起状态 break; //跳出 default: //如果任务状态超出预期 break; //直接跳出 } OS_PendListRemove(p_tcb); //将任务从等待列表移除 } #endif
OSFlagPend ()
与 OSFlagPost () 事件标志组发布函数相对应,OSFlagPend () 函数用于等待事件标志组
OSFlagPend () 函数的定义也位于“os_flag.c”:
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) //返回错误类型 { CPU_BOOLEAN consume; OS_FLAGS flags_rdy; OS_OPT mode; OS_PEND_DATA pend_data; CPU_SR_ALLOC(); //使用到临界段(在关/开中断时)时必需该宏,该宏声明和 //定义一个局部变量,用于保存关中断前的 CPU 状态寄存器 // SR(临界段关中断只需保存SR),开中断时将该值还原。 #ifdef OS_SAFETY_CRITICAL //如果使能(默认禁用)了安全检测 if (p_err == (OS_ERR *)0) { //如果错误类型实参为空 OS_SAFETY_CRITICAL_EXCEPTION(); //执行安全检测异常函数 return ((OS_FLAGS)0); //返回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); //返回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); //返回0(有错误),停止执行 } switch (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); //返回0(有错误),停止执行 } #endif #if OS_CFG_OBJ_TYPE_CHK_EN > 0u //如果使能了对象类型检测 if (p_grp->Type != OS_OBJ_TYPE_FLAG) { //如果 p_grp 不是事件标志组类型 *p_err = OS_ERR_OBJ_TYPE; //错误类型为“对象类型有误” return ((OS_FLAGS)0); //返回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 非空 *p_ts = (CPU_TS)0; //初始化(清零)p_ts,待用于返回时间戳 } 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); //返回0(有错误),停止执行 } else { //如果选择了堵塞任务 if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { //如果调度器被锁 CPU_CRITICAL_EXIT(); //关中断 *p_err = OS_ERR_SCHED_LOCKED; //错误类型为“调度器被锁” return ((OS_FLAGS)0); //返回0(有错误),停止执行 } } /* 如果调度器未被锁 */ OS_CRITICAL_ENTER_CPU_EXIT(); //进入临界段,重开中断 OS_FlagBlock(&pend_data, //阻塞当前运行任务,等待事件标志组 p_grp, flags, opt, timeout); OS_CRITICAL_EXIT_NO_SCHED(); //退出临界段(无调度) } break; //跳出 case OS_OPT_PEND_FLAG_SET_ANY: //如果要求有标志位被置1即可 flags_rdy = (OS_FLAGS)(p_grp->Flags & flags); //提取想要的标志位的值 if (flags_rdy != (OS_FLAGS)0) { //如果有位被置1 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 { //如果没有位被置1 if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) { //如果没设置堵塞任务 CPU_CRITICAL_EXIT(); //关中断 *p_err = OS_ERR_PEND_WOULD_BLOCK; //错误类型为“渴求堵塞” return ((OS_FLAGS)0); //返回0(有错误),停止执行 } else { //如果设置了堵塞任务 if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { //如果调度器被锁 CPU_CRITICAL_EXIT(); //关中断 *p_err = OS_ERR_SCHED_LOCKED; //错误类型为“调度器被锁” return ((OS_FLAGS)0); //返回0(有错误),停止执行 } } /* 如果调度器没被锁 */ OS_CRITICAL_ENTER_CPU_EXIT(); //进入临界段,重开中断 OS_FlagBlock(&pend_data, //阻塞当前运行任务,等待事件标志组 p_grp, flags, opt, timeout); OS_CRITICAL_EXIT_NO_SCHED(); //退出中断(无调度) } break; //跳出 #if OS_CFG_FLAG_MODE_CLR_EN > 0u //如果使能了标志位清0触发模式 case OS_OPT_PEND_FLAG_CLR_ALL: //如果要求所有标志位均要清0 flags_rdy = (OS_FLAGS)(~p_grp->Flags & flags);//提取想要的标志位的值 if (flags_rdy == flags) { //如果该值与期望值匹配 if (consume == DEF_TRUE) { //如果要求将标志位匹配后清0 p_grp->Flags |= flags_rdy; //置1标志组的相关标志位 } 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); //返回0(有错误),停止执行 } 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); //返回0(有错误),停止执行 } else { //如果选择了堵塞任务 if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { //如果调度器被锁 CPU_CRITICAL_EXIT(); //关中断 *p_err = OS_ERR_SCHED_LOCKED; //错误类型为“调度器被锁” return ((OS_FLAGS)0); //返回0(有错误),停止执行 } } /* 如果调度器未被锁 */ OS_CRITICAL_ENTER_CPU_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) { //如果要求将标志位匹配后取反 p_grp->Flags |= flags_rdy; //置1湿巾标志组的相关标志位 } 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); //返回0(有错误),停止执行 } else { //如果没有位被清0 if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) { //如果没设置堵塞任务 CPU_CRITICAL_EXIT(); //开中断 *p_err = OS_ERR_PEND_WOULD_BLOCK; //错误类型为“渴求堵塞” return ((OS_FLAGS)0); //返回0(有错误),停止执行 } else { //如果设置了堵塞任务 if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { //如果调度器被锁 CPU_CRITICAL_EXIT(); //开中断 *p_err = OS_ERR_SCHED_LOCKED; //错误类型为“调度器被锁” return ((OS_FLAGS)0); //返回0(有错误),停止执行 } } /* 如果调度器没被锁 */ OS_CRITICAL_ENTER_CPU_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); //返回0(有错误),停止执行 } OSSched(); //任务调度 /* 任务等到了事件标志组后得以继续运行 */ CPU_CRITICAL_ENTER(); //关中断 switch (OSTCBCurPtr->PendStatus) { //根据运行任务的等待状态分类处理 case OS_STATUS_PEND_OK: //如果等到了事件标志组 if (p_ts != (CPU_TS *)0) { //如果 p_ts 非空 *p_ts = OSTCBCurPtr->TS; //返回等到标志组时的时间戳 } *p_err = OS_ERR_NONE; //错误类型为“无错误” break; //跳出 case OS_STATUS_PEND_ABORT: //如果等待被中止 if (p_ts != (CPU_TS *)0) { //如果 p_ts 非空 *p_ts = OSTCBCurPtr->TS; //返回等待被中止时的时间戳 } CPU_CRITICAL_EXIT(); //开中断 *p_err = OS_ERR_PEND_ABORT; //错误类型为“等待被中止” break; //跳出 case OS_STATUS_PEND_TIMEOUT: //如果等待超时 if (p_ts != (CPU_TS *)0) { //如果 p_ts 非空 *p_ts = (CPU_TS )0; //清零 p_ts } CPU_CRITICAL_EXIT(); //开中断 *p_err = OS_ERR_TIMEOUT; //错误类型为“超时” break; //跳出 case OS_STATUS_PEND_DEL: //如果等待对象被删除 if (p_ts != (CPU_TS *)0) { //如果 p_ts 非空 *p_ts = OSTCBCurPtr->TS; //返回对象被删时的时间戳 } CPU_CRITICAL_EXIT(); //开中断 *p_err = OS_ERR_OBJ_DEL; //错误类型为“对象被删” break; //跳出 default: //如果等待状态超出预期 CPU_CRITICAL_EXIT(); //开中断 *p_err = OS_ERR_STATUS_INVALID; //错误类型为“状态非法” break; //跳出 } if (*p_err != OS_ERR_NONE) { //如果有错误存在 return ((OS_FLAGS)0); //返回0(有错误),停止执行 } /* 如果没有错误存在 */ flags_rdy = OSTCBCurPtr->FlagsRdy; //读取让任务脱离等待的标志值 if (consume == DEF_TRUE) { //如果需要取反触发事件的标志位 switch (mode) { //根据事件触发模式分类处理 case OS_OPT_PEND_FLAG_SET_ALL: //如果是通过置1来标志事件的发生 case OS_OPT_PEND_FLAG_SET_ANY: p_grp->Flags &= ~flags_rdy; //清0标志组里触发事件的标志位 break; //跳出 #if OS_CFG_FLAG_MODE_CLR_EN > 0u //如果使能了标志位清0触发模式 case OS_OPT_PEND_FLAG_CLR_ALL: //如果是通过清0来标志事件的发生 case OS_OPT_PEND_FLAG_CLR_ANY: p_grp->Flags |= flags_rdy; //置1标志组里触发事件的标志位 break; //跳出 #endif default: //如果触发模式超出预期 CPU_CRITICAL_EXIT(); //开中断 *p_err = OS_ERR_OPT_INVALID; //错误类型为“选项非法” return ((OS_FLAGS)0); //返回0(有错误),停止执行 } } CPU_CRITICAL_EXIT(); //开中断 *p_err = OS_ERR_NONE; //错误类型为“无错误” return (flags_rdy); //返回让任务脱离等待的标志值 }
在 OSFlagPend () 函数中,当需要阻塞当前运行任务,等待事件标志组的事件时,会调用 OS_FlagBlock () 函数。
OS_FlagBlock () 函数的定义位于“os_flag.c”:
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_FlagBlock () 函数会调用一个更加底层的等待函数来执行当前任务对事件标志组的等待,该函数就是 OS_Pend()。与 OS_Post() 函数一样,OS_Pend() 函数不仅仅用来等待事件标志组,还可以等待多值信号量、互斥信号量、消息队列、任务消息队列和任务信号量。
OS_Pend() 函数的定义位于“os_core.c”。
void OS_Pend (OS_PEND_DATA *p_pend_data, //待插入等待列表的元素 OS_PEND_OBJ *p_obj, //等待的内核对象 OS_STATE pending_on, //等待哪种对象内核 OS_TICK timeout) //等待期限 { OS_PEND_LIST *p_pend_list; OSTCBCurPtr->PendOn = pending_on; //资源不可用,开始等待 OSTCBCurPtr->PendStatus = OS_STATUS_PEND_OK; //正常等待中 OS_TaskBlock(OSTCBCurPtr, //阻塞当前运行任务, timeout); //如果 timeout 非0,把任务插入的节拍列表 if (p_obj != (OS_PEND_OBJ *)0) { //如果等待对象非空 p_pend_list = &p_obj->PendList; //获取对象的等待列表到 p_pend_list p_pend_data->PendObjPtr = p_obj; //保存要等待的对象 OS_PendDataInit((OS_TCB *)OSTCBCurPtr, //初始化 p_pend_data(待插入等待列表) (OS_PEND_DATA *)p_pend_data, (OS_OBJ_QTY )1); OS_PendListInsertPrio(p_pend_list, //按优先级将 p_pend_data 插入到等待列表 p_pend_data); } else { //如果等待对象为空 OSTCBCurPtr->PendDataTblEntries = (OS_OBJ_QTY )0; //清零当前任务的等待域数据 OSTCBCurPtr->PendDataTblPtr = (OS_PEND_DATA *)0; } #if OS_CFG_DBG_EN > 0u //如果使能了调试代码和变量 OS_PendDbgNameAdd(p_obj, //更新信号量的 DbgNamePtr 元素为其等待 OSTCBCurPtr); //列表中优先级最高的任务的名称。 #endif }
OSFlagPendAbort ()
OSFlagPendAbort() 函 数 用 于 中 止 任 务 对 一 个 事 件 标 志 组 的 等 待 。 要 使 用 OSFlagPendAbort () 函数,还得事先使能 OS_CFG_FLAG_PEND_ABORT_EN(位于“os_cfg.h”),
#define OS_CFG_FLAG_PEND_ABORT_EN 1u //使能/禁用 OSFlagPendAbort() 函数
OSFlagPendAbort () 函数的信息如下表所示。
OSFlagPendAbort () 函数的定义位于“os_flag.c” :
#if OS_CFG_FLAG_PEND_ABORT_EN > 0u //如果使能了 OSFlagPendAbort() 函数 OS_OBJ_QTY OSFlagPendAbort (OS_FLAG_GRP *p_grp, //事件标志组指针 OS_OPT opt, //选项 OS_ERR *p_err) //返回错误类型 { OS_PEND_LIST *p_pend_list; OS_TCB *p_tcb; CPU_TS ts; OS_OBJ_QTY nbr_tasks; CPU_SR_ALLOC(); //使用到临界段(在关/开中断时)时必需该宏,该宏声明和 //定义一个局部变量,用于保存关中断前的 CPU 状态寄存器 // SR(临界段关中断只需保存SR),开中断时将该值还原。 #ifdef OS_SAFETY_CRITICAL //如果使能了安全检测 if (p_err == (OS_ERR *)0) { //如果错误类型实参为空 OS_SAFETY_CRITICAL_EXCEPTION(); //执行安全检测异常函数 return ((OS_OBJ_QTY)0u); //返回0(有错误),停止执行 } #endif #if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u //如果使能了中断中非法调用检测 if (OSIntNestingCtr > (OS_NESTING_CTR)0u) { //如果该函数是在中断中被调用 *p_err = OS_ERR_PEND_ABORT_ISR; //错误类型为“在中断中创建对象” return ((OS_OBJ_QTY)0u); //返回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_OBJ_QTY)0u); //返回0(有错误),停止执行 } switch (opt) { //根据选项分类处理 case OS_OPT_PEND_ABORT_1: //如果选项在预期内 case OS_OPT_PEND_ABORT_ALL: case OS_OPT_PEND_ABORT_1 | OS_OPT_POST_NO_SCHED: case OS_OPT_PEND_ABORT_ALL | OS_OPT_POST_NO_SCHED: break; //直接跳出 default: //如果选项超出预期 *p_err = OS_ERR_OPT_INVALID; //错误类型为“选项非法” return ((OS_OBJ_QTY)0u); //返回0(有错误),停止执行 } #endif #if OS_CFG_OBJ_TYPE_CHK_EN > 0u //如果使能了对象类型检测 if (p_grp->Type != OS_OBJ_TYPE_FLAG) { //如果 p_grp 不是事件标志组类型 *p_err = OS_ERR_OBJ_TYPE; //错误类型为“对象类型有误” return ((OS_OBJ_QTY)0u); //返回0(有错误),停止执行 } #endif CPU_CRITICAL_ENTER(); //关中断 p_pend_list = &p_grp->PendList; //获取消息队列的等待列表 if (p_pend_list->NbrEntries == (OS_OBJ_QTY)0u) { //如果没有任务在等待 CPU_CRITICAL_EXIT(); //开中断 *p_err = OS_ERR_PEND_ABORT_NONE; //错误类型为“没任务在等待” return ((OS_OBJ_QTY)0u); //返回0(有错误),停止执行 } /* 如果有任务在等待 */ OS_CRITICAL_ENTER_CPU_EXIT(); //进入临界段,重开中断 nbr_tasks = 0u; //准备计数被中止的等待任务 ts = OS_TS_GET(); //获取时间戳 while (p_pend_list->NbrEntries > (OS_OBJ_QTY)0u) {//如果还有任务在等待 p_tcb = p_pend_list->HeadPtr->TCBPtr; //获取头端(最高优先级)任务 OS_PendAbort((OS_PEND_OBJ *)((void *)p_grp), //中止该任务对 p_grp 的等待 p_tcb, ts); nbr_tasks++; if (opt != OS_OPT_PEND_ABORT_ALL) { //如果不是选择了中止所有等待任务 break; //立即跳出,不再继续中止 } } OS_CRITICAL_EXIT_NO_SCHED(); //退出临界段(无调度) if ((opt & OS_OPT_POST_NO_SCHED) == (OS_OPT)0u) { //如果选择了任务调度 OSSched(); //进行任务调度 } *p_err = OS_ERR_NONE; //错误类型为“无错误” return (nbr_tasks); //返回被中止的任务数目 } #endif
OSFlagPendAbort () 函数会调用一个更加底层的中止等待函数来执行当前任务对事件标志组的等待,该函数就是 OS_PendAbort()。
OS_PendAbort() 函数不仅仅用来中止对事件标志组的等待,还可以中止对多值信号量、互斥信号量、消息队列、任务消息队列或任务信号量的等待。
OS_PendAbort() 函数的定义位于“os_core.c”。:
void OS_PendAbort (OS_PEND_OBJ *p_obj, //被等待对象的类型 OS_TCB *p_tcb, //任务控制块指针 CPU_TS ts) //等待被中止时的时间戳 { switch (p_tcb->TaskState) { //根据任务状态分类处理 case OS_TASK_STATE_RDY: //如果任务是就绪状态 case OS_TASK_STATE_DLY: //如果任务是延时状态 case OS_TASK_STATE_SUSPENDED: //如果任务是挂起状态 case OS_TASK_STATE_DLY_SUSPENDED: //如果任务是在延时中被挂起 break; //这些情况均与等待无关,直接跳出 case OS_TASK_STATE_PEND: //如果任务是无期限等待状态 case OS_TASK_STATE_PEND_TIMEOUT: //如果任务是有期限等待状态 if (p_tcb->PendOn == OS_TASK_PEND_ON_MULTI) { //如果任务在等待多个信号量或消息队列 OS_PendAbort1(p_obj, //强制解除任务对某一对象的等待 p_tcb, ts); } #if (OS_MSG_EN > 0u) //如果使能了任务队列或消息队列 p_tcb->MsgPtr = (void *)0; //清除(复位)任务的消息域 p_tcb->MsgSize = (OS_MSG_SIZE)0u; #endif p_tcb->TS = ts; //保存等待被中止时的时间戳到任务控制块 if (p_obj != (OS_PEND_OBJ *)0) { //如果等待对象非空 OS_PendListRemove(p_tcb); //将任务从所有等待列表中移除 } OS_TaskRdy(p_tcb); //让任务进准备运行 p_tcb->TaskState = OS_TASK_STATE_RDY; //修改任务状态为就绪状态 p_tcb->PendStatus = OS_STATUS_PEND_ABORT; //标记任务的等待被中止 p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING; //标记任务目前没有等待任何对象 break; //跳出 case OS_TASK_STATE_PEND_SUSPENDED: //如果任务在无期限等待中被挂起 case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED: //如果任务在有期限等待中被挂起 if (p_tcb->PendOn == OS_TASK_PEND_ON_MULTI) { //如果任务在等待多个信号量或消息队列 OS_PendAbort1(p_obj, //强制解除任务对某一对象的等待 p_tcb, ts); } #if (OS_MSG_EN > 0u) //如果使能了任务队列或消息队列 p_tcb->MsgPtr = (void *)0; //清除(复位)任务的消息域 p_tcb->MsgSize = (OS_MSG_SIZE)0u; #endif p_tcb->TS = ts; //保存等待被中止时的时间戳到任务控制块 if (p_obj != (OS_PEND_OBJ *)0) { //如果等待对象非空 OS_PendListRemove(p_tcb); //将任务从所有等待列表中移除 } OS_TickListRemove(p_tcb); //让任务脱离节拍列表 p_tcb->TaskState = OS_TASK_STATE_SUSPENDED; //修改任务状态为挂起状态 p_tcb->PendStatus = OS_STATUS_PEND_ABORT; //标记任务的等待被中止 p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING; //标记任务目前没有等待任何对象 break; //跳出 default: //如果任务状态超出预期 break; //不需处理,直接跳出 } }
OSFlagDel ()
OSFlagDel() 函数用于删除一个消息队列。要使用 OSFlagDel () 函数,还得事先使能OS_CFG_FLAG_DEL_EN(位于“os_cfg.h”)
#define OS_CFG_FLAG_DEL_EN 1u //使能/禁用 OSFlagDel() 函数
OSFlagDel () 函数的信息如下表所示
OSFlagDel () 函数的定义位于“os_flag.c” :
#if OS_CFG_FLAG_DEL_EN > 0u //如果使能了 OSFlagDel() 函数 OS_OBJ_QTY OSFlagDel (OS_FLAG_GRP *p_grp, //事件标志组指针 OS_OPT opt, //选项 OS_ERR *p_err) //返回错误类型 { OS_OBJ_QTY cnt; OS_OBJ_QTY nbr_tasks; OS_PEND_DATA *p_pend_data; OS_PEND_LIST *p_pend_list; OS_TCB *p_tcb; CPU_TS ts; CPU_SR_ALLOC(); //使用到临界段(在关/开中断时)时必需该宏,该宏声明和 //定义一个局部变量,用于保存关中断前的 CPU 状态寄存器 // SR(临界段关中断只需保存SR),开中断时将该值还原。 #ifdef OS_SAFETY_CRITICAL //如果使能(默认禁用)了安全检测 if (p_err == (OS_ERR *)0) { //如果错误类型实参为空 OS_SAFETY_CRITICAL_EXCEPTION(); //执行安全检测异常函数 return ((OS_OBJ_QTY)0); //返回0(有错误),停止执行 } #endif #if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u //如果使能了中断中非法调用检测 if (OSIntNestingCtr > (OS_NESTING_CTR)0) { //如果该函数在中断中被调用 *p_err = OS_ERR_DEL_ISR; //错误类型为“在中断中删除对象” return ((OS_OBJ_QTY)0); //返回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_OBJ_QTY)0); //返回0(有错误),停止执行 } switch (opt) { //根据选项分类处理 case OS_OPT_DEL_NO_PEND: //如果选项在预期内 case OS_OPT_DEL_ALWAYS: break; //直接跳出 default: //如果选项超出预期 *p_err = OS_ERR_OPT_INVALID; //错误类型为“选项非法” return ((OS_OBJ_QTY)0); //返回0(有错误),停止执行 } #endif #if OS_CFG_OBJ_TYPE_CHK_EN > 0u //如果使能了对象类型检测 if (p_grp->Type != OS_OBJ_TYPE_FLAG) {//如果 p_grp 不是事件标志组类型 *p_err = OS_ERR_OBJ_TYPE; //错误类型为“对象类型有误” return ((OS_OBJ_QTY)0); //返回0(有错误),停止执行 } #endif OS_CRITICAL_ENTER(); //进入临界段 p_pend_list = &p_grp->PendList; //获取消息队列的等待列表 cnt = p_pend_list->NbrEntries; //获取等待该队列的任务数 nbr_tasks = cnt; //按照任务数目逐个处理 switch (opt) { //根据选项分类处理 case OS_OPT_DEL_NO_PEND: //如果只在没任务等待时进行删除 if (nbr_tasks == (OS_OBJ_QTY)0) { //如果没有任务在等待该标志组 #if OS_CFG_DBG_EN > 0u //如果使能了调试代码和变量 OS_FlagDbgListRemove(p_grp); //将该标志组从标志组调试列表移除 #endif OSFlagQty--; //标志组数目减1 OS_FlagClr(p_grp); //清除该标志组的内容 OS_CRITICAL_EXIT(); //退出临界段 *p_err = OS_ERR_NONE; //错误类型为“无错误” } else { OS_CRITICAL_EXIT(); //退出临界段 *p_err = OS_ERR_TASK_WAITING; //错误类型为“有任务在等待标志组” } break; //跳出 case OS_OPT_DEL_ALWAYS: //如果必须删除标志组 ts = OS_TS_GET(); //获取时间戳 while (cnt > 0u) { //逐个移除该标志组等待列表中的任务 p_pend_data = p_pend_list->HeadPtr; p_tcb = p_pend_data->TCBPtr; OS_PendObjDel((OS_PEND_OBJ *)((void *)p_grp), p_tcb, ts); cnt--; } #if OS_CFG_DBG_EN > 0u //如果使能了调试代码和变量 OS_FlagDbgListRemove(p_grp); //将该标志组从标志组调试列表移除 #endif OSFlagQty--; //标志组数目减1 OS_FlagClr(p_grp); //清除该标志组的内容 OS_CRITICAL_EXIT_NO_SCHED(); //退出临界段(无调度) OSSched(); //调度任务 *p_err = OS_ERR_NONE; //错误类型为“无错误” break; //跳出 default: //如果选项超出预期 OS_CRITICAL_EXIT(); //退出临界段 *p_err = OS_ERR_OPT_INVALID; //错误类型为“选项非法” break; //跳出 } return (nbr_tasks); //返回删除标志组前等待其的任务数 } #endif
OSFlagDel () 函数会调用一个更加底层的删除等待对象的函数来执行对事件标志组的删除,该函数就是 OS_PendObjDel ()。OS_PendObjDel () 函数不仅仅用来删除事件标志组,还可以删除多值信号量、互斥信号量、消息队列、任务消息队列或任务信号量。
OS_PendObjDel() 函数的定义位于“os_core.c”:
void OS_PendObjDel (OS_PEND_OBJ *p_obj, //被删除对象的类型 OS_TCB *p_tcb, //任务控制块指针 CPU_TS ts) //信号量被删除时的时间戳 { switch (p_tcb->TaskState) { //根据任务状态分类处理 case OS_TASK_STATE_RDY: //如果任务是就绪状态 case OS_TASK_STATE_DLY: //如果任务是延时状态 case OS_TASK_STATE_SUSPENDED: //如果任务是挂起状态 case OS_TASK_STATE_DLY_SUSPENDED: //如果任务是在延时中被挂起 break; //这些情况均与等待无关,直接跳出 case OS_TASK_STATE_PEND: //如果任务是无期限等待状态 case OS_TASK_STATE_PEND_TIMEOUT: //如果任务是有期限等待状态 if (p_tcb->PendOn == OS_TASK_PEND_ON_MULTI) { //如果任务在等待多个信号量或消息队列 OS_PendObjDel1(p_obj, //强制解除任务对某一对象的等待 p_tcb, ts); } #if (OS_MSG_EN > 0u) //如果使能了任务队列或消息队列 p_tcb->MsgPtr = (void *)0; //清除(复位)任务的消息域 p_tcb->MsgSize = (OS_MSG_SIZE)0u; #endif p_tcb->TS = ts; //保存等待被中止时的时间戳到任务控制块 OS_PendListRemove(p_tcb); //将任务从所有等待列表中移除 OS_TaskRdy(p_tcb); //让任务进准备运行 p_tcb->TaskState = OS_TASK_STATE_RDY; //修改任务状态为就绪状态 p_tcb->PendStatus = OS_STATUS_PEND_DEL; //标记任务的等待对象被删除 p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING; //标记任务目前没有等待任何对象 break; //跳出 case OS_TASK_STATE_PEND_SUSPENDED: //如果任务在无期限等待中被挂起 case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED: //如果任务在有期限等待中被挂起 if (p_tcb->PendOn == OS_TASK_PEND_ON_MULTI) {//如果任务在等待多个信号量或消息队列 OS_PendObjDel1(p_obj, //强制解除任务对某一对象的等待 p_tcb, ts); } #if (OS_MSG_EN > 0u) //如果使能了任务队列或消息队列 p_tcb->MsgPtr = (void *)0; //清除(复位)任务的消息域 p_tcb->MsgSize = (OS_MSG_SIZE)0u; #endif p_tcb->TS = ts; //保存等待被中止时的时间戳到任务控制块 OS_TickListRemove(p_tcb); //让任务脱离节拍列表 OS_PendListRemove(p_tcb); //将任务从所有等待列表中移除 p_tcb->TaskState = OS_TASK_STATE_SUSPENDED; //修改任务状态为挂起状态 p_tcb->PendStatus = OS_STATUS_PEND_DEL; //标记任务的等待对象被删除 p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING; //标记任务目前没有等待任何对象 break; //跳出 default: //如果任务状态超出预期 break; //不需处理,直接跳出 } }