(2)FreeRTOS学习笔记---队列

背景

实际的应用中,常常会遇到一个任务或者中断服务需要和另外一个任务进行“沟通交流”,这个“沟通交流”的过程其实就是消息传递的过程。为此,FreeRTOS 提供了一个叫做“队列”的机制来完成任务与任务、任务与中断之间的消息传递。本文主要结合队列源码分析了创建、发送到接收的过程。

队列简介

列队入队和出队的过程可以用下面图演示出来:
创建队列
在这里插入图片描述
向队列发送第一个消息
在这里插入图片描述
向队列发送第二个消息
在这里插入图片描述
从队列中读取消息
在这里插入图片描述

队列结构体

所有API函数都是围绕这个数据结构展开,因此数据结构的每个成员都需要了解。如果你是第一次看到这个结构体,即使有注释,可能你对结构体的某些成员还是不理解,不要着急,这是正常的。后面介绍API函数的时候,会使用这些成员,结合着具体实例,会很容理解的,你需要做的,是要反复翻到这里查看。

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;
typedef xQUEUE Queue_t;

队列创建

创建队列有两种方法,分别是静态xQueueCreateStatic和动态xQueueCreate方法。其中动静态的区分是根据方法中内存申请方式来确定的,静态方法中内存由用户自行分配,而动态方法中由动态内存管理函数pvPortMalloc()分配。
上面两个都是宏,实际对应于 xQueueGenericCreate()和xQueueGenericCreateStatic()函数。

QueueHandle_t xQueueGenericCreateStatic( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, StaticQueue_t *pxStaticQueue, const uint8_t ucQueueType )
{
	Queue_t *pxNewQueue;
	configASSERT( uxQueueLength > ( UBaseType_t ) 0 );
	/* The StaticQueue_t structure and the queue storage area must be
	supplied. */
	configASSERT( pxStaticQueue != NULL );
	/* A queue storage area should be provided if the item size is not 0, and
	should not be provided if the item size is 0. */
	configASSERT( !( ( pucQueueStorage != NULL ) && ( uxItemSize == 0 ) ) );
	configASSERT( !( ( pucQueueStorage == NULL ) && ( uxItemSize != 0 ) ) );
	#if( configASSERT_DEFINED == 1 )
	{
		/* Sanity check that the size of the structure used to declare a
		variable of type StaticQueue_t or StaticSemaphore_t equals the size of
		the real queue and semaphore structures. */
		volatile size_t xSize = sizeof( StaticQueue_t );
		configASSERT( xSize == sizeof( Queue_t ) );
	}
	#endif /* configASSERT_DEFINED */
	/* The address of a statically allocated queue was passed in, use it.
	The address of a statically allocated storage area was also passed in
	but is already set. */
	pxNewQueue = ( Queue_t * ) pxStaticQueue; /*lint !e740 Unusual cast is ok as the structures are designed to have the same alignment, and the size is checked by an assert. */
	if( pxNewQueue != NULL )
	{
		#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
		{
			/* Queues can be allocated wither statically or dynamically, so
			note this queue was allocated statically in case the queue is
			later deleted. */
			pxNewQueue->ucStaticallyAllocated = pdTRUE;
		}
		#endif /* configSUPPORT_DYNAMIC_ALLOCATION */

		prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue );
	}
	return pxNewQueue;
}

参数:
uxQueueLength:	要创建的队列的队列长度,这里是队列的项目数。
uxItemSize:	队列中每个项目(消息)的长度,单位为字节
pucQueueStorage:     指向队列项目的存储区,也就是消息的存储区,这个存储区需要用户自行分配。此参数必须指向一个 uint8_t 类型的数组。这个存储区要大于等于(uxQueueLength * uxItemsSize)字节。
pxStaticQueue:	此参数指向一个 StaticQueue_t 类型的变量,用来保存队列结构体。
ucQueueType:	队列类型。

返回值:
其他值:	队列创捷成功以后队列句柄!
NULL:	队列创建失败。

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 );
	
		if( uxItemSize == ( UBaseType_t ) 0 )
		{
			/* There is not going to be a queue storage area. */
			xQueueSizeInBytes = ( size_t ) 0;
		}
		else
		{
			/* Allocate enough space to hold the maximum number of items that
			can be in the queue at any time. */
			xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
		}
	
		pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes );
	
		if( pxNewQueue != NULL )
		{
			/* Jump past the queue structure to find the location of the queue
			storage area. */
			pucQueueStorage = ( ( uint8_t * ) pxNewQueue ) + 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 );
		}
		return pxNewQueue;
	}
参数:
uxQueueLength: 要创建的队列的队列长度,这里是队列的项目数。uxItemSize:	队列中每个项目(消息)的长度,单位为字节。
ucQueueType:	队列类型,由于 FreeRTOS 中的信号量等也是通过队列来实现的,创建信号量的函数最终也是使用此函数的,因此在创建的时候需要指定此队列的用途, 也就是队列类型,一共有六种类型:
queueQUEUE_TYPE_BASE	普通的消息队列
queueQUEUE_TYPE_SET	队列集
queueQUEUE_TYPE_MUTEX	互斥信号量
queueQUEUE_TYPE_COUNTING_SEMAPHORE	计数型信号量
queueQUEUE_TYPE_BINARY_SEMAPHORE	二值信号量
queueQUEUE_TYPE_RECURSIVE_MUTEX	递归互斥信号量
函数 xQueueCreate() 创 建 队 列 的 时 候 此 参 数 默 认 选 择 的 就 是
queueQUEUE_TYPE_BASE。

返回值:
其他值:	队列创捷成功以后的队列句柄!
NULL:	队列创建失败。

(1)、队列是要存储消息的,所以必须要有消息的存储区,函数的参数 uxQueueLength 和uxItemSize 指定了队列中最大队列项目(消息)数量和每个消息的长度,两者相乘就是消息存储区的大小。
(2)、调用函数 pvPortMalloc()给队列分配内存,注意这里申请的内存大小是队列结构体和队列中消息存储区的总大小。
(3)、计算出消息存储区的首地址,(2)中申请到的内存是队列结构体和队列中消存储区的总大小,队列结构体内存在前,紧跟在后面的就是消息存储区内存。
(4)、调用函数 prvInitialiseNewQueue()初始化队列。
可以看出函数 xQueueGenericCreate()重要的工作就是给队列分配内存,当内存分配成功以后调用函数 prvInitialiseNewQueue()来初始化队列。

列队初始化prvInitialiseNewQueue()
两种列队创建函数最后都调用了列队初始化函数,代码如下:

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 )
	{
		/* No RAM was allocated for the queue storage area, but PC head cannot
		be set to NULL because NULL is used as a key to say the queue is used as
		a mutex.  Therefore just set pcHead to point to the queue as a benign
		value that is known to be within the memory map. */
		pxNewQueue->pcHead = ( int8_t * ) pxNewQueue;
	}
	else
	{
		/* Set the head to the start of the queue storage area. */
		pxNewQueue->pcHead = ( int8_t * ) pucQueueStorage;
	}

	/* Initialise the queue members as described where the queue type is
	defined. */
	pxNewQueue->uxLength = uxQueueLength;
	pxNewQueue->uxItemSize = uxItemSize;
	( void ) xQueueGenericReset( pxNewQueue, pdTRUE );

	#if ( configUSE_TRACE_FACILITY == 1 )
	{
		pxNewQueue->ucQueueType = ucQueueType;
	}
	#endif /* configUSE_TRACE_FACILITY */

	#if( configUSE_QUEUE_SETS == 1 )
	{
		pxNewQueue->pxQueueSetContainer = NULL;
	}
	#endif /* configUSE_QUEUE_SETS */

	traceQUEUE_CREATE( pxNewQueue );
}

BaseType_t xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue )
{
	Queue_t * const pxQueue = ( Queue_t * ) xQueue;
	configASSERT( pxQueue );
	taskENTER_CRITICAL();
	{
		pxQueue->pcTail = pxQueue->pcHead + ( pxQueue->uxLength * pxQueue->uxItemSize );
		pxQueue->uxMessagesWaiting = ( UBaseType_t ) 0U;
		pxQueue->pcWriteTo = pxQueue->pcHead;
		pxQueue->u.pcReadFrom = pxQueue->pcHead + ( ( pxQueue->uxLength - ( UBaseType_t ) 1U ) * pxQueue->uxItemSize );
		pxQueue->cRxLock = queueUNLOCKED;
		pxQueue->cTxLock = queueUNLOCKED;
		if( xNewQueue == pdFALSE )
		{
			/* If there are tasks blocked waiting to read from the queue, then
			the tasks will remain blocked as after this function exits the queue
			will still be empty.  If there are tasks blocked waiting to write to
			the queue, then one should be unblocked as after this function exits
			it will be possible to write to it. */
			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
		{
			/* Ensure the event queues start in the correct state. */
			vListInitialise( &( pxQueue->xTasksWaitingToSend ) );
			vListInitialise( &( pxQueue->xTasksWaitingToReceive ) );
		}
	}
	taskEXIT_CRITICAL();
	/* A value is returned for calling semantic consistency with previous
	versions. */
	return pdPASS;
}

至此,已经完成了列队创建的所有步骤,用下图表示4个队列项,每个队列项长度32字节的队列创建过程:
在这里插入图片描述
黄色、绿色、蓝色先后依次在函数调用中实现初始化。

向队列发送消息

FreeRTOS 提供了 8 个向队列发送消息的 API 函数宏。
4个任务级入队函数:xQueueSend()、xQueueSendToBack()、xQueueSendToFront()和xQueueOverwrite()。
4个中断级入队函数:xQueueSendFromISR()、xQueueSendToBackFromISR()、xQueueSendToFrontFromISR()和xQueueOverwriteFromISR()。
任务和中断入队函数宏是一一对应的功能关系,唯一区别是使用的场合不同。前两个函数是后向入队,第三个是前向入队,最后一个是覆写入队。
这些宏不是真正干活的,干活的是两个函数:xQueueGenericSend()和xQueueGenericSendFromISR()。

下面重点看看他们源码实现:

BaseType_t xQueueGenericSend( QueueHandle_t xQueue,const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition )
参数:
	xQueue: 队列句柄,指明要向哪个队列发送数据,创建队列成功以后会返回此队列的队列句柄。
	pvItemToQueue:指向要发送的消息,发送的过程中会将这个消息拷贝到队列中。xTicksToWait: 阻塞时间。
	xCopyPosition: 入队方式,有三种入队方式:
	queueSEND_TO_BACK: 后向入队
	queueSEND_TO_FRONT: 前向入队
	queueOVERWRITE: 覆写入队。
	上面讲解的入队 API 函数就是通过此参数来决定采用哪种入队方式的。
返回值:
	pdTRUE: 向队列发送消息成功!
	errQUEUE_FULL: 队列已经满了,消息发送失败。
{
BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired;
TimeOut_t xTimeOut;
Queue_t * const pxQueue = ( Queue_t * ) 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


	/* 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?  The running task must be the
			highest priority task wanting to access the queue.  If the head item
			in the queue is to be overwritten then it does not matter if the
			queue is full. */
			if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) )
			{
				traceQUEUE_SEND( pxQueue );
				xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );

				#if ( configUSE_QUEUE_SETS == 1 )
				{
					if( pxQueue->pxQueueSetContainer != NULL )
					{
						if( prvNotifyQueueSetContainer( pxQueue, xCopyPosition ) != pdFALSE )
						{
							/* The queue is a member of a queue set, and posting
							to the queue set caused a higher priority task to
							unblock. A context switch is required. */
							queueYIELD_IF_USING_PREEMPTION();
						}
						else
						{
							mtCOVERAGE_TEST_MARKER();
						}
					}
					else
					{
						/* 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 ) ) != pdFALSE )
							{
								/* 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. */
								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();
						}
					}
				}
				#else /* configUSE_QUEUE_SETS */
				{
					/* 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 ) ) != pdFALSE )
						{
							/* 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. */
							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 )
				{
					/* The queue was full and no block time is specified (or
					the block time has expired) so leave now. */
					taskEXIT_CRITICAL();

					/* Return to the original privilege level before exiting
					the function. */
					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 );
					xEntryTimeSet = pdTRUE;
				}
				else
				{
					/* Entry time was already set. */
					mtCOVERAGE_TEST_MARKER();
				}
			}
		}
		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. */
		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;
		}
	}
}

OS_CODE_IN_RAM BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue, const void * const pvItemToQueue, BaseType_t * const pxHigherPriorityTaskWoken, const BaseType_t xCopyPosition )
参数:
	xQueue: 队列句柄,指明要向哪个队列发送数据,创建队列成功以后会返回此队列的队列句柄。
	pvItemToQueue:指向要发送的消息,发送的过程中会将这个消息拷贝到队列中。pxHigherPriorityTaskWoken: 标记退出此函数以后是否进行任务切换,这个变量的值由这
	三个函数来设置的,用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为 pdTRUE 的时候在退出中断服务函数之前一定要进行一次任务切换。
	xCopyPosition:
	入队方式,有三种入队方式:
	queueSEND_TO_BACK:
	后向入队
	queueSEND_TO_FRONT:
	前向入队
	queueOVERWRITE:
	覆写入队。
返回值:
	pdTRUE: 向队列发送消息成功!
	errQUEUE_FULL: 队列已经满了,消息发送失败。

任务级通用入队函数的逻辑流程图如下:
在这里插入图片描述
中断级通用入队函数的流程图和上面大同小异,这里不做赘述。

队列上锁和解锁

任务级通用入队函数和中断级通用入队函数的时候都提到了队列的上锁和解锁,队列的上锁和解锁是两个 API 函数: prvLockQueue()和 prvUnlockQueue()。上
锁函数 prvLockQueue(),此函数本质上就是一个宏,定义如下:
在这里插入图片描述
解锁函数 prvUnlockQueue(),函数如下:

static void prvUnlockQueue( Queue_t * const pxQueue )
{
	/* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. */

	/* The lock counts contains the number of extra data items placed or
	removed from the queue while the queue was locked.  When a queue is
	locked items can be added or removed, but the event lists cannot be
	updated. */
	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? */
			{
				/* 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 )
					{
						/* The task waiting has a higher priority so record that
						a context switch is required. */
						vTaskMissedYield();
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}
				else
				{
					break;
				}
			}
			--cTxLock;
		}
		pxQueue->cTxLock = queueUNLOCKED;
	}
	taskEXIT_CRITICAL();
	/* Do the same for the Rx lock. */
	taskENTER_CRITICAL();
	{
		int8_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();
}

逻辑流程也比较简单,可以归纳如下:
在这里插入图片描述

从队列读取消息

和向列队发送消息一样,出队也有两类出队函数,分别是任务级和中断级。
任务级或中断级又再根据出队后是否删掉队列项分为xxxReceive和xxxpeek函数。

BaseType_t xQueueGenericReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait, const BaseType_t xJustPeeking )
参数:
xQueue: 队列句柄,指明要读取哪个队列的数据,创建队列成功以后会返回此队列的
队列句柄。
pvBuffer: 保存数据的缓冲区,读取队列的过程中会将读取到的数据拷贝到这个缓冲区
中。
xTicksToWait: 阻塞时间,此参数指示当队列空的时候任务进入阻塞态等待队列有数据的最大
时间。如果为 0 的话当队列空的时候就立即返回;当为 portMAX_DELAY 的
话就会一直等待, 直到队列有数据, 也就是死等, 但是宏
INCLUDE_vTaskSuspend 必须为 1。
xJustPeek: 标记当读取成功以后是否删除掉队列项,当为 pdTRUE 的时候就不用删除,
也就是说你后面再调用函数 xQueueReceive()获取到的队列项是一样的。当为
pdFALSE 的时候就会删除掉这个队列项。
返 回 值 : 
pdTRUE:从队列中读取数据成功。
pdFALSE: 从队列中读取数据失败。
BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue,  void * const pvBuffer )
参数:
xQueue: 队列句柄,指明要读取哪个队列的数据,创建队列成功以后会返回此队列的
队列句柄。
pvBuffer: 保存数据的缓冲区,读取队列的过程中会将读取到的数据拷贝到这个缓冲区
中。
pxTaskWoken: 标记退出此函数以后是否进行任务切换,这个变量的值是由函数来设置的,
用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值
为 pdTRUE 的时候在退出中断服务函数之前一定要进行一次任务切换。
返回值:
pdTRUE: 从队列中读取数据成功。
pdFALSE: 从队列中读取数据失败。
BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue,  void * const pvBuffer )
参数:
xQueue: 队列句柄,指明要读取哪个队列的数据,创建队列成功以后会返回此队列的
队列句柄。
pvBuffer: 保存数据的缓冲区,读取队列的过程中会将读取到的数据拷贝到这个缓冲区
中。
返回值:
pdTRUE: 从队列中读取数据成功。
pdFALSE: 从队列中读取数据失败

源码逻辑流程和入队差不多,这里就不再赘述。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

火山宝 && 王林宝

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值