1. 队列

队列是为了任务与任务、任务与中断之间通信而准备的,可以在任务与任务、任务与中断之间传递消息,队列中可以存储有限的、大小固定的数据项。创建队列的时候需要指定数据项目的大小和队列的长度。

1.1 数据存储

●队列采用的是先进先出(FIFO)的缓存机制

往队列发送数据(入队):永远发送到队列的尾部

从队列提取数据(出队):永远从队列头部提取

FreeRTOS也提供了后进后出(LIFO)数据缓存机制

●队列将要发送的数据复制到队列中(值传递)

队列中存储的是数据的原始值,而非数据的指针(引用),当然也可以使用指针传递。

1.2 多任务访问

队列不属于某个特别指定的任务,任何任务都可以向队列中发送消息或者从队列中提取消息

1.3 出队阻塞

出队就是指的是从队列中读取消息的任务,阻塞是指当任务尝试从一个队列中读取消息时,可以指定一个阻塞时间。比如任务A从队列Q中读取数据,如果此时队列Q为空,那么任务A有三种选择:

1.直接返回:阻塞时间设置为0

2.等待一会儿:等待时间为设置的阻塞时间

3.一直等待,直到有数据才会返回:阻塞时间被设置为了portMAX_DELAY

值得一提的是,当多个任务读取空队列时,这些任务都会进入阻塞状态:有多个任务在等待同一个队列的数据。当队列中有数据时,哪个任务会进入就绪态?

●优先级最高的任务

●如果大家的优先级相同,那等待时间最久的任务会进入就绪态

1.4 入队阻塞

入队阻塞和出队阻塞基本上一样,就是入队时如果队列已满,其处理方法和出队一样。

2. 队列结构体

typedef struct QueuePointers
{int8_t * pcTail;     /*< 用作队列时指向最后一个出队的队列项的末尾地址*/int8_t * pcReadFrom; /*< 用作队列时指向最后一个出队的队列项首地址 */
} QueuePointers_t;typedef struct QueueDefinition /*  */
{int8_t * pcHead;           /*< 指向队列存储区的起始地址 */int8_t * pcWriteTo;        /*<  指向存储区的下一个空闲区域*/union{QueuePointers_t xQueue;     /*<  */SemaphoreData_t xSemaphore; /*< 用作递归互斥量时用来记录递归互斥量被调用的次数 */} u;List_t xTasksWaitingToSend;      /*<  等待发送任务列表,那行因为队列满导致入队失败而进入阻塞的任务会挂到此列表上*/List_t xTasksWaitingToReceive;   /*<  等待接收任务列表,那些因为队列空导致出队失败而进入阻塞态的任务会挂到此列表上*/volatile UBaseType_t uxMessagesWaiting; /*< 队列中当前队列项数量 */UBaseType_t uxLength;                   /*<  队列的长度*/UBaseType_t uxItemSize;                 /*<  队列项的大小,int、char、short或其他*/volatile int8_t cRxLock;/*< 当队列上锁以后用来统计从队列中接收到的队列项数量,也就是出队的队列项数量 */volatile int8_t cTxLock;/*<当队列上锁以后用来统计发送到队列中队列项的数量,也就是入队的队列项数量  */#if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )uint8_t ucStaticallyAllocated; /*< 如果使用静态存储,此字段设置为pdTRUE */#endif#if ( configUSE_QUEUE_SETS == 1 )/*< 队列集相关宏 */struct QueueDefinition * pxQueueSetContainer;#endif#if ( configUSE_TRACE_FACILITY == 1 )UBaseType_t uxQueueNumber;uint8_t ucQueueType;#endif
} xQUEUE;
typedef xQUEUE Queue_t;

3. 队列创建

队列的创建有两种方法:动态分配内存、静态分配内存

●动态分配内存:xQueueCreate,队列的内存在函数内部动态分配,实际调用:xQueueGenericCreate()

QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize );
参数 说明
uxQueueLength 队列长度,最多能存放多少个数据(item)
uxItemSize 每个数据(item)的大小:以字节为单位
返回值 非0:成功,返回句柄,以后使用句柄来操作队列NULL:失败,因为内存不足
QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength,const UBaseType_t uxItemSize,const uint8_t ucQueueType )
参数 说明
uxQueueLength 队列长度,最多能存放多少个数据(item)
uxItemSize 每个数据(item)的大小:以字节为单位
ucQueueType 队列类型。由于FreeRTOS中的信号量等也是通过队列来实现的,创建信号量的函数最终也是使用此函数的,因此,在创建的时候需要指定此队列的用途,也就是队列类型,一共有6种类型
返回值 非0:成功,返回句柄,以后使用句柄来操作队列NULL:失败,因为内存不足
参数 说明
queueQUEUE_TYPE_BASE 普通的队列消息
queueQUEUE_TYPE_SET 队列集
queueQUEUE_TYPE_MUTEX 互斥信号量
queueQUEUE_TYPE_COUNTING_SEMAPHORE 计数型信号量
queueQUEUE_TYPE_BINARY_SEMAPHORE 二值信号量
queueQUEUE_TYPE_RECURSIVE_MUTEX 递归信号量
  • 静态分配内存:xQueueCreateStatic,队列的内存要事先分配好,实际调用:xQueueGenericCreateStatic()
QueueHandle_t xQueueCreateStatic(UBaseType_t uxQueueLength,UBaseType_t uxItemSize,uint8_t *pucQueueStorageBuffer,StaticQueue_t *pxQueueBuffer);
参数 说明
uxQueueLength 队列长度,最多能存放多少个数据(item)
uxItemSize 每个数据(item)的大小:以字节为单位
pucQueueStorageBuffer 如果uxItemSize非0,pucQueueStorageBuffer必须指向一个uint8_t数组,此数组大小至少为"uxQueueLength * uxItemSize"
pxQueueBuffer 必须执行一个StaticQueue_t结构体,用来保存队列的数据结构
返回值 非0:成功,返回句柄,以后使用句柄来操作队列NULL:失败,因为pxQueueBuffer为NULL
QueueHandle_t xQueueGenericCreateStatic( const UBaseType_t uxQueueLength,const UBaseType_t uxItemSize,uint8_t * pucQueueStorage,StaticQueue_t * pxStaticQueue,const uint8_t ucQueueType )

  • 队列创建函数分析(动态创建)
//队列动态创建函数
QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength,const UBaseType_t uxItemSize,const uint8_t ucQueueType ){Queue_t * pxNewQueue;size_t xQueueSizeInBytes;uint8_t * pucQueueStorage;configASSERT( uxQueueLength > ( UBaseType_t ) 0 );//分配足够的存储区,确保随时随地都可以保存所有的消息,队列长度*队列项大小xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ); /* Check for multiplication overflow. */configASSERT( ( uxItemSize == 0 ) || ( uxQueueLength == ( xQueueSizeInBytes / uxItemSize ) ) );/* Check for addition overflow. */configASSERT( ( sizeof( Queue_t ) + xQueueSizeInBytes ) >  xQueueSizeInBytes );//申请内存,申请的是队列结构体和队列中消息存储区的总大小pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes ); if( pxNewQueue != NULL ){//计算出消息存储区的首地址,申请到的内存是队列结构体和队列中消息存储区的总大小,队列结构体内存在前,紧跟在                  //后面的就是消息存储区内存。pucQueueStorage = ( uint8_t * ) pxNewQueue;//指针偏移,计算消息存储区的首地址pucQueueStorage += sizeof( Queue_t );#if ( configSUPPORT_STATIC_ALLOCATION == 1 ){/* Queues can be created either statically or dynamically, so* note this task was created dynamically in case it is later* deleted. */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 )//队列结构体
{/* Remove compiler warnings about unused parameters should* configUSE_TRACE_FACILITY not be set to 1. */( void ) ucQueueType;if( uxItemSize == ( UBaseType_t ) 0 ){/*  队列项长度为0,说明没有队列存储区,这里将pcHead指向队列开始地址*/pxNewQueue->pcHead = ( int8_t * ) pxNewQueue;}else{/* 队列项长度不为0,设置pcHead指向队列存储区的首地址*/pxNewQueue->pcHead = ( int8_t * ) pucQueueStorage;}/*初始化队列结构体相关成员变量 */pxNewQueue->uxLength = uxQueueLength;pxNewQueue->uxItemSize = uxItemSize;( void ) xQueueGenericReset( pxNewQueue, pdTRUE );#if ( configUSE_TRACE_FACILITY == 1 ){pxNewQueue->ucQueueType = ucQueueType;}#endif /* 跟踪调式相关字段初始化 */#if ( configUSE_QUEUE_SETS == 1 ){pxNewQueue->pxQueueSetContainer = NULL;}#endif /* 队列集相关初始化 */traceQUEUE_CREATE( pxNewQueue );
}//队列复位函数
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;//根据xNewQueue字段确定该队列是否是新创建的队列,如果不是需要进行下面的处理if( xNewQueue == pdFALSE ){/* 由于复位队列以后队列依旧是空的,所以对于那些由于出队(从队列中读取消息)* 而阻塞的任务就依旧保持阻塞状态。但是对于那些由于入队(向队列中发送消息)* 而阻塞的任务就不同了,这些任务要解除阻塞状态,从队列的相应列表中移除*/if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ){//如果将阻塞任务从xTasksWaitingToSend列表中移除之后触发了任务切换,则进行任务切换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();/* A value is returned for calling semantic consistency with previous* versions. */return pdPASS;
}

 队列初始化完成之后的结构如下所示:

4. 向队列发送消息

可以把数据写到队列头部,也可以写到尾部,这些函数有两个版本:在任务中使用、在ISR中使用。函数原型如下:

/* 等同于xQueueSendToBack* 往队列尾部写入数据,如果没有空间,阻塞时间为xTicksToWait*/
BaseType_t xQueueSend(QueueHandle_t    xQueue,const void       *pvItemToQueue,TickType_t       xTicksToWait);/* * 往队列尾部写入数据,如果没有空间,阻塞时间为xTicksToWait*/
BaseType_t xQueueSendToBack(QueueHandle_t    xQueue,const void       *pvItemToQueue,TickType_t       xTicksToWait);/* * 往队列尾部写入数据,此函数可以在中断函数中使用,不可阻塞*/
BaseType_t xQueueSendToBackFromISR(QueueHandle_t xQueue,const void *pvItemToQueue,BaseType_t *pxHigherPriorityTaskWoken);/* * 往队列头部写入数据,如果没有空间,阻塞时间为xTicksToWait*/
BaseType_t xQueueSendToFront(QueueHandle_t    xQueue,const void       *pvItemToQueue,TickType_t       xTicksToWait);/* * 往队列头部写入数据,此函数可以在中断函数中使用,不可阻塞*/
BaseType_t xQueueSendToFrontFromISR(QueueHandle_t xQueue,const void *pvItemToQueue,BaseType_t *pxHigherPriorityTaskWoken);
/* 覆盖队列,队列长度必须为1* xQueue: 写哪个队列* pvItemToQueue: 数据地址* 返回值: pdTRUE表示成功, pdFALSE表示失败*/
BaseType_t xQueueOverwrite(QueueHandle_t xQueue,const void * pvItemToQueue);BaseType_t xQueueOverwriteFromISR(QueueHandle_t xQueue,const void * pvItemToQueue,BaseType_t *pxHigherPriorityTaskWoken);

这些函数的参数都类似,这里统一说明一下:

下面讲解一下任务级入队函数xQueueGenericSend()函数(通用入队函数,上面的非中断入队函数最终都是调用的这个):

//入队函数
BaseType_t xQueueGenericSend( QueueHandle_t xQueue,//队列句柄const void * const pvItemToQueue,//指向要发送的消息,发送过程中会将消息复制到队列中TickType_t xTicksToWait,//阻塞时间const BaseType_t xCopyPosition )//入队方式:1.后向入队 2.前向入队 3.覆写入队
{BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired;TimeOut_t xTimeOut;Queue_t * const pxQueue = xQueue;for( ; ; ){taskENTER_CRITICAL();//进入临界区{/* 查询队列是否还有剩余的空间,如果采用覆写的方式入队则不用在乎队列是不是满的*/if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) ){traceQUEUE_SEND( pxQueue );#if ( configUSE_QUEUE_SETS == 1 )//队列集相关........省略相关代码.......}#else /* configUSE_QUEUE_SETS */{//将消息复制到队列中,重点说明一下//如果选择后向入队:将消息复制到队列结构成员pcWriteTo指向的队列项;复制成功后pcWriteTo增加                            //uxItemSize个字节,指向下一个队列项目//如果选择前向入队或者是覆写时:将消息复制到u.pcReadFrom所指向的队列项目;同样复制成功后其向前//移动uxItemSize个字节//最后当消息写入成功之后,会将uxMessagesWaiting加一xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );/* 检查是否有任务由于等待消息而进入阻塞状态 */if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ){//因为前面已经向队列发送了一条消息,所以接下来将阻塞任务从xTasksWaitingToReceive上移除并//将其添加到就绪列表中;如果调度器已经暂停,则这些任务就会挂到列表xPendingReadyList上//如果取消阻塞的任务优先级比当前正在运行的任务的优先级高,则标记需要任务切换,即返回值为pdTRUEif( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ){//解除阻塞态任务优先级最高,因此要进行一次任务切换queueYIELD_IF_USING_PREEMPTION();}else{mtCOVERAGE_TEST_MARKER();}}else if( xYieldRequired != pdFALSE ){/* This path is a special case that will only get* executed if the task was holding multiple mutexes and* the mutexes were given back in an order that is* different to that in which they were taken. */queueYIELD_IF_USING_PREEMPTION();}else{mtCOVERAGE_TEST_MARKER();}}#endif /* configUSE_QUEUE_SETS */taskEXIT_CRITICAL();return pdPASS;}else//队列满的情况并且不采用覆写的方式{if( xTicksToWait == ( TickType_t ) 0 ){//阻塞时间为0,队列已满,之间返回taskEXIT_CRITICAL();traceQUEUE_SEND_FAILED( pxQueue );return errQUEUE_FULL;}else if( xEntryTimeSet == pdFALSE ){//阻塞时间不为0,初始化时间结构体,记录当前系统时钟节拍计数器的值xTickCount和溢出次数xNumOfOverflowsvTaskInternalSetTimeOutState( &xTimeOut );xEntryTimeSet = pdTRUE;}else{/* Entry time was already set. */mtCOVERAGE_TEST_MARKER();}}}taskEXIT_CRITICAL();/* 任务执行到这里说明当前的状况是队列已满,而且设置了不为0的超时时间 *///调度器暂停,先处理当前任务,队列上锁vTaskSuspendAll();prvLockQueue( pxQueue );/* 更新时间状态,检查是否有超时产生 */if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ){//超时时间未到,队列已满if( prvIsQueueFull( pxQueue ) != pdFALSE ){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() == pdFALSE )//恢复任务调度器{portYIELD_WITHIN_API();}}else{//队列存在空闲队列项/* Try again. */prvUnlockQueue( pxQueue );( void ) xTaskResumeAll();}}else{//超时产生,解锁队列,恢复调度/* The timeout has expired. */prvUnlockQueue( pxQueue );( void ) xTaskResumeAll();traceQUEUE_SEND_FAILED( pxQueue );return errQUEUE_FULL;}} /*lint -restore */
}

中断级通用入队函数:xQueueGenericSendFromISR().

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;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 );( void ) prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );/* 队列上锁的时候不能操作时间列表,队列解锁的时候会补上这些操作 */if( cTxLock == queueUNLOCKED )//队列未上锁{#if ( configUSE_QUEUE_SETS == 1 )......省略队列集..............#else /* configUSE_QUEUE_SETS */{//判断接收列表是否为空,不为空则说明有任务在请求消息的时候被阻塞了if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ){if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ){/* The task waiting has a higher priority so record that a* context switch is required. */if( pxHigherPriorityTaskWoken != NULL ){//标记表示要进行任务切换*pxHigherPriorityTaskWoken = pdTRUE;}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}/* Not used in this path. */( void ) uxPreviousMessagesWaiting;}#endif /* configUSE_QUEUE_SETS */}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;
}

5. 队列上锁和解锁

  • 队列上锁函数:prvLockQueue().
#define prvLockQueue( pxQueue )                            \taskENTER_CRITICAL();                                  \{                                                      \if( ( pxQueue )->cRxLock == queueUNLOCKED )        \{                                                  \( pxQueue )->cRxLock = queueLOCKED_UNMODIFIED; \}                                                  \if( ( pxQueue )->cTxLock == queueUNLOCKED )        \{                                                  \( pxQueue )->cTxLock = queueLOCKED_UNMODIFIED; \}                                                  \}                                                      \taskEXIT_CRITICAL()
//上锁函数只是一个宏,本质上就是将cRxLock,cTxLock初始化为queueLOCKED_UNMODIFIED
  • 队列解锁函数:
static void prvUnlockQueue( Queue_t * const pxQueue )
{//上锁计数器(cTxLock 和 cRxLock)记录了在队列上锁期间入队或出队的数量,当队列//上锁以后,队列项是可以加入或者移除队列的,但是相应的列表不会更新*/taskENTER_CRITICAL();{int8_t cTxLock = pxQueue->cTxLock;/* See if data was added to the queue while it was locked. */while( cTxLock > queueLOCKED_UNMODIFIED ){/* Data was posted while the queue was locked.  Are any tasks* blocked waiting for data to become available? */#if ( configUSE_QUEUE_SETS == 1 )#else /* configUSE_QUEUE_SETS */{/* Tasks that are removed from the event list will get added to* the pending ready list as the scheduler is still suspended. */if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ){if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ){/*如果刚刚从列表 xTasksWaitingToReceive 移除的任务优先级比当前任务的优 先 级 高,那 么 就 要 标 记需 要 进行任 务 切换。这 里 调 用 函 数vTaskMissedYield()来完成此任务,函数 vTaskMissedYield()只是简单地将全局变量xYieldPending 设置为 pdTRUE,那么真正的任务切换是在哪里完成的呢? 在时钟节拍处理函数 xTaskIncrementTick()中,此函数会判断 xYieldPending 的值,从而决定是否进行任务切换*/vTaskMissedYield();}else{mtCOVERAGE_TEST_MARKER();}}else{break;}}#endif /* configUSE_QUEUE_SETS *///处理完一条就减一,直到处理完所有--cTxLock;}pxQueue->cTxLock = queueUNLOCKED;}taskEXIT_CRITICAL();/* Do the same for the Rx lock. */taskENTER_CRITICAL();{//处理cRxLockint8_t cRxLock = pxQueue->cRxLock;while( cRxLock > queueLOCKED_UNMODIFIED ){if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ){if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ){vTaskMissedYield();}else{mtCOVERAGE_TEST_MARKER();}--cRxLock;}else{break;}}pxQueue->cRxLock = queueUNLOCKED;}taskEXIT_CRITICAL();
}

6. 从队列读取消息

/* 从队列中读取队列项,完成以后删除掉队列项* xQueue: 读取哪个队列* pvItemToQueue: 数据地址, 用来保存复制出来的数据* xTicksToWait: 没有数据的话阻塞一会* 返回值: pdTRUE表示成功, pdFALSE表示失败*/
BaseType_t xQueueReceive( QueueHandle_t xQueue,void * const pvBuffer,TickType_t xTicksToWait );
/* 从队列中读取队列项,完成以后删除掉队列项,中断中使用* xQueue: 读取哪个队列* pvItemToQueue: 数据地址, 用来保存复制出来的数据* xTicksToWait: 没有数据的话阻塞一会* 返回值: pdTRUE表示成功, pdFALSE表示失败*/
BaseType_t xQueueReceiveFromISR(QueueHandle_t    xQueue,void             *pvBuffer,BaseType_t       *pxTaskWoken);
/* 偷看队列,完成以后不会删除掉队列项* xQueue: 偷看哪个队列* pvItemToQueue: 数据地址, 用来保存复制出来的数据* xTicksToWait: 没有数据的话阻塞一会* 返回值: pdTRUE表示成功, pdFALSE表示失败*/
BaseType_t xQueuePeek(QueueHandle_t xQueue,void * const pvBuffer,TickType_t xTicksToWait);BaseType_t xQueuePeekFromISR(QueueHandle_t xQueue,void *pvBuffer,);

FreeRTOS队列原理相关推荐

  1. python2 队列的使用_python双端队列原理、实现与使用方法分析

    本文实例讲述了python双端队列原理.实现与使用方法.分享给大家供大家参考,具体如下: 双端队列 双端队列(deque,全名double-ended queue),是一种具有队列和栈的性质的数据结构 ...

  2. Java 循环队列原理与用法详解

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:iphone 也是办公神器,用了就知道了,不行送你一个试试个人原创+1博客:点击前往,查看更多 链接:https ...

  3. java线程池_Java多线程并发:线程基本方法+线程池原理+阻塞队列原理技术分享...

    线程基本方法有哪些? 线程相关的基本方法有 wait,notify,notifyAll,sleep,join,yield 等. 线程等待(wait) 调用该方法的线程进入 WAITING 状态,只有等 ...

  4. python 优先队列_示例讲解:python队列原理及实现方法与操作思路

    今天为大家带来的内容是:示例讲解:python队列原理及实现方法与操作思路 本文内容主要介绍了python队列原理及实现方法,结合实例形式详细分析了Python队列的概念.原理.定义及基本操作技巧,需 ...

  5. 【并发编程系列6】Condition队列原理及await和singal(等待/唤醒)机制源码分析

    Condition队列原理分析 前言 初识Condition Condition使用示例 Condition原理分析 condition.wait()源码解读 AQS#await() AQS#addC ...

  6. RabbitMQ镜像队列原理分析

    对于RabbitMQ的节点来说,有单节点模式和集群模式两种,其中集群模式又分为普通集群模式和镜像队列集群模式,在<RabbitMQ集群架构搭建与高可用性实现>文中,介绍了RabbitMQ的 ...

  7. 基于 Kafka 和 ZooKeeper 的分布式消息队列原理

    转载:https://gitbook.cn/books/5bc446269a9adf54c7ccb8bc/index.html 消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量 ...

  8. 再谈基于 Kafka 和 ZooKeeper 的分布式消息队列原理

    关于分布式消息队列,我在几个月前写过一篇文章:<深入浅出理解基于 Kafka 和 ZooKeeper 的分布式消息队列 >.最近,由于写作课程<分布式中间件实践之路>的契机,我 ...

  9. Java 阻塞队列原理

    阻塞队列(BlockingQueue)是一个支持两个附加操作的队列.这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空.当队列满时,存储元素的线程会等待队列可用.阻塞队列常用于生产者和 ...

最新文章

  1. 你知道这些 985、211 院校的隶属吗?
  2. python处理excel教程实例-Python玩转Excel的读写改实例
  3. 数据结构----顺序表与单链表(JAVA)
  4. 清华博士直播 | 如何让AI模型更皮实、更稳定?
  5. python3从零开始学习_从零开始学习PYTHON3讲义(十五)让画面动起来
  6. Java反序列化漏洞研究
  7. 07-02 测试报告-allure
  8. 金华资产封存页面问题
  9. 【重识 HTML + CSS】CSS 特性
  10. 解决百度云下载过慢、Linux下载百度云数据问题
  11. firefox css3 transform样式 位置偏移问题解决
  12. [转]新建一个Android工程项目
  13. 中国传统色彩十六进制颜色码图片大全
  14. 贝塞尔曲线运动n阶追踪方程的数学原理及其匀速化方法和应用
  15. 费曼学习法-超级学习法
  16. Windows 10升级无法选择保留个人文件、设置问题解决
  17. golang interface 与 反射
  18. 游戏术语扫盲贴(手游人必懂)
  19. Cairngorm开发框架
  20. 大数据最佳实践-hbase

热门文章

  1. windows10利用驱动精灵更新网卡后,笔记本的有线网卡和无线网卡一直安装不成功,错误代码:56的完美解决办法(不需要重装系统,轻松解决)
  2. yii框架开启debug和gii
  3. Android校验应用签名是否被篡改
  4. 攻防世界web进阶区Web_php_wrong_nginx_config详解
  5. 手机有没有抠图换背景的软件?这篇文章来告诉你
  6. java之集合Collection之List接口总结
  7. nbu备份恢复catalog
  8. 一款 Java 开源的即时通讯 IM 聊天系统
  9. linuxQt程序打包
  10. matlab 2016b vs2010,在Matlab2010b中调用 Microsoft Visual C++ 2010