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的功能,通过一个接口实现队列、信号量的统一管理;这样可以扩展队列间通讯方法,构建更加复杂的应用。

相关推荐
长江后浪博客10 天前
ESP32-SOLO-1 在 ESP-IDF v5.5 下启动不断重启问题分析与解决
单片机·嵌入式硬件·嵌入式·esp32·freertos·esp-idf
WWJA王文举11 天前
FreeRTOS动态创建任务和静态创建任务详解:xTaskCreate与xTaskCreateStatic区别
操作系统·freertos·嵌入式开发
离凌寒14 天前
一、关于rtthread nano使用上的细节总结
freertos
桃蹊、14 天前
串口/网络透传实战:FreeRTOS 多任务架构与连接池设计
stm32·物联网·wifi·freertos
虎王物联14 天前
ESP32 FreeRTOS多任务优先级翻转与互斥量保护实战
物联网·嵌入式·esp32·freertos
桃蹊、14 天前
FreeRTOS(六)之队列:任务间通信的基石
freertos·队列
桃蹊、15 天前
FreeRTOS(二)之堆和栈:内存基础入门
freertos·内存管理··
桃蹊、15 天前
FreeRTOS(四)之任务管理(上):创建、删除与任务参数
freertos·任务·task·创建任务
论迹复利17 天前
PendSV与RISC-V trap——上下文切换的两种设计哲学
内核·freertos·risc-v
捷瑞电子工坊20 天前
FreeRTOS中断管理:从原理到实践(标准库版)
嵌入式·freertos·实时操作系统·任务通知·临界区·中断管理·二值信号量