操作系统的引入显著提升了软件开发的效率。通过功能划分,复杂系统可以分解为多个独立任务,每个任务专注于特定功能的实现。
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的使用注意事项
-
阻塞时间:合理设置xTicksToWait参数,避免任务长时间阻塞
-
队列长度:根据实际需求设置足够大的队列长度,防止数据丢失
-
数据大小:确保队列项目大小足够容纳要传递的数据
-
中断安全:在ISR中必须使用FromISR版本的API
-
优先级反转:高优先级任务长时间等待低优先级任务释放队列可能导致优先级反转
-
内存分配:确保FreeRTOS堆空间足够创建队列
-
线程安全:队列操作本身是线程安全的,但队列中的数据需要额外保护(如需要)
-
性能考虑:频繁的队列操作可能影响系统性能
六、Queue相关变量分配位置
-
队列控制块:由xQueueCreate()在FreeRTOS堆中动态分配
-
队列存储区:由xQueueCreate()在FreeRTOS堆中动态分配,用于存储队列项目
-
队列句柄:通常定义为全局变量或在任务栈中分配
-
发送/接收缓冲区:通常在任务栈中分配或为静态/全局变量
内存分配示例:
// 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任务间及任务与中断服务程序之间的数据通信,构建松耦合、高效率的嵌入式系统。