FreeRTOS消息队列详解第二讲(全网最全)——队列创建

今天主要来讲讲消息队列相关的API。

一、队列创建

1、函数xQueueCreate()
该函数本质上是一个宏,用来动态创建队列,该宏最终调用的是函数xQueueGenericCreate(),函数原型如下:

xQueueCreate( uxQueueLength, uxItemSize )
参数 描述
uxQueueLength 要创建的队列的队列长度,这里是队列的项目数。
uxItemSize 队列中每个项目(消息)的长度,单位为字节
返回值 队列创捷成功以后返回的队列句柄!

2、函数xQueueCreateStatic()
此函数也是用于创建队列的,但是使用的静态方法创建队列,队列所需要的内存由用户自行分配,此函数本质上也是一个宏,此宏最终调用的是函数xQueueGenericCreateStatic(),函数原型如下:

QueueHandle_t xQueueCreateStatic(
UBaseType_t uxQueueLength, 
uxUBaseType_t uxItemSize,
uint8_t* pucQueueStorageBuffer,
StaticQueue_t* pxQueueBuffer)
参数 描述
uxQueueLength 要创建的队列的队列长度,这里是队列的项目数
uxItemSize 队列中每个项目(消息)的长度,单位为字节
pucQueueStorageBuffer 指向队列项目的存储区,也就是消息的存储区,这个存储区需要用户自行分配。此参数必须指向一个uint8_t类型的数组。这个存储区要大于等于(uxQueueLength*uxItemsSize)字节
pxQueueBuffer 此参数指向一个StaticQueue_t类型的变量,用来保存队列结构体
返回值 创建成功返回队列句柄

3、函数xQueueGenericCreate()
函数xQueueGenericCreate()用于动态创建队列,创建队列过程中需要的内存均通过FreeRTOS中的动态内存管理函数pvPortMalloc()分配,函数原型如下:

QueueHandle_t xQueueGenericCreate(
const UBaseType_t uxQueueLength,
const UBaseType_t uxItemSize,
const uint8_t ucQueueType)
参数 描述
uxQueueLength 要创建的队列的队列长度,这里是队列的项目数。
uxItemSize 队列中每个项目(消息)的长度,单位为字节。
ucQueueType 队列类型,由于FreeRTOS中的信号量等也是通过队列来实现的,创建信号量的函数最终也是使用此函数的,因此在创建的时候需要指定此队列的用途,也就是队列类型,一共有六种类型
返回值 创建成功返回队列句柄,NULL表示创建失败

其中六种类型为:

  • ①、queueQUEUE_TYPE_BASE普通消息队列
  • ②、queueQUEUE_TYPE_SET队列集
  • ③、queueQUEUE_TYPE_MUTEX互斥信号量
  • ④、queueQUEUE_TYPE_COUNTING_SEMAP
    HORE计数型信号量
  • ⑤、queueQUEUE_TYPE_BINARY_SEMAPHO
    RE 二值信号量
  • ⑥、queueQUEUE_TYPE_RECURSIVE_MUTEX 递归互斥信号量

函数xQueueCreate()创建队列的时候此参数默认选择的就是queueQUEUE_TYPE_BASE。

4、函数xQueueGenericCreateStatic()
此函数用于动态创建队列,创建队列过程中需要的内存需要由用户自行分配好,函数原型如下:

QueueHandle_t xQueueGenericCreateStatic(
const UBaseType_t uxQueueLength,
const UBaseType_t uxItemSize,
uint8_t* pucQueueStorage,
StaticQueue_t* pxStaticQueue,
const uint8_t ucQueueType)
参数 描述
uxQueueLength 要创建的队列的队列长度,这里是队列的项目数。
uxItemSize 队列中每个项目(消息)的长度,单位为字节。
pucQueueStorage 指向队列项目的存储区,也就是消息的存储区,这个存储区需要用户自行分配。此参数必须指向一个uint8_t类型的数组。这个存储区要大于等于(uxQueueLength*uxItemsSize)字节。
pxStaticQueue 此参数指向一个StaticQueue_t类型的变量,用来保存队列结构体。
ucQueueType 队列类型。
返回值 创建成功返回队列句柄,NULL表示创建失败

二、队列创建函数详解

1、队列创建函数详解
最终完成队列创建的函数有两个,一个是静态方法的xQueueGenericCreateStatic(),另外一个就是动态方法的xQueueGenericCreate()。现在来详细的分析一下动态创建函数xQueueGenericCreate(),静态方法大同小异,可以自行分析一下。
函数xQueueGenericCreate()在文件queue.c中有如下定义:

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 )
	{
		//队列项大小为0,就不需要存储区
		xQueueSizeInBytes = ( size_t ) 0;
	}
	else
	{
		//分配足够的存储区,确保随时随地可以保存所有消息
		xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize );           (1)
	}

	pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes );   (2)
//内存分配成功
	if( pxNewQueue != NULL )
	{
		pucQueueStorage = ( ( uint8_t * ) pxNewQueue ) + sizeof( Queue_t ); (3)

		#if( configSUPPORT_STATIC_ALLOCATION == 1 )
		{
			//队列是使用动态方法创建的,所以队列字段ucStaticallyAllocated标记为pdFALSE
			pxNewQueue->ucStaticallyAllocated = pdFALSE;
		}
		#endif 

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

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

2、队列初始化函数详解
队列初始化函数prvInitialiseNewQueue() 用于队列的初始化,此函数在文件queue.c中有定义,函数代码如下:

static void prvInitialiseNewQueue( 
const UBaseType_t uxQueueLength, 
const UBaseType_t uxItemSize, 
uint8_t *pucQueueStorage, 
const uint8_t ucQueueType, 
Queue_t *pxNewQueue )
{
//防止编译器报错
	( void ) ucQueueType;

	if( uxItemSize == ( UBaseType_t ) 0 )
	{
//队列项(消息)长度为0,说明没有队列存储区,这里将pcHead指向队列开始地址
		pxNewQueue->pcHead = ( int8_t * ) pxNewQueue;
	}
	else
	{
//设置pcHead指向队列项存储区首地址
		pxNewQueue->pcHead = ( int8_t * ) pucQueueStorage;(1)
	}
//初始化队列结构体相关成员变量
	pxNewQueue->uxLength = uxQueueLength;(2)
	pxNewQueue->uxItemSize = uxItemSize;
	( void ) xQueueGenericReset( pxNewQueue, pdTRUE );(3)

	#if ( configUSE_TRACE_FACILITY == 1 )//跟踪调试相关字段初始化
	{
		pxNewQueue->ucQueueType = ucQueueType;
	}
	#endif 

	#if( configUSE_QUEUE_SETS == 1 )//队列集相关字段初始化
	{
		pxNewQueue->pxQueueSetContainer = NULL;
	}
	#endif

	traceQUEUE_CREATE( pxNewQueue );
}
  • (1)、队列结构体中的成员变量pcHead指向队列存储区中首地址。
  • (2)、初始化队列结构体中的成员变量uxQueueLength和uxItemSize,这两个成员变量保存队列的最大队列项目和每个队列项大小。
  • (3)、调用函数xQueueGenericReset()复位队列。

3、队列复位函数详解
队列初始化函数 prvInitialiseNewQueue()中调用了函数xQueueGenericReset()来复位队列,函数xQueueGenericReset()代码如下:

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 );(1)
		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 )(2)
		{
			/* 由于复位队列以后队列依旧是空的,所以对于那些由于出队(从队列中读取消息)而阻塞的任务就依旧保持阻塞状态。但是对于那些由于入队(向队列中发送消息)而阻塞的任务就不一样。这些任务要解除阻塞状态,从队列的相应列表中移除 */
			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 ) );(3)
			vListInitialise( &( pxQueue->xTasksWaitingToReceive ) );
		}
	}
	taskEXIT_CRITICAL();

	/* A value is returned for calling semantic consistency with previous
	versions. */
	return pdPASS;
}
  • (1)、初始化队列中的相关成员变量。
  • (2)、根据参数xNewQueue确定要复位的队列是否是新创建的队列,如果不是的话还需要做其他的处理。
  • (3)、初始化队列中的列表xTasksWaitingToSend和xTasksWaitingToReceive。至此,队列创建成功,比如我们创建一个有4个队列项,每个队列项长度为32个字节的队列TestQueue,创建成功的队列如下图所示:FreeRTOS消息队列详解第二讲(全网最全)——队列创建_第1张图片

FreeRTOS的消息队列创建函数就讲解到这里啦!下一讲讲队列发送消息函数!

你可能感兴趣的:(FreeRTOS操作系统,嵌入式,消息队列,创建队列,freertos,1024程序员节)