(学习日记)2024.04.09:UCOSIII第三十七节:事件函数接口

写在前面:
由于时间的不足与学习的碎片化,写博客变得有些奢侈。
但是对于记录学习(忘了以后能快速复习)的渴望一天天变得强烈。
既然如此
不如以天为单位,以时间为顺序,仅仅将博客当做一个知识学习的目录,记录笔者认为最通俗、最有帮助的资料,并尽量总结几句话指明本质,以便于日后搜索起来更加容易。


标题的结构如下:“类型”:“知识点”——“简短的解释”
部分内容由于保密协议无法上传。


点击此处进入学习日记的总目录

2024.04.09:UCOSIII第三十七节:事件函数接口

  • 五十一、UCOSIII:事件函数接口
    • 1、事件创建函数OSFlagCreate()
    • 2、事件删除函数OSFlagDel()
    • 3、事件设置函数OSFlagPost()
      • 1. OSFlagPost()
      • 2. OS_FlagPost()
    • 4、事件等待函数OSFlagPend()

五十一、UCOSIII:事件函数接口

1、事件创建函数OSFlagCreate()

事件创建函数,顾名思义,就是创建一个事件,与其他内核对象一样,都是需要先创建才能使用的资源。
μC/OS给我们提供了一个创建事件的函数OSFlagCreate(), 当创建一个事件时,系统会对我们定义的事件控制块进行基本的初始化。
所以,在使用创建函数之前,我们需要先定义一个事件控制块(句柄), 事件创建函数的源码具体如下:

void  OSFlagCreate (OS_FLAG_GRP  *p_grp,  (1)       //事件指针CPU_CHAR     *p_name, (2)       //命名事件OS_FLAGS      flags,  (3)       //标志初始值OS_ERR       *p_err)  (4)       //返回错误类型
{CPU_SR_ALLOC(); //使用到临界段(在关/开中断时)时必须用到该宏,该宏声明和//定义一个局部变量,用于保存关中断前的 CPU 状态寄存器// SR(临界段关中断只需保存SR),开中断时将该值还原。#ifdef OS_SAFETY_CRITICAL(5)//如果启用了安全检测if (p_err == (OS_ERR *)0)           //如果错误类型实参为空{OS_SAFETY_CRITICAL_EXCEPTION(); //执行安全检测异常函数return;                         //返回,停止执行}
#endif#ifdef OS_SAFETY_CRITICAL_IEC61508(6)//如果启用了安全关键if (OSSafetyCriticalStartFlag == DEF_TRUE)   //如果OSSafetyCriticalStart()后创建{*p_err = OS_ERR_ILLEGAL_CREATE_RUN_TIME;  //错误类型为“非法创建内核对象”return;                                  //返回,停止执行}
#endif#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u(7)//如果启用了中断中非法调用检测if (OSIntNestingCtr > (OS_NESTING_CTR)0)   //如果该函数是在中断中被调用{*p_err = OS_ERR_CREATE_ISR;             //错误类型为“在中断中创建对象”return;                                //返回,停止执行}
#endif#if OS_CFG_ARG_CHK_EN > 0u(8)//如果启用了参数检测if (p_grp == (OS_FLAG_GRP *)0)   //如果 p_grp 为空{*p_err = OS_ERR_OBJ_PTR_NULL; //错误类型为“创建对象为空”return;                      //返回,停止执行}
#endifOS_CRITICAL_ENTER();         (9)//进入临界段p_grp->Type    = OS_OBJ_TYPE_FLAG; //标记创建对象数据结构为事件p_grp->NamePtr = p_name;      (10)//标记事件的名称p_grp->Flags   = flags;        (11)//设置标志初始值p_grp->TS      = (CPU_TS)0;    (12)//清零事件的时间戳OS_PendListInit(&p_grp->PendList);(13)//初始化该事件的等待列表#if OS_CFG_DBG_EN > 0u//如果启用了调试代码和变量OS_FlagDbgListAdd(p_grp);          //将该事件添加到事件双向调试链表
#endifOSFlagQty++;                 (14)//事件个数加1OS_CRITICAL_EXIT_NO_SCHED();       //退出临界段(无调度)*p_err = OS_ERR_NONE;         (15)//错误类型为“无错误”
}
  • (1):事件控制块指针,指向我们定义的事件控制块结构体变量,所以在创建之前我们需要先定义一个事件控制块变量。
  • (2):事件名称,字符串形式。
  • (3):事件标志位的初始值,一般为常为0。
  • (4):用于保存返回的错误类型。
  • (5):如果启用了安全检测(默认禁用), 在编译时则会包含安全检测相关的代码,如果错误类型实参为空,系统会执行安全检测异常函数,然后返回,不执行创建互斥量操作。
  • (6):如果启用(默认禁用)了安全关键检测, 在编译时则会包含安全关键检测相关的代码,如果是在调用OSSafetyCriticalStart()后创建该事件,则是非法的,返回错误类型为“非法创建内核对象”错误代码,并且退出,不执行创建事件操作。
  • (7):如果启用了中断中非法调用检测(默认启用), 在编译时则会包含中断非法调用检测相关的代码,如果该函数是在中断中被调用,则是非法的,返回错误类型为“在中断中创建对象”的错误代码,并且退出,不执行创建事件操作。
  • (8):如果启用了参数检测(默认启用), 在编译时则会包含参数检测相关的代码,如果p_grp参数为空,返回错误类型为“创建对象为空”的错误代码,并且退出,不执行创建事件操作。
  • (9):进入临界段,标记创建对象数据结构为事件。
  • (10):初始化事件的名称。
  • (11):设置事件标志的初始值。
  • (12):记录时间戳的变量TS初始化为0。
  • (13):初始化该事件的等待列表。
  • (14):系统事件个数加1。
  • (15):退出临界段(无调度),创建事件成功。

事件结构如下:
在这里插入图片描述

事件创建函数的使用实例具体如下:

OS_FLAG_GRP flag_grp;                   //声明事件OS_ERR      err;/* 创建事件 flag_grp */
OSFlagCreate ((OS_FLAG_GRP  *)&flag_grp,        //指向事件的指针(CPU_CHAR     *)"FLAG For Test",  //事件的名字(OS_FLAGS      )0,                //事件的初始值(OS_ERR       *)&err);            //返回错误类型

2、事件删除函数OSFlagDel()

在很多场合,某些事件只用一次的,就好比在事件应用场景说的危险机器的启动,假如各项指标都达到了,并且机器启动成功了, 那这个事件之后可能就没用了,那就可以进行销毁了。
想要删除事件怎么办?μC/OS给我们提供了一个删除事件的函数——OSFlagDel(), 使用它就能将事件进行删除了。

注意,想要使用删除事件函数则必须将OS_CFG_FLAG_DEL_EN宏定义配置为1,该宏定义在os_cfg.h文件中。

当系统不再使用事件对象时,可以通过删除事件对象控制块来进行删除,具体如下:

#if OS_CFG_FLAG_DEL_EN > 0u//如果启用了 OSFlagDel() 函数
OS_OBJ_QTY  OSFlagDel (OS_FLAG_GRP  *p_grp, (1)     //事件指针OS_OPT        opt,   (2)        //选项OS_ERR       *p_err) (3)        //返回错误类型
{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(4)//如果启用(默认禁用)了安全检测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(5)//如果启用了中断中非法调用检测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(6)//如果启用了参数检测if (p_grp == (OS_FLAG_GRP *)0)        //如果 p_grp 为空{*p_err  = OS_ERR_OBJ_PTR_NULL;     //错误类型为“对象为空”return ((OS_OBJ_QTY)0);           //返回0(有错误),停止执行}switch (opt)                (7)//根据选项分类处理{case OS_OPT_DEL_NO_PEND:          //如果选项在预期内case OS_OPT_DEL_ALWAYS:break;                       //直接跳出default:                   (8)//如果选项超出预期*p_err = OS_ERR_OPT_INVALID;  //错误类型为“选项非法”return ((OS_OBJ_QTY)0);      //返回0(有错误),停止执行}
#endif#if OS_CFG_OBJ_TYPE_CHK_EN > 0u(9)//如果启用了对象类型检测if (p_grp->Type != OS_OBJ_TYPE_FLAG)  //如果 p_grp 不是事件类型{*p_err = OS_ERR_OBJ_TYPE;          //错误类型为“对象类型有误”return ((OS_OBJ_QTY)0);           //返回0(有错误),停止执行}
#endifOS_CRITICAL_ENTER();                         //进入临界段p_pend_list = &p_grp->PendList;       (10)//获取消息队列的等待列表cnt         = p_pend_list->NbrEntries;   (11)//获取等待该队列的任务数nbr_tasks   = cnt;                          //按照任务数目逐个处理switch (opt)                    (12)//根据选项分类处理{case OS_OPT_DEL_NO_PEND:        (13)//如果只在没任务等待时进行删除if (nbr_tasks == (OS_OBJ_QTY)0)     //如果没有任务在等待该事件{
#if OS_CFG_DBG_EN > 0u//如果启用了调试代码和变量OS_FlagDbgListRemove(p_grp); (14)//将该事件从事件调试列表移除
#endifOSFlagQty--;           (15)//事件数目减1OS_FlagClr(p_grp);      (16)//清除该事件的内容OS_CRITICAL_EXIT();             //退出临界段*p_err = OS_ERR_NONE;     (17)//错误类型为“无错误”}else{OS_CRITICAL_EXIT();             //退出临界段*p_err = OS_ERR_TASK_WAITING;  (18)//错误类型为“有任务在等待事件”}break;                              //跳出case OS_OPT_DEL_ALWAYS:           (19)//如果必须删除事件ts = OS_TS_GET();             (20)//获取时间戳while (cnt > 0u)              (21)//逐个移除该事件等待列表中的任务{p_pend_data = p_pend_list->HeadPtr;p_tcb       = p_pend_data->TCBPtr;OS_PendObjDel((OS_PEND_OBJ *)((void *)p_grp),p_tcb,ts);                (22)cnt--;}
#if OS_CFG_DBG_EN > 0u//如果启用了调试代码和变量OS_FlagDbgListRemove(p_grp);        //将该事件从事件调试列表移除
#endifOSFlagQty--;                   (23)//事件数目减1OS_FlagClr(p_grp);             (24)//清除该事件的内容OS_CRITICAL_EXIT_NO_SCHED();                //退出临界段(无调度)OSSched();                      (25)//调度任务*p_err = OS_ERR_NONE;     (26)//错误类型为“无错误”break;                              //跳出default:                       (27)//如果选项超出预期OS_CRITICAL_EXIT();                 //退出临界段*p_err = OS_ERR_OPT_INVALID;         //错误类型为“选项非法”break;                              //跳出}return (nbr_tasks);          (28)//返回删除事件前等待其的任务数
}
#endif
  • (1):事件控制块指针,指向我们定义的事件控制块结构体变量, 所以在删除之前我们需要先定义一个事件控制块变量,并且成功创建事件后再进行删除操作。
  • (2):事件删除的选项。
  • (3):用于保存返回的错误类型。
  • (4):如果启用了安全检测(默认),在编译时则会包含安全检测相关的代码, 如果错误类型实参为空,系统会执行安全检测异常函数,然后返回,不执行删除互斥量操作。
  • (5):如果启用了中断中非法调用检测(默认启用), 在编译时则会包含中断非法调用检测相关的代码,如果该函数是在中断中被调用,则是非法的,返回错误类型为“在中断中删除对象”的错误代码,并且退出,不执行删除事件操作。
  • (6):如果启用了参数检测(默认启用), 在编译时则会包含参数检测相关的代码,如果p_grp参数为空,返回错误类型为“内核对象为空”的错误代码,并且退出,不执行删除事件操作。
  • (7):判断opt选项是否合理,该选项有两个, OS_OPT_DEL_ALWAYS与OS_OPT_DEL_NO_PEND,在os.h文件中定义。此处是判断一下选项是否在预期之内,如果在则跳出switch语句。
  • (8):如果选项超出预期,则返回错误类型为“选项非法”的错误代码,退出,不继续执行。
  • (9):如果启用了对象类型检测,在编译时则会包含对象类型检测相关的代码, 如果 p_grp 不是事件类型,返回错误类型为“内核对象类型错误”的错误代码,并且退出,不执行删除事件操作。
  • (10):进入临界段,程序执行到这里,表示可以删除事件了, 系统首先获取互斥量的等待列表保存到p_pend_list变量中,μC/OS在删事件的时候是通过该变量访问事件等待列表的任务的。
  • (11):获取等待该队列的任务数,按照任务个数逐个处理。
  • (12):根据选项分类处理。
  • (13):如果opt是OS_OPT_DEL_NO_PEND,则表示只在没有任务等待的情况下删除事件, 如果当前系统中有任务还在等待该事件的某些位,则不能进行删除操作,反之,则可以删除事件。
  • (14):如果启用了调试代码和变量,将该事件从事件调试列表移除。
  • (15):系统的事件个数减一。
  • (16):清除该事件的内容。
  • (17):删除成功,返回错误类型为“无错误”的错误代码。
  • (18)::如果有任务在等待该事件,则返回错误类型为“有任务在等待该事件”错误代码。
  • (19):如果opt是OS_OPT_DEL_ALWAYS, 则表示无论如何都必须删除事件,那么在删除之前,系统会把所有阻塞在该事件上的任务恢复。
  • (20):获取删除时候的时间戳。
  • (21):根据前面cnt记录阻塞在该事件上的任务个数,逐个移除该事件等待列表中的任务。
  • (22):调用OS_PendObjDel()函数将阻塞在内核对象(如事件)上的任务从阻塞态恢复, 此时系统在删除内核对象,删除之后,这些等待事件的任务需要被恢复。
  • (23):系统事件数目减1
  • (24):清除该事件的内容。
  • (25):进行一次任务调度。
  • (26):删除事件完成,返回错误类型为“无错误”的错误代码。
  • (27):如果选项超出预期则返回错误类型为“任务状态非法”的错误代码。
  • (28):返回删除事件前等待其的任务数

事件删除函数OSFlagDel()的使用也是很简单的,只需要传入要删除的事件的句柄与选项还有保存返回的错误类型即可,调用函数时, 系统将删除这个事件。
需要注意的是在调用删除事件函数前,系统应存在已创建的事件。
如果删除事件时,系统中有任务正在等待该事件, 则不应该进行删除操作,删除事件函数OSFlagDel()的使用实例具体

OS_FLAG_GRPflag_grp;;                             //声明事件句柄OS_ERR      err;/* 删除事件*/
OSFlagDel((OS_FLAG_GRP*)&flag_grp,      //指向事件的指针
OS_OPT_DEL_NO_PEND,
(OS_ERR      *)&err);             //返回错误类型

3、事件设置函数OSFlagPost()

1. OSFlagPost()

OSFlagPost()用于设置事件组中指定的位,当位被置位之后,并且满足任务的等待事件,那么等待在事件该标志位上的任务将会被恢复。
使用该函数接口时, 通过参数指定的事件标志来设置事件的标志位,然后遍历等待在事件对象上的事件等待列表,判断是否有任务的事件激活要求与当前事件对象标志值匹配, 如果有,则唤醒该任务。
简单来说,就是设置我们自己定义的事件标志位为1,并且看看有没有任务在等待这个事件,有的话就唤醒它。
OSFlagPost()函数源码具体:

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,停止执行}
#endifts = OS_TS_GET();                             //获取时间戳#if OS_CFG_ISR_POST_DEFERRED_EN > 0u(1)//如果启用了中断延迟发布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); (2)return (flags_cur);                         //返回当前标志位的值
}
  • (1):如果启用了中断延迟发布并且该函数在中断中被调用,则将该事件发布到中断消息队列。
  • (2):如果没有启用中断延迟发布, 则直接将该事件对应的标志位置位。

2. OS_FlagPost()

OS_FLAGS  OS_FlagPost (OS_FLAG_GRP  *p_grp,         (1)     //事件指针OS_FLAGS      flags,    (2)     //选定要操作的标志位OS_OPT        opt,      (3)     //选项CPU_TS        ts,       (4)     //时间戳OS_ERR       *p_err)    (5)     //返回错误类型
{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)                         (6)//根据选项分类处理{case OS_OPT_POST_FLAG_SET:          (7)//如果要求将选定位置1case OS_OPT_POST_FLAG_SET | OS_OPT_POST_NO_SCHED:p_grp->Flags |=  flags;                     //将选定位置1break;                                      //跳出case OS_OPT_POST_FLAG_CLR:           (8)//如果要求将选定位请0case OS_OPT_POST_FLAG_CLR | OS_OPT_POST_NO_SCHED:p_grp->Flags &= ~flags;                     //将选定位请0break;                                      //跳出default:                           (9)//如果选项超出预期CPU_CRITICAL_EXIT();                        //开中断*p_err = OS_ERR_OPT_INVALID;                 //错误类型为“选项非法”return ((OS_FLAGS)0);                       //返回0,停止执行}p_grp->TS   = ts;                 (10)//将时间戳存入事件p_pend_list = &p_grp->PendList;    (11)//获取事件的等待列表if (p_pend_list->NbrEntries == 0u) (12)//如果没有任务在等待事件{CPU_CRITICAL_EXIT();                             //开中断*p_err = OS_ERR_NONE;                        //错误类型为“无错误”return (p_grp->Flags);                           //返回事件的标志值}/* 如果有任务在等待事件 */OS_CRITICAL_ENTER_CPU_EXIT();       (13)//进入临界段,重开中断p_pend_data = p_pend_list->HeadPtr; (14)//获取等待列表头个等待任务p_tcb       = p_pend_data->TCBPtr;while (p_tcb != (OS_TCB *)0)        (15)//从头至尾遍历等待列表的所有任务{p_pend_data_next = p_pend_data->NextPtr;mode = p_tcb->FlagsOpt & OS_OPT_PEND_FLAG_MASK; //获取任务的标志选项switch (mode)                (16)//根据任务的标志选项分类处理{OS_OPT_PEND_FLAG_SET_ALL:  (17)//如果要求任务等待的标志位都得置1flags_rdy = (OS_FLAGS)(p_grp->Flags & p_tcb->FlagsPend);if (flags_rdy == p_tcb->FlagsPend) //如果任务等待的标志位都置1了{OS_FlagTaskRdy(p_tcb,            //让该任务准备运行flags_rdy,ts);            (18)}break;                               //跳出case OS_OPT_PEND_FLAG_SET_ANY:     (19)//如果要求任务等待的标志位有1位置1即可flags_rdy = (OS_FLAGS)(p_grp->Flags & p_tcb->FlagsPend);(20)if (flags_rdy != (OS_FLAGS)0)     //如果任务等待的标志位有置1的{OS_FlagTaskRdy(p_tcb,            //让该任务准备运行flags_rdy,ts);            (21)}break;                              //跳出#if OS_CFG_FLAG_MODE_CLR_EN > 0u(22)//如果启用了标志位清零触发模式case OS_OPT_PEND_FLAG_CLR_ALL: (23)//如果要求任务等待的标志位都得请0flags_rdy = (OS_FLAGS)(~p_grp->Flags & p_tcb->FlagsPend);if (flags_rdy == p_tcb->FlagsPend)  //如果任务等待的标志位都请0了{OS_FlagTaskRdy(p_tcb,           //让该任务准备运行flags_rdy,ts);    (24)}break;            //跳出case OS_OPT_PEND_FLAG_CLR_ANY:     (25)//如果要求任务等待的标志位有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);    (26)}break;                            //跳出
#endifdefault:                      (27)//如果标志选项超出预期OS_CRITICAL_EXIT();               //退出临界段*p_err = OS_ERR_FLAG_PEND_OPT;     //错误类型为“标志选项非法”return ((OS_FLAGS)0);             //返回0,停止运行}p_pend_data = p_pend_data_next;   (28)//准备处理下一个等待任务if (p_pend_data != (OS_PEND_DATA *)0)      //如果该任务存在{p_tcb = p_pend_data->TCBPtr;   (29)//获取该任务的任务控制块}else//如果该任务不存在{p_tcb = (OS_TCB *)0;     (30)//清空 p_tcb,退出 while 循环}}OS_CRITICAL_EXIT_NO_SCHED();                  //退出临界段(无调度)if ((opt & OS_OPT_POST_NO_SCHED) == (OS_OPT)0)    //如果 opt没选择“发布时不调度任务”{OSSched();                 (31)//任务调度}CPU_CRITICAL_ENTER();        //关中断flags_cur = p_grp->Flags;    //获取事件的标志值CPU_CRITICAL_EXIT();         //开中断*p_err     = OS_ERR_NONE;     //错误类型为“无错误”return (flags_cur);       (32)//返回事件的当前标志值}
  • (1):事件指针。
  • (2):选定要操作的标志位。
  • (3):设置事件标志位的选项。
  • (4):时间戳。
  • (5):返回错误类型。
  • (6):根据选项分类处理。
  • (7):如果要求将选定位置1,则置1即可,然后跳出switch语句。
  • (8):如果要求将选定位请0,将选定位清零即可,然后跳出switch语句。
  • (9):如果选项超出预期,返回错误类型为“选项非法”的错误代码,退出。
  • (10):将时间戳存入事件的TS成员变量中。
  • (11):获取事件的等待列表。
  • (12):如果当前没有任务在等待事件,置位后直接退出即可,并且返回事件的标志值。
  • (13):如果有任务在等待事件,那么进入临界段,重开中断。
  • (14):获取等待列表头个等待任务,然后获取到对应的任务控制块,保存在p_tcb变量中。
  • (15):当事件等待列表中有任务的时候,就从头至尾遍历等待列表的所有任务。
  • (16):获取任务感兴趣的事件标志选项,根据任务的标志选项分类处理。
  • (17):如果要求任务等待的标志位都得置1,就获取一下任务已经等待到的事件标志,保存在flags_rdy变量中。
  • (18):如果任务等待的标志位都置1了, 就调用OS_FlagTaskRdy()函数让该任务恢复为就绪态,准备运行,然后跳出switch语句。
  • (19):如果要求任务等待的标志位有任意一个位置1即可。
  • (20):那么就获取一下任务已经等待到的事件标志,保存在flags_rdy变量中。
  • (21):如果任务等待的标志位有置1的, 也就是满足了任务唤醒的条件,就调用OS_FlagTaskRdy()函数让该任务恢复为就绪态,准备运行,然后跳出switch语句。
  • (22):如果启用了标志位清零触发模式,在编译的时候就会包含事件标志位清零触发的代码。
  • (23):如果要求任务等待的标志位都得请0,那就看看等待任务对应的标志位是否清零了。
  • (24):如果任务等待的标志位都请0了, 就调用OS_FlagTaskRdy()函数让该任务恢复为就绪态,准备运行,然后跳出switch语句。
  • (25):如果要求任务等待的标志位有1位请0即可。
  • (26):那么如果任务等待的标志位有请0的,就让任务恢复为就绪态。
  • (27):如果标志选项超出预期,返回错误类型为“标志选项非法”的错误代码,并且推出。
  • (28):准备处理下一个等待任务。
  • (29):如果该任务存在,获取该任务的任务控制块。
  • (30):如果该任务不存在,清空 p_tcb,退出 while 循环。
  • (31):进行一次任务调度。
  • (32):事件标志位设置完成,返回事件的当前标志值。

OSFlagPost()的运用很简单,举个例子,比如我们要记录一个事件的发生,这个事件在事件组的位置是bit0,当它还未发生的时候,那么事件组bit0的值也是0, 当它发生的时候,我们往事件标志组的bit0位中写入这个事件,也就是0x01,那这就表示事件已经发生了。
当然,μC/OS也支持事件清零触发。
为了便于理解,一般操作我们都是用宏定义来实现#define EVENT (0x01 << x),“<< x”表示写入事件集合的bit x 。

在使用该函数之前必须先创建事件

应用实例如下:

#define KEY1_EVENT  (0x01 << 0)//设置事件掩码的位0
#define KEY2_EVENT  (0x01 << 1)//设置事件掩码的位1OS_FLAG_GRP flag_grp;                   //声明事件标志组static  void  AppTaskPost ( void * p_arg )
{OS_ERR      err;(void)p_arg;while (DEF_TRUE) {                            //任务体//如果KEY1被按下if ( Key_ReadStatus ( macKEY1_GPIO_PORT, macKEY1_GPIO_PIN, 1 ) == 1 ){macLED1_ON ();                                    //点亮LED1OSFlagPost ((OS_FLAG_GRP  *)&flag_grp,//将标志组的BIT0置1(OS_FLAGS      )KEY1_EVENT,(OS_OPT        )OS_OPT_POST_FLAG_SET,(OS_ERR       *)&err);}else//如果KEY1被释放{macLED1_OFF ();     //熄灭LED1OSFlagPost ((OS_FLAG_GRP  *)&flag_grp,//将标志组的BIT0清零(OS_FLAGS      )KEY1_EVENT,(OS_OPT        )OS_OPT_POST_FLAG_CLR,(OS_ERR       *)&err);}//如果KEY2被按下if ( Key_ReadStatus ( macKEY2_GPIO_PORT, macKEY2_GPIO_PIN, 1 ) == 1 ){macLED2_ON ();                              //点亮LED2OSFlagPost ((OS_FLAG_GRP  *)&flag_grp,//将标志组的BIT1置1(OS_FLAGS      )KEY2_EVENT,(OS_OPT        )OS_OPT_POST_FLAG_SET,(OS_ERR       *)&err);}else//如果KEY2被释放{macLED2_OFF ();    //熄灭LED2OSFlagPost ((OS_FLAG_GRP  *)&flag_grp,//将标志组的BIT1清零(OS_FLAGS      )KEY2_EVENT,(OS_OPT        )OS_OPT_POST_FLAG_CLR,(OS_ERR       *)&err);}//每20ms扫描一次OSTimeDlyHMSM ( 0, 0, 0, 20, OS_OPT_TIME_DLY, & err );}}

4、事件等待函数OSFlagPend()

既然标记了事件的发生,那么我们怎么知道他到底有没有发生,这也是需要一个函数来获取事件是否已经发生。
μC/OS提供了一个等待指定事件的函数——OSFlagPend(), 通过这个函数,任务可以知道事件标志组中的哪些位,有什么事件发生了,然后通过“逻辑与”、“逻辑或”等操作对感兴趣的事件进行获取,并且这个函数实现了等待超时机制, 当且仅当任务等待的事件发生时,任务才能获取到事件信息。
在这段时间中,如果事件一直没发生,该任务将保持阻塞状态以等待事件发生。
当其他任务或中断服务程序往其等待的事件设置对应的标志位,该任务将自动由阻塞态转为就绪态。
当任务等待的时间超过了指定的阻塞时间,即使事件还未发生, 任务也会自动从阻塞态转移为就绪态。
这样子很有效的体现了操作系统的实时性,如果事件正确获取(等待到)则返回对应的事件标志位,由用户判断再做处理, 因为在事件超时的时候也可能会返回一个不能确定的事件值,所以最好判断一下任务所等待的事件是否真的发生。

OSFlagPend()函数源码具体如下:

OS_FLAGS  OSFlagPend (OS_FLAG_GRP  *p_grp,  (1)     //事件指针OS_FLAGS      flags, (2)        //选定要操作的标志位OS_TICK       timeout,(3)       //等待期限(单位:时钟节拍)OS_OPT        opt,    (4)       //选项CPU_TS       *p_ts,   (5)//返回等到事件标志时的时间戳OS_ERR       *p_err)  (6)       //返回错误类型
{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)                 (7)//根据选项分类处理{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:ase OS_OPT_PEND_FLAG_SET_ANY | OS_OPT_PEND_FLAG_CONSUME | OS_OPT_PEND_NON_BLOCKING:break;                     //直接跳出default:                    (8)//如果选项超出预期*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(有错误),停止执行}
#endifif ((opt & OS_OPT_PEND_FLAG_CONSUME) != (OS_OPT)0)(9)//选择了标志位匹配后自动取反{consume = DEF_TRUE;}else(10)//未选择标志位匹配后自动取反{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; (11)//从选项中提取对标志位的要求CPU_CRITICAL_ENTER();                 //关中断switch (mode)                (12)//根据事件触发模式分类处理{case OS_OPT_PEND_FLAG_SET_ALL:   (13)//如果要求所有标志位均要置1flags_rdy = (OS_FLAGS)(p_grp->Flags & flags); //提取想要的标志位的值if (flags_rdy == flags)        (14)//如果该值与期望值匹配{if (consume == DEF_TRUE)(15)//如果要求将标志位匹配后取反{p_grp->Flags &= ~flags_rdy;           //清零事件的相关标志位}OSTCBCurPtr->FlagsRdy = flags_rdy; (16)//保存让任务脱离等待的标志值if (p_ts != (CPU_TS *)0)            //如果 p_ts 非空{*p_ts  = p_grp->TS;          //获取任务等到事件时的时间戳}CPU_CRITICAL_EXIT();            //开中断*p_err = OS_ERR_NONE;            //错误类型为“无错误”return (flags_rdy);     (17)//返回让任务脱离等待的标志值}else(18)//如果想要标志位的值与期望值不匹配{if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) //如果选择了不阻塞任务{CPU_CRITICAL_EXIT();                  //关中断*p_err = OS_ERR_PEND_WOULD_BLOCK;     //错误类型为“渴求阻塞”return ((OS_FLAGS)0);      (19)//返回0(有错误),停止执行}else(20)//如果选择了阻塞任务{if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0)   //如果调度器被锁{CPU_CRITICAL_EXIT();              //关中断*p_err = OS_ERR_SCHED_LOCKED;    //错误类型为“调度器被锁”return ((OS_FLAGS)0);   (21)//返回0(有错误),停止执行}}/* 如果调度器未被锁 */OS_CRITICAL_ENTER_CPU_EXIT();             //进入临界段,重开中断OS_FlagBlock(&pend_data,             //阻塞当前运行任务,等待事件p_grp,flags,opt,timeout);           (22)OS_CRITICAL_EXIT_NO_SCHED();              //退出临界段(无调度)}
break;                                        //跳出case OS_OPT_PEND_FLAG_SET_ANY:      (23)//如果要求有标志位被置1即可flags_rdy = (OS_FLAGS)(p_grp->Flags & flags); //提取想要的标志位的值if (flags_rdy != (OS_FLAGS)0)     (24)//如果有位被置1{if (consume == DEF_TRUE)           //如果要求将标志位匹配后取反{p_grp->Flags &= ~flags_rdy;    //清零事件的相关标志位}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);      (25)//返回让任务脱离等待的标志值}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);     (26)//返回0(有错误),停止执行}else//如果设置了阻塞任务{if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0)   //如果调度器被锁{CPU_CRITICAL_EXIT();              //关中断*p_err = OS_ERR_SCHED_LOCKED;  //错误类型为“调度器被锁”return ((OS_FLAGS)0);(27)//返回0(有错误),停止执行}}/* 如果调度器没被锁 */OS_CRITICAL_ENTER_CPU_EXIT();      //进入临界段,重开中断OS_FlagBlock(&pend_data,         //阻塞当前运行任务,等待事件p_grp,flags,opt,timeout);           (28)OS_CRITICAL_EXIT_NO_SCHED();       //退出中断(无调度)}break;                                        //跳出#if OS_CFG_FLAG_MODE_CLR_EN > 0u          (29)//如果启用了标志位清零触发模式case OS_OPT_PEND_FLAG_CLR_ALL:               //如果要求所有标志位均要清零flags_rdy = (OS_FLAGS)(~p_grp->Flags & flags);//提取想要的标志位的值if (flags_rdy == flags)            (30)//如果该值与期望值匹配{if(consume == DEF_TRUE)          //如果要求将标志位匹配后取反{p_grp->Flags |= flags_rdy;  (31)//置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);   (32)//返回0(有错误),停止执行}else//如果选择了阻塞任务{if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0)   //如果调度器被锁{CPU_CRITICAL_EXIT();           //关中断*p_err = OS_ERR_SCHED_LOCKED;  //错误类型为“调度器被锁”return ((OS_FLAGS)0); (33)//返回0(有错误),停止执行}}/* 如果调度器未被锁 */OS_CRITICAL_ENTER_CPU_EXIT();        //进入临界段,重开中断OS_FlagBlock(&pend_data,             //阻塞当前运行任务,等待事件p_grp,flags,opt,timeout);           (34)OS_CRITICAL_EXIT_NO_SCHED();        //退出临界段(无调度)}break;                                 //跳出case OS_OPT_PEND_FLAG_CLR_ANY:     (35)//如果要求有标志位被清零即可flags_rdy = (OS_FLAGS)(~p_grp->Flags & flags);//提取想要的标志位的值if (flags_rdy != (OS_FLAGS)0)                //如果有位被清零{if (consume == DEF_TRUE)           //如果要求将标志位匹配后取反{p_grp->Flags |= flags_rdy; (36)//置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);        (37)//返回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);      (38)//返回0(有错误),停止执行}else//如果设置了阻塞任务{if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0)   //如果调度器被锁{CPU_CRITICAL_EXIT();             //开中断*p_err = OS_ERR_SCHED_LOCKED;   //错误类型为“调度器被锁”return ((OS_FLAGS)0);   (39)//返回0(有错误),停止执行}}/* 如果调度器没被锁 */OS_CRITICAL_ENTER_CPU_EXIT();            //进入临界段,重开中断OS_FlagBlock(&pend_data,           //阻塞当前运行任务,等待事件p_grp,flags,opt,timeout);           (40)OS_CRITICAL_EXIT_NO_SCHED();             //退出中断(无调度)}break;                                       //跳出
#endifdefault:                                 (41)//如果要求超出预期CPU_CRITICAL_EXIT();*p_err = OS_ERR_OPT_INVALID;                  //错误类型为“选项非法”return ((OS_FLAGS)0);                   //返回0(有错误),停止执行}OSSched();                             (42)//任务调度/* 任务等到了事件后得以继续运行 */CPU_CRITICAL_ENTER();                                 //关中断switch (OSTCBCurPtr->PendStatus)       (43)//根据运行任务的等待状态分类处理{case OS_STATUS_PEND_OK:              (44)//如果等到了事件if (p_ts != (CPU_TS *)0)                     //如果 p_ts 非空{*p_ts  = OSTCBCurPtr->TS;             //返回等到事件时的时间戳}*p_err = OS_ERR_NONE;                         //错误类型为“无错误”break;                                       //跳出case OS_STATUS_PEND_ABORT:           (45)//如果等待被中止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:          (46)//如果等待超时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:           (47)//如果等待对象被删除if (p_ts != (CPU_TS *)0)                     //如果 p_ts 非空{*p_ts  = OSTCBCurPtr->TS;              //返回对象被删时的时间戳}CPU_CRITICAL_EXIT();                         //开中断*p_err = OS_ERR_OBJ_DEL;                      //错误类型为“对象被删”break;                                       //跳出default:                             (48)//如果等待状态超出预期CPU_CRITICAL_EXIT();                         //开中断*p_err = OS_ERR_STATUS_INVALID;               //错误类型为“状态非法”break;                                       //跳出}if (*p_err != OS_ERR_NONE)           (49)//如果有错误存在{return ((OS_FLAGS)0);               //返回0(有错误),停止执行}/* 如果没有错误存在 */flags_rdy = OSTCBCurPtr->FlagsRdy;    (50)//读取让任务脱离等待的标志值if (consume == DEF_TRUE)//如果需要取反触发事件的标志位{switch (mode)                    (51)//根据事件触发模式分类处理{case OS_OPT_PEND_FLAG_SET_ALL:       //如果是通过置1来标志事件的发生case OS_OPT_PEND_FLAG_SET_ANY:p_grp->Flags &= ~flags_rdy;  (52)//清零事件里触发事件的标志位break;                                   //跳出#if OS_CFG_FLAG_MODE_CLR_EN > 0u//如果启用了标志位清零触发模式case OS_OPT_PEND_FLAG_CLR_ALL:       //如果是通过清零来标志事件的发生case OS_OPT_PEND_FLAG_CLR_ANY:p_grp->Flags |=  flags_rdy;  (53)//置1事件里触发事件的标志位break;                                   //跳出
#endifdefault:                                      //如果触发模式超出预期CPU_CRITICAL_EXIT();                     //开中断*p_err = OS_ERR_OPT_INVALID;              //错误类型为“选项非法”return ((OS_FLAGS)0);         (54)//返回0(有错误),停止执行}}CPU_CRITICAL_EXIT();                      //开中断*p_err = OS_ERR_NONE;                     //错误类型为“无错误”
return (flags_rdy);                  (55)//返回让任务脱离等待的标志值
}
  • (1):事件指针。
  • (2):选定要等待的标志位。
  • (3):等待不到事件时指定阻塞时间(单位:时钟节拍)。
  • (4):等待的选项。
  • (5):保存返回等到事件标志时的时间戳。
  • (6):保存返回错误类型。
  • (7):此处是判断一下等待的选项是否在预期内,如果在预期内则继续操作,跳出switch语句。
  • (8):如果选项超出预期,返回错误类型为“选项非法”的错误代码,并且退出,不继续执行等待事件操作。
  • (9):如果用户选择了标志位匹配后自动取反,变量consume就为DEF_TRUE。
  • (10):如果未选择标志位匹配后自动取反,变量consume则为DEF_FALSE。
  • (11):从选项中提取对标志位的要求,利用“&”运算操作符获取选项并且保存在mode变量中。
  • (12):根据事件触发模式分类处理。
  • (13):如果任务要求所有标志位均要置1,那么就提取想要的标志位的值保存在flags_rdy变量中。
  • (14):如果该值与任务的期望值匹配。
  • (15):如果要求将标志位匹配后取反,就将事件的相关标志位清零。
  • (16):保存让任务脱离等待的标志值,此时已经等待到任务要求的事件了,就可以退出了。
  • (17):返回错误类型为“无错误”的错误代码与让任务脱离等待的标志值。
  • (18):如果想要标志位的值与期望值不匹配。
  • (19):并且如果用户选择了不阻塞任务,那么返回错误类型为“渴求阻塞”的错误代码,退出。
  • (20):而如果用户选择了阻塞任务。
  • (21):那就判断一下调度器是否被锁,如果被锁了,返回错误类型为“调度器被锁”的错误代码,并且退出。
  • (22):如果调度器没有被锁,则调用OS_FlagBlock()函数阻塞当前任务,在阻塞中继续等待任务需要的事件。
  • (23):如果要求有标志位被置1即可,那就提取想要的标志位的值保存在flags_rdy变量中。
  • (24):如果有任何一位被置1,就表示等待到了事件。如果要求将标志位匹配后取反,将事件的相关标志位清零。
  • (25):等待成功,就返回让任务脱离等待的标志值。
  • (26):如果没有位被置1,并且用户没有设置阻塞时间,那么就返回错误类型为“渴求阻塞”的错误代码,然后退出。
  • (27):如果设置了阻塞任务,但是调度器被锁了,返回错误类型为“调度器被锁”的错误代码,并且退出。
  • (28):如果调度器没被锁, 则调用OS_FlagBlock()函数阻塞当前任务,在阻塞中继续等待任务需要的事件。
  • (29):如果启用了标志位清零触发模式(宏定义OS_CFG_FLAG_MODE_CLR_EN被配置为1), 则在编译的时候会包含事件清零触发相关代码。
  • (30):如果要求所有标志位均要清零, 首先提取想要的标志位的值保存在flags_rdy变量中,如果该值与任务的期望值匹配,那么就表示等待的事件。
  • (31):如果要求将标志位匹配后取反, 就置1事件的相关标志位,因为现在是清零触发的,事件标志位取反就是将对应标志位置一。
  • (32):如果想要标志位的值与期望值不匹配, 并且如果用户选择了不阻塞任务,那么返回错误类型为“渴求阻塞”的错误代码,退出。
  • (33):如果调度器被锁,返回错误类型为“调度器被锁”的错误代码,并且退出。
  • (34):如果调度器没有被锁,则调用OS_FlagBlock()函数阻塞当前任务,在阻塞中继续等待任务需要的事件。
  • (35):如果要求有标志位被清零即可,提取想要的标志位的值,如果有位被清零则表示等待到事件。
  • (36):如果要求将标志位匹配后取反,将事件的相关标志位置1。
  • (37):等待到事件就返回对应的事件标志位。
  • (38):如果没有位被清零,并且如果用户没设置阻塞任务,那么就返回错误类型为“渴求阻塞”的错误代码,然后退出。
  • (39):如果设置了阻塞任务,但是调度器被锁了,返回错误类型为“调度器被锁”的错误代码,并且退出。
  • (40):如果调度器没有被锁,则调用OS_FlagBlock()函数阻塞当前任务,在阻塞中继续等待任务需要的事件。
  • (41):如果要求超出预期,返回错误类型为“选项非法”的错误代码,退出。
  • (42):执行到这里,说明任务没有等待到事件,并且用户还选择了阻塞任务,那么就进行一次任务调度。
  • (43):当程序能执行到这里,就说明大体上有两种情况, 要么是任务获取到对应的事件了;要么任务还没获取到事件(任务没获取到事件的情况有很多种),无论是哪种情况,都先把中断关掉再说,再根据当前运行任务的等待状态分类处理。
  • (44):如果等到了事件,返回等到事件时的时间戳,然后退出。
  • (45):如果任务在等待事件中被中止,返回等待被中止时的时间戳,记录错误类型为“等待被中止”的错误代码,然后退出。
  • (46):如果等待超时,返回错误类型为“等待超时”的错误代码,退出。
  • (47):如果等待对象被删除,返回对象被删时的时间戳,记录错误类型为“对象被删”的错误代码,退出。
  • (48):如果等待状态超出预期,记录错误类型为“状态非法”的错误代码,退出。
  • (49):如果有错误存在,返回0,表示没有等待到事件。
  • (50):如果没有错误存在,如果需要取反触发事件的标志位。
  • (51):根据事件触发模式分类处理。
  • (52):如果是通过置1来标志事件的发生,将事件里触发事件的标志位清零。
  • (53):如果是通过清零来标志事件的发生,那么就将事件里触发事件的标志位置1。
  • (54):如果触发模式超出预期,返回错误类型为“选项非法”的错误代码。
  • (55):返回让任务脱离等待的标志值。

在这个函数中,根据不同的等待模式,任务会表现出不同的等待行为:

  1. 等待所有指定标志位被置位

    • 当选项为 OS_OPT_PEND_FLAG_SET_ALL 时,任务会等待所有指定的事件标志被置位。
    • 如果所有指定的事件标志被置位,则任务解除等待状态,返回所等待的事件标志,并根据需要进行后续处理,如消费事件标志或保留事件标志。
    • 如果指定的事件标志未被置位:
      • 如果选项包含 OS_OPT_PEND_NON_BLOCKING,则立即返回错误表示任务不会被阻塞。
      • 否则,任务会进入阻塞状态,直到事件标志被置位、超时或者其他任务取消了该等待。
  2. 等待任一指定标志位被置位

    • 当选项为 OS_OPT_PEND_FLAG_SET_ANY 时,任务会等待任一指定的事件标志被置位。
    • 如果任一指定的事件标志被置位,则任务解除等待状态,返回所等待的事件标志,并根据需要进行后续处理,如消费事件标志或保留事件标志。
    • 如果指定的事件标志都未被置位:
      • 如果选项包含 OS_OPT_PEND_NON_BLOCKING,则立即返回错误表示任务不会被阻塞。
      • 否则,任务会进入阻塞状态,直到任一指定的事件标志被置位、超时或者其他任务取消了该等待。
  3. 等待所有指定标志位被清零(如果系统配置支持):

    • 当选项为 OS_OPT_PEND_FLAG_CLR_ALL 时,任务会等待所有指定的事件标志被清零。
    • 如果所有指定的事件标志被清零,则任务解除等待状态,返回所等待的事件标志,并根据需要进行后续处理,如消费事件标志或保留事件标志。
    • 如果指定的事件标志未被清零:
      • 如果选项包含 OS_OPT_PEND_NON_BLOCKING,则立即返回错误表示任务不会被阻塞。
      • 否则,任务会进入阻塞状态,直到事件标志被清零、超时或者其他任务取消了该等待。
  4. 等待任一指定标志位被清零(如果系统配置支持):

    • 当选项为 OS_OPT_PEND_FLAG_CLR_ANY 时,任务会等待任一指定的事件标志被清零。
    • 如果任一指定的事件标志被清零,则任务解除等待状态,返回所等待的事件标志,并根据需要进行后续处理,如消费事件标志或保留事件标志。
    • 如果指定的事件标志都未被清零:
      • 如果选项包含 OS_OPT_PEND_NON_BLOCKING,则立即返回错误表示任务不会被阻塞。
      • 否则,任务会进入阻塞状态,直到任一指定的事件标志被清零、超时或者其他任务取消了该等待。

至此,任务等待事件函数就已经讲解完毕,其实μC/OS这种利用状态机的方法等待事件,根据不一样的情况进行处理,是很好的,省去很多逻辑的代码。

下面简单分析处理过程:
当用户调用这个函数接口时,系统首先根据用户指定参数和接收选项来判断它要等待的事件是否发生。
如果已经发生,则根据等待选项来决定是否清除事件的相应标志位,并且返回事件标志位的值,但是这个值可能不是一个稳定的值, 所以在等待到对应事件的时候,我们最好要判断事件是否与任务需要的一致;
如果事件没有发生,则把任务添加到事件等待列表中, 将当前任务阻塞,直到事件发生或等待时间超时。

事件等待函数OSFlagPend()使用实例具体如下:

#define KEY1_EVENT  (0x01 << 0)//设置事件掩码的位0
#define KEY2_EVENT  (0x01 << 1)//设置事件掩码的位1OS_FLAG_GRP flag_grp;                   //声明事件标志组static  void  AppTaskPend ( void * p_arg )
{OS_ERR      err;(void)p_arg;//任务体while (DEF_TRUE){//等待标志组的的BIT0和BIT1均被置1OSFlagPend ((OS_FLAG_GRP *)&flag_grp,(OS_FLAGS     )( KEY1_EVENT | KEY2_EVENT ),(OS_TICK      )0,(OS_OPT       )OS_OPT_PEND_FLAG_SET_ALL |OS_OPT_PEND_BLOCKING,(CPU_TS      *)0,(OS_ERR      *)&err);LED3_ON ();        //点亮LED3//等待标志组的的BIT0和BIT1有一个被清零OSFlagPend ((OS_FLAG_GRP *)&flag_grp,(OS_FLAGS     )( KEY1_EVENT | KEY2_EVENT ),(OS_TICK      )0,(OS_OPT       )OS_OPT_PEND_FLAG_CLR_ANY |OS_OPT_PEND_BLOCKING,(CPU_TS      *)0,(OS_ERR      *)&err);LED3_OFF ();          //熄灭LED3}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/802268.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

七燕论文可靠吗 #经验分享#经验分享

七燕论文是一个非常好用的论文写作工具&#xff0c;它不仅可以帮助学生提高写作效率&#xff0c;还能帮助他们避免抄袭和提高论文质量。七燕论文的查重降重功能非常靠谱&#xff0c;能够帮助用户检测论文中的重复内容&#xff0c;并提供相应的修改建议&#xff0c;确保论文的原…

设计模式 -- 发布订阅模式

发布订阅模式&#xff1a; 订阅者把自己想订阅的事件注册到调度中心&#xff0c;当发布者发布该事件到调度中心&#xff0c;也就是该事件触发时&#xff0c;由调度者统一调度订阅者注册到调度中心的处理代码。 在javaScript 中我们一般使用事件模型来代替传统的发布订阅模式。 …

CSS设置网页颜色

目录 前言&#xff1a; 1.颜色名字&#xff1a; 2.十六进制码&#xff1a; 3.RGB&#xff1a; 4.RGBA&#xff1a; 5.HSL&#xff1a; 1.hue&#xff1a; 2.saturation&#xff1a; 3.lightness&#xff1a; 6.HSLA&#xff1a; 前言&#xff1a; 我们在电脑显示器&…

Windows 关闭占用指定端口的进程

以下示例以443端口为例&#xff0c;具体哪个端口视自己情况而定 输入命令 # 输出的最后一列就是进程号pid netstat -ano | findstr "443" 找出占用443端口的进程号(pid)&#xff08;第二列是你本机的应用占用的端口&#xff0c;看第二列就行&#xff09;如下图&am…

4 万字 102 道Java经典面试题总结(2024修订版)- 多线程篇

&#x1f345; 作者简介&#xff1a;哪吒&#xff0c;CSDN2021博客之星亚军&#x1f3c6;、新星计划导师✌、博客专家&#x1f4aa; &#x1f345; 哪吒多年工作总结&#xff1a;Java学习路线总结&#xff0c;搬砖工逆袭Java架构师 &#x1f345; 技术交流&#xff1a;定期更新…

关于GNSS硬件延迟初步学习,电离层提取

1、卫星端偏差分为频间和频内偏差&#xff08;inter or intra frequency&#xff09;&#xff0c;下面以GPS的C1C和C2W组合为例分析对PPP解算的影响&#xff1a; 如果不改正卫星端的inter-frequency&#xff08;即&#xff1a;C1C-C1W&#xff09;偏差&#xff08;因为每颗卫星…

AI实时换天解决方案:重塑汽车与旅行拍摄新视界

在汽车拍摄与旅行摄影领域&#xff0c;天空作为画面中的重要元素&#xff0c;往往决定着整体视觉效果的成败。美摄科技作为业界领先的AI视觉技术提供商&#xff0c;近日推出了全新的AI实时换天解决方案&#xff0c;为用户带来了前所未有的创意空间与效率提升。 传统的换天技术…

字母大小写转换(C语言)

一、运行结果&#xff1b; 二、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>int main() {//初始化变量值&#xff1b;char c1 A;char c2 0;//实现大小写转换&#xff1b;c2 c1 32;//输出结果&#xff1b;printf("c2的编码是&…

20240408,C++数组,函数,指针

是谁说回老家学习结果摆烂了两天&#xff0c;是我&#xff0c;Π—Π&#xff01; Π—Π&#xff01;&#xff01; 一&#xff0c;数组——同C 1.1一维数组 1.0 相同类型&#xff0c;连续内存&#xff0c; 1.1 定义格式&#xff1a;数据类型 数组名【长度】&#xff1…

uni-app(H5)论坛 | 社区 表情选择 UI组件

项目源码请移步&#xff1a;bbs 效果 实现思路 表情切换 人物、动物、小黄人不同表情之间的切换实际就是组件的切换 emoji表情 emoji表情本身就是一种字符 如需其他emoji表情可参考 EmojiAll中文官方网站 需要注意的就是数据库的存储格式需要支持emoji表情&#xff0c;我项…

第8章 数据集成和互操作

思维导图 8.1 引言 数据集成和互操作(DII)描述了数据在不同数据存储、应用程序和组织这三者内部和之间进行移动和整合的相关过程。数据集成是将数据整合成物理的或虚拟的一致格式。数据互操作是多个系统之间进行通信的能力。数据集成和互操作的解决方案提供了大多数组织所依赖的…

配置交换机 SSH 管理和端口安全——实验1:配置交换机基本安全和 SSH管理

实验目的 通过本实验可以掌握&#xff1a; 交换机基本安全配置。SSH 的工作原理和 SSH服务端和客户端的配置。 实验拓扑 交换机基本安全和 SSH管理实验拓扑如图所示。 交换机基本安全和 SSH管理实验拓扑 实验步骤 &#xff08;1&#xff09;配置交换机S1 Switch>enab…

【r-tree算法】一篇文章讲透~

目录 一、引言 二、R-tree算法的基本原理 1 数据结构 2 插入操作 3 删除操作 4 查询操作 5 代码事例 三、R-tree算法的性能分析 1 时间复杂度 2 空间复杂度 3 影响因素 四、R-tree算法的变体和改进 1 R*-tree算法 2 X-tree算法 3 QR-tree算法 五、R-tree算法的…

axure谷歌插件(直接下载)

axure谷歌插件 在网上找一个谷歌的axure&#xff0c;不是登陆就是收费&#xff0c;离谱。找了好久才找到这个&#xff0c;我下载保存到网盘了&#xff0c;直接下载就ok&#xff0c;永久无提取码。 下载插件文件&#xff0c;打开开发者模式&#xff0c;直接拖进来就ok。 网盘…

esxi上给centos7虚拟机扩容硬盘

原来centos7硬盘分配的空间只有40GB 需要直接扩容到200GB 扩容前 扩容后 扩容步骤&#xff1a; 1 .在esxi平台上关机虚拟机&#xff0c;将硬盘调整为200GB&#xff0c;然后开机 2.请出chatGPT 1. 创建新分区 使用剩余的磁盘空间创建一个新的分区。你可以使用fdisk&#xf…

Flink Keyed State的优化与实践

本期作者 1.背景 Flink SQL在业务使用中有较多的双流join场景&#xff0c;当左右流的流量都较大&#xff0c;Join的等待时间即使为1小时&#xff0c;Flink Keyed State&#xff08;Flink State分Operator State和Keyed State&#xff0c;后文所有State均代表后者&#xff09;的…

数据库(mysql)-基本查询语句(DQL)

查询语句 这边查询是给予一定表格,这边先做个解释 教师表包括(name(姓名),gender(性别),salary(工资),title(职位),subject_id(课程的编号),comm(奖金)) 学生表包括(姓名(name),gender(性别),job(职位),生日(birth)) 模版 SELECT 字段名 FROM 查询表 WHERE 查询语句 或与非…

k8s_入门_命令详解

命令详解 kubectl是官方的CLI命令行工具&#xff0c;用于与 apiserver进行通信&#xff0c;将用户在命令行输入的命令&#xff0c;组织并转化为 apiserver能识别的信息&#xff0c;进而实现管理k8s各种资源的一种有效途径 1. 帮助 2. 查看版本信息 3. 查看资源对象等 查看No…

Linux应用开发(3):Linux时间操作(time、mktime、localtime等)

1. 简述 在Linux系统中&#xff0c;时间操作函数是编程中经常使用的一部分&#xff0c;它们允许程序获取和设置系统时间&#xff0c;以及对时间进行各种处理。以下是一些常用的时间操作函数的详细介绍。 2. 时间操作 &#xff08;1&#xff09;time(): 获取1970年1月1日以来的…

爬虫入门教程(一)

爬虫入门教程 1.什么是爬虫 爬虫是一种自动获取网站数据的程序或脚本。它可以自动模拟人类访问网站,获取网页源代码,解析并提取出所需的数据。 爬虫的工作原理类似于搜索引擎的索引程序&#xff0c;它们会按照预定的规则和算法在互联网上不断地爬取网页&#xff0c;收集信息…