FreeRTOS学习(十二)——队列发送与接收
2026/7/29 20:20:48 网站建设 项目流程

上一节学习了FreeRTOS队列的基本概念,并通过xQueueCreate()创建了一个队列。

这一节开始真正使用队列,通过xQueueSend()xQueueReceive(),让两个任务之间完成数据传递。

这里使用一个简单例子:任务1每隔1秒产生一个数据并发送到队列,任务2负责从队列中读取数据并通过串口打印出来。


一、发送数据xQueueSend()

向队列发送数据可以使用:

BaseType_t xQueueSend( QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait );

第一个参数:

xQueue

表示要向哪个队列发送数据,也就是上一节创建得到的队列句柄。

第二个参数:

pvItemToQueue

表示要发送数据的地址。

第三个参数:

xTicksToWait

表示队列已满时,最多等待多少个Tick

例如:

xQueueSend( DataQueue, &data, pdMS_TO_TICKS(100) );

表示向DataQueue发送data,如果队列已经满了,最多等待100ms。

二、接收数据xQueueReceive()

从队列读取数据使用:

BaseType_t xQueueReceive( QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait );

第一个参数:

xQueue

表示从哪个队列读取数据。

第二个参数:

pvBuffer

表示接收数据的变量地址。

第三个参数:

xTicksToWait

表示队列为空时,最多等待多少个Tick

例如:

xQueueReceive( DataQueue, &ReceiveData, portMAX_DELAY );

如果队列中没有数据,当前任务就会进入阻塞态。

直到队列中出现数据后,任务才会重新进入就绪态。

三、创建队列

首先定义队列句柄:

QueueHandle_t DataQueue;

然后在main()中创建:

DataQueue = xQueueCreate( 5, sizeof(uint32_t) );

这里表示创建一个最多保存:

5个uint32_t数据

的队列。

队列创建成功以后,再创建发送任务和接收任务:

if(DataQueue != NULL) { xTaskCreate( SendTask, "SendTask", 128, NULL, 2, NULL ); xTaskCreate( ReceiveTask, "ReceiveTask", 128, NULL, 1, NULL ); vTaskStartScheduler(); }

四、编写发送任务

任务1负责产生数据:

void SendTask(void *pvParameters) { uint32_t data = 0; while(1) { data++; if(xQueueSend( DataQueue, &data, pdMS_TO_TICKS(100) ) == pdPASS) { printf("Send: %lu\r\n", (unsigned long)data); } vTaskDelay(pdMS_TO_TICKS(1000)); } }

首先定义:

uint32_t data = 0;

每次循环:

data++;

然后通过:

xQueueSend();

将数据发送到队列。

如果返回:

pdPASS

说明数据成功进入队列。

如果等待时间结束以后队列仍然没有空间,则发送失败。

五、编写接收任务

任务2负责读取数据:

void ReceiveTask(void *pvParameters) { uint32_t ReceiveData; while(1) { if(xQueueReceive( DataQueue, &ReceiveData, portMAX_DELAY ) == pdPASS) { printf("Receive: %lu\r\n", (unsigned long)ReceiveData); } } }

这里使用:

portMAX_DELAY

表示如果队列中没有数据,就一直等待。

因此刚开始运行时,如果队列为空:

ReceiveTask ↓ xQueueReceive() ↓ 没有数据 ↓ 进入阻塞态

此时它不会一直循环查询队列,也不会白白占用CPU。

六、数据到来以后发生什么

SendTask执行:

xQueueSend(DataQueue, &data, ...);

以后,数据被复制到队列中。

此时正在等待队列数据的:

ReceiveTask

就具备了继续运行的条件。

状态变化可以理解为:

ReceiveTask 阻塞态 ↓ 队列收到数据 ↓ 就绪态 ↓ 等待调度器运行

随后执行:

xQueueReceive();

把队列中的数据复制到:

ReceiveData

中。

七、程序运行现象

程序运行以后,可以通过串口看到类似:

Send: 1 Receive: 1 Send: 2 Receive: 2 Send: 3 Receive: 3 Send: 4 Receive: 4

任务1只负责产生数据。

任务2只负责处理数据。

两个任务之间通过:

SendTask ↓ DataQueue ↓ ReceiveTask

完成数据传递。

这就是FreeRTOS中非常典型的任务间通信方式。

八、等待时间怎么设置

队列函数最后一个参数非常重要。

例如:

xQueueReceive( DataQueue, &ReceiveData, 0 );

如果队列为空:

立即返回

不会等待。

如果写:

pdMS_TO_TICKS(100)

表示最多等待100ms。

如果写:

portMAX_DELAY

在相关FreeRTOS配置允许的情况下,可以让任务持续阻塞等待队列数据。

实际程序中,如果任务本身就是专门负责等待某类数据,使用阻塞等待通常比不断循环查询:

while(1) { xQueueReceive(..., 0); }

更加合理。

九、需要注意的问题

首先,发送的数据类型必须和创建队列时设置的元素大小一致。

例如创建:

xQueueCreate( 5, sizeof(uint32_t) );

那么这个队列就是按照uint32_t大小复制每一条数据。

其次,xQueueReceive()成功读取数据后,该条数据会从队列中移除。

所以队列中的:

10 20 30

读取一次后:

20 30

原来的10已经离开队列。

最后,不要在中断服务函数中直接使用普通的xQueueSend()xQueueReceive()。中断中需要使用带有FromISR后缀的接口,这部分后面单独学习。

十、总结

这一节学习了FreeRTOS队列最基本的发送和接收操作。

xQueueSend()负责把数据发送到队列,xQueueReceive()负责从队列读取数据。当队列满或者队列为空时,还可以通过等待时间让当前任务进入阻塞态,而不是一直占用CPU查询。

通过队列,可以把数据产生和数据处理拆分成不同任务:

发送任务 → 队列 → 接收任务

这样不同任务之间既能够交换数据,又不需要直接操作同一个变量。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询