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

相关推荐
hsjiasb19 小时前
FreeRTOS学习(三十九)——最终总结
学习·学习笔记·freertos
捷瑞电子工坊1 天前
FreeRTOS事件组:多任务同步的利器
stm32·嵌入式·freertos·实时操作系统·标准外设库·事件组·任务同步
hsjiasb1 天前
FreeRTOS学习(三十八)——性能优化
学习·学习笔记·freertos
hsjiasb2 天前
FreeRTOS学习(三十七)——常见错误与工程规范
学习·学习笔记·freertos
捷瑞电子工坊3 天前
FreeRTOS任务管理:从入门到实践(标准库版)
stm32·嵌入式·freertos·实时操作系统·任务管理·标准外设库·任务挂起与恢复
hsjiasb3 天前
FreeRTOS学习(三十五)——系统架构综合项目
学习·学习笔记·freertos
拾知_H4 天前
STM32+FreeRTOS基础配置
stm32·嵌入式硬件·freertos
qq_401700414 天前
RTOS的几种常见调度策略
freertos
圆奋奋4 天前
FreeRTOS学习(十)- 最核心的内容:优先级抢占式调度 + 上下文切换
学习·freertos