【FreeRTOS】任务间通讯1: 消息队列-Queue

    操作系统的引入显著提升了软件开发的效率。通过功能划分,复杂系统可以分解为多个独立任务,每个任务专注于特定功能的实现。

    FreeRTOS提供了多样化的任务调度策略,确保各个任务能够稳定运行。为了实现系统整体功能目标,任务间的有效协调和信息传递至关重要,这便涉及到任务间通信机制。

    FreeRTOS为开发者提供了多种任务间通信方法,后续将逐步展开详细介绍。今天我们来看看Queue---队列

一、 Queue概述

    Queue(队列)是FreeRTOS中实现任务间通信和任务与中断服务程序(ISR)间通信的主要机制。它允许任务或ISR以FIFO(先进先出)的方式发送和接收数据,支持一对一、一对多、多对一和多对多的通信模式。

二、Queue相关API函数

创建Queue

QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize );
  • 参数:

    • uxQueueLength: 队列能够存储的最大项目数

    • uxItemSize: 每个队列项目的大小(字节)

  • 返回值:成功返回QueueHandle_t句柄,失败返回NULL

发送数据到Queue

BaseType_t xQueueSend( QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait );
BaseType_t xQueueSendToFront( QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait );
BaseType_t xQueueSendToBack( QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait );
  • 参数:

    • xQueue: Queue句柄

    • pvItemToQueue: 指向要发送的数据的指针

    • xTicksToWait: 队列满时的最大等待时间(portMAX_DELAY表示无限等待)

  • 返回值:成功返回pdPASS,失败返回errQUEUE_FULL

从Queue接收数据

BaseType_t xQueueReceive( QueueHandle_t xQueue, void * pvBuffer, TickType_t xTicksToWait );
  • 参数:

    • xQueue: Queue句柄

    • pvBuffer: 接收数据的缓冲区指针

    • xTicksToWait: 队列空时的最大等待时间(portMAX_DELAY表示无限等待)

  • 返回值:成功返回pdPASS,失败返回errQUEUE_EMPTY

中断安全版本的Queue操作

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

其他Queue操作

UBaseType_t uxQueueMessagesWaiting( QueueHandle_t xQueue ); // 获取队列中当前项目数
void vQueueDelete( QueueHandle_t xQueue ); // 删除队列

三、 Queue的声明、使用和销毁步骤演示

   定义过程

// 某头文件:定义队列句柄
extern QueueHandle_t xDataQueue;


// 某C源文件:定义资源
#define QUEUE_LENGTH    10
#define ITEM_SIZE      sizeof(DataType)
QueueHandle_t xDataQueue;

// 某函数:创建队列(通常在初始化函数中)
xDataQueue = xQueueCreate(QUEUE_LENGTH, ITEM_SIZE);
if(xDataQueue == NULL) {
    // 队列创建失败处理
}

使用Queue发送数据

DataType dataToSend = {...};
if(xQueueSend(xDataQueue, &dataToSend, portMAX_DELAY) != pdPASS) {
    // 发送失败处理
}

使用Queue接收数据

DataType receivedData;
if(xQueueReceive(xDataQueue, &receivedData, portMAX_DELAY) == pdPASS) {
    // 成功接收到数据,进行处理
}

销毁Queue

vQueueDelete(xDataQueue);
xDataQueue = NULL;

 四、Queue传递数据实例

任务间通信示例

// 定义数据结构
typedef struct {
    uint8_t command;
    uint32_t parameter;
} Message_t;

// 创建队列
QueueHandle_t xMessageQueue = xQueueCreate(5, sizeof(Message_t));

// 发送任务
void vSenderTask(void *pvParameters) {
    Message_t message;
    while(1) {
        message.command = 0x01;
        message.parameter = 1234;
        xQueueSend(xMessageQueue, &message, portMAX_DELAY);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

// 接收任务
void vReceiverTask(void *pvParameters) {
    Message_t receivedMessage;
    while(1) {
        if(xQueueReceive(xMessageQueue, &receivedMessage, portMAX_DELAY) == pdPASS) {
            // 处理接收到的消息
            printf("Received cmd: %d, param: %lu\n", 
                   receivedMessage.command, 
                   receivedMessage.parameter);
        }
    }
}

任务与中断间通信示例

// 中断服务程序
void USART_IRQHandler(void) {
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    uint8_t receivedByte = USART_ReceiveData();
    
    // 将接收到的字节发送到队列
    xQueueSendFromISR(xUartQueue, &receivedByte, &xHigherPriorityTaskWoken);
    
    // 如果需要,执行上下文切换
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

// 任务处理接收到的数据
void vUartHandlerTask(void *pvParameters) {
    uint8_t byte;
    while(1) {
        if(xQueueReceive(xUartQueue, &byte, portMAX_DELAY) == pdPASS) {
            // 处理接收到的字节
            processUartByte(byte);
        }
    }
}

五、 Queue的使用注意事项

  1. 阻塞时间:合理设置xTicksToWait参数,避免任务长时间阻塞

  2. 队列长度:根据实际需求设置足够大的队列长度,防止数据丢失

  3. 数据大小:确保队列项目大小足够容纳要传递的数据

  4. 中断安全:在ISR中必须使用FromISR版本的API

  5. 优先级反转:高优先级任务长时间等待低优先级任务释放队列可能导致优先级反转

  6. 内存分配:确保FreeRTOS堆空间足够创建队列

  7. 线程安全:队列操作本身是线程安全的,但队列中的数据需要额外保护(如需要)

  8. 性能考虑:频繁的队列操作可能影响系统性能

六、Queue相关变量分配位置

  1. 队列控制块:由xQueueCreate()在FreeRTOS堆中动态分配

  2. 队列存储区:由xQueueCreate()在FreeRTOS堆中动态分配,用于存储队列项目

  3. 队列句柄:通常定义为全局变量或在任务栈中分配

  4. 发送/接收缓冲区:通常在任务栈中分配或为静态/全局变量

内存分配示例:

// FreeRTOSConfig.h中配置堆大小
#define configTOTAL_HEAP_SIZE ((size_t)(120 * 1024)) // 120KB堆空间

// 队列控制块和存储区在堆中的分配
// 当调用xQueueCreate()时,FreeRTOS会在堆中分配:
// sizeof(Queue_t) + (uxQueueLength * uxItemSize) 字节的内存

// 队列句柄通常定义为全局变量
QueueHandle_t xGlobalQueue;

void vFunction(void) {
    // 也可以在函数内定义为局部变量(但需要确保生命周期)
    QueueHandle_t xLocalQueue;
    
    // 队列项目缓冲区通常在任务栈中
    DataType localBuffer;
}

    小结:通过合理使用Queue,可以有效地实现FreeRTOS任务间及任务与中断服务程序之间的数据通信,构建松耦合、高效率的嵌入式系统。

<think>好的,我需要回答用户关于FreeRTOS消息队列的使用方法和示例的问题。首先,我应该回顾用户提供的引用内容,确保回答准确并正确引用相关部分。 用户提到了队列集、创建函数、接收函数的不同版本(任务和ISR)、静态创建方法以及队列读取时的超时处理。我需要将这些信息整合到回答中。 首先,消息队列的基本概念。根据引用[1],队列集的作用是聚合多个队列,但用户主要问的是消息队列本身,所以可能需要先介绍队列的基本用途,比如任务间通信,传递数据或信号。 接下来是创建队列。引用[1]提到了xQueueCreateSet,但那是队列集的创建。用户需要的是普通队列的创建。根据引用[3],静态创建使用xQueueCreateStatic,而动态创建应该是xQueueCreate。需要确认函数名和参数是否正确。例如,动态创建通常是xQueueCreate(uxQueueLength, uxItemSize),返回QueueHandle_t。 然后,发送和接收消息的函数。引用[2]提到了xQueueSend和xQueueReceive,以及它们的ISR版本。需要说明在任务中使用时使用xQueueSend,在中断中使用xQueueSendFromISR,并注意pxTaskWoken参数的作用,可能触发上下文切换。 示例部分,用户可能需要一个简单的代码示例,比如创建队列,发送数据,接收数据。需要确保代码符合FreeRTOS的语法,比如正确的函数名和参数。同时,注意超时处理,如引用[4]提到的,当队列为空时,超时返回pdFALSE。 此外,需要注意队列的阻塞机制,当队列满或空时任务进入阻塞状态,释放CPU资源,这点可能也需要提及。 关于注意事项,需要提醒不能在中断中使用阻塞函数,必须使用FromISR版本,以及优先处理时间敏感操作。另外,队列传递的是数据拷贝,大块数据可能影响性能,建议使用指针传递。 最后生成相关问题,可以围绕队列的阻塞行为、中断中的使用区别、队列集的应用场景、性能优化以及错误处理等方面提问。 需要检查所有技术术语的正确性,比如函数名、参数类型,并确保引用标记正确。例如,创建队列的函数在引用[3]中提到了静态创建,而动态创建可能在引用[1]或其他地方。可能需要确认xQueueCreate是否在提供的引用中有提到,但用户提供的引用中没有明确提到动态创建函数,所以可能需要根据FreeRTOS的常见用法来补充,同时确保不与引用内容冲突。 此外,用户提供的引用[4]提到超时返回pdFALSE,因此在接收函数示例中需要体现这一点。 最后,确保回答结构清晰,分步骤说明使用方法,并给出代码示例,然后注意事项和相关问题。保持语言简洁,符合中文表达习惯。</think>### FreeRTOS消息队列Queue)使用方法及示例 #### 一、消息队列基础概念 消息队列FreeRTOS中实现任务间通信的核心机制,支持**异步数据传输**和**同步控制**。队列采用FIFO(先进先出)原则,允许任务或中断服务程序(ISR)发送/接收固定大小的数据块,并通过阻塞机制实现资源高效管理[^1][^4]。 --- #### 二、队列操作步骤 ##### 1. 创建队列 - **动态创建**(常用) 使用 `xQueueCreate()` 函数,返回队列句柄: ```c QueueHandle_t xQueueCreate(UBaseType_t uxQueueLength, UBaseType_t uxItemSize); ``` - `uxQueueLength`:队列容量(最大消息数) - `uxItemSize`:每个消息的字节大小 示例:`QueueHandle_t queue = xQueueCreate(5, sizeof(int));` 创建容量为5的整型队列[^3]。 - **静态创建** 使用 `xQueueCreateStatic()`,需预分配内存: ```c QueueHandle_t xQueueCreateStatic( UBaseType_t uxQueueLength, UBaseType_t uxItemSize, uint8_t *pucQueueStorageBuffer, StaticQueue_t *pxQueueBuffer ); ``` ##### 2. 发送消息 - **在任务中发送** 使用 `xQueueSend()` 或 `xQueueSendToBack()`(默认队尾入队): ```c BaseType_t xQueueSend(QueueHandle_t xQueue, const void *pvItemToSend, TickType_t xTicksToWait); ``` 示例:发送整型数据,阻塞等待10个时钟节拍: ```c int data = 42; xQueueSend(queue, &data, pdMS_TO_TICKS(10)); ``` - **在中断中发送** 使用 `xQueueSendFromISR()`,避免阻塞: ```c BaseType_t xQueueSendFromISR(QueueHandle_t xQueue, const void *pvItemToSend, BaseType_t *pxTaskWoken); ``` ##### 3. 接收消息 - **在任务中接收** 使用 `xQueueReceive()`: ```c BaseType_t xQueueReceive(QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait); ``` 示例:接收数据,若队列为空则阻塞100ms: ```c int received_data; if (xQueueReceive(queue, &received_data, pdMS_TO_TICKS(100)) == pdTRUE) { // 处理数据 } ``` - **在中断中接收** 使用 `xQueueReceiveFromISR()`: ```c BaseType_t xQueueReceiveFromISR(QueueHandle_t xQueue, void *pvBuffer, BaseType_t *pxTaskWoken); ``` --- #### 三、完整示例代码 ```c #include "FreeRTOS.h" #include "task.h" #include "queue.h" QueueHandle_t xQueue; // 发送任务 void vSenderTask(void *pvParameters) { int value = 0; while (1) { xQueueSend(xQueue, &value, portMAX_DELAY); // 阻塞直到发送成功 value++; vTaskDelay(pdMS_TO_TICKS(1000)); } } // 接收任务 void vReceiverTask(void *pvParameters) { int received_value; while (1) { if (xQueueReceive(xQueue, &received_value, pdMS_TO_TICKS(2000)) == pdTRUE) { printf("Received: %d\n", received_value); } else { printf("Timeout!\n"); // 超时处理 } } } int main() { xQueue = xQueueCreate(5, sizeof(int)); xTaskCreate(vSenderTask, "Sender", configMINIMAL_STACK_SIZE, NULL, 1, NULL); xTaskCreate(vReceiverTask, "Receiver", configMINIMAL_STACK_SIZE, NULL, 2, NULL); vTaskStartScheduler(); return 0; } ``` --- #### 四、关键注意事项 1. **阻塞行为** - 发送时若队列满,任务进入阻塞状态(由 `xTicksToWait` 指定超时时间)[^4]。 - 接收时若队列空,任务同样阻塞,释放CPU资源。 2. **中断安全** - 在ISR中必须使用 `FromISR` 后缀的函数,且不可指定阻塞时间[^2]。 3. **数据拷贝机制** - 队列通过内存拷贝传递数据,传递大型数据时建议使用指针(需确保内存有效性)。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值