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

简介: 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) {
    // 数据成功从中断服务程序中查看队列
}

总结

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


相关文章
|
5月前
|
调度 C++
cpp随笔——如何实现一个简单的调度程序
调度程序用于周期性运行服务,如生成测试数据或定时发送消息。核心思路是通过`fork`创建子进程,父进程退出,子进程由init进程管理,实现后台运行。示例C++代码展示了一个简单的调度器,接收运行周期和程序参数,不断 fork 新进程执行指定程序,`execv` 用于执行命令。当程序结束,调度器等待一定时间(周期)后重启。程序设计确保不受外部干扰,并忽略SIGCHLD信号避免僵尸进程。
|
5月前
|
消息中间件 API
【FreeRTOS(二)】FreeRTOS新手入门——计数型信号量和二进制信号量的基本使用并附代码解析
【FreeRTOS(二)】FreeRTOS新手入门——计数型信号量和二进制信号量的基本使用并附代码解析
|
6月前
|
消息中间件 调度
FreeRTOS入门教程(任务状态)
FreeRTOS入门教程(任务状态)
243 0
|
5月前
|
存储 安全 算法
从0入门FreeRTOS之第二节FreeRTOS的体系结构
FreeRTOS的体系结构设计精巧且高效,专为嵌入式系统量身打造。理解FreeRTOS的体系结构对开发高效、稳定的嵌入式应用至关重要。下面,我们详细介绍FreeRTOS的核心组件、内核机制、中断管理和内存管理等内容。
190 0
|
5月前
|
传感器 物联网 调度
从0入门FreeRTOS之第一节 什么是FreeRTOS?
FreeRTOS(Free Real-Time Operating System)是一款开源的实时操作系统(RTOS),专为嵌入式系统设计。由Real Time Engineers Ltd.开发和维护,FreeRTOS以其小巧、高效、易于使用的特点广受欢迎。FreeRTOS支持多种微控制器和微处理器平台,提供丰富的实时操作系统功能,使开发者能够轻松构建高效、实时响应的应用程序。
342 0
|
存储 消息中间件 API
FreeRTOS入门教程(堆和栈)
FreeRTOS入门教程(堆和栈)
344 0
|
6月前
|
算法 调度
FreeRTOS入门教程(互斥锁的概念和函数使用)
FreeRTOS入门教程(互斥锁的概念和函数使用)
341 0
|
6月前
|
API
FreeRTOS入门教程(队列详细使用示例)
FreeRTOS入门教程(队列详细使用示例)
191 0
|
6月前
|
存储
FreeRTOS入门教程(事件组概念和函数使用)
FreeRTOS入门教程(事件组概念和函数使用)
130 0
|
6月前
|
存储 调度
FreeRTOS深入教程(队列内部机制和源码分析)
FreeRTOS深入教程(队列内部机制和源码分析)
147 0