本文主要是对UCOS-II中队列的建立过程进行分析及总结。
第一步:OS操作系统会在初始化函数OSInit中对事件进行初始化。
第二步:OS操作系统会在初始化函数OSInit中对队列进行初始化。
第一步和第二步初始化的结果如下图所示,图中只是两个结构体的架构图,其中有些细节需要指出:
1)在初始化完成后,事件结构体中OSEventType为未使用类型。
2)在初始化完成后,事件结构体中OSEventPtr指向下一个事件结构体(该指针代表事件的类型,这里指向下一个指针只是为了临时放置,以便于得到下一个空闲的事件结构体,注意该指针的类型为空指针)
第三步:系统在需要的地方创建消息队列,函数如下所示,通过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;