队列结构的定义如下:
typedef struct QueueDefinition
{
signed portCHAR *pcHead; /*队列 头部指针*/
signed portCHAR *pcTail; /*尾部指针,比长度多一个字节*/
signed portCHAR *pcWriteTo; /*指向下一个可以写入的空闲空间*/
signed portCHAR *pcReadFrom; /*指向队列中最后一个已读的数据 */
xList xTasksWaitingToSend; /*等待发送的任务队列,按优先级排序 */
xList xTasksWaitingToReceive; /*等待读取的任务队列,按优先级排序*/
volatile unsigned portBASE_TYPE uxMessagesWaiting; /*队列中当前的条目数*/
unsigned portBASE_TYPE uxLength; /*队列总的条目个数 */
unsigned portBASE_TYPE uxItemSize; /*每个条目的大小*/
signed portBASE_TYPE xRxLock; /*队列接收锁标志 */
signed portBASE_TYPE xTxLock; /*队列发送锁标志 */
/*这两个标志的值可以为queueUNLOCKED (-1)、queueLOCKED_UNMODIFIED(0)、或正值,当为正直时表示锁的次数*/
} xQUEUE;
队列的创建
队列通过xQueueCreate函数来创建,此函数主要负责申请队列空间,初始化队列的各项的值。
xQueueHandle xQueueCreate( unsigned portBASE_TYPE uxQueueLength, unsigned portBASE_TYPE uxItemSize )
{
xQUEUE *pxNewQueue;
size_t xQueueSizeInBytes;
/* 为队列分配空间*/
if( uxQueueLength > ( unsigned portBASE_TYPE ) 0 )
{
pxNewQueue = ( xQUEUE * ) pvPortMalloc( sizeof( xQUEUE ) );
if( pxNewQueue != NULL )
{
/* Create the list of pointers to queue items. The queue is one byte
longer than asked for to make wrap checking easier/faster. */
xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ) + ( size_t ) 1; /*分配的空间比队列长度多一个字节,方便检测*/
pxNewQueue->pcHead = ( signed portCHAR * ) pvPortMalloc( xQueueSizeInBytes );
if( pxNewQueue->pcHead != NULL )
{
/* Initialise the queue members as described above where the
queue type is defined. */
pxNewQueue->pcTail = pxNewQueue->pcHead + ( uxQueueLength * uxItemSize );
pxNewQueue->uxMessagesWaiting = 0;
pxNewQueue->pcWriteTo = pxNewQueue->pcHead;
pxNewQueue->pcReadFrom = pxNewQueue->pcHead + ( ( uxQueueLength - 1 ) * uxItemSize ); /*指向的是最后一个已读的条目,故初始化为最后一项*/
pxNewQueue->uxLength = uxQueueLength;
pxNewQueue->uxItemSize = uxItemSize;
pxNewQueue->xRxLock = queueUNLOCKED;
pxNewQueue->xTxLock = queueUNLOCKED;
/* Likewise ensure the event queues start with the correct state. */
vListInitialise( &( pxNewQueue->xTasksWaitingToSend ) ); /*初始化两个等待队列*/
vListInitialise( &( pxNewQueue->xTasksWaitingToReceive ) );
traceQUEUE_CREATE( pxNewQueue ); /*用于调试跟踪*/
return pxNewQueue;
}
else
{
traceQUEUE_CREATE_FAILED();
vPortFree( pxNewQueue );
}
}
}
/* Will only reach here if we could not allocate enough memory or no memory
was required. */
return NULL;
}
队列发送
FreeRTOS有三个一般的队列发送函数,xQueueSend(),
xQueueSendToFront() 和 xQueueSendToBack()
,此三个函数不可以在中断中使用,它们都是通过宏的方式实现,其中xQueueSend()与xQueueSendToBack()的定义是一样的,只是
为与早期的版本兼容而保留下来。
此三个宏定义如下:
#define
xQueueSendToFront( xQueue, pvItemToQueue, xTicksToWait )
xQueueGenericSend( xQueue, pvItemToQueue, xTicksToWait,
queueSEND_TO_FRONT )
#define xQueueSendToBack( xQueue,
pvItemToQueue, xTicksToWait ) xQueueGenericSend( xQueue, pvItemToQueue,
xTicksToWait, queueSEND_TO_BACK )
#define xQueueSend( xQueue,
pvItemToQueue, xTicksToWait ) xQueueGenericSend( xQueue, pvItemToQueue,
xTicksToWait, queueSEND_TO_BACK )
三个宏在底层均是通过xQueueGenericSend函数来实现,通过传递给此函数的最后一个参数来区别三个宏的实现,下面分析xQueueGenericSend函数的实现方式
signed
portBASE_TYPE xQueueGenericSend( xQueueHandle pxQueue, const void *
const pvItemToQueue, portTickType xTicksToWait, portBASE_TYPE
xCopyPosition )
{
signed portBASE_TYPE xEntryTimeSet = pdFALSE;
xTimeOutType xTimeOut;
/* This function relaxes the coding standard somewhat to allow return
statements within the function itself. This is done in the interest
of execution time efficiency. */
for( ;; )
{
taskENTER_CRITICAL();
{
/* Is there room on the queue now? To be running we must be
the highest priority task wanting to access the queue. */
if( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) /*判断队列是否已满,如果还有空的空间则插入条目*/
{
traceQUEUE_SEND( pxQueue );
/*
prvCopyDataToQueue函数用于把条目数据复制到队列中
xCopyPosition为queueSEND_TO_FRONT时把条目数据复制到pcReadFrom指向的位置,即放到第一项
为queueSEND_TO_BACK时则复制到pcWriteTo指向的位置,即为最后一项
*/
prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );
/* If there was a task waiting for data to arrive on the
queue then unblock it now. */
/*如果有任务在等待则把任务从等待队列移出然后进行调度*/
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
{
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) == pdTRUE )
{
/* The unblocked task has a priority higher than
our own so yield immediately. Yes it is ok to do
this from within the critical section - the kernel
takes care of that. */
taskYIELD();
}
}
taskEXIT_CRITICAL();
return pdPASS;
}
else /*队列已满,如果xTicksToWait>0则把任务插入到等待队列,否则直接返回队列满信息*/
{
if( xTicksToWait == ( portTickType ) 0 ) /*不等待返回*/
{
/* The queue was full and no block time is specified (or
the block time has expired) so leave now. */
taskEXIT_CRITICAL();
traceQUEUE_SEND_FAILED( pxQueue );
return errQUEUE_FULL;
}
else if( xEntryTimeSet == pdFALSE )
{
/* The queue was full and a block time was specified so
configure the timeout structure. */
vTaskSetTimeOutState( &xTimeOut ); /*把xTimeOut初始化为当前的系统时间计数*/
xEntryTimeSet = pdTRUE;
}
}
}
taskEXIT_CRITICAL();
/* Interrupts and other tasks can send to and receive from the queue
now the critical section has been exited. */
vTaskSuspendAll();
prvLockQueue( pxQueue );
/* Update the timeout state to see if it has expired yet. */
/*检查超时是否已经到达,并且通过当前系统时间来校正xTicksToWait的值*/
if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )
{
if( prvIsQueueFull( pxQueue ) ) /*如果队列已满,则把当前任务插入到队列的等待列表中*/
{
traceBLOCKING_ON_QUEUE_SEND( pxQueue );
vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait );
/* Unlocking the queue means queue events can effect the
event list. It is possible that interrupts occurring now
remove this task from the event list again - but as the
scheduler is suspended the task will go onto the pending
ready last instead of the actual ready list. */
prvUnlockQueue( pxQueue );
/* Resuming the scheduler will move tasks from the pending
ready list into the ready list - so it is feasible that this
task is already in a ready list before it yields - in which
case the yield will not cause a context switch unless there
is also a higher priority task in the pending ready list. */
if( !xTaskResumeAll() )
{
taskYIELD();
}
}
else /*队列未满,重试插入*/
{
/* Try again. */
prvUnlockQueue( pxQueue );
( void ) xTaskResumeAll();
}
}
else /*已经超时,不等待返回*/
{
/* The timeout has expired. */
prvUnlockQueue( pxQueue );
( void ) xTaskResumeAll();
traceQUEUE_SEND_FAILED( pxQueue );
return errQUEUE_FULL;
}
}
}
与队列的发送对应,一般的队列接收函数也是由宏来实现,底层用一个函数来实现,队列接收函数有下面两个:
#define xQueuePeek( xQueue, pvBuffer, xTicksToWait ) xQueueGenericReceive( xQueue, pvBuffer, xTicksToWait, pdTRUE )
#define xQueueReceive( xQueue, pvBuffer, xTicksToWait ) xQueueGenericReceive( xQueue, pvBuffer, xTicksToWait, pdFALSE )
xQueuePeek与xQueueReceive的区别在于前者只接收而不删除条目,后者在成功接收后会删除原来的条目,两者均通过xQueueGenericReceive函数来实现,不能在中断中调用。
下面分析xQueueGenericReceive的实现方式:
文章评论(0条评论)
登录后参与讨论