十六、FreeRTOS之FreeRTOS队列集

2023-12-13 06:28:55

本节需要掌握以下内容:

1,队列集简介(了解)

2,队列集相关API函数介绍(熟悉)

3,队列集操作实验(掌握)

一、队列集简介(了解)

一个队列值允许任务间传递的消息为同一数据类型,如果需要在任务间传递不同数据类型消息时,那么就可以使用队列集!

作用:用于对多个队列或信号量进行“监听”,其中不管哪一个消息到来,都可以让任务退出阻塞状态。这个监听,就是监听有没有收到数据。

假设:有个接收任务,使用到队列接收和信号量的获取,如下:

如果没有使用队列集的情况,按下图实现:

?这样的话,如果等待不到接收队列,任务就会阻塞,队列有个接收阻塞的时间,获取信号量也是一样,如果其它任务一直没释放,那么它也会阻塞,等待获取信号量。

假设其它任务释放了信号量,但是接收队列阻塞,那么它一样获取不到信号量。

或者说接收队列正常接收,但是获取不到信号量,这个任务依然被阻塞。

这种情况就可以使用队列集,不管哪一个消息到来,都可以让任务退出阻塞状态。

队列集如下图方式实现:一个接收任务,里面就是一个等待队列集消息,首先判断以下句柄是队列还是信号量,如果句柄是队列,那么就执行队列的接收函数,如果是信号量,那么就执行信号量的获取函数,这样就可以解除接收任务的阻塞状态了。

这就是队列集的一个使用场景。

,队列集相关API函数介绍(熟悉)

函数

描述

xQueueCreateSet()

创建队列集

xQueueAddToSet()

队列添加到队列集中

xQueueRemoveFromSet()

从队列集中移除队列

xQueueSelectFromSet()

获取队列集中有有效消息的队列

xQueueSelectFromSetFromISR()

在中断中获取队列集中有有效消息的队列

?我们可以使用创建队列集函数xQueueCreateSet()创建一个队列集,再使用队列创建函数xQueueCreate()创建两个队列,使用队列集添加队列函数xQueueAddToSet()将刚刚创建的两个队列添加至队列集中。

2.1?创建队列集函数

QueueSetHandle_t???? xQueueCreateSet( const? UBaseType_t?? uxEventQueueLength );

此函数用于创建队列集。

形参

描述

uxEventQueueLength

队列集可容纳的队列数量

返回值

描述

NULL

队列集创建失败

其他值

队列集创建成功,返回队列集句柄

?2.2?队列添加到队列集中

BaseType_t xQueueAddToSet( QueueSetMemberHandle_t?? ? xQueueOrSemaphore ,

? ????? QueueSetHandle_t?? ? xQueueSet? ? );

此函数用于往队列集中添加队列,要注意的时,队列在被添加到队列集之前,队列中不能有有效的消息

形参

描述

xQueueOrSemaphore

待添加的队列句柄

xQueueSet

队列集

返回值

描述

pdPASS

队列集添加队列成功

pdFAIL

队列集添加队列失败

2.3?队列集中移除队列

BaseType_t?? xQueueRemoveFromSet( QueueSetMemberHandle_t? ? xQueueOrSemaphore ,
? ?????????
QueueSetHandle_t?? ? xQueueSet );

此函数用于往队列集中添加队列,到注意的是,队列在被添加到队列集之前,队列中不能有效的消息

形参

描述

xQueueOrSemaphore

待移除的队列句柄

xQueueSet

队列集

返回值

描述

pdPASS

队列集移除队列成功

pdFAIL

队列集移除队列失败

2.4??获取队列集中有有效消息的队列

QueueSetMemberHandle_t???? xQueueSelectFromSet( QueueSetHandle_t ? xQueueSet,

??????????????????????????????????????????????? ? ??????TickType_t const ? xTicksToWait )

?此函数用于在任务中获取队列集中有效消息的队列

形参

描述

xQueueSet

队列集

xTicksToWait

阻塞超时时间

返回值

描述

NULL

获取消息失败

其他值

获取到消息的队列句柄

?假设:多个队列,是队列1发送的数据,通过调用此函数获取队列集中有效消息的队列,那么返回的是队列1的句柄。

三、 队列集使用流程

1、启用队列集功能需要将宏configUSE_QUEUE_SETS?配置为1

2、创建队列集

3、创建队列或信号量

4、往队列集中添加队列或信号量

5、往队列发送信息或释放信号量

6、获取队列集的消息

四、队列集操作实验(掌握)

4.1、实验目的:

学习 FreeRTOS 的队列集相关API的使用。

4.2、实验设计:

将设计三个任务:start_task、task1、task2

三个任务的功能如下:

  • start_task:用来创建其它任务,并创建队列集,队列/信号量,将队列/信号量添加到队列集中
  • task1:用于扫描按键,当KEY0按下,往队列写入数据,当KEY1按下,释放二值信号量
  • task2:读取队列集中的消息,并打印

4.3 实验代码

demo.c

/******************************************************************************************************/
/*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) );  /* 创建队列 */ 
	if(queue_handle != NULL)
    {
        printf("队列创建成功!!\r\n");
    }
    semphr_handle = xSemaphoreCreateBinary();           /* 创建二值信号量 */
	if(semphr_handle != NULL)
    {
        printf("二值信号量创建成功!!\r\n");
    }
	
    /* 往队列里面添加队列或信号量 */
    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");
        }
    }
}

注意!!!

首先打印的是队列集创建成功、队列创建成功,二值信号量创建成功,

由于任务2的优先级最高,那么首先运行任务2,但是此时队列中没有数据,获取不到数据,那么任务2会进入阻塞状态。

当按下KEY0,任务2解除阻塞,执行队列的接收函数,打印获取到的数据为1,接着又从队列集中获取消息,获取不到消息,就又开始死等,被阻塞。

阻塞之后来到任务1,从被打断的地方继续执行,打印往队列中写入数据成功。

这是按下KEY0的情况,按下KEY1,同理,首先打印获取信号量成功,接下来是释放信号量成功。

文章来源:https://blog.csdn.net/weixin_47730988/article/details/134865385
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。