文章目录
- 基础知识
- 队列结构体
- 创建队列 xQueueCreate 解析
- 队列读数据 xQueueReceive 解析
- 队列写数据 xQueueGenericSend 解析
- 互斥访问与回环队列 内部实现框图
基础知识
[FreeRTOS 基础知识] 互斥访问与回环队列 概念
队列结构体
typedef struct QueueDefinition
{
int8_t *pcHead; /*< Points to the beginning of the queue storage area. */
int8_t *pcTail; /*< Points to the byte at the end of the queue storage area. Once more byte is allocated than necessary to store the queue items, this is used as a marker. */
int8_t *pcWriteTo; /*< Points to the free next place in the storage area. */
union /* Use of a union is an exception to the coding standard to ensure two mutually exclusive structure members don't appear simultaneously (wasting RAM). */
{
int8_t *pcReadFrom; /*< Points to the last place that a queued item was read from when the structure is used as a queue. */
UBaseType_t uxRecursiveCallCount;/*< Maintains a count of the number of times a recursive mutex has been recursively 'taken' when the structure is used as a mutex. */
} u;
List_t xTasksWaitingToSend; /*< List of tasks that are blocked waiting to post onto this queue. Stored in priority order. */
List_t xTasksWaitingToReceive; /*< List of tasks that are blocked waiting to read from this queue. Stored in priority order. */
volatile UBaseType_t uxMessagesWaiting;/*< The number of items currently in the queue. */
UBaseType_t uxLength; /*< The length of the queue defined as the number of items it will hold, not the number of bytes. */
UBaseType_t uxItemSize; /*< The size of each items that the queue will hold. */
volatile int8_t cRxLock; /*< Stores the number of items received from the queue (removed from the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */
volatile int8_t cTxLock; /*< Stores the number of items transmitted to the queue (added to the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */
#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the memory used by the queue was statically allocated to ensure no attempt is made to free the memory. */
#endif
#if ( configUSE_QUEUE_SETS == 1 )
struct QueueDefinition *pxQueueSetContainer;
#endif
#if ( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxQueueNumber;
uint8_t ucQueueType;
#endif
} xQUEUE;
在结构体中主要成员介绍:
List_t xTasksWaitingToSend; 等待发布到此队列的阻塞任务列表。按优先级顺序存储。
List_t xTasksWaitingToReceive; 阻塞等待从此队列读取的任务列表。按优先级顺序存储。
int8_t *pcWriteTo; 指向存储区的下一个空闲位置。
int8_t *pcReadFrom; 指向将该结构用作队列时最后从其中读取队列项的位置。
创建队列 xQueueCreate 解析
xQueueCreate( uxQueueLength, uxItemSize )
-> xQueueGenericCreate
-> xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize );
-> pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes );
uxQueueLength 指 队列长度
uxItemSize 指 每段长度的大小
总长度 = uxQueueLength * uxItemSize + Queue_t结构体大小
在内存中分配如下
队列读数据 xQueueReceive 解析
BaseType_t xQueueReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait )
->
for( ;; )
{
taskENTER_CRITICAL(); // 关中断 portDISABLE_INTERRUPTS();
{
const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting; // 当前队列中值的总数量
if( uxMessagesWaiting > ( UBaseType_t ) 0 ) // 队列中有数值
{
prvCopyDataFromQueue( pxQueue, pvBuffer ); // 将队列中pcReadFrom指向的值取出放到pvBuffer中,pcReadFrom指向下一个位置
traceQUEUE_RECEIVE( pxQueue );
pxQueue->uxMessagesWaiting = uxMessagesWaiting - ( UBaseType_t ) 1; // 当前队列中值的总数量 -1
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) // xTasksWaitingToSend链表中有没等待要写的任务
{
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) // 1、将要写的任务从xTasksWaitingToSend移除;2、将要写的任务从DelayList移动到ReadyList
{
queueYIELD_IF_USING_PREEMPTION(); // 当前任务让出CPU
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
taskEXIT_CRITICAL(); // 打开中断
return pdPASS; // 返回成功
}
else
{
if( xTicksToWait == ( TickType_t ) 0 )
{
// 队列是空的,没有指定阻塞时间(或者阻塞时间已经过期),所以现在离开。
taskEXIT_CRITICAL(); // 打开中断
traceQUEUE_RECEIVE_FAILED( pxQueue );
return errQUEUE_EMPTY; // 返回队列为空
}
else if( xEntryTimeSet == pdFALSE )
{
// 队列为空,阻塞时间被指定,所以配置超时结构。
vTaskInternalSetTimeOutState( &xTimeOut );
xEntryTimeSet = pdTRUE;
}
else
{
/* Entry time was already set. */
mtCOVERAGE_TEST_MARKER();
}
}
}
taskEXIT_CRITICAL();
vTaskSuspendAll();
prvLockQueue( pxQueue );
if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )
{
if( prvIsQueueEmpty( pxQueue ) != pdFALSE )
{
traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue );
// 1、当前的任务加入到队列的xTasksWaitingToReceive链表中;
// 2、当前的任务从ReadyList移动到DelayList
vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait );
prvUnlockQueue( pxQueue );
if( xTaskResumeAll() == pdFALSE )
{
portYIELD_WITHIN_API();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
prvUnlockQueue( pxQueue );
( void ) xTaskResumeAll();
}
}
else
{
prvUnlockQueue( pxQueue );
( void ) xTaskResumeAll();
if( prvIsQueueEmpty( pxQueue ) != pdFALSE )
{
traceQUEUE_RECEIVE_FAILED( pxQueue );
return errQUEUE_EMPTY;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
队列写数据 xQueueGenericSend 解析
BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition )
->
for( ;; )
{
taskENTER_CRITICAL(); // 关中断 portDISABLE_INTERRUPTS();
{
if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) ) // 判断是否有空间写数据
{
// 有空间写数据
traceQUEUE_SEND( pxQueue );
xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );
// 写数据
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) // 判断xTasksWaitingToReceive队列里是否有等待的任务
{
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) // 1、将要写的任务从xTasksWaitingToReceive移除;2、将要写的任务从DelayList移动到ReadyList
{
queueYIELD_IF_USING_PREEMPTION(); //让出CPU使用权
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else if( xYieldRequired != pdFALSE )
{
queueYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
taskEXIT_CRITICAL(); // 开中断
return pdPASS; // 返回成功
}
else
{
if( xTicksToWait == ( TickType_t ) 0 )
{
taskEXIT_CRITICAL();
traceQUEUE_SEND_FAILED( pxQueue );
return errQUEUE_FULL; // 返回队列已满
}
else if( xEntryTimeSet == pdFALSE )
{
vTaskInternalSetTimeOutState( &xTimeOut ); // 阻塞时间被指定,所以配置超时结构。
xEntryTimeSet = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
taskEXIT_CRITICAL(); // 开中断
vTaskSuspendAll();
prvLockQueue( pxQueue );
if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )
{
if( prvIsQueueFull( pxQueue ) != pdFALSE )
{
traceBLOCKING_ON_QUEUE_SEND( pxQueue );
// 1、当前的任务加入到队列的xTasksWaitingToSend链表中;
// 2、当前的任务从ReadyList移动到DelayList
vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait );
prvUnlockQueue( pxQueue );
if( xTaskResumeAll() == pdFALSE )
{
portYIELD_WITHIN_API();
}
}
else
{
prvUnlockQueue( pxQueue );
( void ) xTaskResumeAll();
}
}
else
{
prvUnlockQueue( pxQueue );
( void ) xTaskResumeAll();
traceQUEUE_SEND_FAILED( pxQueue );
return errQUEUE_FULL;
}
}