FreeRTOS从入门到精通 第十四章(队列集)
参考教程:【正点原子】手把手教你学FreeRTOS实时系统_哔哩哔哩_bilibili
一、队列集简介
1、队列集概述
(1)一个队列只允许任务间传递的消息为同一种数据类型,如果需要在任务间传递不同数据类型的消息时,那么就可以使用队列集。
(2)队列集可用于对多个队列或信号量进行“监听”,其中不管哪一个消息到来,都可让任务退出阻塞状态。
(3)队列集大致使用流程:
①启用队列集功能需要将宏configUSE_QUEUE_SETS配置为1。
②创建队列集。
③创建队列或信号量。
④往队列集中添加队列或信号量。
⑤往队列发送信息或释放信号量。
⑥获取队列集的消息。
2、队列集相关API函数介绍
(1)队列集相关API函数概览:
函数 | 描述 |
xQueueCreateSet() | 创建队列集 |
xQueueAddToSet() | 队列添加到队列集中【队列在被添加到队列集之前,队列中不能有有效的消息】 |
xQueueRemoveFromSet() | 从队列集中移除队列【队列在从队列集移除之前,必须没有有效的消息】 |
xQueueSelectFromSet() | 获取队列集中有有效消息的队列 |
xQueueSelectFromSetFromISR() | 在中断中获取队列集中有有效消息的队列 |
(2)xQueueCreateSet函数:
①函数定义:
QueueSetHandle_t xQueueCreateSet
(
const UBaseType_t uxEventQueueLength
);
②函数参数:
形参 | 描述 |
uxEventQueueLength | 队列集可容纳的队列数量 |
③返回值:
返回值 | 描述 |
NULL | 队列集创建失败 |
其它值 | 队列集创建成功,返回队列集句柄 |
(3)xQueueAddToSet函数:
①函数定义:
BaseType_t xQueueAddToSet
(
QueueSetMemberHandle_t xQueueOrSemaphore ,
QueueSetHandle_t xQueueSet
);
②函数参数:
形参 | 描述 |
xQueueOrSemaphore | 待添加的队列句柄 |
xQueueSet | 队列集 |
③返回值:
返回值 | 描述 |
pdPASS | 队列集添加队列成功 |
pdFAIL | 队列集添加队列失败 |
(4)xQueueRemoveFromSet函数:
①函数定义:
BaseType_t xQueueRemoveFromSet
(
QueueSetMemberHandle_t xQueueOrSemaphore ,
QueueSetHandle_t xQueueSet
);
②函数参数:
形参 | 描述 |
xQueueOrSemaphore | 待移除的队列句柄 |
xQueueSet | 队列集 |
③返回值:
返回值 | 描述 |
pdPASS | 队列集添加队列成功 |
pdFAIL | 队列集添加队列失败 |
(5)xQueueSelectFromSet函数:
①函数定义:
QueueSetMemberHandle_t xQueueSelectFromSet
(
QueueSetHandle_t xQueueSet,
TickType_t const xTicksToWait
)
②函数参数:
形参 | 描述 |
xQueueSet | 队列集 |
xTicksToWait | 阻塞超时时间 |
③返回值:
返回值 | 描述 |
NULL | 获取消息失败 |
其他值 | 获取到消息的队列句柄 |
二、队列集操作实验
1、原理图与实验目标
(1)原理图(串口外设的接法与列表项的插入和删除实验相同,下图未示出):
(2)实验目标:
①设计3个任务——start_task、task1、task2:
[1]start_task:用于创建其它任务,并创建队列集,队列/信号量,将队列/信号量添加到队列集中。
[2]task1:用于扫描按键,当按键1按下,往队列写入数据,当按键2按下,释放二值信号量。
[3]task2:读取队列集中的消息,并打印。
②预期实验现象:
[1]程序下载到板子上后,板子上无任何明显现象。
[2]串口助手中输出相关调试信息。
2、实验步骤
(1)将“列表项的插入和删除实验”的工程文件夹复制一份,在拷贝版中进行实验。
(2)在FreeRTOSConfig.h文件中将宏configUSE_QUEUE_SETS配置为1。
#define configUSE_QUEUE_SETS 1
(3)更改FreeRTOS_experiment.c文件的内容,如下所示。
#include "FreeRTOS.h"
#include "task.h"
#include "LED.h"
#include "Key.h"
#include "Serial.h"
#include "queue.h"
#include "semphr.h"
//宏定义
#define START_TASK_STACK_SIZE 128 //start_task任务的堆栈大小
#define START_TASK_PRIO 1 //start_task任务的优先级
#define TASK1_STACK_SIZE 128 //task1任务的堆栈大小
#define TASK1_PRIO 2 //task1任务的优先级
#define TASK2_STACK_SIZE 128 //task2任务的堆栈大小
#define TASK2_PRIO 3 //task2任务的优先级
//任务函数声明
void start_task(void);
void task1(void);
void task2(void);
//任务句柄
TaskHandle_t start_task_handler; //start_task任务的句柄
TaskHandle_t task1_handler; //task1任务的句柄
TaskHandle_t task2_handler; //task2任务的句柄
QueueSetHandle_t queueset_handle;
QueueHandle_t queue_handle;
QueueHandle_t semphr_handle;
void FreeRTOS_Test(void)
{
//创建任务start_task
xTaskCreate((TaskFunction_t)start_task, //指向任务函数的指针
"start_task", //任务名字
START_TASK_STACK_SIZE, //任务堆栈大小,单位为字
NULL, //传递给任务函数的参数
START_TASK_PRIO, //任务优先级
(TaskHandle_t *) &start_task_handler //任务句柄,就是任务的任务控制块
);
//开启任务调度器
vTaskStartScheduler();
}
void start_task(void)
{
taskENTER_CRITICAL(); //进入临界区
queueset_handle = xQueueCreateSet(2); //创建队列集,可以存放2个队列
if(queueset_handle != NULL)
{
Serial_Printf("队列集创建成功!!\r\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);
taskEXIT_CRITICAL();
}
void task1(void)
{
uint8_t key = 0;
BaseType_t err = 0;
while(1)
{
key = Key_GetNum();
if(key == 1)
{
err = xQueueSend(queue_handle, &key, portMAX_DELAY );
if(err == pdPASS)
{
Serial_Printf("往队列queue_handle写入数据成功!!\r\n");
}
}
else if(key == 2)
{
err = xSemaphoreGive(semphr_handle);
if(err == pdPASS)
{
Serial_Printf("释放信号量成功!!\r\n");
}
}
vTaskDelay(10);
}
}
void task2(void)
{
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);
Serial_Printf("获取到的队列数据为:%d\r\n",key);
}
else if(member_handle == semphr_handle)
{
xSemaphoreTake(member_handle, portMAX_DELAY);
Serial_Printf("获取信号量成功!!\r\n");
}
}
}
(4)程序完善好后点击“编译”,然后将程序下载到开发板上,打开串口助手分析信息。
3、程序执行流程
(1)main函数全流程:
①初始化串口模块。
②调用FreeRTOS_Test函数。
(2)测试函数全流程:
①创建任务start_task。
②开启任务调度器。
(3)多任务调度执行阶段较为简单,这里不再赘述。