FreeRTOS入门教程(队列的概念及相关函数介绍)


前言

本篇文章将带大家学习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) {
    
    
    // 数据成功从中断服务程序中查看队列
}

总结

本篇文章主要详细的讲解了队列的概念和队列的使用方法,看完文章后大家可以使用代码进行测试巩固复习。

猜你喜欢

转载自blog.csdn.net/m0_49476241/article/details/133612311