这个函数是请求信号量,邮箱,消息队列的,但是对互斥信号量和标志不起作用。
#if ((OS_EVENT_EN) && (OS_EVENT_MULTI_EN > 0u))
INT16U OSEventPendMulti (OS_EVENT **pevents_pend,//挂起的事件组(需要请求资源)
OS_EVENT **pevents_rdy, //就绪的事件组
void **pmsgs_rdy, //保存请求中返回的信息
INT32U timeout, //请求资源的时间,如果超时任务进入休眠态
INT8U *perr)
{
OS_EVENT **pevents;
OS_EVENT *pevent;
#if ((OS_Q_EN > 0u) && (OS_MAX_QS > 0u))
OS_Q *pq; //如果使用队列且最大队列控制块数大于0则申请一个队列控制块指针
#endif
BOOLEAN events_rdy; //是否有准备好的事件标志
INT16U events_rdy_nbr; //准备好的事件数
INT8U events_stat; //事件状态
#if (OS_CRITICAL_METHOD == 3u)
OS_CPU_SR cpu_sr = 0u;
#endif
#ifdef OS_SAFETY_CRITICAL
if (perr == (INT8U *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
}
#endif
//验证各个参数的有效性
#if (OS_ARG_CHK_EN > 0u)
if (pevents_pend == (OS_EVENT **)0) {
*perr = OS_ERR_PEVENT_NULL;
return (0u);
}
if (*pevents_pend == (OS_EVENT *)0) {
*perr = OS_ERR_PEVENT_NULL;
return (0u);
}
if (pevents_rdy == (OS_EVENT **)0) {
*perr = OS_ERR_PEVENT_NULL;
return (0u);
}
if (pmsgs_rdy == (void **)0) {
*perr = OS_ERR_PEVENT_NULL;
return (0u);
}
#endif
//初始化指向就绪事件数组的指针为NULL
*pevents_rdy = (OS_EVENT *)0;
//pevents指向挂起等待的事件组,已经处于就绪态的事件组是不需要再判断的,pevent指向该事件组中的事件.
pevents = pevents_pend;
pevent = *pevents;
//对事件组中的所有事件进行类型检查
while (pevent != (OS_EVENT *)0) {
switch (pevent->OSEventType) {
#if (OS_SEM_EN > 0u)
case OS_EVENT_TYPE_SEM:
break;
#endif
#if (OS_MBOX_EN > 0u)
case OS_EVENT_TYPE_MBOX:
break;
#endif
#if ((OS_Q_EN > 0u) && (OS_MAX_QS > 0u))
case OS_EVENT_TYPE_Q:
break;
#endif
case OS_EVENT_TYPE_MUTEX:
case OS_EVENT_TYPE_FLAG:
default:
*perr = OS_ERR_EVENT_TYPE;
return (0u);
}
pevents++; //即数组地址+1
pevent = *pevents;//指向下一个元素
}
//不能在中断程序中调用
if (OSIntNesting > 0u) {
*perr = OS_ERR_PEND_ISR;
return (0u);
}
//任务被锁住也是不能挂起的
if (OSLockNesting > 0u) {
*perr = OS_ERR_PEND_LOCKED;
return (0u);
}
OS_ENTER_CRITICAL(); //进入中断沿
events_rdy = OS_FALSE; //事件就绪标志初始化为0
events_rdy_nbr = 0u; //就绪事件数初始化为0
events_stat = OS_STAT_RDY; //事件状态初始化为就绪态
pevents = pevents_pend; //pevents指向挂起等待的事件组 pevent指向事件组中的事件
pevent = *pevents;
while (pevent != (OS_EVENT *)0) { //当最后一个元素值为(OS_EVENT *)0时等待事件组都判断完了
switch (pevent->OSEventType) { //对事件组中请求特定资源的事件做特定处理
#if (OS_SEM_EN > 0u)
case OS_EVENT_TYPE_SEM: //信号量事件,信号量大于0说明资源是可用的
if (pevent->OSEventCnt > 0u) {
pevent->OSEventCnt--;
//请求得到满足,可用的信号量数就减少了所以要--
*pevents_rdy++ = pevent;
//这个事件进入就绪事件表
events_rdy = OS_TRUE;
//标志一下有事件就绪了
*pmsgs_rdy++ = (void *)0;
//信号量没有返回消息
events_rdy_nbr++;
//就绪的事件数加1
} else {
events_stat |= OS_STAT_SEM;
//请求失败则标记为事件组等待信号量
}
break;
#endif
#if (OS_MBOX_EN > 0u)
case OS_EVENT_TYPE_MBOX://是邮箱事件如果邮箱有东西则请求得到满足
if (pevent->OSEventPtr != (void *)0) { *pmsgs_rdy++=(void *)pevent->OSEventPtr;
//把邮箱的消息地址保存在message_ready数组中
pevent->OSEventPtr = (void *)0;
//请求了一个邮箱邮箱就由非空状态变空
*pevents_rdy++=pevent;
//这个事件进入就绪态
events_rdy=OS_TRUE;
//标志一下有事件就绪了
events_rdy_nbr++;
//准备好的事件数加1
} else {
events_stat |= OS_STAT_MBOX;
//请求邮箱失败标记事件组等待邮箱
}
break;
#endif
#if ((OS_Q_EN > 0u) && (OS_MAX_QS > 0u))
case OS_EVENT_TYPE_Q: //如果是队列事件
pq = (OS_Q *)pevent->OSEventPtr;//pq指向队列事件指针
if (pq->OSQEntries > 0u) { //如果队列非空
*pmsgs_rdy++ = (void *)*pq->OSQOut++;
//把队列的消息保存在pmsgs_rdy的数组中
if (pq->OSQOut == pq->OSQEnd) {
pq->OSQOut = pq->OSQStart;
} //队列的链表要把首尾连接起来
pq->OSQEntries--;
//请求了一个队列队列中消息就减少了(队列是链表,其中一块少了即入口少了)
*pevents_rdy++ = pevent;
//请求得到允许事件进入就绪态
events_rdy = OS_TRUE; //标志一下有事件就绪了
events_rdy_nbr++; //就绪事件数加1
} else {
events_stat |= OS_STAT_Q; }
//请求队列未得到允许则标志事件组状态为等待队列
break;
#endif
case OS_EVENT_TYPE_MUTEX:
case OS_EVENT_TYPE_FLAG:
//对互斥信号量事件和标志事件则不做处理
default:
//如果都不是这些事件类型则退出中断沿并返回事件类型错误
OS_EXIT_CRITICAL();
*pevents_rdy = (OS_EVENT *)0;//这里用这个(OS_EVENT *)0返回空的消息
//因为是先保存再加所以出现错误不会有后续判断这里就不用++
*perr= OS_ERR_EVENT_TYPE;
return (events_rdy_nbr); //返回就绪的事件数
}
pevents++;
pevent = *pevents;
//对事件组中的每一个事件做判断
}
if ( events_rdy == OS_TRUE) {
*pevents_rdy = (OS_EVENT *)0;
//所有的事件都判断完了补上一个 (OS_EVENT *)0的结束标志
OS_EXIT_CRITICAL();
*perr = OS_ERR_NONE;
return (events_rdy_nbr); //返回事件的就绪数(调用这个函数的任务继续运行)
}
//如果没有事件就绪即所有请求都失败则挂起调用该函数的函数
OSTCBCur->OSTCBStat |= events_stat | OS_STAT_MULTI;
//标记当前事件控制块中的事件状态为等待资源而且是等待多个资源
OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK;
//配置当前事件控制块中的事件状态标志为等待完成,因为这个事件即将被切换掉,进入挂起态
OSTCBCur->OSTCBDly = timeout;
//设置该任务挂起等待的时间为timeout时间,超时任务就被切换掉。
OS_EventTaskWaitMulti(pevents_pend);
//暂停任务直到出现新的事件呼叫或者挂起时间到
OS_EXIT_CRITICAL();
//退出中断沿
OS_Sched();
//如果当前任务不处于就绪态将进行任务调度将优先级最高的任务调入运行态
OS_ENTER_CRITICAL();
//进入中断沿
//对当前最高优先级的事件进行状态判断(下一个任务或还是之前的任务)
switch (OSTCBCur->OSTCBStatPend) {
case OS_STAT_PEND_OK: //等待完成和等待终止都进行相同处理
case OS_STAT_PEND_ABORT:
pevent = OSTCBCur->OSTCBEventPtr;
if (pevent != (OS_EVENT *)0) {
//有任务就绪了
*pevents_rdy++ = pevent;
//把这个任务的地址存储到pevents_rdy数组中
*pevents_rdy = (OS_EVENT *)0;
//最后一个标志为结束符
events_rdy_nbr++;
//就绪事件数加1
} else {
OSTCBCur->OSTCBStatPend = OS_STAT_PEND_TO;
//如果没有任务就绪标志为超时处理,这里不明白########
OS_EventTaskRemoveMulti(OSTCBCur, pevents_pend);
//解除暂停将任务从等待列表中删除
}
break;
case OS_STAT_PEND_TO:
default:
OS_EventTaskRemoveMulti(OSTCBCur, pevents_pend);//挂起时间到将任务从等待列表中删除
break;
}
//这个switch是用来消息传递的
switch (OSTCBCur->OSTCBStatPend) {
case OS_STAT_PEND_OK: //挂起完成也标志该事件又处在就绪态了
switch (pevent->OSEventType) {
#if (OS_SEM_EN > 0u)
case OS_EVENT_TYPE_SEM:
*pmsgs_rdy++ = (void *)0;
//信号量没有返回消息
break;
#endif
#if ((OS_MBOX_EN > 0u) ||((OS_Q_EN > 0u) && (OS_MAX_QS > 0u)))
case OS_EVENT_TYPE_MBOX:
case OS_EVENT_TYPE_Q:
*pmsgs_rdy++ = (void *)OSTCBCur->OSTCBMsg;
break; //把邮箱和队列中的消息保存在pmsgs_rdy数组中
#endif
case OS_EVENT_TYPE_MUTEX:
case OS_EVENT_TYPE_FLAG:
default:
OS_EXIT_CRITICAL();
*pevents_rdy = (OS_EVENT *)0;
*perr = OS_ERR_EVENT_TYPE;
return (events_rdy_nbr);
}
*perr = OS_ERR_NONE;
break;
case OS_STAT_PEND_ABORT: //等待终止返回空消息
*pmsgs_rdy++ = (void *)0;
*perr = OS_ERR_PEND_ABORT;
break;
case OS_STAT_PEND_TO: //等待超时也返回空消息
default:
*pmsgs_rdy++ = (void *)0;
*perr = OS_ERR_TIMEOUT;
break;
}
OSTCBCur->OSTCBStat = OS_STAT_RDY;
//事件标记为就绪态
OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK;
//清除挂起标志
OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0;
OSTCBCur->OSTCBEventMultiPtr = (OS_EVENT **)0;
#if ((OS_MBOX_EN > 0u) ||((OS_Q_EN > 0u) && (OS_MAX_QS > 0u)))
OSTCBCur->OSTCBMsg = (void *)0;
#endif
OS_EXIT_CRITICAL();
return (events_rdy_nbr);
}
#endif