FreeRTOS学习(十二)——队列发送与接收

发布时间:2026/7/29 20:20:51
FreeRTOS学习(十二)——队列发送与接收 上一节学习了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查询。通过队列可以把数据产生和数据处理拆分成不同任务发送任务 → 队列 → 接收任务这样不同任务之间既能够交换数据又不需要直接操作同一个变量。