FreeRTOS_第17章_消息队列_函数讲解

目录

 

简介:

为什么通讯要用消息队列:

队列定义:

创建消息队列:

要点一:

要点二:

要点三:

消息队列删除函数:

 


简介:

本文是 [野火®]《FreeRTOS 内核实现与应用开发实战—基于STM32》 这本书第第17章消息队列的一些资料整理。

书籍下载链接:https://pan.baidu.com/s/1dvoYbFf_p5bwlJPDIsiiZA   提取码:q01a 

为什么通讯要用消息队列:

使用全局变量不可以?是可以使用全局变量的,但是任务之间的同步超时的问题不好解决,使用全局变量要考虑怎么让任务轮询查询这个全局变量,还要考虑轮询的顺序。而消息队列则可以避免这些问题。具体避免的方法,需要详细查看下面代码。

队列定义:

这是队列的控制信息,存放一些自身属性,最重要的是存放阻塞队列指针,这是实现任务之间同步的关键!

/*
 * Definition of the queue used by the scheduler.
 * Items are queued by copy, not reference.  See the following link for the
 * rationale: http://www.freertos.org/Embedded-RTOS-Queues.html
 */
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). */
	{
		//pcReadFrom指向出队消息空间的最后一个
		//当队列用作互斥量时,uxRecursiveCallCount 用于计数,记录递归互斥量被“调用”的次数
		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;

/* The old xQUEUE name is maintained above then typedefed to the new Queue_t
name below to enable the use of older kernel aware debuggers. */
typedef xQUEUE Queue_t;

创建消息队列:

要点一:

队列不单纯可以用来存数据,还可以用于以下几个用途。

其中队列控制头中的联合体union{  int8_t *pcReadFrom;  UBaseType_t uxRecursiveCallCount; } u; 平常情况下*pcReadFrom用于存放出队的读取数据的地址,但是用于互斥量的情况下就使用uxRecursiveCallCount用来记录互斥量的调用次数

*参数const uint8_t        ucQueueType            消息队列类型。
*                                    =queueQUEUE_TYPE_BASE:                                       表示队列 。 
*                                    =queueQUEUE_TYPE_SET:                                         表示队列集合 。 
*                                    =queueQUEUE_TYPE_MUTEX:                                    表示互斥量。 
*                                    =queueQUEUE_TYPE_COUNTING_SEMAPHORE:    表示计数信号量 。
*                                    =queueQUEUE_TYPE_BINARY_SEMAPHORE:          表示二进制信号量 。
*                                    =queueQUEUE_TYPE_RECURSIVE_MUTEX :            表示递归互斥量。

要点二:

队列的大小包含控制块和消息空间。其中队列控制头包含两个阻塞队列,List_t xTasksWaitingToSend,List_t xTasksWaitingToReceive,这个是任务同步的核心。具体如下图所示。

 

要点三:

函数流程图和详解如下。

 </pre>
 * \defgroup xQueueCreate xQueueCreate
 * \ingroup QueueManagement
 */
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
	#define xQueueCreate( uxQueueLength, uxItemSize ) xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) )
#endif
/**************************************************************************************************
* 函数:                                   xQueueGenericCreate
* 说明:  
* 输入:  
*		const UBaseType_t 	uxQueueLength	队列长度	
*		const UBaseType_t 	uxItemSize		队列元素的字节数
*		const uint8_t 		ucQueueType		队列类型
* 关联变量: 
* 返回:  
* 注释作者:  日南方  11/20/2020
**************************************************************************************************/
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
	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;
	}

#endif /* configSUPPORT_STATIC_ALLOCATION */
/**************************************************************************************************
* 函数:                                   prvInitialiseNewQueue
* 说明:  
* 输入:  
*		const UBaseType_t 	uxQueueLength		消息队列长度。
*		const UBaseType_t 	uxItemSize			单个消息大小。
*		uint8_t 			*pucQueueStorage	存储消息起始地址。
*		const uint8_t 		ucQueueType			消息队列类型。
* 							=queueQUEUE_TYPE_BASE:					表示队列 。 
*							=queueQUEUE_TYPE_SET:					表示队列集合 。 
*							=queueQUEUE_TYPE_MUTEX:					表示互斥量。 
*							=queueQUEUE_TYPE_COUNTING_SEMAPHORE:	表示计数信号量 。
*							=queueQUEUE_TYPE_BINARY_SEMAPHORE:		表示二进制信号量 。
*							=queueQUEUE_TYPE_RECURSIVE_MUTEX :		表示递归互斥量。
*		Queue_t 			*pxNewQueue			消息队列控制块。
* 关联变量: 
* 返回:  
* 注释作者:  日南方  11/20/2020
**************************************************************************************************/
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. */
		//没有为消息存储分配内存,但是 pcHead 指针不能设置为 NULL,
 		//因为队列用作互斥量时,pcHead 要设置成 NULL。
 		//这里只是将 pcHead 指向一个已知的区域 
		pxNewQueue->pcHead = ( int8_t * ) pxNewQueue;
	}
	else
	{
		/* Set the head to the start of the queue storage area. */
		//设置 pcHead 指向存储消息的起始地址
		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;
}

消息队列删除函数:

这没啥好说的,就一个释放空间的子函数vPortFree( pxQueue );

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值