队列

发布于 2023-08-11  995 次阅读


内容纲要

队列

队列结构体

Queue_t

typedef struct QueueDefinition
{
 int8_t * pcHead; /* 存储区域的起始地址 */
 int8_t * pcWriteTo; /* 下一个写入的位置 */
 /* 信号量是由队列实现的,
 * 此结构体能用于队列和信号量,
 * 当用于队列时,使用联合体中的 xQueue,
 * 当用于信号量时,使用联合体中的 xSemaphore
 */
 union
 {
 QueuePointers_t xQueue;
 SemaphoreData_t xSemaphore;
 } u;

 List_t xTasksWaitingToSend; /* 写入阻塞任务列表 */
 List_t xTasksWaitingToReceive; /* 读取阻塞任务列表 */
    volatile UBaseType_t uxMessagesWaiting; /* 非空闲项目的数量 */
 UBaseType_t uxLength; /* 队列的长度 */
 UBaseType_t uxItemSize; /* 队列项目的大小 */

 /* 锁用于在任务因队列操作被阻塞前,防止中断或其他任务操作队列。
 * 上锁期间,队列可以写入和读取消息,但不会操作队列阻塞任务列表,
 * 当有消息写入时,cTxLock 加 1,当有消息被读取时,cRxLock 加 1,
 * 在解锁时,会统一处理队列的阻塞任务列表
 */
 volatile int8_t cRxLock; /* 读取上锁计数器 */
 volatile int8_t cTxLock; /* 写入上锁计数器 */

 /* 同时启用了静态和动态内存管理 */
#if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && \
 ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
 uint8_t ucStaticallyAllocated; /* 静态创建标志 */
#endif

 /* 此宏用于使能启用队列集 */
#if ( configUSE_QUEUE_SETS == 1 )
 struct QueueDefinition * pxQueueSetContainer; /* 指向队列所在队列集 */
#endif

 /* 此宏用于使能可视化跟踪调试 */
#if ( configUSE_TRACE_FACILITY == 1 )
 /* 仅用于调试,不用理会 */
 UBaseType_t uxQueueNumber;
 /* 队列的类型
 * 0: 队列或队列集
 * 1: 互斥信号量
 * 2: 计数型信号量
 * 3: 二值信号量
 * 4: 可递归信号量
 */
 uint8_t ucQueueType;
#endif
} xQUEUE;
/* 重定义成 Queue_t */
typedef xQUEUE Queue_t;

​ 前面说过 FreeRTOS 基于队列实现了互斥信号量和递归互斥信号量功能,在队列的结构体 中,就包含了一个联合体 u,当队列结构体用作队列时,使用联合体 u 中的 xQueue,其数据类型为 QueuePointers_t,在 queue.c 文件中有定义,具体的代码如下所示:

typedef struct QueuePointers
{
 int8_t * pcTail; /* 存储区域的结束地址 */
 int8_t * pcReadFrom; /* 最后一次读取队列的位置 */
} QueuePointers_t;

​ 而当队列结构体用于互斥信号量和递归互斥信号量时,则是使用联合体 u 中的 xSemaphore, 其数据类型为 SemaphoreData_t,在 queue.c 文件中有定义,具体的代码如下所示:

typedef struct SemaphoreData
{
 TaskHandle_t xMutexHolder; /* 互斥信号量的持有者 */
 UBaseType_t uxRecursiveCallCount; /* 递归互斥信号量被递归获取计数器 */
} SemaphoreData_t;

创建队列

1. 函数 xQueueCreate()

​ 此函数用于使用动态方式创建队列,队列所需的内存空间由 FreeRTOS 从 FreeRTOS 管理 的堆中分配。函数 xQueueCreate()实际上是一个宏定义,在 queue.h 文件中有定义,具体的代码 如下所示:

#define xQueueCreate( uxQueueLength, \
 uxItemSize ) \
 xQueueGenericCreate( ( uxQueueLength ), \
 ( uxItemSize ), \
 ( queueQUEUE_TYPE_BASE ))

形参描述

返回值

2. 函数 xQueueCreateStatic()

#define xQueueCreateStatic( uxQueueLength, \
 uxItemSize, \
 pucQueueStorage, \
 pxQueueBuffer) \
 xQueueGenericCreateStatic( ( uxQueueLength ), \
 ( uxItemSize ), \
 ( pucQueueStorage ), \
 ( pxQueueBuffer ), \
 ( queueQUEUE_TYPE_BASE ))

形参描述

返回值

队列写入消息

1. 在任务中往队列写入消息的函数

​ 在任务中往队列写入消息的函数有函数 xQueueSend() 、 xQueueSendToBack() 、 xQueueSendToFront()、xQueueOverwrite(),这 4 个函数实际上都是宏定义,在 queue.h 文件中有 定义,具体的代码如下所示:

#define xQueueSend( xQueue, \
 pvItemToQueue, \
 xTicksToWait) \
 xQueueGenericSend( ( xQueue ), \
 ( pvItemToQueue ), \
 ( xTicksToWait ), \
 queueSEND_TO_BACK)
#define xQueueSendToBack( xQueue, \
 pvItemToQueue, \
 xTicksToWait) \
 xQueueGenericSend( ( xQueue ), \
 ( pvItemToQueue ), \
 ( xTicksToWait ), \
 queueSEND_TO_BACK)
#define xQueueSendToFront( xQueue, \
 pvItemToQueue, \
 xTicksToWait) \
 xQueueGenericSend( ( xQueue ), \
 ( pvItemToQueue ), \
 ( xTicksToWait ), \
 queueSEND_TO_FRONT)
#define xQueueOverwrite( xQueue, \
 pvItemToQueue) \
 xQueueGenericSend( ( xQueue ), \
 ( pvItemToQueue ), \
 0,\
 queueOVERWRITE)

​ 从上面的代码中可以看到,函数 xQueueSend()、函数 xQueueSendToBack()、函数 xQueueSendToFront()和函数 xQueueOverwrite()实际上都是调用了函数 xQueueGenericSend(),只 是指定了不同的写入位置,队列一共有 3 种写入位置,在 queue.h 文件中有定义,具体的代码 如下所示:

#define queueSEND_TO_BACK ( ( BaseType_t ) 0 ) /* 写入队列尾部 */
#define queueSEND_TO_FRONT ( ( BaseType_t ) 1 ) /* 写入队列头部 */
#define queueOVERWRITE ( ( BaseType_t ) 2 ) /* 覆写队列 */

​ 要注意的是,覆写方式写入队列,只有在队列的队列长度为 1 时,才能够使用,这在下文 讲解函数 xQueueGenericSend()时,会提到。 函 数 xQueueGenericSend() 用 于 在 任 务 中 往 队 列 的 指 定 位 置 写 入 消 息 。

函数xQueueGenericSend()

​ 函 数 xQueueGenericSend() 用 于 在 任 务 中 往 队 列 的 指 定 位 置 写 入 消 息 。 函 数xQueueGenericSend()的函数原型如下所示:

BaseType_t xQueueGenericSend( QueueHandle_t xQueue,
 const void * const pvItemToQueue,
 TickType_t xTicksToWait,
 const BaseType_t xCopyPosition);
形参描述

返回值

2. 在中断中往队列写入消息的函数

​ 在 任 务 中 往 队 列 写 入 消 息 的 函 数 有 函 数 xQueueSendFromISR()xQueueSendToBackFromISR()xQueueSendToFrontFromISR()、xQueueOverwriteFromISR(),这 4 个函数实际上都是宏定义,在 queue.h 文件中有定义,具体的代码如下所示:

#define xQueueSendFromISR( xQueue, \
 pvItemToQueue, \
 pxHigherPriorityTaskWoken) \
 xQueueGenericSendFromISR( ( xQueue ), \
 ( pvItemToQueue ), \
 ( pxHigherPriorityTaskWoken ), \
 queueSEND_TO_BACK)
#define xQueueSendToBackFromISR( xQueue, \
 pvItemToQueue, \
 pxHigherPriorityTaskWoken) \
 xQueueGenericSendFromISR( ( xQueue ), \
 ( pvItemToQueue ), \
 ( pxHigherPriorityTaskWoken ), \
 queueSEND_TO_BACK)
#define xQueueSendToFrontFromISR( xQueue, \
 pvItemToQueue, \
 pxHigherPriorityTaskWoken) \
 xQueueGenericSendFromISR( ( xQueue ), \
 ( pvItemToQueue ), \
 ( pxHigherPriorityTaskWoken ), \
 queueSEND_TO_FRONT)
#define xQueueOverwriteFromISR( xQueue, \
 pvItemToQueue, \
 pxHigherPriorityTaskWoken) \
 xQueueGenericSendFromISR( ( xQueue ), \
 ( pvItemToQueue ), \
 ( pxHigherPriorityTaskWoken ), \
 queueOVERWRITE)

​ 从上面的代码中可以看到,函数 xQueueSendFromISR()、函数 xQueueSendToBackFromISR()、 函数 xQueueSendToFrontFromISR()和函数 xQueueOverwriteFromISR()实际上都是调用了函数 xQueueGenericSendFromISR(),只是指定了不同的写入位置。

函数 xQueueGenericSendFromISR()

​ 函数 xQueueGenericSendFromISR()用于在中断中往队列的指定位置写入消息。函数 xQueueGenericSendFromISR()的函数原型如下所示:

BaseType_t xQueueGenericSendFromISR(
 QueueHandle_t xQueue,
 const void * const pvItemToQueue,
 BaseType_t * const pxHigherPriorityTaskWoken,
 const BaseType_t xCopyPosition);
形参描述

返回值

队列读取消息

函数 xQueueReceive()

​ 此函数用于在任务中,从队列中读取消息,并且消息读取成功后,会将消息从队列中移除。 消息的读取是通过拷贝的形式传递的,具体拷贝数据的大小,为队列项目的大小。该函数的函 数原型如下所示:

BaseType_t xQueueReceive( QueueHandle_t xQueue,
 void * const pvBuffer,
 TickType_t xTicksToWait);

形参描述

返回值

函数 xQueuePeek()

​ 此函数用于在任务中,从队列中读取消息,但与函数 xQueueReceive()不同,此函数在成功 读取消息后,并不会移除已读取的消息,这意味着,下次读取队列时,还能够读取到相同的内 容。消息的读取是通过拷贝的形式传递的,具体拷贝数据的大小,为队列项目的大小。该函数 的函数原型如下所示:

BaseType_t xQueuePeek( QueueHandle_t xQueue,
 void * const pvBuffer,
 TickType_t xTicksToWait);

形参描述

返回值

函数 xQueueReceiveFromISR()

​ 此函数用于在中断中,从队列中读取消息,并且消息读取成功后,会将消息从队列中移除。 消息的读取是通过拷贝的形式传递的,具体拷贝数据的大小,为队列项目的大小。该函数的函 数原型如下所示:

BaseType_t xQueueReceiveFromISR(
 QueueHandle_t xQueue,
 void * const pvBuffer,
 BaseType_t * const pxHigherPriorityTaskWoken);

形参描述

返回值

函数 xQueuePeekFromISR()

​ 此函数用于在中断中,从队列中读取消息,但与函数 xQueueReceiveFromISR()不同,此函 数在成功读取消息后,并不会移除已读取的消息,这意味着,下次读取队列时,还能够读取到 相同的内容。消息的读取是通过拷贝的形式传递的,具体拷贝数据的大小,为队列项目的大小。 该函数的函数原型如下所示:

BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue,
 void * const pvBuffer);

形参描述

返回值

队列锁

​ 在上文讲解队列操作的函数时,提到了队列的上锁与解锁,通过在队列的结构提供,也包 含了队列读取上锁计数器和队列写入上锁计数器。在队列被上锁后,可以往队列中写入消息和 读取消息,但是队列消息的读取和写入不会影响到队列读取和写入阻塞任务列表中的任务阻塞, 队列的写入和读取阻塞任务列表会在队列解锁后,统一处理。

​ 队列上锁的函数为 prvLockQueue(),函数 prvLockQueue()实际上是一个宏定义,在 queue.c 文件中有定义,具体的代码如下所示:

#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 成员变量就是队列的读取和写入上锁计数器,这两个 成员变量用来表示队列的上锁状态。

队列集

队列集相关API

函数 xQueueCreateSet()

​ 此函数用于创建队列集,该函数在 queue.c 文件中有定义,函数的原型如下所示:

QueueSetHandle_t xQueueCreateSet(const UBaseType_t uxEventQueueLength);
形参描述

返回值

函数 xQueueAddToSet()

​ 此函数用于往队列集中添加队列,要注意的时,队列在被添加到队列集之前,队列中不能 有有效的消息,该函数在 queue.c 文件中有定义,函数的原型如下所示:

BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore,
 QueueSetHandle_t xQueueSet);
形参描述

返回值

函数 xQueueRemoveFromSet()

​ 此函数用于从队列集中移除队列,要注意的时,队列在从队列集移除之前,必须没有有效 的消息,该函数在 queue.c 文件中有定义,函数的原型如下所示:

BaseType_t xQueueRemoveFromSet(QueueSetMemberHandle_t xQueueOrSemaphore,
 QueueSetHandle_t xQueueSet);
形参描述

返回值

函数 xQueueSelectFromSet()

​ 此函数用于在任务中获取队列集中有有效消息的队列,该函数在 queue.c 文件中有定义,函 数的原型如下所示:

QueueSetMemberHandle_t xQueueSelectFromSet(
 QueueSetHandle_t xQueueSet,
 TickType_t const xTicksToWait);
形参描述

返回值

函数 xQueueSelectFromSetFromISR()

​ 此函数用于在中断中获取队列集中有有效消息的队列,该函数在 queue.c 文件中有定义,函 数的原型如下所示:

QueueSetMemberHandle_t xQueueSelectFromSetFromISR(
 QueueSetHandle_t xQueueSet );
形参描述

返回值

世界のネズミは彼らが望むものに依存し、彼らは彼ら自身から誰も求めません
最后更新于 2023-08-27