若有错误还望指出!
任务之间通信之事件与任务关系
之前的文章中提到了任务之间的切换。这次我们来讲解一下两个任务之间是如何通信的?即任务1如何将信息映射到任务2。
①建立空闲事件控制链表
//事件控制块列表初始化
//事件与任务的区别是,触发事件后主程序可以继续往下执行,不受触发的事件何时结束的影响;而调用一个任务后,必须等任务执行完毕,才可继续往下执行。
static void OS_InitEventList (void)
{
#if (OS_EVENT_EN) && (OS_MAX_EVENTS > 0u) //事件数大于0
//如果有多个事件,那就得制作事件链表
#if (OS_MAX_EVENTS > 1u)
INT16U ix;
INT16U ix_next;
OS_EVENT *pevent1; //ECB结构体指针
OS_EVENT *pevent2; //ECB结构体指针 呐呐呐,这些个就是作链表
OS_MemClr((INT8U *)&OSEventTbl[0], sizeof(OSEventTbl)); /* 事件控制列表清空 */
for (ix = 0u; ix < (OS_MAX_EVENTS - 1u); ix++) { /* 开始制作链表 */
ix_next = ix + 1u;
//将事件列表对应的事件地址赋值给结构体指针
pevent1 = &OSEventTbl[ix]; //事件列表下的地址作为传递
pevent2 = &OSEventTbl[ix_next];
pevent1->OSEventType = OS_EVENT_TYPE_UNUSED;
pevent1->OSEventPtr = pevent2;//OSEventTbl[ix]指向下一个OSEventTbl[ix_next]
#if OS_EVENT_NAME_EN > 0u
pevent1->OSEventName = (INT8U *)(void *)"?"; /* Unknown name */
#endif
}
pevent1 = &OSEventTbl[ix]; //将事件列表对应的事件地址赋值给结构体指针
pevent1->OSEventType = OS_EVENT_TYPE_UNUSED; //事件类型
pevent1->OSEventPtr = (OS_EVENT *)0; //指针类型0做结尾
#if OS_EVENT_NAME_EN > 0u
pevent1->OSEventName = (INT8U *)(void *)"?"; /* Unknown name */
#endif
OSEventFreeList = &OSEventTbl[0]; //OSEventFreeList作表头
#else
//如果事件数只有1
OSEventFreeList = &OSEventTbl[0]; /* OSEventFreeList作表头且只有这一个 */
OSEventFreeList->OSEventType = OS_EVENT_TYPE_UNUSED;
OSEventFreeList->OSEventPtr = (OS_EVENT *)0; //指针类型0做结尾
#if OS_EVENT_NAME_EN > 0u
OSEventFreeList->OSEventName = (INT8U *)"?"; /* Unknown name */
#endif
#endif
#endif
}
//写在最后:事件的链表是通过,事件数组下的地址作为指针传递,就是说要取数组内值的地址,相对于任务列表不同,
//任务列表本质为指针数组,即数组内的值为指针形式
总而言之,OS_InitEventList()函数的功能就是将结构体指针嵌套指向结构体指针从而达到产生链表的效果。如下图所示:
②任务等待队列初始化
批注:任务的通信中,事件相对于一个信号,任务需要等待事件信号传递信息之后才运行。
void OS_EventWaitListInit (OS_EVENT *pevent)//OS_EVENT事件结构体指针pevent作形参
{
INT8U i;
pevent->OSEventGrp = 0u; /*OSEventGrp=0,说明没有事件列表就绪 */
for (i = 0u; i < OS_EVENT_TBL_SIZE; i++) { //该队列下没有任务等待该事件
pevent->OSEventTbl[i] = 0u;
}
}
本次函数说明,任务是可以挂载在事件下的,当事件发生之后任务就可以发生,任务列表初始化,那就是将事件下的任务全部清除。
③任务挂载到就绪队列
要进行的两个操作:
1,退出事件等待列表
2,挂载于就序列表中
#if (OS_EVENT_EN)
INT8U OS_EventTaskRdy (OS_EVENT *pevent, //ECB结构体指针
void *pmsg, //邮箱指针
INT8U msk, //整型
INT8U pend_stat)
{
OS_TCB *ptcb; //OSTCB任务结构体指针
INT8U y;
INT8U x;
INT8U prio; //任务优先级
#if OS_LOWEST_PRIO > 63u
OS_PRIO *ptbl;
#endif
............
//1 :位图法解码。计算出该事件下挂载任务的优先级
y = OSUnMapTbl[pevent->OSEventGrp]; /* Find HPT waiting for message */
x = OSUnMapTbl[pevent->OSEventTbl[y]];
prio = (INT8U)((y << 3u) + x); /* Find priority of task getting the msg */
//最高优先级列表指针赋值
ptcb = OSTCBPrioTbl[prio]; /* prio优先级列表下的结构体指针, */
ptcb->OSTCBDly = 0u; /* Prevent OSTimeTick() from readying task */
#if ((OS_Q_EN > 0u) && (OS_MAX_QS > 0u)) || (OS_MBOX_EN > 0u)
ptcb->OSTCBMsg = pmsg; /* 将信息直接传入最高优先级的参数里面 */
#else
pmsg = pmsg; /* Prevent compiler warning if not used */
#endif
.......
//2:将任务挂载就绪列表
if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) {
OSRdyGrp |= ptcb->OSTCBBitY; /* 任务挂载就序列表中 */
OSRdyTbl[y] |= ptcb->OSTCBBitX;
}
//3:任务移除于事件列表
OS_EventTaskRemove(ptcb, pevent);
........
}
#endif
④任务由就绪列表到事件等待列表
两步:1退出就绪列表,2挂载到事件等待列表.。恰好和上面一条相反。
void OS_EventTaskWait (OS_EVENT *pevent)//ecb形参
{
INT8U y;
OSTCBCur->OSTCBEventPtr = pevent; /* ECB的指针给TCB的事件指针 */
//1,加载到事件等待列表
pevent->OSEventTbl[OSTCBCur->OSTCBY] |= OSTCBCur->OSTCBBitX; /* 当前任务处于事件就绪列表中 */
pevent->OSEventGrp |= OSTCBCur->OSTCBBitY;
//2,退出任务就绪列表
y = OSTCBCur->OSTCBY; /* Task no longer ready */
OSRdyTbl[y] &= (OS_PRIO)~OSTCBCur->OSTCBBitX;
if (OSRdyTbl[y] == 0u) { /* Clear event grp bit if this was only task pending */
OSRdyGrp &= (OS_PRIO)~OSTCBCur->OSTCBBitY;
}
}
注:事件等待列表,任务列表不同,前者挂载在事件中,任务由事件触发,后者为正常运行的任务列表。
⑤超时的任务进入就绪状态
任务是可以挂载在事件的等待列表的。任务等待事件触发运行,当任务无法获取事件时,任务会等待一段预定时间,这个时候如果等待时间太长,便会超时,那我们的机制会将等待超时的任务直接设置为就绪状态。
void OSSemPend (OS_EVENT *pevent, //事件结构体变量
INT32U timeout,//超时参数
INT8U *perr) //错误类型指针
{
........
OS_ENTER_CRITICAL();
switch (OSTCBCur->OSTCBStatPend) { /* 查看当前任务状态 */
........
case OS_STAT_PEND_TO: //接收超时
default:
OS_EventTaskRemove(OSTCBCur, pevent); /* 将当前任务从事件等待列表中删除*/
*perr = OS_ERR_TIMEOUT; /* 返回超时错误 */
break;
}
OSTCBCur->OSTCBStat = OS_STAT_RDY; /* 当前任务状态为就绪状态 */
OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK; /* 获取信息成功 */
OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; /* 指向链表底部 */
.....
OS_EXIT_CRITICAL();
}
OSEventTO()函数的主要目的:①将任务拉出事件等待列表,②并且让任务处于就绪状态。