uCOS II v2.85内核OSMboxPend()和OSMboxPost()函数工作原理
2013-02-20
标签: uCOS

//http://gliethttp.cublog.cn

//----------------------------------------------------------------------

//1.OSMboxPend()函数

void *OSMboxPend (OS_EVENT *pevent, INT16U timeout, INT8U *perr)

{

void *pmsg;

INT8U pend_stat;

#if OS_CRITICAL_METHOD == 3

OS_CPU_SR cpu_sr = 0;

#endif

#if OS_ARG_CHK_EN > 0

if (perr == (INT8U *)0) {

return ((void *)0);

}

if (pevent == (OS_EVENT *)0) {

*perr = OS_ERR_PEVENT_NULL;

return ((void *)0);

}

#endif

if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) {

//确保该event控制块是Mbox类型

*perr = OS_ERR_EVENT_TYPE;

return ((void *)0);

}

if (OSIntNesting > 0) {

//ISR中,不能使用OSMboxPend()

*perr = OS_ERR_PEND_ISR;

return ((void *)0);

}

if (OSLockNesting > 0) {

//uCOS/II v2.85内核已经被强制锁住

*perr = OS_ERR_PEND_LOCKED;

return ((void *)0);

}

//非法的统统不是,信号正常,所以有必要进一步处理

OS_ENTER_CRITICAL();

pmsg = pevent->OSEventPtr;

if (pmsg != (void *)0) {

//pevent->OSEventPtr存放对应的消息指针,如果为0,说明还没有消息

//程序的其他地方已经触发了事件

//所以该task无需悬停,直接获得事件的使用权

pevent->OSEventPtr = (void *)0;

OS_EXIT_CRITICAL();

*perr = OS_ERR_NONE;

return (pmsg);

}

//当前还没有任何事件发生,所以本task需要悬停,让出cpu

OSTCBCur->OSTCBStat |= OS_STAT_MBOX;//是Mbox事件让本task进入悬停等待的

OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK;//假定不是超时,为正常收到信号

//超时,如果timeout=0,那么,本task将一直悬停,仅仅当收到事件触发信号后才重新进入调度队列

OSTCBCur->OSTCBDly = timeout;

//OS_EventTaskWait()函数实现的功能:

//把本task从就绪控制矩阵中摘下,放到pevent事件专有的进程事件控制矩阵表中.

OS_EventTaskWait(pevent);

OS_EXIT_CRITICAL();

//因为本task正在运行,所以本task现在的优先级最高,现在本task已经将自己从就绪控制矩阵--调度器(x,y)矩形阵列中

//把自己摘掉,所以调度函数OS_Sched()一定会切换到另一个task中执行新task的代码

OS_Sched();//具体参见《浅析uC/OS-II v2.85内核调度函数》

OS_ENTER_CRITICAL();

//可能因为OSMboxPend()中指定的timeout已经超时

//[由OSTimeTick()函数把本task重新置入了就绪队列,具体参考《浅析uC/OS-II v2.85内核OSTimeDly()函数工作原理》],

//又或者确实在应用程序的某个地方调用了OSMboxPost(),以下代码将具体解析是有什么引起的:1.超时,2.收到正常信号

if (OSTCBCur->OSTCBStatPend != OS_STAT_PEND_OK) {

//是因为timeout超时,使得本task获得重新执行的机会

pend_stat = OSTCBCur->OSTCBStatPend;

//清除event事件块上本task的标志

OS_EventTOAbort(pevent);

OS_EXIT_CRITICAL();

switch (pend_stat) {

case OS_STAT_PEND_TO:

default:

*perr = OS_ERR_TIMEOUT;

break;

case OS_STAT_PEND_ABORT:

*perr = OS_ERR_PEND_ABORT;

break;

}

return ((void *)0);

}

//由OSMboxPost()抛出正常事件,唤醒了本task,因为在OSMboxPost()时,

//已经将本task在event事件控制矩阵上的对应位清除掉,并且把本task放入了就绪控制矩阵中,

//否则本task也不会执行至此.

pmsg = OSTCBCur->OSTCBMsg;//由OSMboxPost()传递来的消息指针

OSTCBCur->OSTCBMsg = (void *)0;//清空消息指针

//状态ok,等待os调度登记到就绪控制矩阵中的自己

OSTCBCur->OSTCBStat = OS_STAT_RDY;

OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0;//现在本task不悬停在任何event事件上

OS_EXIT_CRITICAL();

*perr = OS_ERR_NONE;

return (pmsg);

}

//----------------------------------------------------------------------

//2.OS_EventTaskWait()函数

void OS_EventTaskWait (OS_EVENT *pevent)

{

INT8U y;

//pevent为此次task挂起的EventPtr单元

OSTCBCur->OSTCBEventPtr = pevent;

//清除调度器中该task对应的标志位

y = OSTCBCur->OSTCBY;

OSRdyTbl[y] &= ~OSTCBCur->OSTCBBitX;

if (OSRdyTbl[y] == 0) {

//当前y行对应的8个或16个task都已经悬停,那么当前y行也清除.

OSRdyGrp &= ~OSTCBCur->OSTCBBitY;

}

//将该task的prio添加到pevent事件控制矩阵中,这个矩阵的功能和OSRdyGrp、OSRdyTbl就绪控制矩阵没有区别

//都是用来就算出已经就绪tasks中的优先级最高的那个

pevent->OSEventTbl[OSTCBCur->OSTCBY] |= OSTCBCur->OSTCBBitX;

pevent->OSEventGrp |= OSTCBCur->OSTCBBitY;

}

//----------------------------------------------------------------------

//3.OS_EventTOAbort()函数

void OS_EventTOAbort (OS_EVENT *pevent)

{

INT8U y;

//清除event事件控制矩阵上本task的标志,因为OSTimeTick()函数未清除该单元

//它仅仅把本task放入就绪控制矩阵,使得本task重新获得被OS调度的机会而已

//具体的清除工作还要自己完成

//具体参考《浅析uC/OS-II v2.85内核OSTimeDly()函数工作原理》

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;//现在本task不悬停在任何event事件上

}

//----------------------------------------------------------------------

//3.OSMboxPost()函数

INT8U OSMboxPost (OS_EVENT *pevent, void *pmsg)

{

#if OS_CRITICAL_METHOD == 3

OS_CPU_SR cpu_sr = 0;//方式3将把cpsr状态寄存器推入临时堆栈cpu_sr中,可以安全返回之前的中断状态

#endif

#if OS_ARG_CHK_EN > 0

if (pevent == (OS_EVENT *)0) {

return (OS_ERR_PEVENT_NULL);

}

if (pmsg == (void *)0) {

return (OS_ERR_POST_NULL_PTR);

}

#endif

if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) {

return (OS_ERR_EVENT_TYPE);

}

OS_ENTER_CRITICAL();

if (pevent->OSEventGrp != 0) {

//OS_EventTaskRdy()函数将摘掉等待在pevent事件控制矩阵上的task中优先级最高的task

//如果该task仅仅等待该pevent事件,那么将该task添加到就绪控制矩阵中

//OSRdyGrp |= bity;

//OSRdyTbl[y] |= bitx;这样调度程序就会根据情况调度OS_Sched()该task了

(void)OS_EventTaskRdy(pevent, pmsg, OS_STAT_MBOX, OS_STAT_PEND_OK);

OS_EXIT_CRITICAL();

//可能刚刚放到就绪控制矩阵上的被唤醒的task的优先级比调用OSMboxPost()函数的进程B优先级高

//所以需要调用shedule函数,

//如果真的高,那么调用OSMboxPost()函数的进程B就要被抢占,os将会切换到新的task去执行

//如果没有调用OSMboxPost()函数的进程B优先级高,那么os不会切换,仍然继续执行进程B,OSMboxPost()正常返回

OS_Sched();

return (OS_ERR_NONE);

}

//没有任何一个task悬停在本event事件控制矩阵上

if (pevent->OSEventPtr != (void *)0) {

OS_EXIT_CRITICAL();

return (OS_ERR_MBOX_FULL);//邮箱已经满了

}

pevent->OSEventPtr = pmsg;//把该消息指针推到pevent->OSEventPtr中

OS_EXIT_CRITICAL();

return (OS_ERR_NONE);

}

//----------------------------------------------------------------------

//5.OS_EventTaskRdy()函数

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

//小于64个task时,快速计算

//最有优先权的task位于事件控制矩阵中的第y行的第x列

y = OSUnMapTbl[pevent->OSEventGrp];

bity = (INT8U)(1 << y);

x = OSUnMapTbl[pevent->OSEventTbl[y]];

bitx = (INT8U)(1 << x);

prio = (INT8U)((y << 3) + x);

#else

//对于256个task

//最有优先权的task位于事件控制矩阵中的第y行的第x列

//以下的操作原理具体参见《浅析uC/OS-II v2.85内核调度函数》

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);

//该task对应的prio优先级值

//ok,等待在event事件上的所有task中,只有在事件控制矩阵中的第y行的第x列task

//优先级最高、最值的成为此次事件的唤醒对象

#endif

//清除此task在event事件控制矩阵中的bit位

pevent->OSEventTbl[y] &= ~bitx;

if (pevent->OSEventTbl[y] == 0) {

pevent->OSEventGrp &= ~bity;

}

//通过prio优先级找到该prio唯一对应的task对应的ptcb进程上下文控制块

ptcb = OSTCBPrioTbl[prio];

ptcb->OSTCBDly = 0;//复原为正常

ptcb->OSTCBEventPtr = (OS_EVENT *)0;//现在本task不悬停在任何event事件上

#if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0)

ptcb->OSTCBMsg = pmsg;//传递消息指针

#else

pmsg = pmsg;

#endif

ptcb->OSTCBStatPend = pend_stat;//悬停状态值

ptcb->OSTCBStat &= ~msk;//该msk事件已经发生,清除task上下文控制块上的msk位,如:OS_STAT_MBOX

if (ptcb->OSTCBStat == OS_STAT_RDY) {

//如果当前task只是等待该事件,那么把该task放到就绪控制矩阵中,允许内核调度本task

OSRdyGrp |= bity;

OSRdyTbl[y] |= bitx;

}

return (prio);//返回本task对应的优先级值

}

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

PS:"所以从这里来看,os中的各个功能单元管理着自己的事情,就像面向对象的封装一样,"

"事件控制矩阵和就绪控制矩阵是各个对象独立自治的关键因素"

"其他对象,都努力说服自己相信别的对象是独立的、可信任的、安全的"[gliethttp]

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

可能会用到的工具/仪表
本站简介 | 意见建议 | 免责声明 | 版权声明 | 联系我们
CopyRight@2024-2039 嵌入式资源网
蜀ICP备2021025729号