1.简介
在使用队列进行任务之间的“沟通交流”时,一个队列只允许任务间传递的消息为同一种数据类型,如果需要在任务间传递不同数据类型的消息时,那么就可以使用队列集。FreeRTOS提供的队列集功能可以对多个队列进行“监听”,只要被监听的队列中有一个队列有有效的消息,那么队列集的读取任务都可以读取到消息,如果读取任务因读取队列集而被阻塞,那么队列集将解除读取任务的阻塞。使用队列集的好处在于,队列集可以是的任务可以读取多个队列中的消息,而无需遍历所有待读取的队列,以确定具体读取哪一个队列。
使用队列集功能,需要在 FreeRTOSConfig.h 文件中将配置项 configUSE_QUEUE_SETS 配
置为 1,来启用队列集功能。
2.相关API函数
1. 函数 xQueueCreateSet()
此函数用于创建队列集,该函数在 queue.c 文件中有定义,函数的原型如下所示:
QueueSetHandle_t xQueueCreateSet(const UBaseType_t uxEventQueueLength);
QueueSetHandle_t xQueueCreateSet(const UBaseType_t uxEventQueueLength)
{
QueueSetHandle_t pxQueue;
/* 创建一个队列作为队列集
* 队列长度为队列集可容纳的队列数量
* 队列项目的大小为队列控制块的大小
* 队列的类型为队列集
*/
pxQueue = xQueueGenericCreate( uxEventQueueLength,
( UBaseType_t ) sizeof( Queue_t * ),
queueQUEUE_TYPE_SET );
return pxQueue;
}
2. 函数 xQueueAddToSet()
此函数用于往队列集中添加队列,要注意的时,队列在被添加到队列集之前,队列中不能
有有效的消息,该函数在 queue.c 文件中有定义,函数的原型如下所示:
BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore,
QueueSetHandle_t xQueueSet);
函数 xQueueAddToSet()的具体代码如下所示:
BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore,
QueueSetHandle_t xQueueSet)
{
BaseType_t xReturn;
/* 进入临界区 */
taskENTER_CRITICAL();
{
if( ( ( Queue_t * ) xQueueOrSemaphore )->pxQueueSetContainer != NULL )
{
xReturn = pdFAIL;
}
/* 队列中要求没有有效消息 */
else if( ( ( Queue_t * ) xQueueOrSemaphore )->uxMessagesWaiting !=
( UBaseType_t ) 0 )
{
xReturn = pdFAIL;
}
else
{
/* 将队列所在队列集设为队列集 */
( ( Queue_t * ) xQueueOrSemaphore )->pxQueueSetContainer =
xQueueSet;
xReturn = pdPASS;
}
}
/* 退出临界区 */
taskEXIT_CRITICAL();
return xReturn;
}
3. 函数 xQueueRemoveFromSet()
此函数用于从队列集中移除队列,要注意的时,队列在从队列集移除之前,必须没有有效
的消息,该函数在 queue.c 文件中有定义,函数的原型如下所示:
BaseType_t xQueueRemoveFromSet(QueueSetMemberHandle_t xQueueOrSemaphore,
QueueSetHandle_t xQueueSet);
函数 xQueueRemoveFromSet()的具体代码如下所示:
BaseType_t xQueueRemoveFromSet(QueueSetMemberHandle_t xQueueOrSemaphore,
QueueSetHandle_t xQueueSet)
{
BaseType_t xReturn;
Queue_t * const pxQueueOrSemaphore = ( Queue_t * ) xQueueOrSemaphore;
/* 队列需在队列集中,才能移除 */
if( pxQueueOrSemaphore->pxQueueSetContainer != xQueueSet )
{
xReturn = pdFAIL;
}
/* 队列中没有有效消息时,才能移除 */
else if( pxQueueOrSemaphore->uxMessagesWaiting != ( UBaseType_t ) 0 )
{
xReturn = pdFAIL;
}
else
{
/* 进入临界区 */
taskENTER_CRITICAL();
{
/* 将队列所在队列集设为空 */
pxQueueOrSemaphore->pxQueueSetContainer = NULL;
}
/* 退出临界区 */
taskEXIT_CRITICAL();
xReturn = pdPASS;
}
return xReturn;
}
4. 函数 xQueueSelectFromSet()
此函数用于在任务中获取队列集中有有效消息的队列,该函数在 queue.c 文件中有定义,函
数的原型如下所示:
QueueSetMemberHandle_t xQueueSelectFromSet(
QueueSetHandle_t xQueueSet,
TickType_t const xTicksToWait);
函数 xQueueSelectFromSet()的具体代码如下所示:
QueueSetMemberHandle_t xQueueSelectFromSet(
QueueSetHandle_t xQueueSet,
TickType_t const xTicksToWait)
{
QueueSetMemberHandle_t xReturn = NULL;
/* 读取队列集的消息
* 读取到的消息,
* 即为队列集中有空闲消息的队列
*/
( void ) xQueueReceive( ( QueueHandle_t ) xQueueSet,
&xReturn,
xTicksToWait);
return xReturn;
}
5. 函数 xQueueSelectFromSetFromISR()
此函数用于在中断中获取队列集中有有效消息的队列,该函数在 queue.c 文件中有定义,函
数的原型如下所示:
QueueSetMemberHandle_t xQueueSelectFromSetFromISR(
QueueSetHandle_t xQueueSet );
QueueSetMemberHandle_t xQueueSelectFromSetFromISR(
QueueSetHandle_t xQueueSet )
{
QueueSetMemberHandle_t xReturn = NULL;
/* 在中断中读取队列集的消息
* 读取到的消息,
* 即为队列集中有空闲消息的队列
*/
( void ) xQueueReceiveFromISR( ( QueueHandle_t ) xQueueSet,
&xReturn,
NULL);
return xReturn;
}
3.相关实验
#include "freertos_demo.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./SYSTEM/delay/delay.h"
#include "./MALLOC/malloc.h"
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
/******************************************************************************************************/
/*FreeRTOS配置*/
/* START_TASK 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define START_TASK_PRIO 1
#define START_TASK_STACK_SIZE 128
TaskHandle_t start_task_handler;
void start_task( void * pvParameters );
/* TASK1 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK1_PRIO 2
#define TASK1_STACK_SIZE 128
TaskHandle_t task1_handler;
void task1( void * pvParameters );
/* TASK2 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*/
#define TASK2_PRIO 3
#define TASK2_STACK_SIZE 128
TaskHandle_t task2_handler;
void task2( void * pvParameters );
/******************************************************************************************************/
QueueSetHandle_t queueset_handle;
QueueHandle_t queue_handle;
QueueHandle_t semphr_handle;
/**
* @brief FreeRTOS例程入口函数
* @param 无
* @retval 无
*/
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();
}
void start_task( void * pvParameters )
{
taskENTER_CRITICAL(); /* 进入临界区 */
queueset_handle = xQueueCreateSet( 2 ); /* 创建队列集,可以存放2个队列 */
if(queueset_handle != NULL)
{
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 * pvParameters )
{
uint8_t key = 0;
BaseType_t err = 0;
while(1)
{
key = key_scan(0);
if(key == KEY0_PRES)
{
err = xQueueSend( queue_handle, &key, portMAX_DELAY );
if(err == pdPASS)
{
printf("往队列queue_handle写入数据成功!!\r\n");
}
}else if(key == KEY1_PRES)
{
err = xSemaphoreGive(semphr_handle);
if(err == pdPASS)
{
printf("释放信号量成功!!\r\n");
}
}
vTaskDelay(10);
}
}
/* 任务二,获取队列集的消息 */
void task2( void * pvParameters )
{
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\r\n",key);
}else if(member_handle == semphr_handle)
{
xSemaphoreTake( member_handle, portMAX_DELAY );
printf("获取信号量成功!!\r\n");
}
}
}