4.FreeRTOS学习笔记-消息队列

消息队列

  • 传输的数据不定长
  • 支持先进先出FIFO,同时支持后进先出LIFO
  • 均支持超时机制。
  • 每个消息队列都与消息空间在同一段连续的内存空间中
  • 消息队列的大小是消息队列控制块大小+(单个消息空间大小 * 消息队列长度)
  • 任务或者中断服务程序都可以给消息队列发送消息
  • 超时发送,超时发送失败返回 errQUEUE_FULL
  • 发送紧急消息,就是放在队列头部
  • 支持 消息读超时

同步互斥的方法对比

4.FreeRTOS学习笔记-消息队列_第1张图片

包含头文件

#include 
  • 消息队列运作模型
    4.FreeRTOS学习笔记-消息队列_第2张图片
    读消息的3中情况
  1. 有就取出,没有 扭头就走
  2. 没有等一会儿
  3. 死等消息
    发送消息
    队列满了就等会儿,超时了就返回errQUEUE_FULL

消息队列的内存结构

数据结构

typedef struct QueueDefinition
{
	int8_t *pcHead;					/*< 指向队列消息存储区起始位置,即第一个消息空间 */
	int8_t *pcTail;					/* 指向队列消息存储区结束位置地址 */
	int8_t *pcWriteTo;				/*< 指向队列消息存储区下一个可用消息空间. */

	union							/* 是一对互斥变量*/. */
	{
		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;/*当结构体用于互斥量时,uxRecursiveCallCount 用于计数 */
	} u;

	List_t xTasksWaitingToSend;		/*< 发送消息阻塞列表 */
	List_t xTasksWaitingToReceive;	/*< 个获取消息阻塞列表 */

	volatile UBaseType_t uxMessagesWaiting;/*< 当前消息队列的消息个数 */
	UBaseType_t uxLength;			/*< 队列的长度 */
	UBaseType_t uxItemSize;			/*< 示单个消息的大小*/

	volatile int8_t cRxLock;		/*< 队列上锁后,储存从队列收到的列表项数目,也就是出队的数量 如果队列没有上锁,设置为 queueUNLOCKED*/
	volatile int8_t cTxLock;		/*< 队列上锁后,储存发送到队列的列表项数目,也就是入队的数量 如果队列没有上锁,设置为 queueUNLOCKED*/

	#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;

4.FreeRTOS学习笔记-消息队列_第3张图片

消息队列函数

1. 创建消息队列

//宏定义
#define xQueueCreate( uxQueueLength, uxItemSize ) xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) )


QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength,// 队列能够存储的最大消息单元数目,能够存储的消息个数
 											UBaseType_t uxItemSize );//队列中消息单元的大小,以字节为单位
返回NULL,创建失败,成功返回队列句柄
枚举值 说明
queueQUEUE_TYPE_BASE 表示队列
queueQUEUE_TYPE_SET 表示队列集合 。
queueQUEUE_TYPE_MUTEX 表示互斥量。
queueQUEUE_TYPE_COUNTING_SEMAPHORE 表示计数信号量。
queueQUEUE_TYPE_BINARY_SEMAPHORE 表示二进制信号量。
queueQUEUE_TYPE_RECURSIVE_MUTEX 表示递归互斥量。

2. 消息队列删除函数

 void vQueueDelete( QueueHandle_t xQueue )

1. 发送消息函数

  1. 向队列尾部发送一个队列消息
  2. 消息以拷贝的形式入队,而不是以引用的形式
  3. 该xQueueCreate函数绝对不能在中断服务程序里面被调用,
  4. 中断中使用有中断保护功能的 xQueueSendFromISR()来代替

1.1 发送消息到–尾部

//
#define       xQueueSend( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )

#define xQueueSendToBack( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )

BaseType_t xQueueSend(QueueHandle_t xQueue,
									 const void * pvItemToQueue,
									 TickType_t xTicksToWait);

						//返回:
参数 说明
xQueue 队列句柄
pvItemToQueue 指针,指向要发送到队列尾部的队列消息
xTicksToWait 队列满时,等待队列空闲的最大超时时间,超时时间的单位为系统节拍周期,portMAX_DELAY 一直等指导成功
返回值 发送消息成功返回 pdTRUE,否则返回 errQUEUE_FULL表示队列已满。

1.2 发送消息到–头部


//
#define xQueueSendToFront( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_FRONT )

BaseType_t xQueueSendToFront( QueueHandle_t xQueue,
												 const void * pvItemToQueue,
												 TickType_t xTicksToWait );
参数 说明
xQueue 队列句柄
pvItemToQueue 指针,指向要发送到队列尾部的队列消息
xTicksToWait 队列满时,等待队列空闲的最大超时时间,超时时间的单位为系统节拍周期,portMAX_DELAY 一直等指导成功
返回值 发送消息成功返回 pdTRUE,否则返回 errQUEUE_FULL表示队列已满。

1.3 通用版本

其他的非中断发送都是使用他的宏定义实现

BaseType_t xQueueGenericSend( 
			QueueHandle_t xQueue, 
			const void * const pvItemToQueue, 
			TickType_t xTicksToWait, 
			const BaseType_t xCopyPosition ) }

参数 说明
xQueue 队列句柄
pvItemToQueue 指针,指向要发送到队列尾部的队列消息
xTicksToWait 队列满时,等待队列空闲的最大超时时间,超时时间的单位为系统节拍周期,portMAX_DELAY 一直等指导成功
xCopyPosition queueSEND_TO_BACK:发送到队尾;queueSEND_TO_FRONT:发送到队头;queueOVERWRITE:以覆盖的方式发送
返回值 发送消息成功返回 pdTRUE,否则返回 errQUEUE_FULL表示队列已满。

2. 中断保护版本的消息发送–中断版本

只能用于中断中执行,是不带阻塞机制的

2.1 发送消息到队列头

//
#define xQueueSendToFrontFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_FRONT )

BaseType_t xQueueSendToFrontFromISR(QueueHandle_t xQueue,
									 const void *pvItemToQueue,
									 BaseType_t *pxHigherPriorityTaskWoken);
参数 说明
xQueue 队列句柄
pvItemToQueue 指针,指向要发送到队列尾部的队列消息
pxHigherPriorityTaskWoken 如果入队导致一个任务解锁,并且解锁的任务优先级高于当前被中断的任务,将*pxHigherPriorityTaskWoken设置成 pdTRUE,然后在中断退出前需要进行一次上下文切换, 去 执 行 被 唤醒 的 优 先 级 更高 的 任 务 。从FreeRTOS V7.3.0 起,pxHigherPriorityTaskWoken 作为一个可选参数,可以设置为 NULL。
返回值 发送消息成功返回 pdTRUE,否则返回 errQUEUE_FULL表示队列已满。

2.2 发送消息到队列尾–中断版本


#define xQueueSendToBackFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK )

BaseType_t xQueueSendFromISR(QueueHandle_t xQueue,
											 const void *pvItemToQueue,
											 BaseType_t *pxHigherPriorityTaskWoken);
参数 说明
xQueue 队列句柄
pvItemToQueue 指针,指向要发送到队列尾部的队列消息
pxHigherPriorityTaskWoken 如果入队导致一个任务解锁,并且解锁的任务优先级高于当前被中断的任务,将*pxHigherPriorityTaskWoken设置成 pdTRUE,然后在中断退出前需要进行一次上下文切换, 去 执 行 被 唤醒 的 优 先 级 更高 的 任 务 。从FreeRTOS V7.3.0 起,pxHigherPriorityTaskWoken 作为一个可选参数,可以设置为 NULL。
返回值 发送消息成功返回 pdTRUE,否则返回 errQUEUE_FULL表示队列已满。

2.3 中断发送的通用版本–中断版本

 BaseType_t xQueueGenericSendFromISR(
										   QueueHandle_t		xQueue,
										   const	void	*pvItemToQueue,
										   BaseType_t	*pxHigherPriorityTaskWoken,
										   BaseType_t	xCopyPosition//{ queueSEND_TO_BACK:发送到队尾;queueSEND_TO_FRONT:发送到队头;queueOVERWRITE:以覆盖的方式发送 }
									   );
参数 说明
xQueue 队列句柄
pvItemToQueue 指针,指向要发送到队列尾部的队列消息
pxHigherPriorityTaskWoken 如果入队导致一个任务解锁,并且解锁的任务优先级高于当前被中断的任务,将*pxHigherPriorityTaskWoken设置成 pdTRUE,然后在中断退出前需要进行一次上下文切换, 去 执 行 被 唤醒 的 优 先 级 更高 的 任 务 。从FreeRTOS V7.3.0 起,pxHigherPriorityTaskWoken 作为一个可选参数,可以设置为 NULL。
xCopyPosition queueSEND_TO_BACK:发送到队尾;queueSEND_TO_FRONT:发送到队头;queueOVERWRITE:以覆盖的方式发送
返回值 发送消息成功返回 pdTRUE,否则返回 errQUEUE_FULL表示队列已满。

3. 消息读取函数-普通版本

这两个函数不能用于中断,是带有阻塞机制的

移除已经获取的消息

#define xQueueReceive( xQueue, pvBuffer, xTicksToWait ) xQueueGenericReceive( ( xQueue ), ( pvBuffer ), ( xTicksToWait ), pdFALSE )

获取消息但是不移除消息

#define xQueuePeek( xQueue, pvBuffer, xTicksToWait ) xQueueGenericReceive( ( xQueue ), ( pvBuffer ), ( xTicksToWait ), pdTRUE )

通用版本的实现

 BaseType_t xQueueGenericReceive(
		   QueueHandle_t	xQueue,
		   void	*pvBuffer,
		   TickType_t	xTicksToWait,
		   BaseType_t	xJustPeek,
		);

参数 说明
xQueue 队列句柄
pvItemToQueue 指针,指向接收到要保存的数据。
xTicksToWait 队列空时,阻塞超时的最大时间。如果该参数设置为 0,函数立刻返回。超时时间的单位为系统节拍周期,常量 portTICK_PERIOD_MS 用于辅助计算真实的时间,单位为 ms。如果 INCLUDE_vTaskSuspend 设置成 1,并且指定延时为 portMAX_DELAY 将导致任务无限阻塞(没有超时)。
xJustPeek 是否移除 pdFALSE 移除; pdTRUE 不移除
返回值 队列项接收成功返回 pdTRUE,否则返回 pdFALSE。

4. 消息读取函数-中断版本

4.1移除已经获取的消息

//移除已经获取的消息
BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue, //队列句柄。
													void * const pvBuffer, //指针,指向接收到要保存的数据。
													BaseType_t * const pxHigherPriorityTaskWoken )//
//返回值 队列项接收成功返回 pdTRUE,否则返回 pdFALSE。
参数 说明
xQueue 队列句柄
pvItemToQueue 指针,指向接收到要保存的数据。
pxHigherPriorityTaskWoken 任务在往队列投递信息时,如果队列满,则任务将阻塞在该队列上。如果 xQueueReceiveFromISR()到账了一个任 务 解 锁 了 则将 *pxHigherPriorityTaskWoken 设 置为pdTRUE , 否 则 *pxHigherPriorityTaskWoken 的 值将不变。从 FreeRTOS V7.3.0 起,pxHigherPriorityTaskWoken作为一个可选参数,可以设置为 NULL。
返回值 队列项接收成功返回 pdTRUE,否则返回 pdFALSE。

4.2不会把消息从该队列中移除

BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, //队列句柄
												 void * const pvBuffer )//指针,指向接收到要保存的数据。

消息队列使用注意事项

  1. 使用 xQueueSend()、xQueueSendFromISR()、xQueueReceive()等这些函数之前应先创建需消息队列,并根据队列句柄进行操作。
  2. 队列读取采用的是先进先出(FIFO)模式,会先读取先存储在队列中的数据。当然也 FreeRTOS 也支持后进先出(LIFO)模式,那读取的时候就会读取到后进队列的数据。
  3. 在获取队列中的消息时候,我们必须要定义一个存储读取数据的地方,并且该数据区域大小不小于消息大小,否则,很可能引发地址非法的错误。
  4. 无论是发送或者是接收消息都是以拷贝的方式进行,如果消息过于庞大,可以将消息的地址作为消息进行发送、接收

消息队列的内部机制

队列的 作用

  1. 解决互斥问题,
  2. 解决cpu空转的问题

队列的核心:关中断解决互斥问题,链表实现任务的休眠和唤醒,数组实现数据的保存

typedef struct QueueDefinition /* The old naming convention is used to prevent breaking kernel aware debuggers. */
{
    int8_t * pcHead;           /*< 保存数据空间的指针*/
    int8_t * pcWriteTo;        /*< Points to the free next place in the storage area. */

    union
    {
        QueuePointers_t xQueue;     /*< 消息队列的队列头*/
        SemaphoreData_t xSemaphore; /*<用于信号量的信号量头 */
    } u;

    List_t xTasksWaitingToSend;             /*是一个发送消息阻塞列表, 用于保存阻塞在此队列的任务, 任务按照优先级进行排序, 由于队列已满,想要发送消息的任务无法发送消息 */
    List_t xTasksWaitingToReceive;          /*是一个获取消息阻塞列表, 用于保存阻塞在此队列的任务, 任务按照优先级进行排序, 由于队列是空的,想要获取消息的任务无法获取到消息。*/

    volatile UBaseType_t uxMessagesWaiting; /*< 当前队列中保存的数据元素的个数 */
    UBaseType_t uxLength;                   /*队列的长度*/
    UBaseType_t uxItemSize;                 /*< 单个消息的大小 */
    
	/*
	这两个成员变量为 queueUNLOCKED 时,表示队列未上锁;当这两个成员变量为queueLOCKED_UNMODIFIED 时,表示队列上锁
	*/
    volatile int8_t cRxLock;                /*队列上锁后, 储存从队列收到的列表项数目, 也就是出队的数量,
如果队列没有上锁,设置为 queueUNLOCKED*/
    volatile int8_t cTxLock;                /*队列上锁后, 储存从队列收到的列表项数目, 也就是出队的数量,
如果队列没有上锁,设置为 queueUNLOCKED */

    #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;

消息队列的初始化

  1. 分配内存,内存包括消息的队列头的空间和保存消息的空间,消息的空间 = 消息的个数 * 单个消息的大小
  2. 初始化QueueDefinition 成员

发送消息函数

4.FreeRTOS学习笔记-消息队列_第4张图片

接收数据函数

4.FreeRTOS学习笔记-消息队列_第5张图片

你可能感兴趣的:(FreeRTOS,STM32,消息队列,freertos)