FreeRTOS学习仓库地址:https://gitee.com/killerp/free-rtos_-study
消息队列是RTOS的基础数据结构,用于任务之间、任务与中断之间进行数据传递。
没有使用消息队列时,若想要在两个任务之间进行数据传递,那么必须通过全局变量来传递,而在多任务系统中,访问全局变量往往需要用户对资源进行保护,这样就使得编程变得麻烦。
消息队列封装了对共享数据的访问保护,同时还加入了阻塞等待机制。使用户编程时不用去考虑复杂的并发访问。
消息队列结构体的定义如下:
消息队列可理解为一个环形的队列,通过pcHead和pcTail将队列的首位连接起来。
pcWriteTo和pcReadFrom分别是队列的首部和尾部(若采用默认的FIFO)
队列中每个消息的大小是固定的,一个队列可容纳若干个消息,由消息大小和数量决定队列所占内存的大小
队列有两个链表,分别用于存放因发送/接收消息而进入阻塞的任务。
队列支持锁定,当队列锁定时,中断函数不能修改以上链表,实现对数据的保护。
/*
* 消息队列 入队使用的是内存复制
*/
typedef struct QueueDefinition /* The old naming convention is used to prevent breaking kernel aware debuggers. */
{
int8_t * pcHead; /*< 指向队列内存起始地址. */
int8_t * pcWriteTo; /*< 指向队列下一个空闲地址. */
union
{
QueuePointers_t xQueue; //TODO 当结构体作为队列时
SemaphoreData_t xSemaphore; //当作为信号量时
} u;
List_t xTasksWaitingToSend; //链表:保存那些因发送信号量而进入阻塞的任务 (按优先级排序)
List_t xTasksWaitingToReceive; //链表:保存那些因接收信号量而进入阻塞的任务 (按优先级排序)
volatile UBaseType_t uxMessagesWaiting; //队列中的消息数量
UBaseType_t uxLength; //队列的最大消息数量
UBaseType_t uxItemSize; //一个消息的大小
//队列上锁
volatile int8_t cRxLock; //队列上锁时,任务从队列接收消息的数量
volatile int8_t cTxLock; //队列上锁时,任务向队列发送的消息数量
uint8_t ucStaticallyAllocated; //标记队列的内存分配方式
} xQUEUE;
消息队列可简单的抽象成如下图片:
在同一时间内,只能有一个任务 or 中断在修改队列的链表。是的,队列锁并不能阻止中断向队列复制数据。
在这里插入图片描述
创建队列的过程与创建任务类似,需要为队列结构体和队列存储区分配内存,并初始化队列结构体的成员变量。
以动态内存分配为例:首先分配内存
QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength,
const UBaseType_t uxItemSize,
const uint8_t ucQueueType )
{
Queue_t * pxNewQueue; //指向新的队列结构体
size_t xQueueSizeInBytes; //队列总大小(字节)
uint8_t * pucQueueStorage; //指向队列存储区域起始地址
//队列的长度至少为1
configASSERT( uxQueueLength > ( UBaseType_t ) 0 );
//计算队列使用的内存大小
xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize );
/* 检查乘法溢出 */
configASSERT( ( uxItemSize == 0 ) || ( uxQueueLength == ( xQueueSizeInBytes / uxItemSize ) ) );
/* 检查溢出. */
configASSERT( ( sizeof( Queue_t ) + xQueueSizeInBytes ) > xQueueSizeInBytes );
//todo 申请内存, pvPortMalloc字节对齐问题
pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes );
if( pxNewQueue != NULL )
{
//队列结构体与队列实际内存区域在同一连续的内存中 所以pucQueueStorage跳过Queue_t
pucQueueStorage = ( uint8_t * ) pxNewQueue;
pucQueueStorage += sizeof( Queue_t );
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
{
pxNewQueue->ucStaticallyAllocated = pdFALSE;
}
#endif /* configSUPPORT_STATIC_ALLOCATION */
//初始化队列结构体
prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue );
}
else
{
traceQUEUE_CREATE_FAILED( ucQueueType );
mtCOVERAGE_TEST_MARKER();
}
return pxNewQueue;
}
初始化队列结构体的成员变量,初始化后的内存大概为:
static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength,
const UBaseType_t uxItemSize,
uint8_t * pucQueueStorage,
const uint8_t ucQueueType,
Queue_t * pxNewQueue )
{
//移除编译器的警告
( void ) ucQueueType;
//设置队列结构体的成员
//当队列作为信号量时,uxItemSize为0
if( uxItemSize == ( UBaseType_t ) 0 )
{
//pcHead不能为null,因为pcHead为null表示互斥信号量 所以设为一个确定的值
pxNewQueue->pcHead = ( int8_t * ) pxNewQueue;
}
else
{
//作为队列,pcHead指向存储区
pxNewQueue->pcHead = ( int8_t * ) pucQueueStorage;
}
//设置队列长度
pxNewQueue->uxLength = uxQueueLength;
pxNewQueue->uxItemSize = uxItemSize;
//重置队列
( void ) xQueueGenericReset( pxNewQueue, pdTRUE );
}
BaseType_t xQueueGenericReset( QueueHandle_t xQueue,
BaseType_t xNewQueue )
{
Queue_t * const pxQueue = xQueue;
configASSERT( pxQueue );
taskENTER_CRITICAL();
{
//重新设置队列的指针
pxQueue->u.xQueue.pcTail = pxQueue->pcHead + ( pxQueue->uxLength * pxQueue->uxItemSize );
pxQueue->uxMessagesWaiting = ( UBaseType_t ) 0U;
pxQueue->pcWriteTo = pxQueue->pcHead;
pxQueue->u.xQueue.pcReadFrom = pxQueue->pcHead + ( ( pxQueue->uxLength - 1U ) * pxQueue->uxItemSize );
pxQueue->cRxLock = queueUNLOCKED;
pxQueue->cTxLock = queueUNLOCKED;
if( xNewQueue == pdFALSE )
{
//不是新的队列
/* xTasksWaitingToSend的任务可以发送消息,而 xTasksWaitingToReceive则继续等待 */
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )
{
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )
{
queueYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
//初始化队列使用的链表
vListInitialise( &( pxQueue->xTasksWaitingToSend ) );
vListInitialise( &( pxQueue->xTasksWaitingToReceive ) );
}
}
taskEXIT_CRITICAL();
return pdPASS;
}
RTOS中发送消息的函数五花八门,但万变不离其宗,只要掌握了以下这个函数,其他的发送函数都是这个函数的变体。
任务调用发送消息函数、在该函数中任务会进入死循环,若任务能成功发送消息,则退出循环。否则,任务就需要进入阻塞,等待队列中出现空闲位置。
同时通过进入临界区来保证当前任务对队列的独占性访问。具体代码逻辑如下:
BaseType_t xQueueGenericSend( QueueHandle_t xQueue,
const void * const pvItemToQueue,
TickType_t xTicksToWait,
const BaseType_t xCopyPosition )
{
//xEntryTimeSet标记任务是否已经设置过阻塞时间
BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired;
TimeOut_t xTimeOut; //阻塞时间结构体
Queue_t * const pxQueue = xQueue;
//检查一些错误的用法
configASSERT( pxQueue );
configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) );
configASSERT( !( ( xCopyPosition == queueOVERWRITE ) && ( pxQueue->uxLength != 1 ) ) );
#if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) )
{
configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
}
#endif
//循环
for( ; ; )
{
//进入临界区 要操作链表
taskENTER_CRITICAL();
{
//队列未满或是覆盖写入时,消息可以写入队列
if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) )
{
traceQUEUE_SEND( pxQueue );
{
//将队列项复制到队列指定位置
xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );
//如果有任务在等待消息
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
{
//将该任务从事件链表中移出
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
{
//移出的任务优先级更高,需要任务调度
queueYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else if( xYieldRequired != pdFALSE ) //没有任务在等消息,但需要任务调度
{
queueYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
taskEXIT_CRITICAL(); //消息发送完成 退出临界区
return pdPASS;
}
else //队列满不能入队 需要延时 or 退出
{
//任务不要求延时 直接退出
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();
//挂起调度器、防止其他任务访问队列的xTasksWaitingToSend链表
vTaskSuspendAll();
//锁住队列 防止中断修改xTasksWaitingToSend链表
prvLockQueue( pxQueue );
//检查当前任务的阻塞时间是否到达
if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )
{
//阻塞时间未到,且队列中为满 则将当前任务插入队列的WaitingToSend
if( prvIsQueueFull( pxQueue ) != pdFALSE )
{
traceBLOCKING_ON_QUEUE_SEND( pxQueue );
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;
}
} /*lint -restore */
}
FreeRTOS的消息是通过内存复制实现的。过程如图:先从write指针写入消息,再将write指针移动到下一个空闲位置。
static BaseType_t prvCopyDataToQueue( Queue_t * const pxQueue,
const void * pvItemToQueue,
const BaseType_t xPosition )
{
BaseType_t xReturn = pdFALSE;
UBaseType_t uxMessagesWaiting;
/* 当前函数必须在临界区内调用 */
uxMessagesWaiting = pxQueue->uxMessagesWaiting;
//作为信号量的情况
if( pxQueue->uxItemSize == ( UBaseType_t ) 0 )
{
{
//若是互斥信号量的话,说明信号量被释放、需要解除优先级反转
if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
{
xReturn = xTaskPriorityDisinherit( pxQueue->u.xSemaphore.xMutexHolder );
pxQueue->u.xSemaphore.xMutexHolder = NULL;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
//作为消息队列 发送到队列尾部(writeto)
else if( xPosition == queueSEND_TO_BACK )
{
//内存复制
( void ) memcpy( ( void * ) pxQueue->pcWriteTo, pvItemToQueue, ( size_t ) pxQueue->uxItemSize );
//pcWriteTo增加
pxQueue->pcWriteTo += pxQueue->uxItemSize;
//若pcWriteTo到达内存末尾,则回到内存首部
if( pxQueue->pcWriteTo >= pxQueue->u.xQueue.pcTail )
{
pxQueue->pcWriteTo = pxQueue->pcHead;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
//复制到队首(pcReadFrom)
( void ) memcpy( ( void * ) pxQueue->u.xQueue.pcReadFrom, pvItemToQueue, ( size_t ) pxQueue->uxItemSize );
pxQueue->u.xQueue.pcReadFrom -= pxQueue->uxItemSize;
if( pxQueue->u.xQueue.pcReadFrom < pxQueue->pcHead )
{
pxQueue->u.xQueue.pcReadFrom = ( pxQueue->u.xQueue.pcTail - pxQueue->uxItemSize );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
//若是覆盖写入,则有一个消息被覆盖,uxMessagesWaiting-1
if( xPosition == queueOVERWRITE )
{
if( uxMessagesWaiting > ( UBaseType_t ) 0 )
{
--uxMessagesWaiting;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
//入队成功
pxQueue->uxMessagesWaiting = uxMessagesWaiting + ( UBaseType_t ) 1;
return xReturn;
}
由于在中断中不能进入阻塞,所以需要有一个函数来另外实现在中断发送消息。与在任务中的区别在于,临界区保护以及当不满足发送条件时,直接退出函数,不会进入延时等待。
同时,在中断中若队列上锁,则不能修改链表。这是因为中断触发时,任务可能正在访问队列链表。
BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue,
const void * const pvItemToQueue,
BaseType_t * const pxHigherPriorityTaskWoken,
const BaseType_t xCopyPosition )
{
BaseType_t xReturn; //返回值:表示是否需要任务调度
UBaseType_t uxSavedInterruptStatus; //保存中断状态
Queue_t * const pxQueue = xQueue;
//检查队列
configASSERT( pxQueue );
configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) );
configASSERT( !( ( xCopyPosition == queueOVERWRITE ) && ( pxQueue->uxLength != 1 ) ) );
portASSERT_IF_INTERRUPT_PRIORITY_INVALID();
//进入临界区
uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
{
//队列未满或是覆盖写入时,消息可以写入队列
if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) )
{
const int8_t cTxLock = pxQueue->cTxLock; //获得发送锁
const UBaseType_t uxPreviousMessagesWaiting = pxQueue->uxMessagesWaiting;
traceQUEUE_SEND_FROM_ISR( pxQueue );
/* Semaphores use xQueueGiveFromISR(), so pxQueue will not be a
* semaphore or mutex. That means prvCopyDataToQueue() cannot result
* in a task disinheriting a priority and prvCopyDataToQueue() can be
* called here even though the disinherit function does not check if
* the scheduler is suspended before accessing the ready lists. */
//复制数据到队列
( void ) prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );
//若队列没有被锁
if( cTxLock == queueUNLOCKED )
{
{
//如果有任务正在等待数据
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
{
//将任务从等待链表中移除
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
{
//标记有更高优先级的任务解锁 退出中断后需要任务调度
if( pxHigherPriorityTaskWoken != NULL )
{
*pxHigherPriorityTaskWoken = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
( void ) uxPreviousMessagesWaiting;
}
}
else
{
//队列被锁定,增加cTxLock的值,使解锁队列的任务明白有多少消息未能成功入队
configASSERT( cTxLock != queueINT8_MAX );
pxQueue->cTxLock = ( int8_t ) ( cTxLock + 1 );
}
xReturn = pdPASS;
}
else //队列已满 不能阻塞直接退出
{
traceQUEUE_SEND_FROM_ISR_FAILED( pxQueue );
xReturn = errQUEUE_FULL;
}
}
//退出临界区
portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );
return xReturn;
}
接收消息的实现与发送消息类似,同样需要在循环中,成功获取队列的消息则退出,否则进入阻塞,等待消息到来。
代码注释如下:
BaseType_t xQueueReceive( QueueHandle_t xQueue,
void * const pvBuffer,
TickType_t xTicksToWait )
{
BaseType_t xEntryTimeSet = pdFALSE; //标记是否初始化的阻塞时间
TimeOut_t xTimeOut;
Queue_t * const pxQueue = xQueue;
configASSERT( ( pxQueue ) );
configASSERT( !( ( ( pvBuffer ) == NULL ) && ( ( pxQueue )->uxItemSize != ( UBaseType_t ) 0U ) ) );
#if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) )
{
configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
}
#endif
//循环
for( ; ; )
{
taskENTER_CRITICAL();
{
//获取队列中的消息数量
const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting;
//若队列中有数据
if( uxMessagesWaiting > ( UBaseType_t ) 0 )
{
//复制数据到buffer,消息数量-1
prvCopyDataFromQueue( pxQueue, pvBuffer );
traceQUEUE_RECEIVE( pxQueue );
pxQueue->uxMessagesWaiting = uxMessagesWaiting - ( UBaseType_t ) 1;
//消息出队后,若有任务在等待发送消息,则恢复该任务去发送
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )
{
//将等待发送的任务移出
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )
{
//移出的任务优先级更高,发生抢占
queueYIELD_IF_USING_PREEMPTION();
}
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 );
//将任务放到等待接收链表
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();
}
}
} /*lint -restore */
}
向队列复制消息。在这里需要注意当队列的消息大小为0时,这个队列是信号量,不需要内存的复制,这个部分在下一章中讲信号量说明。
读取消息时,是先将read指针移动,再读取。
static void prvCopyDataFromQueue( Queue_t * const pxQueue,
void * const pvBuffer )
{
if( pxQueue->uxItemSize != ( UBaseType_t ) 0 )
{
//pcReadFrom向前移动一个单元
pxQueue->u.xQueue.pcReadFrom += pxQueue->uxItemSize;
//是否溢出、需要回到队列头
if( pxQueue->u.xQueue.pcReadFrom >= pxQueue->u.xQueue.pcTail )
{
pxQueue->u.xQueue.pcReadFrom = pxQueue->pcHead;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
//内存复制
( void ) memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->u.xQueue.pcReadFrom, ( size_t ) pxQueue->uxItemSize );
}
}
同理,在中断中读取消息,当队列中没有消息时,不能进入等待,必须马上退出。
BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue,
void * const pvBuffer,
BaseType_t * const pxHigherPriorityTaskWoken )
{
BaseType_t xReturn;
UBaseType_t uxSavedInterruptStatus;
Queue_t * const pxQueue = xQueue;
configASSERT( pxQueue );
configASSERT( !( ( pvBuffer == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) );
portASSERT_IF_INTERRUPT_PRIORITY_INVALID();
//进入临界区 保存中断状态
uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
{
//获取消息数量
const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting;
//检查是否有消息
if( uxMessagesWaiting > ( UBaseType_t ) 0 )
{
const int8_t cRxLock = pxQueue->cRxLock; //获取接收锁
traceQUEUE_RECEIVE_FROM_ISR( pxQueue );
//数据有效,复制数据到buffer
prvCopyDataFromQueue( pxQueue, pvBuffer );
pxQueue->uxMessagesWaiting = uxMessagesWaiting - ( UBaseType_t ) 1;
//队列未锁,可以修改队列链表
if( cRxLock == queueUNLOCKED )
{
//消息出队后,队列有空闲的位置,若有任务在等待发送消息,则需要恢复该任务
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )
{
//将等待发送的任务移出
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )
{
//移出的任务优先级更高,需要退出中断后启动任务调度
if( pxHigherPriorityTaskWoken != NULL )
{
//设置返回参数
*pxHigherPriorityTaskWoken = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else //队列上锁,不能操作链表
{
configASSERT( cRxLock != queueINT8_MAX );
//设置cRxLock-1 使队列知道在其锁定的时候有消息被读取
pxQueue->cRxLock = ( int8_t ) ( cRxLock + 1 );
}
//成功获取消息
xReturn = pdPASS;
}
else //队列为空 立刻返回
{
xReturn = pdFAIL;
traceQUEUE_RECEIVE_FROM_ISR_FAILED( pxQueue );
}
}
portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );
return xReturn;
}