FreeRTOS队列详解:从原理到实战
发布时间:2026/9/30 8:52:54来源:尧图网络
1.1 前课准备1.1.1 课程内容了解 FreeRTOS 的队列掌握 FreeRTOS 队列的使用场景学会如何利用队列实现任务间的通信熟悉队列的 API。1.1.2 课程资料《FreeRTOS 中文实战教程》第 2 章。1.2 FreeRTOS 队列概述1.2.1 使用背景在实际的应用中常常会遇到一个任务或者中断服务函数需要和另外一个任务进行“沟通交流”这个“沟通交流”的过程其实就是消息传递的过程。在没有操作系统的时候两个应用程序进行消息传递一般使用全局变量的方式但是如果在使用操作系统的应用中用全局变量来传递消息就会涉及到“资源管理”的问题。FreeRTOS 对此提供了一个叫做“队列”的机制来完成任务与任务、任务与中断之间的消息传递。1.2.2 队列介绍队列是为了任务与任务、任务与中断之间的通信而准备的可以在任务与任务、任务与中断之间传递消息。队列中可以存储有限的、大小固定的数据项目。任务与任务、任务与中断之间要交流的数据保存在队列中叫做队列项目。队列所能保存的最大数据项目数量叫做队列的长度创建队列的时候会指定数据项目的大小和队列的长度。由于队列是用来传递消息的所以也称为消息队列。1.2.3 数据存储队列可以保存有限个具有确定长度的数据单元。队列可以保存的最大单元数目被称为队列的“深度”。在队列创建时需要设定其深度和每个单元的大小。通常情况下队列被作为 FIFO先进先出使用即数据由队列尾写入从队列首读出。当然由队列首写入也是可能的。往队列写入数据是通过字节拷贝把数据复制存储到队列中从队列读出数据则是把队列中的数据拷贝删除。读出一个字节数据队列中的数据就少一个1.2.4 多任务操作队列队列是具有自己独立权限的内核对象并不属于或赋予任何任务。所有任务都可以向同一队列写入和读出。一个队列由多方写入是经常的事但由多方读出倒是很少遇到。1.2.5 读阻塞当某个任务试图读一个队列时其可以指定一个阻塞超时时间。在这段时间中如果队列为空该任务将保持阻塞状态以等待队列数据有效。当其它任务或中断服务例程往其等待的队列中写入了数据该任务将自动由阻塞态转移为就绪态。当等待的时间超过了指定的阻塞时间即使队列中尚无有效数据任务也会自动从阻塞态转移为就绪态。由于队列可以被多个任务读取所以对单个队列而言可能存在多个任务处于阻塞状态以等待数据有效。这种情况下一旦队列数据有效这个任务就是所有等待任务中优先级最高的任务。而如果所有等待任务的优先级相同那么被解除阻塞的任务将是等待最久的任务。1.2.6 写阻塞同读队列一样任务也可以在写队列时指定一个阻塞超时时间。这个时间是当被写队列已满时任务进入阻塞态以等待队列空间有效的最长时间。由于队列可以被多个任务写入所以对单个队列而言也可能有多个任务处于阻塞状态以等待队列空间有效。这种情况下一旦队列空间有效只会有一个任务会被解除阻塞这个任务就是所有等待任务中优先级最高的任务。而如果所有等待任务的优先级相同那么被解除阻塞的任务将是等待最久的任务。1.3 队列的 API1.3.1 创建队列函数原型QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize );函数功能创建一个队列深度为 uxQueueLength、数据大小为 uxItemSize 的队列。函数参数uxQueueLength队列深度保存最大的数据单元数目。uxItemSize每个数据单元的大小单位是字节。函数说明xQueueGenericCreate( ( uxQueueLength ), // 队列深度 ( uxItemSize ), // 每个数据单元大小 ( queueQUEUE_TYPE_BASE ) // 队列类型 )函数返回值NULL表示没有足够的堆空间分配给队列而导致创建失败。非 NULL创建成功返回队列的句柄。示例定义队列的句柄变量QueueHandle_t Queue_t;创建队列Queue_t xQueueCreate(10, 5);1.3.2 写入数据函数原型BaseType_t xQueueSend( QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait );函数功能向指定的队列写入数据写入的是队列的队尾。函数参数QueueHandle_t xQueue写入的队列的句柄。const void * pvItemToQueue写入的数据缓存空间。TickType_t xTicksToWait超时时间。函数说明xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK );函数返回值pdPASS数据被成功发送到队列。errQUEUE_FULL1、写阻塞没有设置超时时间2、写阻塞超时时间到达。中断服务函数专用xQueueSendFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken )函数原型BaseType_t xQueueSendToBack( QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait );函数功能向指定的队列写入数据写入的是队列的队尾。函数参数QueueHandle_t xQueue写入的队列的句柄。const void * pvItemToQueue写入的数据缓存空间。TickType_t xTicksToWait超时时间。函数说明xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK );xQueueSendToBackFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken )函数原型BaseType_t xQueueSendToFront( QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait );函数功能向指定的队列写入数据写入的是队列的队首。函数参数QueueHandle_t xQueue写入的队列的句柄。const void * pvItemToQueue写入的数据缓存空间。TickType_t xTicksToWait超时时间。函数说明xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_FRONT );xQueueSendToFrontFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken )示例定义队列的句柄变量QueueHandle_t Queue_t;创建队列Queue_t xQueueCreate(10, 5);向队列写入数据xQueueSend(Queue_t, ABCDE, 0);1.3.3 读取数据函数原型BaseType_t xQueueReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait );函数功能从指定的队列读取数据读取的是队列的队首并删除读取的数据。函数参数QueueHandle_t xQueue读取的队列的句柄。void * const pvBuffer读取的数据存放缓存空间。TickType_t xTicksToWait超时时间。函数返回值pdPASS数据被成功从队列读取。errQUEUE_FULL1、读阻塞没有设置超时时间2、读阻塞超时时间到达。xQueueReceiveFromISR( QueueHandle_t xQueue, void * const pvBuffer, BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION;函数原型xQueuePeek( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION;函数功能从指定的队列读取数据读取的是队列的队首不会删除读取的数据。函数参数QueueHandle_t xQueue读取的队列的句柄。void * const pvBuffer读取的数据存放缓存空间。TickType_t xTicksToWait超时时间。函数返回值pdPASS数据被成功从队列读取。errQUEUE_FULL1、读阻塞没有设置超时时间2、读阻塞超时时间到达。xQueuePeekFromISR( QueueHandle_t xQueue, void * const pvBuffer ) PRIVILEGED_FUNCTION;1.3.4 删除队列函数原型void vQueueDelete( QueueHandle_t xQueue );函数参数QueueHandle_t xQueue删除队列的句柄。1.4 队列实战1.4.1 实战 1创建两个任务一个任务获取光照强度和 MCU 内部温度然后写入到队列另一个任务进行读取并打印。注意所使用的模块一定不能有 SysTick 延时函数从现在开始就不能使用该延时函数。1.4.2 实战 2#include main.h QueueHandle_t Quete_Handler; TaskHandle_t TaskA_Handler; TaskHandle_t TaskB_Handler; TaskHandle_t TaskC_Handler; typedef struct{ char name[10]; u8 flag; } LED_Status; void Start_Task(void *pvParameters) { Quete_Handler xQueueCreate(4, sizeof(LED_Status)); xTaskCreate(Task_A, LED1_FZ, 100, NULL, 2, TaskA_Handler); xTaskCreate(Task_B, LED2_FZ, 100, NULL, 1, TaskB_Handler); xTaskCreate(Task_C, READ_STATUS, 100, NULL, 0, TaskC_Handler); vTaskDelete(NULL); } void Task_A(void *pvParameters) { LED_Status LED1 {LED_1, 0}; while(1) { LED_G_Tog; if(!GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_8)) LED1.flag 1; else LED1.flag 0; xQueueSend(Quete_Handler, LED1, 0); vTaskDelay(50); } vTaskDelete(NULL); } void Task_B(void *pvParameters) { LED_Status LED2 {LED_2, 0}; while(1) { LED_R_Tog; if(!GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_9)) LED2.flag 1; else LED2.flag 0; xQueueSend(Quete_Handler, LED2, 0); vTaskDelay(30); } vTaskDelete(NULL); } void Task_C(void *pvParameters) { LED_Status Status; LED_Status Dev[2]; while(1) { while(xQueueReceive(Quete_Handler, Status, 0) pdPASS) { if(strncmp(Status.name, LED_1, 5) 0) { Dev[0] Status; } else { Dev[1] Status; } } printf(name:%s,Led_Status:%d\r\n, Dev[0].name, Dev[0].flag); printf(name:%s,Led_Status:%d\r\n, Dev[1].name, Dev[1].flag); printf(\r\n); vTaskDelay(40); } vTaskDelete(NULL); }
网站建设高端定制企业官网