前言
本篇文章将带大家学习FreeRTOS中的队列,掌握什么是队列,并且学习如何使用队列,在什么场景会使用到队列。
一、队列概念
FreeRTOS中的队列(Queue)是一种用于在任务之间传递数据的数据结构,它遵循先进先出(FIFO)的原则。队列在实时嵌入式系统中非常有用,因为它们允许任务之间以安全和同步的方式共享信息,而无需使用全局变量或其他不安全的方法。以下是有关FreeRTOS中队列的详细解释:
1.队列的用途:
队列主要用于任务之间的数据传递和通信。它们提供了一种机制,使一个任务能够将数据发送到队列,而另一个任务可以从队列中接收数据。这种方式允许任务在不同的执行速度下工作,以及以异步的方式进行通信。
2.队列的特性:
FIFO原则:队列遵循先进先出的原则,即最早进入队列的数据项将首先被取出。
有限容量:队列通常有一定的容量限制,这意味着一旦队列满了,试图向其添加更多数据项的操作将被阻塞,直到有空间可用。
阻塞操作:队列的操作可以是阻塞的,这意味着任务可以在队列为空或队列已满时被阻塞,直到条件满足为止。
队列操作图解:
下面举一个两个任务之间使用队列进行通信的例子:
Task1不断向队列中写入数据,Task2不断从队列中读取数据,当读取完第一个数据时,队列中第二个数据将向前移动一个位置变成第一个数据。
二、队列的使用方法
使用队列的流程:创建队列、写队列、读队列、删除队列。
1.创建队列
动态创建
动态创建队列是在运行时使用函数来分配内存以创建队列。这需要使用动态内存分配函数,如pvPortMalloc()来分配内存,并使用vQueueDelete()函数来删除队列以释放内存。
动态创建队列:xQueueCreate()
QueueHandle_t xQueueCreate(UBaseType_t uxQueueLength, UBaseType_t uxItemSize);
uxQueueLength:这是队列的长度,即队列可以容纳的数据项数量。通常以数字的形式表示。
uxItemSize:这是队列中每个数据项的大小,以字节为单位。通常用sizeof()操作符来确定。
函数返回值:
如果队列创建成功,将返回一个有效的队列句柄(QueueHandle_t类型)。
如果队列创建失败,返回值将为NULL。
使用示例:
QueueHandle_t xQueue; xQueue = xQueueCreate(QUEUE_LENGTH, ITEM_SIZE); if (xQueue == NULL) { // 队列创建失败处理 } // 使用队列 vQueueDelete(xQueue);
静态创建
静态创建队列是在编译时通过定义全局变量或静态变量来创建队列。这种方法在编译时为队列分配内存,不需要在运行时动态分配和释放内存。这种方法适用于内存资源有限且可预先确定的情况。
静态创建队列:xQueueCreateStatic()
QueueHandle_t xQueueCreateStatic(UBaseType_t uxQueueLength, UBaseType_t uxItemSize, uint8_t *pucQueueStorageBuffer, StaticQueue_t *pxStaticQueue);
uxQueueLength:这是队列的长度,即队列可以容纳的数据项数量。通常以数字的形式表示。
uxItemSize:这是队列中每个数据项的大小,以字节为单位。通常用sizeof()操作符来确定。
pucQueueStorageBuffer:这是指向用于存储队列数据的静态缓冲区的指针。这个缓冲区的大小应该足够容纳指定长度和数据项大小的队列。
pxStaticQueue:这是指向StaticQueue_t类型的静态队列结构的指针。StaticQueue_t是FreeRTOS内部用于管理静态队列的数据结构。
函数返回值:
如果队列创建成功,将返回一个有效的队列句柄(QueueHandle_t类型)。
如果队列创建失败,返回值将为NULL。
使用示例:
xStaticQueue_t xQueueBuffer; StaticQueue_t xStaticQueue; QueueHandle_t xQueue; xQueue = xQueueCreateStatic(QUEUE_LENGTH, ITEM_SIZE, xQueueBuffer.ucQueueStorage, &xStaticQueue); if (xQueue == NULL) { // 队列创建失败处理 } // 使用队列
2.复位队列
复位队列:xQueueReset()
BaseType_t xQueueReset(QueueHandle_t xQueue);
xQueue:这是要复位的队列的句柄,即要操作的队列。
函数返回值:
如果成功复位队列,函数返回pdPASS。
如果队列复位失败,函数返回pdFAIL。
使用示例:
// 创建一个队列 QueueHandle_t xQueue = xQueueCreate(5, sizeof(int)); // 复位队列 if (xQueueReset(xQueue) == pdPASS) { // 队列复位成功 } else { // 队列复位失败 }
3.删除队列
删除队列:vQueueDelete()
void vQueueDelete(QueueHandle_t xQueue);
xQueue:要删除的队列的句柄,即要操作的队列。
使用示例:
// 创建一个队列 QueueHandle_t xQueue = xQueueCreate(5, sizeof(int)); // 删除队列 vQueueDelete(xQueue);
4.写队列
写队列:xQueueSend() 和 xQueueSendFromISR()
xQueueSend() 用于从任务中发送数据到队列。
BaseType_t xQueueSend(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait);
xQueue:要写入的队列的句柄。
pvItemToQueue:指向要发送的数据的指针。
xTicksToWait:如果队列已满,任务将等待的最大时间(以时钟节拍为单位),或者传递portMAX_DELAY以一直等待。
xQueueSendFromISR() 用于从中断服务程序中发送数据到队列。
BaseType_t xQueueSendFromISR(QueueHandle_t xQueue, const void *pvItemToQueue, BaseType_t *pxHigherPriorityTaskWoken);
xQueue:要写入的队列的句柄。
pvItemToQueue:指向要发送的数据的指针。
pxHigherPriorityTaskWoken:如果发送导致了高优先级任务唤醒,则设置为pdTRUE,否则为pdFALSE。
函数返回值:
xQueueSend() 成功写入队列返回pdPASS,否则返回pdFAIL。
xQueueSendFromISR() 成功写入队列返回pdPASS,否则返回pdFAIL。
使用示例:
// 创建一个队列 QueueHandle_t xQueue = xQueueCreate(5, sizeof(int)); int data = 42; // 写队列(任务中) if (xQueueSend(xQueue, &data, portMAX_DELAY) == pdPASS) { // 数据成功写入队列 } // 写队列(中断服务程序中) BaseType_t xHigherPriorityTaskWoken = pdFALSE; if (xQueueSendFromISR(xQueue, &data, &xHigherPriorityTaskWoken) == pdPASS) { // 数据成功写入队列并可能唤醒了高优先级任务 }
5.读队列
读队列:xQueueReceive() 和 xQueueReceiveFromISR()
xQueueReceive() 用于从任务中读取队列中的数据。
BaseType_t xQueueReceive(QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait);
xQueue:要读取的队列的句柄。
pvBuffer:指向存储接收数据的缓冲区的指针。
xTicksToWait:如果队列为空,任务将等待的最大时间(以时钟节拍为单位),或者传递portMAX_DELAY以一直等待。
xQueueReceiveFromISR() 用于从中断服务程序中读取队列中的数据。
BaseType_t xQueueReceiveFromISR(QueueHandle_t xQueue, void *pvBuffer, BaseType_t *pxHigherPriorityTaskWoken);
xQueue:要读取的队列的句柄。
pvBuffer:指向存储接收数据的缓冲区的指针。
pxHigherPriorityTaskWoken:如果接收导致了高优先级任务唤醒,则设置为pdTRUE,否则为pdFALSE。
函数返回值:
xQueueReceive() 成功接收数据返回pdPASS,否则返回pdFAIL。
xQueueReceiveFromISR() 成功接收数据返回pdPASS,否则返回pdFAIL。
使用示例:
// 创建一个队列 QueueHandle_t xQueue = xQueueCreate(5, sizeof(int)); int receivedData; // 从队列中读取数据(任务中) if (xQueueReceive(xQueue, &receivedData, portMAX_DELAY) == pdPASS) { // 数据成功从队列中读取 } // 从队列中读取数据(中断服务程序中) BaseType_t xHigherPriorityTaskWoken = pdFALSE; if (xQueueReceiveFromISR(xQueue, &receivedData, &xHigherPriorityTaskWoken) == pdPASS) { // 数据成功从队列中读取并可能唤醒了高优先级任务 }
6.查询队列
查询队列:uxQueueMessagesWaiting() 和 uxQueueSpacesAvailable()
这些函数用于查询队列中当前等待的消息数量和剩余可用空间。
UBaseType_t uxQueueMessagesWaiting(QueueHandle_t xQueue);
xQueue:要查询的队列的句柄。
UBaseType_t uxQueueSpacesAvailable(QueueHandle_t xQueue);
xQueue:要查询的队列的句柄。
使用示例:
// 创建一个队列 QueueHandle_t xQueue = xQueueCreate(5, sizeof(int)); // 查询队列中等待的消息数量 UBaseType_t messagesWaiting = uxQueueMessagesWaiting(xQueue); // 查询队列中剩余的可用空间 UBaseType_t spacesAvailable = uxQueueSpacesAvailable(xQueue);
7.覆盖/查看
覆盖
xQueueOverwrite()
BaseType_t xQueueOverwrite(QueueHandle_t xQueue, const void *pvItemToQueue);
xQueue:要覆写的队列句柄。
pvItemToQueue:指向要写入队列的数据的指针。
函数返回值:
如果成功写入队列,函数返回 pdPASS。
如果队列已满,数据将被覆写,函数同样返回 pdPASS。
如果队列操作失败,函数返回 errQUEUE_FULL。
使用示例:
QueueHandle_t xQueue = xQueueCreate(5, sizeof(int)); int data = 42; if (xQueueOverwrite(xQueue, &data) == pdPASS) { // 数据成功写入队列(即使队列已满) }
xQueueOverwriteFromISR()
BaseType_t xQueueOverwriteFromISR(QueueHandle_t xQueue, const void *pvItemToQueue, BaseType_t *pxHigherPriorityTaskWoken);
xQueue:要从中断服务程序中覆写的队列句柄。
pvItemToQueue:指向要写入队列的数据的指针。
pxHigherPriorityTaskWoken:如果操作导致较高优先级任务唤醒,则设置为 pdTRUE,否则为 pdFALSE。
函数返回值:
如果成功写入队列,函数返回 pdPASS。
如果队列已满,数据将被覆写,函数同样返回 pdPASS。
如果队列操作失败,函数返回 errQUEUE_FULL。
使用示例:
QueueHandle_t xQueue = xQueueCreate(5, sizeof(int)); int data = 42; BaseType_t xHigherPriorityTaskWoken = pdFALSE; if (xQueueOverwriteFromISR(xQueue, &data, &xHigherPriorityTaskWoken) == pdPASS) { // 数据成功从中断服务程序中写入队列(即使队列已满) }
查看
xQueuePeek()
BaseType_t xQueuePeek(QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait);
xQueue:要查看的队列句柄。
pvBuffer:存储查看结果的指针。
xTicksToWait:如果队列为空,任务将等待的最大时间(以时钟节拍为单位),或者传递 portMAX_DELAY 以一直等待。
函数返回值:
如果成功查看队列中的数据,函数返回 pdTRUE。
如果队列为空或等待超时,函数返回 pdFALSE。
使用示例:
QueueHandle_t xQueue = xQueueCreate(5, sizeof(int)); int peekedData; if (xQueuePeek(xQueue, &peekedData, 0) == pdTRUE) { // 数据成功从队列中查看 }
xQueuePeekFromISR()
BaseType_t xQueuePeekFromISR(QueueHandle_t xQueue, void *pvBuffer);
xQueue:要从中断服务程序中查看的队列句柄。
pvBuffer:存储查看结果的指针。
函数返回值:
如果成功查看队列中的数据,函数返回 pdTRUE。
如果队列为空,函数返回 pdFALSE。
使用示例:
QueueHandle_t xQueue = xQueueCreate(5, sizeof(int)); int peekedData; if (xQueuePeekFromISR(xQueue, &peekedData) == pdTRUE) { // 数据成功从中断服务程序中查看队列 }
总结
本篇文章主要详细的讲解了队列的概念和队列的使用方法,看完文章后大家可以使用代码进行测试巩固复习。