队列集(Queue Set)通常指的是一组队列,它们可以用于处理不同的任务或数据流。每个队列可以独立地处理自己的元素,但作为一个集群,它们可以协同工作来完成更复杂的任务。下面进行介绍。
目录
一、队列集简介
二、队列集相关API函数介绍
2.1 队列集使用流程
2.2 队列集相关API函数接口
2.2.1 创建队列集
2.2.2 向队列集中添加消息
2.2.3 从队列集中移除队列
2.2.4 获取队列集中有有效消息的队列
三、队列集操作实验
一、队列集简介
一个队列只允许任务间传递的消息为同一种数据类型,如果需要在任务间传递不同数据类型的消息时,那么就可以使用队列集 !它的作用:用于对多个队列或信号量进行“监听”,其中不管哪一个消息到来,都可让任务退出阻塞状态。如下所示,假设:有个接收任务,使用到队列接收和信号量的获取,如下:
队列集(Queue Set)是一个高级的同步机制,它允许一个任务同时等待多个队列或信号量的事件,并在任何一个队列或信号量变为可用时被唤醒。相比于单纯的队列,队列集具有以下几个优点:
集中管理多路事件: 队列集可以将多个队列和信号量集中在一起,任务只需等待一个队列集,就可以响应多个事件源。这大大简化了任务需要处理多个队列和信号量的情况。
减少任务切换: 如果一个任务需要从多个队列中接收数据,使用队列集可以避免任务在多个队列上阻塞和切换。任务只需阻塞在一个队列集上,当任何一个队列有数据时,任务就会被唤醒处理,减少了不必要的任务切换,提高了系统效率。
简化代码逻辑: 队列集简化了等待多个事件源的代码逻辑,避免了复杂的多重等待和查询操作。代码更加清晰和易于维护。
提高响应速度: 任务可以立即响应队列集中任意一个队列或信号量的变化,而不需要轮询多个队列或信号量,提升了系统的实时响应能力。
二、队列集相关API函数介绍
2.1 队列集使用流程
- 启用队列集功能需要将宏configUSE_ QUEUE SETS配置为1
- 创建队列集
- 创建队列或信号量
- 往队列集中添加队列或信号量
- 任务往队列发送信息或释放信号量
- 其他任务获取队列集任意一个的队列的消息或者信号量;
2.2 队列集相关API函数接口
函数 | 描述 |
xQueueCreateSet() | 创建队列集 |
xQueueAddToSet() | 队列添加到队列集中 |
xQueueRemoveFromSet() | 从队列集中移除队列 |
xQueueSelectFromSet() | 获取队列集中有有效消息的队列 |
xQueueSelectFromSetFromISR() | 在中断中获取队列集中有有效消息的队列 |
2.2.1 创建队列集
QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQueueLength );
从上面可以知道,利用此函数可以创建一个队列集,需要指定队列集可容纳的队列的数量,队列集创建成功,返回队列集句柄,因此,需要提前定义好队列集句柄!
2.2.2 向队列集中添加消息
BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore ,QueueSetHandle_t xQueueSet );
此函数用于往队列集中添加队列,要注意的时,队列在被添加到队列集之前,队列中不能有有效的消息。
从上面可以知道,利用此函数可以往队列集中添加队列,需要指定添加的队列句柄,以及队列集句柄。
2.2.3 从队列集中移除队列
BaseType_t xQueueRemoveFromSet( QueueSetMemberHandle_t xQueueOrSemaphore , QueueSetHandle_t xQueueSet );
此函数用于从队列集中移除队列, 要注意的是,队列在从队列集移除之前,必须没有有效的消息
从上面可以知道,利用此函数可以从队列集中移除队列,需要指定移除的队列句柄,以及队列集句柄。
2.2.4 获取队列集中有有效消息的队列
QueueSetMemberHandle_t xQueueSelectFromSet( QueueSetHandle_t xQueueSet,TickType_t const xTicksToWait )
此函数用于在任务中获取队列集中有有效消息的队列。
这个函数,用于从队列集中选择一个有数据或可用的队列或信号量。它允许任务阻塞等待,直到队列集中的任意一个队列或信号量变为可用,从而简化了任务需要同时等待多个队列或信号量的情况。 返回队列集中的一个成员(队列或信号量)的句柄,该成员已变为可用。如果在指定时间内没有任何队列或信号量变为可用,则返回 NULL
。
当一个任务需要同时等待多个队列或信号量时,使用
xQueueSelectFromSet
可以简化代码逻辑并提高效率。任务不需要分别等待每个队列或信号量,而是可以统一等待队列集。
三、队列集操作实验
任务创建文件
#include "stm32f4xx.h" // Device header
#include "stdio.h"
#include "FreeRTOS.h"
#include "task.h"
#include "dynamic.h"
#include "mydelay.h"
#include "mykey.h"
#include "queue.h"
#include "semphr.h"/**********************START_TASK任务配置******************************/
/***********包括任务堆栈大小、任务优先级、任务句柄、创建任务***********/#define START_TASK_STACK_SIZE 128 //定义堆栈大小为128字(1字等于4字节)
#define START_TASK_PRIO 1 //定义任务优先级,0-31根据任务需求
TaskHandle_t start_task_handler; //定义任务句柄(结构体指针)
void start_task(void* args);/**********************TASK1任务配置******************************/
/***********包括任务堆栈大小、任务优先级、任务句柄、创建任务***********/
#define TASK1_STACK_SIZE 128 //定义堆栈大小为128字(1字等于4字节)
#define TASK1_PRIO 2 //定义任务优先级,0-31根据任务需求
TaskHandle_t task1_handler; //定义任务句柄(结构体指针)
void task1(void* args);/**********************TASK2任务配置******************************/
/***********包括任务堆栈大小、任务优先级、任务句柄、创建任务***********/
#define TASK2_STACK_SIZE 128 //定义堆栈大小为128字(1字等于4字节)
#define TASK2_PRIO 3 //定义任务优先级,0-31根据任务需求
TaskHandle_t task2_handler; //定义任务句柄(结构体指针)
void task2(void* args);QueueSetHandle_t queueset_handle; //队列集句柄
QueueHandle_t queue_handle; //队列句柄
QueueHandle_t semphr_handle; //信号量句柄/*********开始任务用来创建一个任务,只创建一次,不能是死循环,创建完这个任务后删除开始任务本身***********/
void start_task(void* args)
{taskENTER_CRITICAL(); /*进入临界区*/queueset_handle = xQueueCreateSet(2); //创建队列集,可以存放两个队列,一个是队列,一个是信号量if(queueset_handle!=NULL){printf("队列集创建成功!\n");}queue_handle = xQueueCreate(1,sizeof(uint8_t)); //创建队列semphr_handle = xSemaphoreCreateBinary(); //创建二值信号量xQueueAddToSet(queue_handle,queueset_handle); //将队列添加到队列集xQueueAddToSet(semphr_handle,queueset_handle); //将信号量添加到队列集xTaskCreate( (TaskFunction_t) task1,(char *) "task1", ( configSTACK_DEPTH_TYPE) TASK1_STACK_SIZE,(void *) NULL,(UBaseType_t) TASK1_PRIO ,(TaskHandle_t *) &task1_handler );xTaskCreate( (TaskFunction_t) task2,(char *) "task2", ( configSTACK_DEPTH_TYPE) TASK2_STACK_SIZE,(void *) NULL,(UBaseType_t) TASK2_PRIO ,(TaskHandle_t *) &task2_handler ); vTaskDelete(NULL); //删除开始任务自身,传参NULLtaskEXIT_CRITICAL(); /*退出临界区*///临界区内不会进行任务的调度切换,出了临界区才会进行任务调度,抢占式
}/********任务1的任务函数,无返回值且是死循环***********//*****任务1:实现队列发送消息以及信号量的释放*******/
void task1(void* args)
{uint8_t key=0; BaseType_t xReturn;while(1){key= KEY_Scan(0);if(key==KEY0_PRES){xReturn=xQueueSend(queue_handle,&key,portMAX_DELAY); //按键1按下,向队列写入消息if(xReturn==pdPASS){printf("往队列queue_handle写入数据成功!!\n");}}else if(key==KEY1_PRES){xSemaphoreGive(semphr_handle); //按键0按下,释放二值信号量if(xReturn==pdPASS){printf("释放信号量成功!!\n");}}vTaskDelay(10); //FreeRTOS自带的延时函数,延时10毫秒}
}/********任务2的任务函数,无返回值且是死循环***********//***任务2:获取队列集的消息*******/
void task2(void* args)
{QueueSetMemberHandle_t member_handle;uint8_t key;while(1){member_handle=xQueueSelectFromSet(queueset_handle,portMAX_DELAY);if(member_handle==queue_handle){xQueueReceive(member_handle,&key,portMAX_DELAY);printf("获取到的队列数据为:%d\n",key);}else if(member_handle==semphr_handle){xSemaphoreTake(member_handle,portMAX_DELAY);printf("获取信号量成功!\n");}}
}//FreeRTO入口例程函数,无参数,无返回值
void freertos_demo(void)
{/***开始任务的创建***/xTaskCreate( (TaskFunction_t) start_task,(char *) "start_task", ( configSTACK_DEPTH_TYPE) START_TASK_STACK_SIZE,(void *) NULL,(UBaseType_t) START_TASK_PRIO ,(TaskHandle_t *) &start_task_handler );vTaskStartScheduler(); //开启任务调度器}
主函数调用文件
#include "stm32f4xx.h" // Device header
#include "stdio.h"
#include "myled.h"
#include "myusart.h"#include "FreeRTOS.h"
#include "task.h"
#include "dynamic.h"extern TaskHandle_t Start_Handle;int main(void)
{//硬件初始化My_UsartInit();//调用入口函数freertos_demo();}
至此,已经讲解完毕!初次学习,循序渐进,一步步掌握即可!以上就是全部内容!请务必掌握,创作不易,欢迎大家点赞加关注评论,您的支持是我前进最大的动力!下期再见!