事件标志组

---恢复内容开始---

  事件标志组,顾名思义,就是若干个事件标志的组合,代表若干个事件是否发生,通常用于集合两个或两个以上事件的状态。

   如果想要使用事件标志组,就必须事先使能事件标志组。消息队列的使能位于“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()

  其中,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_PendListInit()

  如果使能了 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;                       //该标志组成为列表的新首元素
}

OS_FlagDbgListAdd()

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);                         //返回当前标志位的值
}

OSFlagPost()

  其实,不管是否使能了中断延迟发布,最终都是调用 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_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

OS_FlagTaskRdy()

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 ()

  在 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_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
}

OS_Pend()

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()

  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;                                         //不需处理,直接跳出
    }
}

OS_PendAbort()

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()

  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;                                        //不需处理,直接跳出
    }
}

OS_PendObjDel()

原文地址:https://www.cnblogs.com/tianxxl/p/10376616.html

时间: 2024-10-06 10:29:13

事件标志组的相关文章

μC/OSIII——任务同步(事件标志组)

使用情况 当任务需要与多个事件的发生同步,可以使用事件标志组.有两种情况: 或同步——等待多个事件发生,任何一个事件发生,任务就发生(同步) 与同步——等待多个事件发生,当所有事件都发生,任务就发生(同步) 使用方法 事件标志组服务函数的实现代码在os_flag.c文件中,在编译时,将os_cfg.h文件中的配置常数OS_CFG_FLAG+EN设为1就可启用这些服务函数. 常用的事件标志组的服务函数有: OSFlagCreate() 创建事件标志组 OSFlagPend()    等待事件标志

FreeRTOS 事件标志组

为什么要使用事件标志事件标志组是实现多任务同步的有效机制之一.也许有不理解的初学者会问采用事件标志组多麻烦,搞个全局变量不是更简单?其实不然,在裸机编程时,使用全局变量的确比较方便,但是在加上 RTOS 后就是另一种情况了. 使用全局变量相比事件标志组主要有如下三个问题:? 使用事件标志组可以让 RTOS 内核有效地管理任务,而全局变量是无法做到的,任务的超时等机制需要用户自己去实现.? 使用了全局变量就要防止多任务的访问冲突,而使用事件标志组则处理好了这个问题,用户无需担心.? 使用事件标志组

FreeRTOS 任务计数信号量,任务二值信号量,任务事件标志组

本章节为大家讲解 FreeRTOS 计数信号量的另一种实现方式----基于任务通知(Task Notifications)的计数信号量,这里我们将这种方式实现的计数信号量称之为任务计数信号量. 任务计数信号量效率更高,需要的 RAM 空间更小.当然,缺点也是有的,它没有之前介绍的计数信号量实现的功能全面. 任务通知(Task Notifications)介绍FreeRTOS 每个已经创建的任务都有一个任务控制块(task control block),任务控制块就是一个结构体变量,用于记录任务的

μC/OS-II事件标志组的入门级使用方法

试想如下情况,有A.B.C三个事件,当A.B都满足某一条件(或执行某一动作)后C才能得到运行(持续运行或运行一次后继续等待A.B条件再次满足后再运行). 如果需要实现这样的功能,就可以用事件标志组来实现了! 具体看实例: //定义一个事件标志 OS_FLAG_GRP *Sem_Flg = 0; //LED0任务 void led0_task(void *pdata) { INT8U  err = 0; pdata = pdata; //创建一个事件标志 Sem_Flg = OSFlagCreat

uC/OS-II同步行为----事件标志组

主要API讲解: 1.OS_FLAG_GRP *OSFlagCreate( OS_FLAGS flags, //其中参数OS_FLAGS flags是信号的初始值 INT8U *perr //参数 *perr 是错误信息, ); //返回值为OS_FLAG_GRP型的指针,即为创建的信号量集的标志组的指针 2.OS_FLAGS OSFlagPost( OS_FLAG_GRP *pgrp,  //发送信号量集的指针 OS_FLAGS flags,  //选择要发送的信号 :例如:给第1位发信号 0

Freertos-事件标志组,消息队列,信号量,二值信号量,互斥信号量

任务间的通信和同步机制  在裸机编程时,使用全局变量的确比较方便,但是在加上 RTOS 后就是另一种情况了. 使用全局变量相比事件标志组主要有如下三个问题: 1.使用事件标志组可以让 RTOS 内核有效地管理任务,而全局变量是无法做到的,任务的超时等机制需要用户自己去实现.2.使用了全局变量就要防止多任务的访问冲突,而使用事件标志组则处理好了这个问题,用户无需担心.3.使用事件标志组可以有效地解决中断服务程序和任务之间的同步问题. 事件标志组:事件标志组是实现多任务同步的有效机制之一. 每创建一

基础入门_Python-模块和包.Gevent事件/队列/组/池/信号量/子进程?

常用结构: 1.Event类,事件主要用于Greenlet之间的异步通信 e = gevent.event.Event() -> Event 说明: 创建一个信号对象 e.set() -> None 说明: 设置标志位 e.clear() -> None 说明: 清除标志位 e.wait() -> None 说明: 阻塞直至标志位被设置 #!/usr/bin/env python # -*- coding: utf-8 -*- """ # # Auth

uC/OS-II实验程序之一(事件标志)

2017年,共享经济持续成为大众关注的焦点,从共享单车.共享雨伞.共享充电宝,到共享电动车.共享汽车.共享床位,甚至连女友都拿来共享了.戴上"共享"高帽的创业项目一茬接一茬地冒出来,正如收割的韭菜,最开始两茬是最嫩的,接下来生长出来的则会让人觉得食之无味又弃之可惜.对于投资人如此,对于用户们来说有何尝不是呢? 让我们盘点下近一年出现过的"共享"明星们,对于它们,死亡还是生存?这是个问题. 据统计,2016年中国的共享经济市场规模接近4万亿元:2017年,共享系宣告进

FreeRTOS 定时器组

本章节为大家讲解 FreeRTOS 支持的定时器组,或者叫软件定时器,又或者叫用户定时器均可.软件定时器的功能比较简单,也容易掌握. 被称为定时器组是因为用户可以创建多个定时器,创建的个数是可配置的. 定时器组介绍FreeRTOS 软件定时器组的时基是基于系统时钟节拍实现的,之所以叫软件定时器是因为它的实现不需要使用任何硬件定时器,而且可以创建很多个,综合这些因素,这个功能就被称之为软件定时器组.既然是定时器,那么它实现的功能与硬件定时器也是类似的. 在硬件定时器中,我们是在定时器中断中实现需要