现在的位置: 首页 > 综合 > 正文

uC/OS-II V2.86 发送和等待一个队列消息的工作原理

2013年08月02日 ⁄ 综合 ⁄ 共 8263字 ⁄ 字号 评论关闭

一、向消息队列发送一则消息(FIFO OSQPost())

 

/*

*********************************************************************************************************

*                                        POST MESSAGE TO A QUEUE

*

* Description: This function sends a message to a queue

*

* Arguments  : pevent        is a pointer to the event control block associated with the desired queue

*

*                     pmsg          is a pointer to the message to send.

*

* Returns    : OS_ERR_NONE           The call was successful and the message was sent

*                  OS_ERR_Q_FULL         If the queue cannot accept any more messages because it is full.

*                  OS_ERR_EVENT_TYPE     If you didn't pass a pointer to a queue.

*                  OS_ERR_PEVENT_NULL    If 'pevent' is a NULL pointer

*

* Note(s)    : As of V2.60, this function allows you to send NULL pointer messages.

*********************************************************************************************************

*/

 

#if OS_Q_POST_EN > 0

INT8U  OSQPost (OS_EVENT *pevent, void *pmsg)    /* 实际上是:进行消息入队操作   */

{

    OS_Q      *pq;

#if OS_CRITICAL_METHOD == 3                            /* Allocate storage for CPU status register     */

    OS_CPU_SR  cpu_sr = 0;

#endif

 

#if OS_ARG_CHK_EN > 0

    if (pevent == (OS_EVENT *)0) {                     /* Validate 'pevent'    空指针?                        */

        return (OS_ERR_PEVENT_NULL);

    }

#endif

    if (pevent->OSEventType != OS_EVENT_TYPE_Q) {      /* Validate event block type    发送的消息为空指针?                */

        return (OS_ERR_EVENT_TYPE);

    }

    OS_ENTER_CRITICAL();

    if (pevent->OSEventGrp != 0) {                       /* See if any task pending on queue   是否有任务在等待该队列中的消息?     */                                                  

                                                              /* Ready highest priority task waiting on event */

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

                            /* 从等待列表中取出最高优先级的任务,并将它置于就绪态 */

        OS_EXIT_CRITICAL();

        OS_Sched();                                        /* Find highest priority task ready to run 

                             任务调度,若上面进入就绪态的任务是就绪态任务中优先级最高的任务,而且OSPost()函数是被

                             任务级调用的,就执行任务切换,运行最高优先级任务,否则, OS_Sched()函数返回,

                             让调用本函数的任务继续执行     */

        return (OS_ERR_NONE);

    }

    pq = (OS_Q *)pevent->OSEventPt                  /* Point to queue control block   从事件控制块的数据成员 OSEventPtr 中取得队列控制块的地址 */

    if (pq->OSQEntries >= pq->OSQSize) {           /* Make sure queue is not full    若队列控制块中当前消息个数OSQEntries 已达队列容OSQSize                                                                          /* 量,表明队列已满,新的消息无法入队,操作失败 */

        OS_EXIT_CRITICAL();

        return (OS_ERR_Q_FULL);

    }

    *pq->OSQIn++ = pmsg;                               /* Insert message into queue   否则(队列未满) 将消息插入队列,然后调整入队指针OSQIn */

    pq->OSQEntries++;                                      /* Update the nbr of entries in the queue  调整当前有效消息个数计数器     */

    if (pq->OSQIn == pq->OSQEnd) {                  /* Wrap IN ptr if we are at end of queue  调整环形队列的指针,避免超出指针数组的范围 */

        pq->OSQIn = pq->OSQStart;

    }

    OS_EXIT_CRITICAL();

    return (OS_ERR_NONE);

}

#endif

二、等待消息队列中的消息(OSQPend())
/*
*********************************************************************************************************
*                                     PEND ON A QUEUE FOR A MESSAGE
*
* Description: This function waits for a message to be sent to a queue
*
* Arguments  : pevent        is a pointer to the event control block associated with the desired queue
*
*                     timeout       is an optional timeout period (in clock ticks).  If non-zero, your task will
*                            wait for a message to arrive at the queue up to the amount of time
*                            specified by this argument.  If you specify 0, however, your task will wait
*                            forever at the specified queue or, until a message arrives.
*
*                     perr          is a pointer to where an error message will be deposited.  Possible error
*                            messages are:
*
*                            OS_ERR_NONE         The call was successful and your task received a
*                                                message.
*                            OS_ERR_TIMEOUT      A message was not received within the specified 'timeout'.
*                            OS_ERR_PEND_ABORT   The wait on the queue was aborted.
*                            OS_ERR_EVENT_TYPE   You didn't pass a pointer to a queue
*                            OS_ERR_PEVENT_NULL  If 'pevent' is a NULL pointer
*                            OS_ERR_PEND_ISR     If you called this function from an ISR and the result
*                                                would lead to a suspension.
*                            OS_ERR_PEND_LOCKED  If you called this function with the scheduler is locked
*
* Returns    : != (void *)0  is a pointer to the message received
*                 == (void *)0  if you received a NULL pointer message or,
*                            if no message was received or,
*                            if 'pevent' is a NULL pointer or,
*                            if you didn't pass a pointer to a queue.
*
* Note(s)    : As of V2.60, this function allows you to receive NULL pointer messages.
*********************************************************************************************************
*/
void  *OSQPend (OS_EVENT *pevent, INT16U timeout, INT8U *perr)      /* 实际上是:进行消息出队操作   */
{
    void      *pmsg;
    OS_Q      *pq;
#if OS_CRITICAL_METHOD == 3                      /* Allocate storage for CPU status register           */
    OS_CPU_SR  cpu_sr = 0;
#endif
#if OS_ARG_CHK_EN > 0
    if (perr == (INT8U *)0) {                         /* Validate 'perr'                                    */
        return ((void *)0);
    }
    if (pevent == (OS_EVENT *)0) {                 /* Validate 'pevent'                                  */
        *perr = OS_ERR_PEVENT_NULL;
        return ((void *)0);
    }
#endif
    if (pevent->OSEventType != OS_EVENT_TYPE_Q) {/* Validate event block type                          */
        *perr = OS_ERR_EVENT_TYPE;
        return ((void *)0);
    }
    if (OSIntNesting > 0) {                            /* See if called from ISR ...                         */
        *perr = OS_ERR_PEND_ISR;                       /* ... can't PEND from an ISR                         */
        return ((void *)0);
    }
    if (OSLockNesting > 0) {                         /* See if called with scheduler locked ...            */
        *perr = OS_ERR_PEND_LOCKED;               /* ... can't PEND when locked                         */
        return ((void *)0);
    }
    OS_ENTER_CRITICAL();
    pq = (OS_Q *)pevent->OSEventPtr;             /* Point at queue control block   从事件控制块的数据成员 OSEventPtr 中取得队列控制块的地址 */
    if (pq->OSQEntries > 0) {                            /* See if any messages in the queue  有消息可用?                 */
        pmsg = *pq->OSQOut++;                       /* Yes, extract oldest message from the queue    取出消息,然后调整入队指针OSQout  */
        pq->OSQEntries--;                                  /* Update the number of entries in the queue   调整当前有效消息个数计数器       */
        if (pq->OSQOut == pq->OSQEnd) {          /* Wrap OUT pointer if we are at the end of the queue  是否越界? */
            pq->OSQOut = pq->OSQStart;
        }
        OS_EXIT_CRITICAL();
        *perr = OS_ERR_NONE;
        return (pmsg);                                /* Return message received   返回消息指针,该指针指向消息的存放地址                         */
    }
 /* 队列中无消息时的处理  */
    OSTCBCur->OSTCBStat     |= OS_STAT_Q;              /* Task will have to pend for a message to be posted  */
    OSTCBCur->OSTCBStatPend  = OS_STAT_PEND_OK;
                                  /* 以此表明等待队列消息的任务被挂起 */

    OSTCBCur->OSTCBDly       = timeout;                  /* Load timeout into TCB   将超时值置入当前任务控制块  */

    OS_EventTaskWait(pevent);                          /* Suspend task until event or timeout occurs   真正将等待队列消息的任务进入睡眠状态 */ 
                        

    OS_EXIT_CRITICAL();

    OS_Sched();                                  /* Find next highest priority task ready to run      
                        由于调用本函数的任务不再在就绪态,调度器会安排下一个进入就绪态的优先级最高的任务执行 
                      在用户程序中,调用本函数的任务并不知道在消息没有来到之前,自己已经被挂起,
                        当队列接收到一则消息(或等待超时)后,本函数就会在调用 OS_Sched() 之后恢复执行 */
    OS_ENTER_CRITICAL();
                      /* 当OS_Sched() 返回后,本函数会检查OSPost()是否已经将消息放在任务的任务控制块TCB中   */                   
    switch (OSTCBCur->OSTCBStatPend) {                  /* See if we timed-out or aborted                */
        case OS_STAT_PEND_OK:                                 /* Extract message from TCB (Put there by QPost)  等到消息 */
             pmsg =  OSTCBCur->OSTCBMsg;          /* 取出消息 */
            *perr =  OS_ERR_NONE;
             break;
        case OS_STAT_PEND_ABORT:             /* 主动放弃等待 */
             pmsg = (void *)0;
            *perr =  OS_ERR_PEND_ABORT;                  /* Indicate that we aborted                      */
             break;
        case OS_STAT_PEND_TO:                /* 等待超时 */
        default:
             OS_EventTaskRemove(OSTCBCur, pevent);    /* 把调用本函数的任务从等待列表中除去 */
             pmsg = (void *)0;
            *perr =  OS_ERR_TIMEOUT;                       /* Indicate that we didn't get event within TO   */
             break;
    }
    OSTCBCur->OSTCBStat          =  OS_STAT_RDY;          /* Set   task  status to ready                   */
    OSTCBCur->OSTCBStatPend      =  OS_STAT_PEND_OK;     /* Clear pend  status                            */
    OSTCBCur->OSTCBEventPtr      = (OS_EVENT  *)0;         /* Clear event pointers                          */
#if (OS_EVENT_MULTI_EN > 0)
    OSTCBCur->OSTCBEventMultiPtr = (OS_EVENT **)0;
#endif
    OSTCBCur->OSTCBMsg           = (void      *)0;             /* Clear  received message                       */
    OS_EXIT_CRITICAL();
    return (pmsg);                                                /* Return received message                       */
}

 

抱歉!评论已关闭.