在μC/OS-II中,采用事件进行通信,如下所示,任务1将信息发送到事件,即POST,任务2对信息进行查询,即PEND。事件一般包括信号量、邮箱、消息或消息队列。事件在使用前需要先创建,而创建事件要用到事件控制块,这个事件控制块存储事件的信息。
#define OS_EVENT_EN (((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0) || (OS_SEM_EN > 0) || (OS_MUTEX_EN > 0))//是否允许使用事件
#if OS_LOWEST_PRIO <= 63
#define OS_EVENT_TBL_SIZE ((OS_LOWEST_PRIO) / 8 + 1) //事件表大小
#define OS_RDY_TBL_SIZE ((OS_LOWEST_PRIO) / 8 + 1) //事件就续表大小
#else
#define OS_EVENT_TBL_SIZE ((OS_LOWEST_PRIO) / 16 + 1)
#define OS_RDY_TBL_SIZE ((OS_LOWEST_PRIO) / 16 + 1)
#endif
#define OS_EVENT_TYPE_UNUSED 0u//事件类型为未使用
#define OS_EVENT_TYPE_MBOX 1u//事件类型为消息邮箱
#define OS_EVENT_TYPE_Q 2u//事件类型为消息队列
#define OS_EVENT_TYPE_SEM 3u//事件类型为信号量
#define OS_EVENT_TYPE_MUTEX 4u//事件类型为互斥型信号量
#define OS_EVENT_TYPE_FLAG 5u//事件类型为标志组,但这个类型不适用事件控制块
#define OS_MAX_EVENTS 10 //事件控制块数组大小
#if OS_EVENT_EN && (OS_MAX_EVENTS > 0)
OS_EXT OS_EVENT *OSEventFreeList; //指向下一个可用的事件控制块
OS_EXT OS_EVENT OSEventTbl[OS_MAX_EVENTS];//事件控制块数组
#endif
为方便管理事件,引入事件控制块,如下所示。OSEventType为时间类型,包括OS_EVENT_TYPE_MBOX(消息邮箱1u) 、OS_EVENT_TYPE_Q(消息队列2u) OS_EVENT_TYPE_SEM(信号量3u) OS_EVENT_TYPE_MUTEX(互斥型信号量4u) OS_EVENT_TYPE_FLAG(事件标志5u)。OSEventPtr只有在所定义的事件是消息邮箱和消息队列时,所定义的事件是一个消息邮箱时,他指向一个消息,当所定义的消息队列时,他指向一个数据结构。OSEventCnt,当事件控制块用于信号量时,OSEventCnt是用于信号量的计数器,当事件用于互斥型信号量时,OSEventCnt是用于互斥型信号量和优先级继承优先级的计数器。(OSEventGrp、OSEventTbl)和(OSRdyGrp、OSRdyTbl)类似,为等待某事件的任务。每个等待事件发生的任务都被列入事件控制块等待事件的任务列表中。OSEventGrp的每一位用来指示是否有任务处于等待该时间的状态,相应的OSEventTbl的对应位置为1。当一个事件发生时,该事件的等待事件列表中优先级最高的任务得到该事件。
/*
*********************************************************************************************************
* EVENT CONTROL BLOCK
*********************************************************************************************************
*/
#if OS_EVENT_EN && (OS_MAX_EVENTS > 0)
typedef struct os_event {
INT8U OSEventType; //事件类型
void *OSEventPtr; //指向消息或消息队列的指针
INT16U OSEventCnt; //当事件是信号量是的计数器
#if OS_LOWEST_PRIO <= 63
INT8U OSEventGrp; //等待任务所在组
INT8U OSEventTbl[OS_EVENT_TBL_SIZE]; //等待任务列表
#else
INT16U OSEventGrp;
INT16U OSEventTbl[OS_EVENT_TBL_SIZE];
#endif
#if OS_EVENT_NAME_SIZE > 1
INT8U OSEventName[OS_EVENT_NAME_SIZE];//事件名
#endif
} OS_EVENT;
#endif
调用OS_InitEventList进行事件控制块的初始化。首次判断事件控制块数组的大小,如果大小为1,直接OSEventFreeList指向事件控制数组第一个元素,设置事件类型为未使用类型,事件指针为空,事件名为未知,如果大小大于1,OSEventFreeList指向事件控制数组第一个元素,设置事件类型为未使用类型,事件指针指向下一个单元,串成一个单向链表,最后一个单元事件指针指向空。
static void OS_InitEventList (void)
{
#if OS_EVENT_EN && (OS_MAX_EVENTS > 0)
#if (OS_MAX_EVENTS > 1)
INT16U i;
OS_EVENT *pevent1;
OS_EVENT *pevent2;
OS_MemClr((INT8U *)&OSEventTbl[0], sizeof(OSEventTbl)); //事件表清空
pevent1 = &OSEventTbl[0]; //事件表0
pevent2 = &OSEventTbl[1]; //事件表1
for (i = 0; i < (OS_MAX_EVENTS - 1); i++) { //初始化事件控制块
pevent1->OSEventType = OS_EVENT_TYPE_UNUSED;//未使用
pevent1->OSEventPtr = pevent2; //指向下一个块
#if OS_EVENT_NAME_SIZE > 1
pevent1->OSEventName[0] = '?'; //设置为未知名
pevent1->OSEventName[1] = OS_ASCII_NUL;
#endif
pevent1++; //偏移
pevent2++;
}
pevent1->OSEventType = OS_EVENT_TYPE_UNUSED;//最后一个空间设置为
pevent1->OSEventPtr = (OS_EVENT *)0;//指向空
#if OS_EVENT_NAME_SIZE > 1
pevent1->OSEventName[0] = '?'; //设置为未知名
pevent1->OSEventName[1] = OS_ASCII_NUL;
#endif
OSEventFreeList = &OSEventTbl[0];//表头为可用空间
#else
OSEventFreeList = &OSEventTbl[0]; //只有一个空间
OSEventFreeList->OSEventType = OS_EVENT_TYPE_UNUSED;
OSEventFreeList->OSEventPtr = (OS_EVENT *)0;
#if OS_EVENT_NAME_SIZE > 1
OSEventFreeList->OSEventName[0] = '?'; //设置为未知名
OSEventFreeList->OSEventName[1] = OS_ASCII_NUL;
#endif
#endif
#endif
}
初始化事件调用OS_EventWaitListInit完成,pevent为指向需要初始化事件的指针,初始化事件只是将将等待事件任务表和任务组都清0。OSSemCreate( ),OSMutexCreate( ),OSMboxCreate( ),OSQCreate( )建立时,必须调用此函数进行初始化。
#if OS_EVENT_EN
void OS_EventWaitListInit (OS_EVENT *pevent)
{
#if OS_LOWEST_PRIO <= 63
INT8U *ptbl;
#else
INT16U *ptbl;
#endif
INT8U i;
//将等待事件任务表和任务组都清0
pevent->OSEventGrp = 0; /* No task waiting on event */
ptbl = &pevent->OSEventTbl[0];
for (i = 0; i < OS_EVENT_TBL_SIZE; i++) {
*ptbl++ = 0;
}
}
#endif
当某个事件发生了时,要将事件等待任务列表中最高优先级的任务进入就绪态。调用OS_EventTaskRdy使一个任务等待某个事件,输入参数为pevent(相应事件控制块指针) ,pmsg(消息指针 ), msk(事件类型相关的位掩码), pend_stat(等待状态)。函数OSSemPost,OSMutexPost( ),OSMboxPost( ),OSQPost( )必将调用此函数从而使一个任务进入就绪态。首先找出事件控制块中等待列表最高优先级的任务,将任务从等待列表中移除,将该任务等待延时时间清零,指向事件的指针清零,将消息指针发送给任务,调整响应的等待状态,将该事件等待状态通过相应的掩码清零。如果任务为就绪态,将该任务加入到就绪列表中。
#if OS_EVENT_EN
INT8U OS_EventTaskRdy (OS_EVENT *pevent, void *pmsg, INT8U msk, INT8U pend_stat)
{
OS_TCB *ptcb;
INT8U x;
INT8U y;
INT8U prio;
#if OS_LOWEST_PRIO <= 63
INT8U bitx;
INT8U bity;
#else
INT16U bitx;
INT16U bity;
INT16U *ptbl;
#endif
#if OS_LOWEST_PRIO <= 63
y = OSUnMapTbl[pevent->OSEventGrp]; //找出等待列表中最高优先级
bity = (INT8U)(1 << y);
x = OSUnMapTbl[pevent->OSEventTbl[y]];
bitx = (INT8U)(1 << x);
prio = (INT8U)((y << 3) + x);
#else
if ((pevent->OSEventGrp & 0xFF) != 0) { //找出等待列表中最高优先级
y = OSUnMapTbl[pevent->OSEventGrp & 0xFF];
} else {
y = OSUnMapTbl[(pevent->OSEventGrp >> 8) & 0xFF] + 8;
}
bity = (INT16U)(1 << y);
ptbl = &pevent->OSEventTbl[y];
if ((*ptbl & 0xFF) != 0) {
x = OSUnMapTbl[*ptbl & 0xFF];
} else {
x = OSUnMapTbl[(*ptbl >> 8) & 0xFF] + 8;
}
bitx = (INT16U)(1 << x);
prio = (INT8U)((y << 4) + x);
#endif
pevent->OSEventTbl[y] &= ~bitx; //将该任务从等待列表中移除
if (pevent->OSEventTbl[y] == 0) {
pevent->OSEventGrp &= ~bity; //清除组位
}
ptcb = OSTCBPrioTbl[prio]; //指向相应任务控制块
ptcb->OSTCBDly = 0; //清除延时
ptcb->OSTCBEventPtr = (OS_EVENT *)0; //指向事件指针清空
#if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0)
ptcb->OSTCBMsg = pmsg; //发送信息到任务
#else
pmsg = pmsg;
#endif
ptcb->OSTCBStatPend = pend_stat; //设置等待状态为/ post or abort
ptcb->OSTCBStat &= ~msk; //清除与事件类型相关的位
if (ptcb->OSTCBStat == OS_STAT_RDY) { //就绪
OSRdyGrp |= bity;
OSRdyTbl[y] |= bitx;
}
return (prio);
}
#endif
调用OS_EventTaskWait使当前正在运行的任务等待pevent指向的事件发生,清除当前任务的就绪状态,将OSEventTbl和OSEventGrp置位。
#if OS_EVENT_EN
void OS_EventTaskWait (OS_EVENT *pevent)
{
INT8U y;
OSTCBCur->OSTCBEventPtr = pevent; //任务OSTCBEventPtr指向事件控制块
y = OSTCBCur->OSTCBY; //清除就绪状态
OSRdyTbl[y] &= ~OSTCBCur->OSTCBBitX;
if (OSRdyTbl[y] == 0) {
OSRdyGrp &= ~OSTCBCur->OSTCBBitY;
}
pevent->OSEventTbl[OSTCBCur->OSTCBY] |= OSTCBCur->OSTCBBitX; //将任务加入等待列表
pevent->OSEventGrp |= OSTCBCur->OSTCBBitY;
}
#endif
调用OS_EventTOAbort,是当前运行任务从pevent指向的事件从事件等待列表中移除,重新切换到就绪状态,等待状态变成等待成功。
#if OS_EVENT_EN
void OS_EventTOAbort (OS_EVENT *pevent)
{
INT8U y;
y = OSTCBCur->OSTCBY;
pevent->OSEventTbl[y] &= ~OSTCBCur->OSTCBBitX; //将任务从等待列表移除
if (pevent->OSEventTbl[y] == 0x00) {
pevent->OSEventGrp &= ~OSTCBCur->OSTCBBitY;
}
OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK; //清除等待状态
OSTCBCur->OSTCBStat = OS_STAT_RDY; //切换为就绪状态
OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; //无事件等待
}
#endif
另外,还有OSEventNameGet和OSEventNameSet函数,分别获取和设置事件名。