事件控制块
任务或中断服务子程序可以通过ecb向另外的任务发信号。
事件:信号!!!
控制块:数据结构!!!
事件等待列表,类似于就绪表。当一个事件发生后,处于事件等待列表中的优先级最高的任务得到该事件
一. 事件控制块ECB数据结构:
typedef struct
{
INT8U OSEventType; //事件类型
INT8U OSEventGrp; //等待任务所在的组
INT16U OSEventCnt; //当事件是信号量时的计数器
void *OSEventPtr; //指向消息或消息队列的指针
INT8U OSEventTbl[OS_EVENT_TBL_SIZE]; //等待任务列表
} OS_EVENT;
二.关键算法:
1,将一个任务插入到等待事件的任务列表中:
当调用函数OS_EventTaskWait( )使一个任务进入等待某事件发生时,需要此算法,从而将任务插入到等待事件的任务列表中。
pevent -> OSEventGrp |= OSMapTbl[prio >> 3];
pevent -> OSEventTbl[prio>>3] = OSMapTbl[prio & 0x07];
2,从等待事件的任务列表中使任务脱离等待状态:
当调用函数OS_EventTaskRdy( )使一个任务进入就绪态时,需要调用此算法,从而使等待的任务脱离等待状态进入就绪。
if ((pevent -> OSEventTbl[prio >>3] &= ~OSMapTbl[prio & 0x07]) == 0)
{
pevent -> OSEventGrp &= ~OSMapTbl[prio >>3];
}
3,在等待事件的任务列表中查找优先级最高的任务:
当调用函数OS_EventTaskRdy( )使一个任务进入就绪态时,需要调用此算法,从而首先找出在等待事件任务列表中最高优先级的任务进入就绪状态。
y = OSUnMapTbl[pevent -> OSEventGrp];
x = OSUnMapTbl[pevent -> OSEventTbl[y];
prio = (y << 3) + x;
三.对事件控制块ECB的基本操作
1,初始化一个事件控制块:
函数OSSemCreate( ),OSMutexCreate( ),OSMboxCreate( ),OSQCreate( )建立时,必须调用此函数进行初始化,初始化一个空的等待列表,表中没有任何等待事件的任务。
OS_EventWaitListInit( );
2,使一个任务进入就绪态:
当某个事件发生了时,要将事件等待任务列表中最高优先级的任务进入就绪态,函数OSSemPost,OSMutexPost( ),OSMboxPost( ),OSQPost( )必将调用此函数从而使一个任务进入就绪态。
OS_EventTaskRdy( );
3,使一个任务进入等待某事件发生:
当某个任务须等待一个事件的发生时,信号量、互斥型信号量、邮箱、消息队列会通过相应的PEND函数来调用这个函数。
OS_EventTaskWait( );
4,由于等待超时而将任务置为就绪态:
如果在预先指定的等待时间内任务等待的事件没有发生,那么PNED类型函数将会调用此函数从而将等待超时的任务进入就绪态。
OS_EventTo( );
四.事件控制块ECB基本操作代码的分析。
1,OS_EventWaitListInit( );
void OS_EventWaitListInit(OS_EVENT *pevent)
{
INT8U *ptbl; //定义指针变量ptbl
pevent -> OSEventGrp = 0x00; //清除任务所在的组
OS_EVENT_TBL_SIZE在UCOS_ II.H中定义大小
ptbl = &pevent -> OSEventTbl[0];
#if OS_EVENT_TBL_SIZE > 0
*ptbl++ = 0x00; //清除等待任务列表,在这里没有使用for循环是为了节省系统开销
#endif
#if OS_EVENT_TBL_SIZE > 1
*ptbl++ = 0x00;
#endif
#if OS_EVENT_TBL_SIZE > 2
*ptbl++ = 0x00;
#endif
#if OS_EVENT_TBL_SIZE > 3
*ptbl++ = 0x00;
#endif
#if OS_EVENT_TBL_SIZE > 4
*ptbl++ = 0x00;
#endif
#if OS_EVENT_TBL_SIZE > 5
*ptbl++ = 0x00;
#endif
#if OS_EVENT_TBL_SIZE > 6
*ptbl++ = 0x00;
#if OS_EVENT_TBL_SIZE > 7
*ptbl++ = 0x00;
#endif
}
2,OS_EventTaskRdy( );
INT8U OS_EventTaskRdy(OS_EVENT *pevent, void *msg,INT8U msk)
{
OS_TCB *ptcb;
INT8U x;
INT8U y;
按照关键算法3,在等待事件的任务列表中找到优先级最高的任务,并确定其优先级。
INT8U bitx;
INT8U bity;
INT8U prio;
y = OSUnMapTbl[pevent -> OSEventGrp];
bity = OSMapTbl[y];
x = OSUnMapTbl[pevent -> OSEventTbl[y]];
利用算法2,从等待任务列表中删除此优先级最高的任务。
bitx = OSMapTbl[x];
prio = (INT8U)(( y << 3) + x);
if ((pevent -> OSEventTbl[y] &= ~bitx) == 0x00)
{ pevent -> OSEventGrp &= ~bity;}
ptcb = OSTCBPrioTbl[prio]; //知道了任务优先级,从而找到任务控制块的指针
ptcb -> OSTCBDly = 0; //由于此任务已经不再等待事件的发生,因此直接清0
ptcb -> OSTCBEventPtr = (OS_EVENT *)0;//由于不再等待事件的发生,使指向事件控制块的指
//针指向NULL
#if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0)
ptcb -> OSTCBMsg = msg;//如果此函数是邮箱或队列POST函数调用的,则需要将传递来的参
//数放到它的任务控制块中
#else
msg = msg;
#endif
ptcb -> OSTCBStat &= ~msk;
if (ptcb -> OSTCBStat == OS_STAT_RDY)]
{
OSRdyGrp |= bity; ---最高优先级的任务插入到就绪任务列表,由后续POST函数中的
OSRdyTbl[y] |= bitx; ---OSSched( )函数进行调度
使任务进入就绪,请参考第三章之就绪表
}
return (prio);
}
3,OS_EventTaskWait( );
void OS_EventTaskWait(OS_EVENT *pevent)
{
OSTCBCur -> OSTCBEventPtr = pevent;
if (( OSRdyTbl[OSTCBCur -> OSTCBY] &= ~OSTCBCur->OSTCBBitx) == 0x00)
{ OSRdyGrp &= ~OSTCBCur -> OSTCBBity; } //如果在PEND函数中没有收到有效的信号量,
//那么将调用此函数进入睡眠态,在这里将任务在任务就绪列表中删除
pevent -> OSEventTbl[OSTCBCur -> OSTCBY] |= OSTCBCur -> OSTCBBitx;
pevent -> OSEventGrp |= OSTCBCur -> OSTCBBity;//把此任务放到ECB的等待事件的任务列
//表中去
}
4,OS_EventTo( );
void OS_EventTO (OS_EVENT *pevent)
{
if (( pevent -> OSEventTbl[OSTCBCur -> OSTCBY] &= ~OSTCBCur -> OSTCBBitx) ==0x00)
{ pevent -> OSEventGrp &= ~OSTCBCur -> OSTCBBitY; }//将超时的任务在等待任务列表中删
//除
OSTCBCur -> OSTCBStat = OS_STAT_RDY; //任务置为就绪态
OSTCBCur -> OSTCBEventPtr = (OS_EVENT *)0;
}
附:
任务或中断服务子程序可以通过事件控制块ECB向另外的任务发信号。这里,信号被看成是事件,这也说明为什么前面把用于通信的数据结构叫做事件控制块。
※ 事件控制块ECB是用于实现一下功能函数的基本数据结构,如“信号量管理”、“互斥型信号量管理”、“消息邮箱管理”、“消息队列管理”等。
※ uC/OS-II通过uCOS_II.H中定义的OS_EVENT数据结构,维护一个事件控制块ECB的所有信息。该结构:
1) 该结构出了包含事件本身的定义外
2) 该结构还定义了等待该事件的所有任务的列表
3) 每个信号量、互斥型信号量、消息邮箱及消息队列都应分配到一个时间控制块ECB。
※ 事件控制块ECB的数据结构
typedef struct {
INT8U OSEventType;
INT8U OSEventGrp;
INT16U OSEventCnt;
void * OSEventPtr;
INT8U OSEventTbl[OS_EVENT_TBL_SIZE];
} OS_EVENT;
1) .OSEventType
定义了事件的具体类型,它可以是:
信号量
互斥型信号量
邮箱
消息队列
OSEventType是数据结构的第1个域(第1个字节),这便于实时检测,以确定该指针
是指向ECB,还是事件标志。
2) .OSEventPtr
只有在所定义的事件是消息邮箱或者消息队列时才使用。
当所定义的事件是邮箱时,它指向一个消息;
当所定义的事件是消息队列时,它指向一个数据结构;
3) .OSEventTbl[]和.OSEventGrp
分别与OSRdyTbl[]和OSRdyGrp相似,只不过前两者包含的是等待某事件的任务,而后
两者包含的是系统中处于就绪状态的任务。
4) .OSEventCnt
当事件控制块用于信号量时,.OSEventCnt是用于信号量的计数器;
当事件控制块用于互斥型信号量时,.OSEventCnt用于互斥型信号量和优先级继承优先
级的计数器。
6.00 将任务置于等待事件的任务列表
将一个任务插入到等待事件的任务列表中的程序:
pevent -> OSEventGrp |= OSMapTbl[prio >>3];
pevent -> OSEventTbl[prio >>3] |= OSMapTbl[prio & 0x07];
1) prio是任务的优先级;pevent是指向事件控制块的指针。
2) 由程序看出,将一个任务插入到等待事件任务列表中所需的时间是常数,与表中现有多少个任务无关。
3) 该算法的原理:
任务优先级的最低3位决定了该任务在相应的.OSEventTbl[]中的位置,紧接着的高3位则决定了该任务优先级在.OSEventTBl[]中的字节索引。
6.01 从等待事件的任务列表中使任务脱离等待状态
程序如下:
if ((pevent -> OSEventTbl[prio >>3] &= ~OSMapTbl[prio & 0x07]) == 0) {
pevent -> OSEventGrp &= ~OSMapTbl[prio >>3];
}
1) 该代码消除了任务在.OSEventTbl[]中的相应位;
2) 并且,如果其所在的组中不再有处于等待该事件的任务(.OSEventTbl[prio >>3]为0),则
.OSEventGrp中的相应位也消除了。
6.02 在等待事件的任务列表中查找优先级最高的任务
代码如下:
y = OSUnMapTbl[pevent -> OSEventGrp];
x = OSUnMapTbl[pevent -> OSEventTbl[y]];
prio = (y << 3) + x;
1) 第一行,确定优先级最高的任务所在的组;
2) 第二行,确定其在该组中的具体位置;
3) 第三行,确定优先级最高的任务的优先级序号值,其值范围为0~63。
6.03 空余事件控制块链表
1) 事件控制块ECB的总数由应用程序所需要的信号量、互斥型信号量、邮箱及消息队列
的总数决定。该值由文件OS_CFG.H中的“# define OS_MAX_EVENTS”语句定义。
2) 在调用OSInit()时,所有事件控制块ECB被链接成一个单向链表—空余事件控制块链表
每当建立一个信号量、互斥型信号量、邮箱及消息队列时,就从该链表中取出一个空余
事件控制块,并对它进行初始化。
调用删除信号量、互斥型信号量、邮箱及消息队列的函数,可将事件控制块放回到空余
事件控制块链表中。
3) 对事件控制块进行的操作一般包括:
初始化一个事件控制块; --OS_EventWaitListInit()
使一个任务进入就绪态; --OS_EventTaskRdy()
使一个任务进入等待某事件的状态; --OS_EventWait()
因为等待超时而使一个任务进入就绪态。--OS_EventO()