十六、FreeRTOS之FreeRTOS队列集

本节需要掌握以下内容:

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,同理,首先打印获取信号量成功,接下来是释放信号量成功。

  • 16
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
FreeRTOS队列集是FreeRTOS操作系统提供的一种数据结构,用于实现任务与任务、任务与中断之间的通信。队列集可以存储有限的、大小固定的数据项目,并提供了入队和出队的操作。入队是将数据项添加到队列中,而出队则是从队列中获取队列项(消息)。队列集在任务与任务、任务与中断之间传递消息,并且可以根据需要指定队列的长度和数据项目的大小。因为队列集用于传递消息,所以也被称为消息队列。在FreeRTOS中,信号量也是基于队列实现的,因此深入了解FreeRTOS队列对于使用该操作系统非常重要。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [基于matlab数字pid控制代码-ARClab:先进的机器人控制-实验室课程](https://download.csdn.net/download/weixin_38739101/18987007)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [FreeRTOS队列](https://blog.csdn.net/qq_26195179/article/details/114657409)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

I am Supreme

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值