FreeRTOS队列集--允许任务同时在多个队列或信号量上阻塞等待读取操作

对于FreeRTOS来说,队列集提供了一种机制,允许 RTOS 任务同时在多个 RTOS 队列或信号量上 阻塞(挂起),等待读取操作,队列集可以用于将多个队列、信号量绑定在同一个接口进行监听,从而实现统一管理;队列集是对传统线程交互接口的补充,用于Task中需要监视多个信号的场景。

队列集就好像我们在火锅店吃火锅时候的摆台,我们点完餐以后,服务员会将菜品放到摆台上,供我们顾客去取用,羊肉、牛肉这些菜品就好像队列和信号量,菜品准备好了以后,直接放到摆台这里,也就是将队列放到队列集,顾客在检查队列集(摆台)的时候, 知道哪个队列里面有数据了,或者信号量被释放了,信号量释放一般也代表数据准备好了

队列集处理接口

1. 任务集处理接口

对于任务集的处理接口如下所示。

|-------------------------------------------------|-------------|
| xQueueCreateSet/ xQueueCreateSetStatic | 创建队列集 |
| xQueueAddToSet | 添加队列/信号到队列集 |
| xQueueRemoveFromSet | 从队列集中移除数据 |
| xQueueSelectFromSet/ xQueueSelectFromSetFromISR | 任务监听和处理 |

对于上述接口,比较核心的API如下所示。

cpp 复制代码
/// @brief 创建队列集
/// @param uxEventQueueLength 用于指定队列集中一次最多可以排队的事件数量
/// @return QueueSetHandle_t 返回创建的队列集
QueueSetHandle_t xQueueCreateSet(const UBaseType_t uxEventQueueLength)

/// @brief 创建队列集
/// @param uxEventQueueLength 用于指定队列集中一次最多可以排队的事件数量
/// @param pucQueueStorage 指向容纳uxEventQueueLength事件的uint8_t数组
/// @param pxStaticQueue 存储队列的数据结构
/// @return QueueSetHandle_t 返回创建的队列集
QueueSetHandle_t xQueueCreateSetStatic( const UBaseType_t uxEventQueueLength, uint8_t * pucQueueStorage,StaticQueue_t * pxStaticQueue )

/// @brief 将队列或者信号量添加到队列集
/// @param xQueueOrSemaphore 添加到队列集的队列或信号量的句柄
/// @param xQueueSet 队列或信号量被添加到的队列集合的句柄
/// @return BaseType_t 加入队列集的结果
BaseType_t xQueueAddToSet(QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet)

/// @brief 从队列集中移除队列或者信号量
/// @param xQueueOrSemaphore 队列或信号量的句柄从队列集中移除
/// @param xQueueSet 包含队列或信号量的队列集合的句柄
/// @return BaseType_t 移除的结果,成功返回pdPASS
BaseType_t xQueueRemoveFromSet(QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet);

/// @brief 从队列集的成员中选择队列或信号量(存在信号)
/// @param xQueueSet 任务可能被阻塞的队列集合
/// @param xTicksToWait 调用任务保持阻塞状态的最长时间
/// @return QueueSetMemberHandle_t 返回队列的句柄 
QueueSetMemberHandle_t xQueueSelectFromSet(QueueSetHandle_t xQueueSet,const TickType_t xTicksToWait)

在系统中支持的队列集需要的宏命令如下所示。

cpp 复制代码
// 定义支持队列集功能
#define configUSE_QUEUE_SETS  1

队列集接口应用

1. 任务集应用接口

对于队列集来说,这里以队列和信号量共同组成的应用如下所示。

cpp 复制代码
static TaskHandle_t xQsrHandle = NULL;
static TaskHandle_t xQswHandle = NULL;
static QueueHandle_t xQueue1 = NULL;
static QueueHandle_t xQueue2 = NULL;
static SemaphoreHandle_t xSemaphore1 = NULL;
static QueueSetHandle_t xQueueSet = NULL;

#define QUEUE1_LENGTH 10
#define QUEUE2_LENGTH 10
#define BINARY_SEMAPHORE_LENGTH 1
#define QUEUE_SET_COMBIAND_LENGTH (QUEUE1_LENGTH + QUEUE2_LENGTH + BINARY_SEMAPHORE_LENGTH)

//队列集,分别往队列里面添加数据,释放信号量
static void queueset_writer_task(void *argument)
{
    uint32_t value = 0;

    while(1) 
    {
        xQueueSend(xQueue1, &value, portMAX_DELAY);
        value++;

        xQueueSend(xQueue2, &value, portMAX_DELAY);
        value++;

        //xSemaphoreGive(xSemaphore1)是 ‌FreeRTOS‌ 中用于‌释放信号量‌的核心 API 函数。调用该函数会将信号量 xSemaphore1 的计数器加 1,从而允许其他等待该信号量的任务解除阻塞并继续执行。
        xSemaphoreGive(xSemaphore1);

        vTaskDelay(1000);
    }
}

static void queueset_reader_task(void *argument)
{
    QueueSetMemberHandle_t xActivatedMember;

    while(1) 
    {
        //检查队列集中的队列或者信号量,也就是阻塞并监听多个队列或信号量
        xActivatedMember = xQueueSelectFromSet(xQueueSet, portMAX_DELAY);

        //检测到xQueue1有数据了
        if (xActivatedMember == xQueue1) 
        { 
            int value;
            xQueueReceive(xQueue1, &value, portMAX_DELAY);
            PRINT_LOG(LOG_INFO, 0, "queueset_reader_task: xQueue1: %d", value);
        }
        else if (xActivatedMember == xQueue2) 
        {
            int value;
            xQueueReceive(xQueue2, &value, portMAX_DELAY);
            PRINT_LOG(LOG_INFO, 0, "queueset_reader_task: xQueue2: %d", value);
        }
        else if (xActivatedMember == xSemaphore1) 
        {    
            xSemaphoreTake(xSemaphore1, portMAX_DELAY);
            PRINT_LOG(LOG_INFO, 0, "queueset_reader_task: xSemaphore1");
        }
        else
        {
            PRINT_LOG(LOG_INFO, 0, "queueset_reader_task: xQueueSet: %d", xActivatedMember);
        }
    }
}

GlobalType_t app_queueset_init(void)
{
    BaseType_t xReturned;

    // task reader create
    xReturned = xTaskCreate(queueset_reader_task, 
            "queueset_reader_task", 
            256, 
            NULL, 
            tskIDLE_PRIORITY + 2, 
            &xQsrHandle);

    xReturned &= xTaskCreate(queueset_writer_task, 
            "queueset_writer_task", 
            256, 
            NULL, 
            tskIDLE_PRIORITY + 2, 
            &xQswHandle);

    // queue create
    xQueue1 = xQueueCreate(10, sizeof(int));
    xQueue2 = xQueueCreate(10, sizeof(int));
    vQueueAddToRegistry(xQueue1, "testQueue1");
    vQueueAddToRegistry(xQueue2, "testQueue2");
    xSemaphore1 = xSemaphoreCreateBinary();

    // queue set create
    xQueueSet = xQueueCreateSet(QUEUE_SET_COMBIAND_LENGTH);
    xReturned &= xQueueAddToSet(xQueue1, xQueueSet);   //队列集添加队列xQueue1
    xReturned &= xQueueAddToSet(xQueue2, xQueueSet);    //队列集添加队列xQueue2
    xReturned &= xQueueAddToSet(xSemaphore1, xQueueSet); //队列集添加信号量xSemaphore1

    //检测队列集、队列、信号量是否创建成功
    if (xReturned != pdPASS
        || xQueue1 == NULL
        || xQueue2 == NULL
        || xSemaphore1 == NULL
        || xQueueSet == NULL) 
    {
        return RT_FAIL;
    }

    return RT_OK; 
}

可以看到,队列集可以实现类似Linux平台的select的功能,通过一个接口实现队列、信号量的统一管理;这样可以扩展队列间通讯方法,构建更加复杂的应用。

相关推荐
zhangzhangkeji5 天前
FreeRTos笔记(12c):从事件链表里删除节点 xTaskRemoveFromEventList、
freertos
La Pulga6 天前
【FreeRTOS源码学习笔记】任务延时列表
笔记·mcu·学习·嵌入式·freertos·实时操作系统
橘色的喵6 天前
coact:让嵌入式事件处理更可控
freertos·rt-thread·事件驱动·coact
人邮异步社区7 天前
学习FreeRTOS推荐哪本书?
freertos·rtos·嵌入式固件工程师
La Pulga9 天前
【FreeRTOS源码学习笔记】空闲任务与阻塞延时
笔记·mcu·学习·嵌入式·freertos·实时操作系统
La Pulga10 天前
【FreeRTOS源码学习笔记】任务切换
单片机·mcu·学习·嵌入式·freertos·实时操作系统
La Pulga12 天前
【FreeRTOS源码学习笔记】链表的实现
数据结构·笔记·stm32·mcu·链表·嵌入式·freertos
La Pulga12 天前
【FreeRTOS源码学习笔记】任务创建
笔记·stm32·mcu·学习·嵌入式·freertos·实时操作系统
天吾cc17 天前
FreeRTOS 事件标志组
freertos
编码追梦人19 天前
基于 ESP32-S3 的便携式多参数环境监护仪设计:从硬件选型到 FreeRTOS 多任务落地
数据库·单片机·嵌入式硬件·mongodb·esp32·freertos·低功耗设计