FreeRTOS--队列集
示例源码基于FreeRTOS V9.0.0
队列集
1. 概述
队列集的本质也是队列,只不过里面存放的是“队列句柄”。
当任务需要及时读取多个队列时,可以使用队列集。它类似于posix的多路复用思想。可以将想要监听消息的队列放入队列集中,当其中有队列有数据达到时,队列集的接口会返回可读的队列句柄,用户获得句柄后,就可以从队列中读取数据。
2. 接口API
2.1 创建队列集
#if( ( configUSE_QUEUE_SETS == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQueueLength )
{
QueueSetHandle_t pxQueue;
pxQueue = xQueueGenericCreate( uxEventQueueLength, sizeof( Queue_t * ), queueQUEUE_TYPE_SET );
return pxQueue;
}
#endif /* configUSE_QUEUE_SETS */
创建队列集使用xQueueCreateSet接口,仅需传入队列深度参数。使用队列集,需要开启宏configUSE_QUEUE_SETS和configSUPPORT_DYNAMIC_ALLOCATION;
队列集创建实际调用的是xQueueGenericCreate接口,它本质也是个队列,其数据项是队列句柄。
2.2 添加队列到队列集
#if ( configUSE_QUEUE_SETS == 1 )
BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet )
{
BaseType_t xReturn;
taskENTER_CRITICAL();
{
if( ( ( Queue_t * ) xQueueOrSemaphore )->pxQueueSetContainer != NULL )
{
/* Cannot add a queue/semaphore to more than one queue set. */
xReturn = pdFAIL;
}
else if( ( ( Queue_t * ) xQueueOrSemaphore )->uxMessagesWaiting != ( UBaseType_t ) 0 )
{
/* Cannot add a queue/semaphore to a queue set if there are already
items in the queue/semaphore. */
xReturn = pdFAIL;
}
else
{
( ( Queue_t * ) xQueueOrSemaphore )->pxQueueSetContainer = xQueueSet;
xReturn = pdPASS;
}
}
taskEXIT_CRITICAL();
return xReturn;
}
#endif /* configUSE_QUEUE_SETS */
添加队列到队列集,需要在临界区内操作。同时,待添加的队列,其原先必须不属于任何队列集(pxQueueSetContainer需指向空),且队列需为空。
添加队列到队列集,本质操作是把队列的pxQueueSetContainer指针指向队列集即可。
2.3 删除队列集的队列
#if ( configUSE_QUEUE_SETS == 1 )
BaseType_t xQueueRemoveFromSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet )
{
BaseType_t xReturn;
Queue_t * const pxQueueOrSemaphore = ( Queue_t * ) xQueueOrSemaphore;
if( pxQueueOrSemaphore->pxQueueSetContainer != xQueueSet )
{
/* The queue was not a member of the set. */
xReturn = pdFAIL;
}
else if( pxQueueOrSemaphore->uxMessagesWaiting != ( UBaseType_t ) 0 )
{
/* It is dangerous to remove a queue from a set when the queue is
not empty because the queue set will still hold pending events for
the queue. */
xReturn = pdFAIL;
}
else
{
taskENTER_CRITICAL();
{
/* The queue is no longer contained in the set. */
pxQueueOrSemaphore->pxQueueSetContainer = NULL;
}
taskEXIT_CRITICAL();
xReturn = pdPASS;
}
return xReturn;
} /*lint !e818 xQueueSet could not be declared as pointing to const as it is a typedef. */
#endif /* configUSE_QUEUE_SETS */
待删除的队列,其队列集需存在,且只有空队列才允许从队列集中删除。
删除队列集的队列,需要在临界区内操作。删除的本质操作是把队列的pxQueueSetContainer指针置空。
2.4 等待队列可读
#if ( configUSE_QUEUE_SETS == 1 )
QueueSetMemberHandle_t xQueueSelectFromSet( QueueSetHandle_t xQueueSet, TickType_t const xTicksToWait )
{
QueueSetMemberHandle_t xReturn = NULL;
( void ) xQueueGenericReceive( ( QueueHandle_t ) xQueueSet, &xReturn, xTicksToWait, pdFALSE ); /*lint !e961 Casting from one typedef to another is not redundant. */
return xReturn;
}
#endif /* configUSE_QUEUE_SETS */
/*-----------------------------------------------------------*/
#if ( configUSE_QUEUE_SETS == 1 )
QueueSetMemberHandle_t xQueueSelectFromSetFromISR( QueueSetHandle_t xQueueSet )
{
QueueSetMemberHandle_t xReturn = NULL;
( void ) xQueueReceiveFromISR( ( QueueHandle_t ) xQueueSet, &xReturn, NULL ); /*lint !e961 Casting from one typedef to another is not redundant. */
return xReturn;
}
#endif /* configUSE_QUEUE_SETS */
FreeRTOS提供了类似posix select操作的xQueueSelectFromSet接口,它等待队列集可读,并返回从队列集读取的数据(可读队列的句柄)。
队列集内的队列句柄,均是可读的队列。当有任务或中断往队列写数据时,队列非空可读,如果开启了configUSE_QUEUE_SETS,会同时把队列句柄置入队列集中(见xQueueGenericSend)。
xQueueSelectFromSet对应的中断版本是xQueueSelectFromSetFromISR。
关于xQueueGenericReceive和xQueueReceiveFromISR以及xQueueGenericSend、xQueueGenericSendFromISR,见 队列
2.5 通知队列可读
#if ( configUSE_QUEUE_SETS == 1 )
static BaseType_t prvNotifyQueueSetContainer( const Queue_t * const pxQueue, const BaseType_t xCopyPosition )
{
Queue_t *pxQueueSetContainer = pxQueue->pxQueueSetContainer;
BaseType_t xReturn = pdFALSE;
/* This function must be called form a critical section. */
configASSERT( pxQueueSetContainer );
configASSERT( pxQueueSetContainer->uxMessagesWaiting < pxQueueSetContainer->uxLength );
if( pxQueueSetContainer->uxMessagesWaiting < pxQueueSetContainer->uxLength )
{
const int8_t cTxLock = pxQueueSetContainer->cTxLock;
traceQUEUE_SEND( pxQueueSetContainer );
/* The data copied is the handle of the queue that contains data. */
xReturn = prvCopyDataToQueue( pxQueueSetContainer, &pxQueue, xCopyPosition );
if( cTxLock == queueUNLOCKED )
{
if( listLIST_IS_EMPTY( &( pxQueueSetContainer->xTasksWaitingToReceive ) ) == pdFALSE )
{
if( xTaskRemoveFromEventList( &( pxQueueSetContainer->xTasksWaitingToReceive ) ) != pdFALSE )
{
/* The task waiting has a higher priority. */
xReturn = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
pxQueueSetContainer->cTxLock = ( int8_t ) ( cTxLock + 1 );
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
return xReturn;
}
#endif /* configUSE_QUEUE_SETS */
prvNotifyQueueSetContainer是静态接口,定义在queue.c文件,仅对内使用,不对外开放;
prvNotifyQueueSetContainer在xQueueGenericSend、xQueueGenericSendFromISR、xQueueGiveFromISR、prvUnlockQueue内调用,见 队列
prvNotifyQueueSetContainer支持任务和中断内使用。
通知队列可读的本质操作是将可读队列自身的句柄拷贝入队列集中。
当队列集满则接口返回失败;
当队列cTxLock非锁定时,判断是否有任务阻塞等待从队列集接收数据(等待可读队列),若有则将最高优先级任务从阻塞列表移除,移入就绪等待列表,如果任务的优先级高于当前任务,返回pdTRUE表示需要进行任务切换;
当队列cTxLock锁定时,累加cTxLock计数,待调用prvUnlockQueue时再操作阻塞列表和任务切换;
因为prvNotifyQueueSetContainer可能在中断内使用,所以函数内部不直接唤醒切换任务,而是根据返回值标记,返回pdTRUE表示需要任务切换;
本文来自博客园,作者:流翎,转载请注明原文链接:https://www.cnblogs.com/hjx168/p/17872018.html