写在前面:
由于时间的不足与学习的碎片化,写博客变得有些奢侈。
但是对于记录学习(忘了以后能快速复习)的渴望一天天变得强烈。
既然如此
不如以天为单位,以时间为顺序,仅仅将博客当做一个知识学习的目录,记录笔者认为最通俗、最有帮助的资料,并尽量总结几句话指明本质,以便于日后搜索起来更加容易。
标题的结构如下:“类型”:“知识点”——“简短的解释”
部分内容由于保密协议无法上传。
点击此处进入学习日记的总目录
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):返回让任务脱离等待的标志值。
在这个函数中,根据不同的等待模式,任务会表现出不同的等待行为:
-
等待所有指定标志位被置位:
- 当选项为
OS_OPT_PEND_FLAG_SET_ALL
时,任务会等待所有指定的事件标志被置位。 - 如果所有指定的事件标志被置位,则任务解除等待状态,返回所等待的事件标志,并根据需要进行后续处理,如消费事件标志或保留事件标志。
- 如果指定的事件标志未被置位:
- 如果选项包含
OS_OPT_PEND_NON_BLOCKING
,则立即返回错误表示任务不会被阻塞。 - 否则,任务会进入阻塞状态,直到事件标志被置位、超时或者其他任务取消了该等待。
- 如果选项包含
- 当选项为
-
等待任一指定标志位被置位:
- 当选项为
OS_OPT_PEND_FLAG_SET_ANY
时,任务会等待任一指定的事件标志被置位。 - 如果任一指定的事件标志被置位,则任务解除等待状态,返回所等待的事件标志,并根据需要进行后续处理,如消费事件标志或保留事件标志。
- 如果指定的事件标志都未被置位:
- 如果选项包含
OS_OPT_PEND_NON_BLOCKING
,则立即返回错误表示任务不会被阻塞。 - 否则,任务会进入阻塞状态,直到任一指定的事件标志被置位、超时或者其他任务取消了该等待。
- 如果选项包含
- 当选项为
-
等待所有指定标志位被清零(如果系统配置支持):
- 当选项为
OS_OPT_PEND_FLAG_CLR_ALL
时,任务会等待所有指定的事件标志被清零。 - 如果所有指定的事件标志被清零,则任务解除等待状态,返回所等待的事件标志,并根据需要进行后续处理,如消费事件标志或保留事件标志。
- 如果指定的事件标志未被清零:
- 如果选项包含
OS_OPT_PEND_NON_BLOCKING
,则立即返回错误表示任务不会被阻塞。 - 否则,任务会进入阻塞状态,直到事件标志被清零、超时或者其他任务取消了该等待。
- 如果选项包含
- 当选项为
-
等待任一指定标志位被清零(如果系统配置支持):
- 当选项为
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}}