FreeRTOS—队列集
文章目录
- 一、队列集简介
- 二、队列集相关API函数
- 2.1.创建队列集
- 2.2.队列添加到队列集中
- 2.3.从队列集中移除队列
- 2.4.获取队列集中有有效消息的队列
- 三、实验
- 3.1.实验设计
- 3.2.软件设计
一、队列集简介
一个队列只允许任务之间传递的消息为同一种数据类型,如果需要在任务间传递不同的数据类型的消息时,那么就可以使用队列集。 队列集的作用是用于多个队列或信号量进行监听,其中不管哪一个消息到来,都可以让任务退出阻塞状态。假设有一个接收任务,分别使用普通的接收和使用接收队列集的方法:
下面代码是使用普通的方法,它的缺点是如果接收不到队列,就无法进行下一步的获取信号量:
void receive_task()
{等待接收队列;获取信号量;
}
下面代码是使用接收队列集的方法,队列集里面有不同的数据,接收到之后进行if
判断:
void receive_task()
{等待队列集中消息;if(队列还是信号量){...;}
}
二、队列集相关API函数
函数 | 描述 |
---|---|
xQueueCreateSet( ) | 创建队列集 |
xQueueAddToSet( ) | 队列添加到队列集中 |
xQueueRemoveFromSet( ) | 从队列集中移除队列 |
xQueueSelectFromSet( ) | 获取队列集中有有效消息的队列 |
xQueueSelectFromSetFromISR( ) | 在中断中获取队列集中有有效消息的队列 |
2.1.创建队列集
此函数用于创建队列集,该函数在 queue.c 文件中有定义,函数的原型如下所示:
QueueSetHandle_t xQueueCreateSet(const UBaseType_t uxEventQueueLength);
下面表格是它的形参和描述:
形参 | 描述 |
---|---|
uxEventQueueLength | 队列集可容纳的队列数量 |
下面表格是它的返回值:
返回值 | 描述 |
---|---|
NULL | 队列集创建失败 |
其他值 | 队列集创建成功,返回队列集 |
2.2.队列添加到队列集中
此函数用于往队列集中添加队列,要注意的时,队列在被添加到队列集之前,队列中不能有有效的消息(队列里面为空),该函数在 queue.c 文件中有定义,函数的原型如下所示:
BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet);
下面表格是它的形参和描述:
形参 | 描述 |
---|---|
xQueueOrSemaphore | 待添加的句柄 |
xQueueSet | 队列集 |
下面表格是它的返回值:
返回值 | 描述 |
---|---|
pdPASS | 队列集添加队列成功 |
pdFAIL | 队列集添加队列失败 |
2.3.从队列集中移除队列
此函数用于从队列集中移除队列,要注意的时,队列在从队列集移除之前,必须没有有效的消息(队列里面为空),该函数在 queue.c 文件中有定义,函数的原型如下所示:
BaseType_t xQueueRemoveFromSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet);
下面表格是它的形参和描述:
形参 | 描述 |
---|---|
xQueueOrSemaphore | 待移除的句柄 |
xQueueSet | 队列集 |
下面表格是它的返回值:
返回值 | 描述 |
---|---|
pdPASS | 队列集移除队列成功 |
pdFAIL | 队列集移除队列失败 |
2.4.获取队列集中有有效消息的队列
此函数用于在任务中获取队列集中有有效消息的队列,该函数在 queue.c 文件中有定义,函数的原型如下所示:
QueueSetMemberHandle_t xQueueSelectFromSet(QueueSetHandle_t xQueueSet, TickType_t const xTicksToWait);
下面表格是它的形参和描述:
形参 | 描述 |
---|---|
xQueueSet | 队列集 |
xTicksToWait | 阻塞超时时间 |
下面表格是它的返回值:
返回值 | 描述 |
---|---|
NULL | 获取消息失败 |
其他值 | 获取到消息的队列 |
三、实验
3.1.实验设计
本实验将设计三个任务,其中 task2 的任务优先级最高,依此递减:
- start_task:创建其他任务,并创建队列集,队列和信号量,将队列和信号量添加到队列集中
- task1:用于扫描按键,当 KEY0 按下,往队列写入数据;当 KEY1 按下,释放二值信号量
- task2:读取队列集中的消息,并打印
本次实验的流程:
- 启用队列集功能需要将宏
configUSE_QUEUE_SETS
配置为 1 - 创建队列集、队列、信号量
- 往队列集中添加队列或信号量
- 往队列集发送信息或释放信号量
- 获取队列集的消息
3.2.软件设计
在 start_task 函数里创建另外两个任务、创建队列集、队列、信号量,并将队列和信号量添加至队列集中,下面是 start_task 代码:
QueueSetHandle_t queueSet_handle; //定义队列集的句柄
QueueHandle_t queue_handle; //定义队列的句柄
QueueHandle_t semaphore_handle; //定义二值信号量的句柄void start_task(void *pvParameters)
{taskENTER_CRITICAL(); BaseType_t err1 = 0;BaseType_t err2 = 0;queueSet_handle = xQueueCreateSet(2);if(queueSet_handle != NULL){printf("队列集创建成功\r\n");}queue_handle = xQueueCreate(1, sizeof(uint8_t));if(queue_handle != NULL){printf("队列创建成功\r\n");}semaphore_handle = xSemaphoreCreateBinary();if(queue_handle != NULL){printf("二值信号量创建成功\r\n");}err1 = xQueueAddToSet(queue_handle, queueSet_handle);if(err1 == pdPASS){printf("队列已添加至队列集中\r\n");}err2 = xQueueAddToSet(semaphore_handle, queueSet_handle);if(err2 == pdPASS){printf("信号量已添加至队列集中\r\n");}xTaskCreate((TaskFunction_t) task1, (char*) "task1",(uint16_t) TASK1_STACK_SIZE,(void*) NULL,(UBaseType_t) TASK1_PRIO,(TaskHandle_t*) &task1_handler);xTaskCreate((TaskFunction_t) task2, (char*) "task2",(uint16_t) TASK2_STACK_SIZE,(void*) NULL,(UBaseType_t) TASK2_PRIO,(TaskHandle_t*) &task2_handler);vTaskDelete(NULL); taskEXIT_CRITICAL();
}
下面是 task1 的代码实现,task1 的任务优先级为 2:
void task1(void *pvParameters)
{uint8_t key = 0;BaseType_t err1 = 0;BaseType_t err2 = 0;while(1){key = key_scan(0);if(key == KEY0_PRES){err1 = xQueueSend(queue_handle, &key, portMAX_DELAY);if(err1 == pdTRUE){printf("数据写入成功\r\n");}}else if(key == KEY1_PRES){err2 = xSemaphoreGive(semaphore_handle);if(err2 == pdTRUE){printf("信号量释放成功\r\n");}}}
}
下面是 task2 的代码实现,task2 的任务优先级为 3:
void task2(void *pvParameters)
{QueueSetMemberHandle_t member_handle; uint8_t key = 0;while(1){member_handle = xQueueSelectFromSet(queueSet_handle, portMAX_DELAY);if(member_handle == queue_handle){xQueueReceive(member_handle, &key, portMAX_DELAY);printf("获取到的队列数据为:%d\r\n",key);}else if(member_handle == semaphore_handle){xSemaphoreTake(member_handle, portMAX_DELAY);printf("信号量获取成功\r\n");}}
}
下图是运行的结果图:
为什么按下按键之后,打印出来的信息顺序的倒过来的呢?这是因为 task2 的任务优先级高于 task1 的任务优先级;开始的时候所有的东西创建完毕之后,task2 获取不到有内容的队列或信号量,因此 task2 进入阻塞状态,如果按下 KEY0,数据就被写入队列,task2 就立刻抢占 task1,进行 task2 的打印,task2 的打印结束之后,又进入阻塞状态,这个时候才会运行 task1 的打印结果,按下 KEY1 也同理。