UCOS-II 队列的建立过程

本文主要是对UCOS-II中队列的建立过程进行分析及总结。

第一步:OS操作系统会在初始化函数OSInit中对事件进行初始化。

第二步:OS操作系统会在初始化函数OSInit中对队列进行初始化。

第一步和第二步初始化的结果如下图所示,图中只是两个结构体的架构图,其中有些细节需要指出:

1)在初始化完成后,事件结构体中OSEventType为未使用类型。

2)在初始化完成后,事件结构体中OSEventPtr指向下一个事件结构体(该指针代表事件的类型,这里指向下一个指针只是为了临时放置,以便于得到下一个空闲的事件结构体,注意该指针的类型为空指针)

UCOS-II 队列的建立过程_第1张图片

第三步:系统在需要的地方创建消息队列,函数如下所示,通过OSQCreate函数来创建队列。

队列如何工作?

在创建完成队列之后,我们下面来看下,消息队列是如何工作的。由下面的代码可以看出,在OS系统中,建立的是一个环形队列。

//数据存入队列
  if (pq->OSQEntries >= pq->OSQSize)
  {   
      OS_EXIT_CRITICAL();
      return (OS_ERR_Q_FULL);
  }
 *pq->OSQIn++ = pmsg;               /* Insert message into queue   */
  pq->OSQEntries++;               /* Update the nbr of entries in the queue  */
  if (pq->OSQIn == pq->OSQEnd) 
  {                     
      pq->OSQIn = pq->OSQStart;
  }
//数据读出队列
if (pq->OSQEntries > 0u)                   
{   
    pmsg = *pq->OSQOut++;    /* Yes, extract oldest message from the queue   */
    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;
    }
}
                        

为什么在中断中能使用OSQPost函数,但是不能使用OSQPend函数?

首先我们知道,当执行任务调度时,实际上是触发了PendSV中断,如果在任务中执行任务调度,那么ARM将直接跳转到PendSV中断去执行。但是如果在中断中触发了PendSV中断,由于PendSV中断的中断优先级设置的较低,所以系统会等所有的中断执行完了后,再去执行PendSV中断。

那么为什么不能再中断中调用Pend函数呢?我们先看下Pend函数的源码,源码如下所示,注意观察到如果在执行任务调度前,函数没有被返回的话,那么系统将执行任务调度。

如果是在中断中执行任务调度,那么系统不会立即跳转到最高优先级的任务,而是等待函数执行完了再去跳转,那么在该函数中,执行了任务调度后所执行的函数就具有具体意义了。

void  *OSQPend (OS_EVENT  *pevent,
                INT32U     timeout,
                INT8U     *perr)
{
    void      *pmsg;
    OS_Q      *pq;
#if OS_CRITICAL_METHOD == 3u                     /* Allocate storage for CPU status register           */
    OS_CPU_SR  cpu_sr = 0u;
#endif

#ifdef OS_SAFETY_CRITICAL
    if (perr == (INT8U *)0)
    {
        OS_SAFETY_CRITICAL_EXCEPTION();
    }
#endif

#if OS_ARG_CHK_EN > 0u
    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 > 0u)
    {   /* See if called from ISR ...                         */
        *perr = OS_ERR_PEND_ISR;                 /* ... can't PEND from an ISR                         */
        return ((void *)0);
    }
    if (OSLockNesting > 0u)
    {   /* 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        */
    if (pq->OSQEntries > 0u)                   
    {                 */
        pmsg = *pq->OSQOut++;                   
        pq->OSQEntries--;                        
        if (pq->OSQOut == pq->OSQEnd)  					 
        {
            pq->OSQOut = pq->OSQStart;
        }
        OS_EXIT_CRITICAL();
        *perr = OS_ERR_NONE;
        return (pmsg);                           
    }
    OSTCBCur->OSTCBStat     |= OS_STAT_Q;        
    OSTCBCur->OSTCBStatPend  = OS_STAT_PEND_OK;
    OSTCBCur->OSTCBDly       = timeout;          
    OS_EventTaskWait(pevent);                   
    OS_EXIT_CRITICAL();
    OS_Sched();                                 
    OS_ENTER_CRITICAL();
    switch (OSTCBCur->OSTCBStatPend)
    {   /* See if we timed-out or aborted                */
    case OS_STAT_PEND_OK:                         
        pmsg =  OSTCBCur->OSTCBMsg;
        *perr =  OS_ERR_NONE;
        break;
    case OS_STAT_PEND_ABORT:
        pmsg = (void *)0;
        *perr =  OS_ERR_PEND_ABORT;               
        break;
    case OS_STAT_PEND_TO:
    default:
        OS_EventTaskRemove(OSTCBCur, pevent);
        pmsg = (void *)0;
        *perr =  OS_ERR_TIMEOUT;                  
        break;
    }
    OSTCBCur->OSTCBStat          =  OS_STAT_RDY;      
    OSTCBCur->OSTCBStatPend      =  OS_STAT_PEND_OK;  
    OSTCBCur->OSTCBEventPtr      = (OS_EVENT  *)0;    
#if (OS_EVENT_MULTI_EN > 0u)
    OSTCBCur->OSTCBEventMultiPtr = (OS_EVENT **)0;
#endif
    OSTCBCur->OSTCBMsg           = (void      *)0;    
    OS_EXIT_CRITICAL();
    return (pmsg);                                    
}

 

 

附录:重要结构体的定义如下所示。

//函数调用:
can1_recv_unit.q_Can_Msg = OSQCreate(&(can1_recv_unit.MsgGrp[0]), CAN1_MSG_GRP_NUM); 
//其中结构体的定义如下:   
//CAN1_MSG_GRP_NUM == 30
typedef struct
{
    OS_EVENT *q_Can_Msg;                
    void  *MsgGrp[CAN1_MSG_GRP_NUM];    
    can_message_t   storage[CAN1_MSG_GRP_NUM];  
    u8  mbox_windex;         
    can_message_t *prmsg;    
}can1_recv_unit_t;


typedef struct os_event 
{
   INT8U    OSEventType;   /* Type of event control block (see OS_EVENT_TYPE_xxxx)    */
   void    *OSEventPtr;    /* Pointer to message or queue structure                   */
   INT16U   OSEventCnt;    /* Semaphore Count (not used if other EVENT type)          */
   OS_PRIO  OSEventGrp;    /* Group corresponding to tasks waiting for event to occur */
   OS_PRIO  OSEventTbl[OS_EVENT_TBL_SIZE];/* List of tasks waiting for event to occur */
} OS_EVENT;

typedef struct os_q 
{                                                        
    struct 	os_q   *OSQPtr;  // Link to next queue control block in listof free blocks     
    void         **OSQStart;            // Pointer to start of queue data               
    void         **OSQEnd;              // Pointer to end   of queue data                       
    void         **OSQIn;   // Pointer to where next message will be inserted  in   the Q  
    void         **OSQOut;  // Pointer to where next message will be extracted from the Q  
    INT16U         OSQSize;             //Size of queue (maximum number of entries)                                                       
    INT16U         OSQEntries;          /* Current number of entries in the queue   */
} OS_Q;

 

你可能感兴趣的:(UCOS-II 队列的建立过程)