消息队列
Posted tianxxl
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了消息队列相关的知识,希望对你有一定的参考价值。
多值信号量和和互斥信号量主要用来标志事件是否发生和协调资源的访问。如果要给资源赋予内容进行传递,信号量就力有所不及了。这时候就需要用到 uC/OS 操作系统的另一个内核机制了,那就是消息队列。
如果想要使用消息队列,就必须事先使能消息队列。消息队列的使能位于“os_cfg.h”。
/* --------------------------- MESSAGE QUEUES -------------------------- */ #define OS_CFG_Q_EN 1u //使能/禁用消息队列 #define OS_CFG_Q_DEL_EN 1u //使能或禁用 OSQDel() 函数 #define OS_CFG_Q_FLUSH_EN 1u //使能或禁用 OSQFlush() 函数 #define OS_CFG_Q_PEND_ABORT_EN 1u //使能或禁用 OSQPendAbort() 函数
消息队列的消息都是从消息池获取的。消息队列可以有一个或多个,但消息池却只有一个。当有消息队列要发布消息时,就从消息池(OS_MSG 类型数组的元素依次组成的单向列表)获取一个元素用于存放消息,并将该消息插入到消息队列。当有消息队列释放(等到)消息时,又会把这个元素释放回消息池,该元素又可以继续供发布消息使用。
如果用户使能了消息队列,在调用 OSInit() 函数对系统进行初始化时,就会初始化消息池。
#if (OS_MSG_EN) > 0u /* Initialize the free list of OS_MSGs */ OS_MsgPoolInit(p_err); if (*p_err != OS_ERR_NONE) { return; } #endif
其中,OS_MsgPoolInit() 函数就是用来初始化消息池的。OS_MsgPoolInit() 函数的定义位于“os_msg.c”。
void OS_MsgPoolInit (OS_ERR *p_err) //返回错误类型 { OS_MSG *p_msg1; OS_MSG *p_msg2; OS_MSG_QTY i; OS_MSG_QTY loops; #ifdef OS_SAFETY_CRITICAL //如果使能(默认禁用)了安全检测 if (p_err == (OS_ERR *)0) { //如果错误类型实参为空 OS_SAFETY_CRITICAL_EXCEPTION(); //执行安全检测异常函数 return; //返回,停止执行 } #endif #if OS_CFG_ARG_CHK_EN > 0u //如果使能了参数检测 if (OSCfg_MsgPoolBasePtr == (OS_MSG *)0) {//如果消息池不存在 *p_err = OS_ERR_MSG_POOL_NULL_PTR; //错误类型为“消息池指针为空” return; //返回,停止执行 } if (OSCfg_MsgPoolSize == (OS_MSG_QTY)0) { //如果消息池不能存放消息 *p_err = OS_ERR_MSG_POOL_EMPTY; //错误类型为“消息池为空” return; //返回,停止执行 } #endif /* 将消息池里的消息逐条串成单向链表,方便管理 */ p_msg1 = OSCfg_MsgPoolBasePtr; p_msg2 = OSCfg_MsgPoolBasePtr; p_msg2++; loops = OSCfg_MsgPoolSize - 1u; for (i = 0u; i < loops; i++) { //初始化每一条消息 p_msg1->NextPtr = p_msg2; p_msg1->MsgPtr = (void *)0; p_msg1->MsgSize = (OS_MSG_SIZE)0u; p_msg1->MsgTS = (CPU_TS )0u; p_msg1++; p_msg2++; } p_msg1->NextPtr = (OS_MSG *)0; //最后一条消息 p_msg1->MsgPtr = (void *)0; p_msg1->MsgSize = (OS_MSG_SIZE)0u; p_msg1->MsgTS = (CPU_TS )0u; /* 初始化消息池数据 */ OSMsgPool.NextPtr = OSCfg_MsgPoolBasePtr; OSMsgPool.NbrFree = OSCfg_MsgPoolSize; OSMsgPool.NbrUsed = (OS_MSG_QTY)0; OSMsgPool.NbrUsedMax = (OS_MSG_QTY)0; *p_err = OS_ERR_NONE; //错误类型为“无错误” }
OSQCreate ()
要使用 uC/OS 的消息队列必须先声明和创建消息队列,调用 OSQCreate () 函数可以创建一个消息队列。OSQCreate () 函数的信息如下表所示。
OSQCreate () 函数的定义位于“os_q.c”:
void OSQCreate (OS_Q *p_q, //消息队列指针 CPU_CHAR *p_name, //消息队列名称 OS_MSG_QTY max_qty, //消息队列大小(不能为0) 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_q == (OS_Q *)0) { //如果 p_q 为空 *p_err = OS_ERR_OBJ_PTR_NULL; //错误类型为“创建对象为空” return; //返回,停止执行 } if (max_qty == (OS_MSG_QTY)0) { //如果 max_qty = 0 *p_err = OS_ERR_Q_SIZE; //错误类型为“队列空间为0” return; //返回,停止执行 } #endif OS_CRITICAL_ENTER(); //进入临界段 p_q->Type = OS_OBJ_TYPE_Q; //标记创建对象数据结构为消息队列 p_q->NamePtr = p_name; //标记消息队列的名称 OS_MsgQInit(&p_q->MsgQ, //初始化消息队列 max_qty); OS_PendListInit(&p_q->PendList); //初始化该消息队列的等待列表 #if OS_CFG_DBG_EN > 0u //如果使能了调试代码和变量 OS_QDbgListAdd(p_q); //将该队列添加到消息队列双向调试链表 #endif OSQQty++; //消息队列个数加1 OS_CRITICAL_EXIT_NO_SCHED(); //退出临界段(无调度) *p_err = OS_ERR_NONE; //错误类型为“无错误” }
其中,OSQCreate () 函数调用了 OS_MsgQInit () 函数初始化了消息队列。
OS_MsgQInit()函数的定义位于“os_msg.c”:
void OS_MsgQInit (OS_MSG_Q *p_msg_q, //消息队列指针 OS_MSG_QTY size) //消息队列空间 { p_msg_q->NbrEntriesSize = (OS_MSG_QTY)size; //消息队列可存放消息数目 p_msg_q->NbrEntries = (OS_MSG_QTY)0; //消息队列目前可用消息数 p_msg_q->NbrEntriesMax = (OS_MSG_QTY)0; //可用消息数的最大历史记录 p_msg_q->InPtr = (OS_MSG *)0; //队列的入队指针 p_msg_q->OutPtr = (OS_MSG *)0; //队列的出队指针 }
另外,OSQCreate () 函数还调用了 OS_PendListInit() 函数初始化了消息队列的等待列表。每个消息队列都有一个等待列表,凡是等待该消息队列的的任务都会被插入到这个等待列表,方便高效管理。
OS_PendListInit() 函数的定义位于“os_core.c”:
void OS_PendListInit (OS_PEND_LIST *p_pend_list) { p_pend_list->HeadPtr = (OS_PEND_DATA *)0; //复位等待列表的所有成员 p_pend_list->TailPtr = (OS_PEND_DATA *)0; p_pend_list->NbrEntries = (OS_OBJ_QTY )0; }
如 果 使 能 了 OS_CFG_DBG_EN ( 位 于 “ os_cfg.h ”), 创 建 消 息 队 列 时 还 会 调 用OS_QDbgListAdd() 函数将该消息队列插入到一个消息队列调试列表,是为方便调试所设。
OS_QDbgListAdd () 函数的定义位于“os_q.c”:
#if OS_CFG_DBG_EN > 0u //如果使能(默认使能)了调试代码和变量 void OS_QDbgListAdd (OS_Q *p_q) //将消息队列插入到消息队列列表的最前端 { p_q->DbgNamePtr = (CPU_CHAR *)((void *)" "); //先不指向任何任务的名称 p_q->DbgPrevPtr = (OS_Q *)0; //将该队列作为列表的最前端 if (OSQDbgListPtr == (OS_Q *)0) { //如果列表为空 p_q->DbgNextPtr = (OS_Q *)0; //该队列的下一个元素也为空 } else { //如果列表非空 p_q->DbgNextPtr = OSQDbgListPtr; //列表原来的首元素作为该队列的下一个元素 OSQDbgListPtr->DbgPrevPtr = p_q; //原来的首元素的前面变为了该队列 } OSQDbgListPtr = p_q; //该信号量成为列表的新首元素 }
OSQPost ()
OSQPost () 函数用于向消息队列发布一个消息。OSQPost () 函数的信息如下表所示
OSQPost () 函数的定义也位于“os_q.c
void OSQPost (OS_Q *p_q, //消息队列指针 void *p_void, //消息指针 OS_MSG_SIZE msg_size, //消息大小(单位:字节) OS_OPT opt, //选项 OS_ERR *p_err) //返回错误类型 { CPU_TS ts; #ifdef OS_SAFETY_CRITICAL //如果使能(默认禁用)了安全检测 if (p_err == (OS_ERR *)0) { //如果错误类型实参为空 OS_SAFETY_CRITICAL_EXCEPTION(); //执行安全检测异常函数 return; //返回,停止执行 } #endif #if OS_CFG_ARG_CHK_EN > 0u //如果使能了参数检测 if (p_q == (OS_Q *)0) { //如果 p_q 为空 *p_err = OS_ERR_OBJ_PTR_NULL; //错误类型为“内核对象为空” return; //返回,停止执行 } switch (opt) { //根据选项分类处理 case OS_OPT_POST_FIFO: //如果选项在预期内 case OS_OPT_POST_LIFO: case OS_OPT_POST_FIFO | OS_OPT_POST_ALL: case OS_OPT_POST_LIFO | OS_OPT_POST_ALL: case OS_OPT_POST_FIFO | OS_OPT_POST_NO_SCHED: case OS_OPT_POST_LIFO | OS_OPT_POST_NO_SCHED: case OS_OPT_POST_FIFO | OS_OPT_POST_ALL | OS_OPT_POST_NO_SCHED: case OS_OPT_POST_LIFO | OS_OPT_POST_ALL | OS_OPT_POST_NO_SCHED: break; //直接跳出 default: //如果选项超出预期 *p_err = OS_ERR_OPT_INVALID; //错误类型为“选项非法” return; //返回,停止执行 } #endif #if OS_CFG_OBJ_TYPE_CHK_EN > 0u //如果使能了对象类型检测 if (p_q->Type != OS_OBJ_TYPE_Q) { //如果 p_q 不是消息队列类型 *p_err = OS_ERR_OBJ_TYPE; //错误类型为“对象类型错误” return; //返回,停止执行 } #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_Q, //将该消息发布到中断消息队列 (void *)p_q, (void *)p_void, (OS_MSG_SIZE)msg_size, (OS_FLAGS )0, (OS_OPT )opt, (CPU_TS )ts, (OS_ERR *)p_err); return; //返回(尚未发布),停止执行 } #endif OS_QPost(p_q, //将消息按照普通方式 p_void, msg_size, opt, ts, p_err); }
其实,不管是否使能了中断延迟发布,最终都是调用 OS_QPost () 函数进行发布消息。只是使能了中断延迟发布的发布过程会比较曲折,中间会有许多插曲,这是中断管理范畴的内容,留到后面再作介绍。
OS_QPost () 函数的定义位于“os_q.c”。
void OS_QPost (OS_Q *p_q, //消息队列指针 void *p_void, //消息指针 OS_MSG_SIZE msg_size, //消息大小(单位:字节) OS_OPT opt, //选项 CPU_TS ts, //消息被发布时的时间戳 OS_ERR *p_err) //返回错误类型 { OS_OBJ_QTY cnt; OS_OPT post_type; OS_PEND_LIST *p_pend_list; OS_PEND_DATA *p_pend_data; OS_PEND_DATA *p_pend_data_next; OS_TCB *p_tcb; CPU_SR_ALLOC(); //使用到临界段(在关/开中断时)时必需该宏,该宏声明和 //定义一个局部变量,用于保存关中断前的 CPU 状态寄存器 // SR(临界段关中断只需保存SR),开中断时将该值还原。 OS_CRITICAL_ENTER(); //进入临界段 p_pend_list = &p_q->PendList; //取出该队列的等待列表 if (p_pend_list->NbrEntries == (OS_OBJ_QTY)0) { //如果没有任务在等待该队列 if ((opt & OS_OPT_POST_LIFO) == (OS_OPT)0) { //把消息发布到队列的末端 post_type = OS_OPT_POST_FIFO; } else { //把消息发布到队列的前端 post_type = OS_OPT_POST_LIFO; } OS_MsgQPut(&p_q->MsgQ, //把消息放入消息队列 /* Place message in the message queue */ p_void, msg_size, post_type, ts, p_err); OS_CRITICAL_EXIT(); //退出临界段 return; //返回,执行完毕 } /* 如果有任务在等待该队列 */ if ((opt & OS_OPT_POST_ALL) != (OS_OPT)0) { //如果要把消息发布给所有等待任务 cnt = p_pend_list->NbrEntries; //获取等待任务数目 } else { //如果要把消息发布给一个等待任务 cnt = (OS_OBJ_QTY)1; //要处理的任务数目为1 } p_pend_data = p_pend_list->HeadPtr; //获取等待列表的头部(任务) while (cnt > 0u) { //根据要发布的任务数目逐个发布 p_tcb = p_pend_data->TCBPtr; p_pend_data_next = p_pend_data->NextPtr; OS_Post((OS_PEND_OBJ *)((void *)p_q), //把消息发布给任务 p_tcb, p_void, msg_size, ts); p_pend_data = p_pend_data_next; cnt--; } OS_CRITICAL_EXIT_NO_SCHED(); //退出临界段(无调度) if ((opt & OS_OPT_POST_NO_SCHED) == (OS_OPT)0) {//如果没选择“发布完不调度任务” OSSched(); //任务调度 } *p_err = OS_ERR_NONE; //错误类型为“无错误” }
在 OS_QPost () 函数中,会调用 OS_MsgQPut () 函数从消息池获取一个消息插入到消息队列。
OS_MsgQPut () 函数的定义位于“os_msg.c”。
void OS_MsgQPut (OS_MSG_Q *p_msg_q, //消息队列指针 void *p_void, //消息指针 OS_MSG_SIZE msg_size, //消息大小(单位:字节) OS_OPT opt, //选项 CPU_TS ts, //消息被发布时的时间戳 OS_ERR *p_err) //返回错误类型 { OS_MSG *p_msg; OS_MSG *p_msg_in; #ifdef OS_SAFETY_CRITICAL //如果使能了安全检测 if (p_err == (OS_ERR *)0) { //如果错误类型实参为空 OS_SAFETY_CRITICAL_EXCEPTION(); //执行安全检测异常函数 return; //返回,停止执行 } #endif if (p_msg_q->NbrEntries >= p_msg_q->NbrEntriesSize) { //如果消息队列已没有可用空间 *p_err = OS_ERR_Q_MAX; //错误类型为“队列已满” return; //返回,停止执行 } if (OSMsgPool.NbrFree == (OS_MSG_QTY)0) { //如果消息池没有可用消息 *p_err = OS_ERR_MSG_POOL_EMPTY; //错误类型为“消息池没有消息” return; //返回,停止执行 } /* 从消息池获取一个消息(暂存于 p_msg )*/ p_msg = OSMsgPool.NextPtr; //将消息控制块从消息池移除 OSMsgPool.NextPtr = p_msg->NextPtr; //指向下一个消息(取走首个消息) OSMsgPool.NbrFree--; //消息池可用消息数减1 OSMsgPool.NbrUsed++; //消息池被用消息数加1 if (OSMsgPool.NbrUsedMax < OSMsgPool.NbrUsed) { //更新消息被用最大数目的历史记录 OSMsgPool.NbrUsedMax = OSMsgPool.NbrUsed; } /* 将获取的消息插入到消息队列 */ if (p_msg_q->NbrEntries == (OS_MSG_QTY)0) { //如果消息队列目前没有消息 p_msg_q->InPtr = p_msg; //将其入队指针指向该消息 p_msg_q->OutPtr = p_msg; //出队指针也指向该消息 p_msg_q->NbrEntries = (OS_MSG_QTY)1; //队列的消息数为1 p_msg->NextPtr = (OS_MSG *)0; //该消息的下一个消息为空 } else { //如果消息队列目前已有消息 if ((opt & OS_OPT_POST_LIFO) == OS_OPT_POST_FIFO) { //如果用FIFO方式插入队列, p_msg_in = p_msg_q->InPtr; //将消息插入到入队端,入队 p_msg_in->NextPtr = p_msg; //指针指向该消息。 p_msg_q->InPtr = p_msg; p_msg->NextPtr = (OS_MSG *)0; } else { //如果用LIFO方式插入队列, p_msg->NextPtr = p_msg_q->OutPtr; //将消息插入到出队端,出队 p_msg_q->OutPtr = p_msg; //指针指向该消息。 } p_msg_q->NbrEntries++; //消息队列的消息数目加1 } if (p_msg_q->NbrEntriesMax < p_msg_q->NbrEntries) { //更新改消息队列的最大消息 p_msg_q->NbrEntriesMax = p_msg_q->NbrEntries; //数目的历史记录。 } p_msg->MsgPtr = p_void; //给该消息填写消息内容 p_msg->MsgSize = msg_size; //给该消息填写消息大小 p_msg->MsgTS = ts; //填写发布该消息时的时间戳 *p_err = OS_ERR_NONE; //错误类型为“无错误” }
另外,OS_QPost () 函数还调用了 OS_Post() 函数发布内核对象。OS_Post() 函数是一个底层的发布函数,它不仅仅用来发布消息队列,还可以发布多值信号量、互斥信号量、事件标志组、任务消息队列和任务信号量。
OS_Post() 函数的定义位于“os_core.c”:
void OS_Post (OS_PEND_OBJ *p_obj, //内核对象类型指针 OS_TCB *p_tcb, //任务控制块 void *p_void, //消息 OS_MSG_SIZE msg_size, //消息大小 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_Post1(p_obj, //标记哪个内核对象被发布 p_tcb, p_void, msg_size, ts); } else { //如果任务不是在等待多个信号量或消息队列 #if (OS_MSG_EN > 0u) //如果使能了任务队列或消息队列 p_tcb->MsgPtr = p_void; //保存消息到等待任务 p_tcb->MsgSize = msg_size; #endif p_tcb->TS = ts; //保存时间戳到等待任务 } if (p_obj != (OS_PEND_OBJ *)0) { //如果内核对象为空 OS_PendListRemove(p_tcb); //从等待列表移除该等待任务 #if OS_CFG_DBG_EN > 0u //如果使能了调试代码和变量 OS_PendDbgNameRemove(p_obj, //移除内核对象的调试名 p_tcb); #endif } OS_TaskRdy(p_tcb); //让该等待任务准备运行 p_tcb->TaskState = OS_TASK_STATE_RDY; //任务状态改为就绪状态 p_tcb->PendStatus = OS_STATUS_PEND_OK; //清除等待状态 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_Post1(p_obj, //标记哪个内核对象被发布 p_tcb, p_void, msg_size, ts); } else { //如果任务不在等待多个信号量或消息队列 #if (OS_MSG_EN > 0u) //如果使能了调试代码和变量 p_tcb->MsgPtr = p_void; //保存消息到等待任务 p_tcb->MsgSize = msg_size; #endif p_tcb->TS = ts; //保存时间戳到等待任务 } OS_TickListRemove(p_tcb); //从节拍列表移除该等待任务 if (p_obj != (OS_PEND_OBJ *)0) { //如果内核对象为空 OS_PendListRemove(p_tcb); //从等待列表移除该等待任务 #if OS_CFG_DBG_EN > 0u //如果使能了调试代码和变量 OS_PendDbgNameRemove(p_obj, //移除内核对象的调试名 p_tcb); #endif } p_tcb->TaskState = OS_TASK_STATE_SUSPENDED; //任务状态改为被挂起状态 p_tcb->PendStatus = OS_STATUS_PEND_OK; //清除等待状态 p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING; //标记不再等待 break; default: //如果任务状态超出预期 break; //直接跳出 } }
OSQPend ()
与 OSQPost () 消息队列发布消息函数相对应,OSQPend () 函数用于等待获取消息队列的消息。
OSQPend () 函数的定义也位于“os_q.c” :
void *OSQPend (OS_Q *p_q, //消息队列指针 OS_TICK timeout, //等待期限(单位:时钟节拍) OS_OPT opt, //选项 OS_MSG_SIZE *p_msg_size,//返回消息大小(单位:字节) CPU_TS *p_ts, //获取等到消息时的时间戳 OS_ERR *p_err) //返回错误类型 { OS_PEND_DATA pend_data; void *p_void; CPU_SR_ALLOC(); //使用到临界段(在关/开中断时)时必需该宏,该宏声明和 //定义一个局部变量,用于保存关中断前的 CPU 状态寄存器 // SR(临界段关中断只需保存SR),开中断时将该值还原。 #ifdef OS_SAFETY_CRITICAL //如果使能(默认禁用)了安全检测 if (p_err == (OS_ERR *)0) { //如果错误类型实参为空 OS_SAFETY_CRITICAL_EXCEPTION(); //执行安全检测异常函数 return ((void *)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 ((void *)0); //返回0(有错误),停止执行 } #endif #if OS_CFG_ARG_CHK_EN > 0u //如果使能了参数检测 if (p_q == (OS_Q *)0) { //如果 p_q 为空 *p_err = OS_ERR_OBJ_PTR_NULL; //错误类型为“对象为空” return ((void *)0); //返回0(有错误),停止执行 } if (p_msg_size == (OS_MSG_SIZE *)0) { //如果 p_msg_size 为空 *p_err = OS_ERR_PTR_INVALID; //错误类型为“指针不可用” return ((void *)0); //返回0(有错误),停止执行 } switch (opt) { //根据选项分类处理 case OS_OPT_PEND_BLOCKING: //如果选项在预期内 case OS_OPT_PEND_NON_BLOCKING: break; //直接跳出 default: //如果选项超出预期 *p_err = OS_ERR_OPT_INVALID; //返回错误类型为“选项非法” return ((void *)0); //返回0(有错误),停止执行 } #endif #if OS_CFG_OBJ_TYPE_CHK_EN > 0u //如果使能了对象类型检测 if (p_q->Type != OS_OBJ_TYPE_Q) { //如果 p_q 不是消息队列类型 *p_err = OS_ERR_OBJ_TYPE; //错误类型为“对象类型有误” return ((void *)0); //返回0(有错误),停止执行 } #endif if (p_ts != (CPU_TS *)0) { //如果 p_ts 非空 *p_ts = (CPU_TS )0; //初始化(清零)p_ts,待用于返回时间戳 } CPU_CRITICAL_ENTER(); //关中断 p_void = OS_MsgQGet(&p_q->MsgQ, //从消息队列获取一个消息 p_msg_size, p_ts, p_err); if (*p_err == OS_ERR_NONE) { //如果获取消息成功 CPU_CRITICAL_EXIT(); //开中断 return (p_void); //返回消息内容 } /* 如果获取消息不成功 */ if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) { //如果选择了不堵塞任务 CPU_CRITICAL_EXIT(); //开中断 *p_err = OS_ERR_PEND_WOULD_BLOCK; //错误类型为“等待渴求堵塞” return ((void *)0); //返回0(有错误),停止执行 } else { //如果选择了堵塞任务 if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { //如果调度器被锁 CPU_CRITICAL_EXIT(); //开中断 *p_err = OS_ERR_SCHED_LOCKED; //错误类型为“调度器被锁” return ((void *)0); //返回0(有错误),停止执行 } } /* 如果调度器未被锁 */ OS_CRITICAL_ENTER_CPU_EXIT(); //锁调度器,重开中断 OS_Pend(&pend_data, //堵塞当前任务,等待消息队列, (OS_PEND_OBJ *)((void *)p_q), //将当前任务脱离就绪列表,并 OS_TASK_PEND_ON_Q, //插入到节拍列表和等待列表。 timeout); OS_CRITICAL_EXIT_NO_SCHED(); //开调度器,但不进行调度 OSSched(); //找到并调度最高优先级就绪任务 /* 当前任务(获得消息队列的消息)得以继续运行 */ CPU_CRITICAL_ENTER(); //关中断 switch (OSTCBCurPtr->PendStatus) { //根据当前运行任务的等待状态分类处理 case OS_STATUS_PEND_OK: //如果等待状态正常 p_void = OSTCBCurPtr->MsgPtr; //从(发布时放于)任务控制块提取消息 *p_msg_size = OSTCBCurPtr->MsgSize; //提取消息大小 if (p_ts != (CPU_TS *)0) { //如果 p_ts 非空 *p_ts = OSTCBCurPtr->TS; //获取任务等到消息时的时间戳 } *p_err = OS_ERR_NONE; //错误类型为“无错误” break; //跳出 case OS_STATUS_PEND_ABORT: //如果等待被中止 p_void = (void *)0; //返回消息内容为空 *p_msg_size = (OS_MSG_SIZE)0; //返回消息大小为0 if (p_ts != (CPU_TS *)0) { //如果 p_ts 非空 *p_ts = OSTCBCurPtr->TS; //获取等待被中止时的时间戳 } *p_err = OS_ERR_PEND_ABORT; //错误类型为“等待被中止” break; //跳出 case OS_STATUS_PEND_TIMEOUT: //如果等待超时 p_void = (void *)0; //返回消息内容为空 *p_msg_size = (OS_MSG_SIZE)0; //返回消息大小为0 if (p_ts != (CPU_TS *)0) { //如果 p_ts 非空 *p_ts = (CPU_TS )0; //清零 p_ts } *p_err = OS_ERR_TIMEOUT; //错误类型为“等待超时” break; //跳出 case OS_STATUS_PEND_DEL: //如果等待的内核对象被删除 p_void = (void *)0; //返回消息内容为空 *p_msg_size = (OS_MSG_SIZE)0; //返回消息大小为0 if (p_ts != (CPU_TS *)0) { //如果 p_ts 非空 *p_ts = OSTCBCurPtr->TS; //获取对象被删时的时间戳 } *p_err = OS_ERR_OBJ_DEL; //错误类型为“等待对象被删” break; //跳出 default: //如果等待状态超出预期 p_void = (void *)0; //返回消息内容为空 *p_msg_size = (OS_MSG_SIZE)0; //返回消息大小为0 *p_err = OS_ERR_STATUS_INVALID; //错误类型为“状态非法” break; //跳出 } CPU_CRITICAL_EXIT(); //开中断 return (p_void); //返回消息内容 }
在 OSQPend () 函数中,会调用 OS_MsgQGet () 函数从消息队列获取一个消息。
OS_MsgQGet () 函数的定义位于“os_msg.c”:
void *OS_MsgQGet (OS_MSG_Q *p_msg_q, //消息队列 OS_MSG_SIZE *p_msg_size, //返回消息大小 CPU_TS *p_ts, //返回某些操作的时间戳 OS_ERR *p_err) //返回错误类型 { OS_MSG *p_msg; void *p_void; #ifdef OS_SAFETY_CRITICAL //如果使能(默认禁用)了安全检测 if (p_err == (OS_ERR *)0) { //如果错误类型实参为空 OS_SAFETY_CRITICAL_EXCEPTION(); //执行安全检测异常函数 return ((void *)0); //返回空消息,停止执行 } #endif if (p_msg_q->NbrEntries == (OS_MSG_QTY)0) { //如果消息队列没有消息 *p_msg_size = (OS_MSG_SIZE)0; //返回消息长度为0 if (p_ts != (CPU_TS *)0) { //如果 p_ts 非空 *p_ts = (CPU_TS )0; //清零 p_ts } *p_err = OS_ERR_Q_EMPTY; //错误类型为“队列没消息” return ((void *)0); //返回空消息,停止执行 } /* 如果消息队列有消息 */ p_msg = p_msg_q->OutPtr; //从队列的出口端提取消息 p_void = p_msg->MsgPtr; //提取消息内容 *p_msg_size = p_msg->MsgSize; //提取消息长度 if (p_ts != (CPU_TS *)0) { //如果 p_ts 非空 *p_ts = p_msg->MsgTS; //获取消息被发布时的时间戳 } p_msg_q->OutPtr = p_msg->NextPtr; //修改队列的出队指针 if (p_msg_q->OutPtr == (OS_MSG *)0) { //如果队列没有消息了 p_msg_q->InPtr = (OS_MSG *)0; //清零出队指针 p_msg_q->NbrEntries = (OS_MSG_QTY)0; //清零消息数 } else { //如果队列还有消息 p_msg_q->NbrEntries--; //队列的消息数减1 } /* 从消息队列提取完消息信息后,将消息释放回消息池供继续使用 */ p_msg->NextPtr = OSMsgPool.NextPtr; //消息插回消息池 OSMsgPool.NextPtr = p_msg; OSMsgPool.NbrFree++; //消息池的可用消息数加1 OSMsgPool.NbrUsed--; //消息池的已用消息数减1 *p_err = OS_ERR_NONE; //错误类型为“无错误” return (p_void); //返回罅隙内容 }
OSQPend () 函数会调用一个更加底层的等待函数来执行当前任务对消息队列的等待,该函数就是 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 }
OSQPendAbort ()
OSQPendAbort () 函数用于中止任务对一个消息队列的等待。要使用 OSQPendAbort ()函数,还得事先使能 OS_CFG_Q_PEND_ABORT_EN(位于“os_cfg.h”)
#define OS_CFG_Q_PEND_ABORT_EN 1u //使能或禁用 OSQPendAbort() 函数
OSQPendAbort () 函数的信息如下表所示。
OSQPendAbort () 函数的定义位于“os_q.c” :
#if OS_CFG_Q_PEND_ABORT_EN > 0u //如果使能了 OSQPendAbort() 函数 OS_OBJ_QTY OSQPendAbort (OS_Q *p_q, //消息队列 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 //返回0(有错误),停止执行 if (p_q == (OS_Q *)0) { //如果 p_q 为空 *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_q->Type != OS_OBJ_TYPE_Q) { //如果 p_q 不是消息队列类型 *p_err = OS_ERR_OBJ_TYPE; //错误类型为“对象类型有误” return ((OS_OBJ_QTY)0u); //返回0(有错误),停止执行 } #endif CPU_CRITICAL_ENTER(); //关中断 p_pend_list = &p_q->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); } /* 如果有任务在等待 */ 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_q), //中止该任务对 p_q 的等待 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
OSQPendAbort () 函数会调用一个更加底层的中止等待函数来执行当前任务对消息队列的等待,该函数就是 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; //不需处理,直接跳出 } }
OSQDel ()
OSQDel() 函数用于删除一个消息队列。要使用 OSQDel () 函数,还得事先使能OS_CFG_Q_DEL_EN(位于“os_cfg.h”)
#define OS_CFG_Q_DEL_EN 1u //使能或禁用 OSQDel() 函数
OSQDel () 函数的信息如下表所示。
OSQDel() 函数的定义位于“os_q.c”
#if OS_CFG_Q_DEL_EN > 0u //如果使能了 OSQDel() 函数 OS_OBJ_QTY OSQDel (OS_Q *p_q, //消息队列指针 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_q == (OS_Q *)0) { //如果 p_q 为空 *p_err = OS_ERR_OBJ_PTR_NULL; //错误类型为“对象为空” return ((OS_OBJ_QTY)0u); //返回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)0u); //返回0(有错误),停止执行 } #endif #if OS_CFG_OBJ_TYPE_CHK_EN > 0u //如果使能了对象类型检测 if (p_q->Type != OS_OBJ_TYPE_Q) { //如果 p_q 不是消息队列类型 *p_err = OS_ERR_OBJ_TYPE; //错误类型为“对象类型有误” return ((OS_OBJ_QTY)0); //返回0(有错误),停止执行 } #endif CPU_CRITICAL_ENTER(); //关中断 p_pend_list = &p_q->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_QDbgListRemove(p_q); //将该队列从消息队列调试列表移除 #endif OSQQty--; //消息队列数目减1 OS_QClr(p_q); //清除该队列的内容 CPU_CRITICAL_EXIT(); //开中断 *p_err = OS_ERR_NONE; //错误类型为“无错误” } else { //如果有任务在等待该队列 CPU_CRITICAL_EXIT(); //开中断 *p_err = OS_ERR_TASK_WAITING; //错误类型为“有任务在等待该队列” } break; case OS_OPT_DEL_ALWAYS: //如果必须删除信号量 OS_CRITICAL_ENTER_CPU_EXIT(); //进入临界段,重开中断 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_q), p_tcb, ts); cnt--; } #if OS_CFG_DBG_EN > 0u //如果使能了调试代码和变量 OS_QDbgListRemove(p_q); //将该队列从消息队列调试列表移除 #endif OSQQty--; //消息队列数目减1 OS_QClr(p_q); //清除消息队列内容 OS_CRITICAL_EXIT_NO_SCHED(); //退出临界段(无调度) OSSched(); //调度任务 *p_err = OS_ERR_NONE; //错误类型为“无错误” break; //跳出 default: //如果选项超出预期 CPU_CRITICAL_EXIT(); //开中断 *p_err = OS_ERR_OPT_INVALID; //错误类型为“选项非法” break; //跳出 } return (nbr_tasks); //返回删除队列前等待其的任务数 } #endif
OSQDel() 函数会调用一个更加底层的删除等待对象的函数来执行对消息队列的删除,该函数就是 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; //不需处理,直接跳出 } }
OSQFlush ()
OSQFlush () 函数用于清空一个消息队列。要使用 OSQFlush () 函数,还得事先使能OS_CFG_Q_FLUSH_EN(位于“os_cfg.h”)
#define OS_CFG_Q_FLUSH_EN 1u //使能或禁用 OSQFlush() 函数
OSQFlush () 函数的信息如下表所示
OSQFlush () 函数的定义也位于“os_q.c :
#if OS_CFG_Q_FLUSH_EN > 0u //如果使能了 OSQFlush() 函数 OS_MSG_QTY OSQFlush (OS_Q *p_q, //消息队列指针 OS_ERR *p_err) //返回错误类型 { OS_MSG_QTY entries; CPU_SR_ALLOC(); //使用到临界段(在关/开中断时)时必需该宏,该宏声明和 //定义一个局部变量,用于保存关中断前的 CPU 状态寄存器 // SR(临界段关中断只需保存SR),开中断时将该值还原。 #ifdef OS_SAFETY_CRITICAL //如果使能(默认禁用)了安全检测 if (p_err == (OS_ERR *)0) { //如果错误类型实参为空 OS_SAFETY_CRITICAL_EXCEPTION(); //执行安全检测异常函数 return ((OS_MSG_QTY)0); //返回0(有错误),停止执行 } #endif #if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u //如果使能了中断中非法调用检测 if (OSIntNestingCtr > (OS_NESTING_CTR)0) { //如果该函数在中断中被调用 *p_err = OS_ERR_FLUSH_ISR; //错误类型为“在中断中请空队列” return ((OS_MSG_QTY)0); //返回0(有错误),停止执行 } #endif #if OS_CFG_ARG_CHK_EN > 0u //如果使能了参数检测 if (p_q == (OS_Q *)0) { //如果 p_q 为空 *p_err = OS_ERR_OBJ_PTR_NULL; //错误类型为“对象为空” return ((OS_MSG_QTY)0); //返回0(有错误),停止执行 } #endif #if OS_CFG_OBJ_TYPE_CHK_EN > 0u //如果使能了对象类型检测 if (p_q->Type != OS_OBJ_TYPE_Q) { //如果 p_q 不是消息队列类型 *p_err = OS_ERR_OBJ_TYPE; //错误类型为“对象类型有误” return ((OS_MSG_QTY)0); //返回0(有错误),停止执行 } #endif OS_CRITICAL_ENTER(); //进入临界段 entries = OS_MsgQFreeAll(&p_q->MsgQ); //把队列的所有消息均释放回消息池 OS_CRITICAL_EXIT(); //退出临界段 *p_err = OS_ERR_NONE; //错误类型为“无错误” return ((OS_MSG_QTY)entries); //返回清空队列前队列的消息数目 } #endif
以上是关于消息队列的主要内容,如果未能解决你的问题,请参考以下文章