FreeRTOS_队列集

犀利的毛毛虫 发布于 2025-03-11 352 次阅读


队列集简介(了解)

队列集(Queue Set)是 FreeRTOS 中的一种数据结构,用于管理多个队列。它提供了一种有效的方式,通过单个 API 调用来操作和访问一组相关的队列。

在多任务系统中,任务之间可能需要共享数据,而这些数据可能存储在不同的队列中。队列集的作用就是为了更方便地管理这些相关队列,使得任务能够轻松地访问和处理多个队列的数据。

队列集的特点和用法:

  • 集中管理多个队列:队列集允许你将多个相关联的队列组织在一起,方便集中管理。
  • 单一 API 调用:通过单一的 API 调用,任务可以同时操作多个队列,而无需分别处理每个队列。
  • 简化任务代码:对于需要处理多个相关队列的任务,使用队列集可以简化代码,提高可读性和维护性。
  • 提高系统效率:在需要协同工作的任务之间共享和传递数据时,队列集可以提高系统的效率。
  • 协同工作:任务可以更方便地协同工作,共享数据,实现更复杂的任务间通信和同步。

使用队列集时,你需要了解如何创建、添加和访问队列集,以及如何使用队列集 API 进行数据的发送和接收。队列集是 FreeRTOS 提供的一个强大工具,用于更灵活地组织和处理任务之间的数据流。

想象一下你有一个智能家居系统,有一个任务负责处理温度信息,另一个任务负责光照信息。你可能有两个队列,一个用于温度,一个用于光照。现在,通过队列集,你可以方便地管理这两个队列,让控制任务能够在需要时从这两个队列中获取信息,从而更智能地控制环境。

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

队列集相关函数:

函数描述
xQueueCreateSet()创建队列集
xQueueAddToSet()队列添加到队列集中
xQueueRemoveFromSet()从队列集中移除队列
xQueueSelectFromSet()获取队列集中有有效消息的队列
xQueueSelectFromSetFromISR()在中断中获取队列集中有有效消息的队列

队列集操作实验(掌握)实验目标

学习使用 FreeRTOS 的队列集相关函数:

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

FreeRTOSConfig.h代码清单

#include "freertos_demo.h"
/* freertos相关的头文件,必须的 */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"  //队列相关的头文件
#include "semphr.h" //信号量相关的头文件
/* 需要用到的其他头文件 */
#include "LED.h"
#include "Key.h"

/* 启动任务的配置 */
#define START_TASK_STACK 128
#define START_TASK_PRIORITY 1
TaskHandle_t start_task_handle;
void start_task(void *pvParameters);

/* 任务1的配置 */
#define TASK1_STACK 128
#define TASK1_PRIORITY 2
TaskHandle_t task1_handle;
void task1(void *pvParameters);

/* 任务2的配置 */
#define TASK2_STACK 128
#define TASK2_PRIORITY 3
TaskHandle_t task2_handle;
void task2(void *pvParameters);

/* 队列句柄 */
QueueHandle_t queue1;
QueueHandle_t sem_handle;
QueueSetHandle_t queue_set_handle;

/**
 * @description: 启动FreeRTOS
 * @return {*}
 */
void freertos_start(void)
{

    BaseType_t res = 0;

    /* 在创建任务之前,先创建好需要的队列 */
    /* 创建一个存放KEY值编号的小队列queue1 */
    queue1 = xQueueCreate(2, sizeof(uint8_t));
    if (queue1 != NULL)
    {
        printf("queue1创建成功\r\n");
    }
    else
    {
        printf("queue1创建失败\r\n");
    }

    /* 创建二值信号量,创建完不主动释放 */
    sem_handle = xSemaphoreCreateBinary();
    if (sem_handle != NULL)
    {
        printf("二值信号量创建成功\r\n");
    }
    else
    {
        printf("二值信号量创建失败\r\n");
    }

    /* 创建队列集 */
    queue_set_handle = xQueueCreateSet(2);
    if (queue_set_handle != NULL)
    {
        printf("创建队列集成功\r\n");
    }

    /* 将队列、信号量添加到队列集中:添加时,队列/信号量 需要为空 */
    res = xQueueAddToSet(queue1, queue_set_handle);
    if (res == pdPASS)
    {
        printf("queue1添加到队列集成功\r\n");
    }
    res = xQueueAddToSet(sem_handle, queue_set_handle);
    if (res == pdPASS)
    {
        printf("二值信号量添加到队列集成功\r\n");
    }

    /* 1.创建一个启动任务 */
    xTaskCreate((TaskFunction_t)start_task,               // 任务函数的地址
                (char *)"start_task",                     // 任务名字符串
                (configSTACK_DEPTH_TYPE)START_TASK_STACK, // 任务栈大小,默认最小128,单位4字节
                (void *)NULL,                             // 传递给任务的参数
                (UBaseType_t)START_TASK_PRIORITY,         // 任务的优先级
                (TaskHandle_t *)&start_task_handle);      // 任务句柄的地址

    /* 2.启动调度器:会自动创建空闲任务 */
    vTaskStartScheduler();
}

/**
 * @description: 启动任务:用来创建其他Task
 * @param {void} *pvParameters
 * @return {*}
 */
void start_task(void *pvParameters)
{
    /* 进入临界区:保护临界区里的代码不会被打断 */
    taskENTER_CRITICAL();

    /* 创建3个任务 */
    xTaskCreate((TaskFunction_t)task1,
                (char *)"task1",
                (configSTACK_DEPTH_TYPE)TASK1_STACK,
                (void *)NULL,
                (UBaseType_t)TASK1_PRIORITY,
                (TaskHandle_t *)&task1_handle);
    xTaskCreate((TaskFunction_t)task2,
                (char *)"task2",
                (configSTACK_DEPTH_TYPE)TASK2_STACK,
                (void *)NULL,
                (UBaseType_t)TASK2_PRIORITY,
                (TaskHandle_t *)&task2_handle);

    /* 启动任务只需要执行一次即可,用完就删除自己 */
    vTaskDelete(NULL);

    /* 退出临界区 */
    taskEXIT_CRITICAL();
}

/**
 * @description: 任务一:用于扫描按键,当KEY1按下,往队列写入数据,当KEY2按下,释放二值信号量。
 * @param {void} *pvParameters
 * @return {*}
 */
void task1(void *pvParameters)
{
    uint8_t key = 0;
    uint8_t res = 0;
    while (1)
    {
        key = Key_Detect();
        if (key == KEY1_PRESS)
        {
            /* 将key的编号写入queue1 */
            res = xQueueSend(queue1, &key, portMAX_DELAY);
            if (res == pdPASS)
            {
                printf("往queue1发送数据[%d]成功\r\n", key);
            }
            else
            {
                printf("往queue1发送数据失败\r\n");
            }
        }
        else if (key == KEY2_PRESS)
        {
            /* 释放信号量 */
            res = xSemaphoreGive(sem_handle);
            if (res == pdPASS)
            {
                printf("释放信号量成功\r\n");
            }
            else
            {
                printf("释放信号量失败\r\n");
            }
        }

        vTaskDelay(500);
    }
}

/**
 * @description: 任务二:读取队列集中的消息,并打印
 * @param {void} *pvParameters
 * @return {*}
 */
void task2(void *pvParameters)
{

    QueueSetMemberHandle_t member_handle;
    uint8_t receive = 0;
    uint8_t res = 0;
    while (1)
    {
        /* 查看哪个队列有数据来了 */
        member_handle = xQueueSelectFromSet(queue_set_handle, portMAX_DELAY);
        /* 根据对应的队列,去获取数据 */
        if (member_handle == queue1)
        {
            res = xQueueReceive(queue1, &receive, portMAX_DELAY);
            if (res == pdPASS)
            {
                printf("task2从queue1成功读取数据[%d]\r\n", receive);
            }
            else
            {
                printf("task2从queue1读取数据失败\r\n");
            }
        }
        else if (member_handle == sem_handle)
        {
            res = xSemaphoreTake(sem_handle,portMAX_DELAY);
            if(res == pdPASS)
            {
                printf("获取信号量成功\r\n");
            }
        }
        else 
        {
            printf("从队列集获取信息失败\r\n");
        }
    }
}