跳转至

ESP32freeRTOS(上)

ESP32 freeRTOS(上)

教程来自:https://www.bilibili.com/video/BV1FD4y1c7rA

环境的搭建就不多说了,参考这里:esp32ctf_thu学习

搭建好之后可以找一个例子先试一下,目录在 idf 下面的 examples\get-started,就用那个 hello_world 吧,打开 idf cmd 后切换到那个目录,使用命令 idf.py build 编译,完成之后可以使用 idf.py flash 将程序烧录进去

Task创建与删除

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

// 声明一个 task
void myTask(void *pvParam){
    while(1){
        printf("hello world from myTask \n\n");
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
    //vTaskDelete(NULL); //vTaskDelete如果放在task里且句柄为空 则会删除当前task
}

void app_main(void)
{
    TaskHandle_t myHandle = NULL;
    xTaskCreate(myTask, "myTask1", 1024, NULL, 1, &myHandle);
    // 参数:task函数指针,task名字,分配内存,传递的参数,优先级别,句柄地址
    // 空闲task优先级是0,我们设置为1表示比空闲task优先级高
    vTaskDelay(8000/portTICK_PERIOD_MS);
    //根据创建的task的句柄调用vTaskDelete删除task
    if(myHandle){
        vTaskDelete(myHandle);
    }
}

Task输入参数

task 的参数是一个无类型的指针

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

// 声明一个 task
void myTask(void *pvParam){
    int *pInt;
    pInt = (int *)pvParam;

    printf("I got a num = %d\n", *pInt);
    vTaskDelay(1000/portTICK_PERIOD_MS);
    vTaskDelete(NULL); //vTaskDelete如果放在task里且句柄为空 则会删除当前task
}

int testNum = 1;  //要传的值

void app_main(void)
{
    xTaskCreate(myTask, "myTask1", 2048, (void *) &testNum, 1, NULL);
}
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

// 声明一个 task
void myTask(void *pvParam){
    int *pArrayAddr;
    pArrayAddr = (int *)pvParam;

    printf("I got a num0 = %d\n", *pArrayAddr);
    printf("I got a num1 = %d\n", *(pArrayAddr+1));
    printf("I got a num2 = %d\n", *(pArrayAddr+2));
    printf("I got a num3 = %d\n", *(pArrayAddr+3));
    vTaskDelay(1000/portTICK_PERIOD_MS);
    vTaskDelete(NULL); //vTaskDelete如果放在task里且句柄为空 则会删除当前task
}

int testNum[] = {2,0,2,3};

void app_main(void)
{
    xTaskCreate(myTask, "myTask1", 2048, (void *) testNum, 1, NULL);
}
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

typedef struct A_STRUCT{
    int num;
    char name[];
} xStruct;

xStruct myStruct = {1,"yichen\x00"};

void myTask(void *pvParam){
    xStruct *pStruct;
    pStruct = (xStruct *)pvParam;

    printf("I got a num = %d\n", pStruct->num);
    printf("I got a name = %s\n", pStruct->name);

    vTaskDelay(1000/portTICK_PERIOD_MS);
    vTaskDelete(NULL); //vTaskDelete如果放在task里且句柄为空 则会删除当前task
}

void app_main(void)
{
    xTaskCreate(myTask, "myTask1", 2048, (void *) &myStruct, 1, NULL);
}

Task优先级别

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

void myTask(void *pvParam){

    vTaskDelay(1000/portTICK_PERIOD_MS);
    vTaskDelete(NULL); //vTaskDelete如果放在task里且句柄为空 则会删除当前task
}

void app_main(void)
{
    TaskHandle_t pxTask = NULL;
    UBaseType_t iPriority = 0;
    xTaskCreate(myTask, "myTask1", 2048, NULL, 1, &pxTask);
    iPriority = uxTaskPriorityGet(pxTask); //使用句柄获取优先级
    printf("\niPriority = %d\n",iPriority);
}

相同优先级的 task 会共享处理器时间,基于时间片运行,谁先创建谁先运行,然后就轮换着

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

void myTask1(void *pvParam){
    while(1){
        printf("this message from task 1\n");
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}

void myTask2(void *pvParam){
    while(1){
        printf("this message from task 2\n");
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}

void app_main(void)
{
    xTaskCreate(myTask1, "myTask1", 2048, NULL, 1, NULL);
    xTaskCreate(myTask2, "myTask2", 2048, NULL, 1, NULL);
}

不同的优先级会先运行优先级更高的

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

void myTask1(void *pvParam){
    while(1){
        printf("this message from task 1\n");
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}

void myTask2(void *pvParam){
    while(1){
        printf("this message from task 2\n");
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}

void app_main(void)
{
    xTaskCreate(myTask1, "myTask1", 2048, NULL, 1, NULL);
    xTaskCreate(myTask2, "myTask2", 2048, NULL, 2, NULL);
}

这里如果不管用是因为你手上的 esp32 是双核运行,idf.py menuconfig 修改一下配置,选择 Run FreeRTOS only on first core

1675605365239-a62b1420-062e-4122-8b92-d545247a7a12.png

1675605270295-c402ddf2-3456-48b4-bc68-c8b380ac1c22.png

1675605248042-0b1587ca-e858-417f-8e9e-723f12f2ec49.png

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

void myTask1(void *pvParam){
    while(1){
        printf("this message from task 1\n");
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}

void myTask2(void *pvParam){
    while(1){
        printf("this message from task 2\n");
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}

void app_main(void)
{
    TaskHandle_t pxTask = NULL;
    UBaseType_t iPriority = 0;
    xTaskCreate(myTask1, "myTask1", 2048, NULL, 1, &pxTask);
    xTaskCreate(myTask2, "myTask2", 2048, NULL, 2, NULL);
    iPriority = uxTaskPriorityGet(pxTask);
    printf("\nTask1 iPriority = %d\n",iPriority);
    vTaskPrioritySet(pxTask,3);// 根据task句柄修改优先级
    iPriority = uxTaskPriorityGet(pxTask);
    printf("\nTask1 iPriority = %d\n",iPriority);
}

task挂起和恢复

任务一共有 4 种状态:执行、就绪、阻塞、挂起

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

void myTask1(void *pvParam){
    while(1){
        printf("this message from task 1\n");
        vTaskDelay(1000/portTICK_PERIOD_MS);

        //vTaskSuspend(NULL); //挂起当前任务
    }
}

void myTask2(void *pvParam){
    while(1){
        printf("this message from task 2\n");
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}

void app_main(void)
{
    TaskHandle_t pxTask = NULL;
    UBaseType_t iPriority = 0;
    xTaskCreate(myTask1, "myTask1", 2048, NULL, 1, &pxTask);
    xTaskCreate(myTask2, "myTask2", 2048, NULL, 2, NULL);
    vTaskDelay(3000/portTICK_PERIOD_MS);
    vTaskSuspend(pxTask); //通过句柄挂起任务
}
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

void myTask1(void *pvParam){
    while(1){
        printf("this message from task 1\n");
        vTaskDelay(1000/portTICK_PERIOD_MS);

        //vTaskSuspend(NULL); //挂起当前任务
    }
}

void myTask2(void *pvParam){
    while(1){
        printf("this message from task 2\n");
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}

void app_main(void)
{
    TaskHandle_t pxTask = NULL;
    UBaseType_t iPriority = 0;
    xTaskCreate(myTask1, "myTask1", 2048, NULL, 1, &pxTask);
    xTaskCreate(myTask2, "myTask2", 2048, NULL, 2, NULL);
    vTaskSuspend(pxTask); //通过句柄挂起任务
    vTaskDelay(3000/portTICK_PERIOD_MS);
    vTaskResume(pxTask); //通过句柄恢复任务
}
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

void myTask1(void *pvParam){
    printf("Test begin\n");
    vTaskSuspendAll(); //挂起调度器,挂起后不能再调用FreeRTOS的API函数
    for (int i=0;i<9999;i++){   //这段代码执行期间不会调度其他任务,也就是说不会执行myTask2
        for(int j=0;j<5000;j++){
            ;
        }
    }
    xTaskResumeAll(); //恢复调度器
    printf("Test end\n");
    vTaskDelete(NULL);
}

void myTask2(void *pvParam){
    while(1){
        printf("this message from task 2\n");
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}

void app_main(void)
{
    TaskHandle_t pxTask = NULL;
    // UBaseType_t iPriority = 0;
    xTaskCreate(myTask1, "myTask1", 2048, NULL, 1, &pxTask);
    xTaskCreate(myTask2, "myTask2", 2048, NULL, 1, NULL);
    // vTaskSuspend(pxTask); //通过句柄挂起任务
    // vTaskDelay(3000/portTICK_PERIOD_MS);
    // vTaskResume(pxTask); //通过句柄恢复任务
}

Task系统

使用 tasklist 查看系统中所有任务的状态,需要在 idf.py menuconfig 修改一下配置,把其中的 Enable FreeRTOS trace facility 和 Enable FreeRTOS stats formatting functions 打开

1676171695638-2d599539-1d61-4cd1-bcae-7385d2084140.png

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

void myTask1(void *pvParam){
    printf("Test begin\n");
    vTaskSuspendAll(); //挂起调度器,挂起后不能再调用FreeRTOS的API函数
    for (int i=0;i<9999;i++){   //这段代码执行期间不会调度其他任务,也就是说不会执行myTask2
        for(int j=0;j<5000;j++){
            ;
        }
    }
    xTaskResumeAll(); //恢复调度器
    printf("Test end\n");
    vTaskDelete(NULL);
}

void myTask2(void *pvParam){
    while(1){
        printf("this message from task 2\n");
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}

void app_main(void)
{
    TaskHandle_t pxTask = NULL;
    xTaskCreate(myTask1, "myTask1", 2048, NULL, 1, &pxTask);
    xTaskCreate(myTask2, "myTask2", 2048, NULL, 1, NULL);
    static char taskBuffer[512] = {0}; //声明字符数组用来存储结果
    while(1){
        vTaskList(taskBuffer);
        printf("---------------------------------------------\n");
        printf("Name          State  priority   Stack  Num\n");
        printf("%s\n",taskBuffer);
        vTaskDelay(3000/portTICK_PERIOD_MS);
    }

}

结果解析:

Name 表示任务的名字,State 表示状态,priority 表示优先级,Stack 表示可用栈空间的大小,Num 表示任务的 id

Name      State  priority   Stack  Num
myTask2     R     1         1748    5
main        X     1         3712    2
myTask1     R     1         1756    4
IDLE        R     0         1244    3
esp_timer   S     22        3652    1

其中状态有这么些个:

X 表示正在运行,B 表示阻塞,R 表示就绪,S 表示挂起,D 表示被删除

Task堆栈

在创建任务的时候有个参数是设置堆栈深度,也就是说这个值并不是栈的大小,如果说栈的宽度是 4 个字节,设置的这个值为 100 的话,那么栈的小大就是 400 个字节,可以使用 uxTaskGetStackHighWaterMark 获取

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

void myTask1(void *pvParam){
    while(1){
        printf("Task1 begin\n");
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }

}

void app_main(void)
{
    TaskHandle_t pxTask = NULL;
    xTaskCreate(myTask1, "myTask1", 2048, NULL, 1, &pxTask);
    UBaseType_t iStack;
    while(1){
        iStack = uxTaskGetStackHighWaterMark(pxTask); //uxTaskGetStackHighWaterMark取值
        printf("task1 iStack = %d\n",iStack);
        vTaskDelay(3000/portTICK_PERIOD_MS);
    }

}

Task看门狗

有一些 Task 需要周期性运行,每隔一段时间要运行一次,如果一段时间内没有被运行,会触发任务的看门狗,打印出一些信息或者重启系统,看门狗也有 callback 函数可以自定义操作

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

void myTask1(void *pvParam){
    while(1){
        ;
    }
}

void app_main(void)
{
    TaskHandle_t pxTask = NULL;
    xTaskCreate(myTask1, "myTask1", 1024, NULL, 1, &pxTask);
}

回显结果中有这样的信息,触发了看门狗,这是因为 IDLE 这个任务每隔一段时间就需要运行一下,esp32 IDLE 任务里面注册了看门狗且会有喂狗的行为,而我们的 myTask1 优先级比 IDLE 高且没有调用阻塞函数(vTaskDelay 等)导致 IDLE 一直没有被执行,就没法喂狗,狗饿了就叫了🦮:

(15305) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
(15305) task_wdt:  - IDLE (CPU 0)
(15305) task_wdt: Tasks currently running:
(15305) task_wdt: CPU 0: myTask1
(15305) task_wdt: Print CPU 0 (current core) backtrace

解决这个问题的一个方法是在 myTask1 调用阻塞或者挂起函数,让他释放资源

还有一个方法就是把 myTask1 的优先级也变成 0,这样就不会因为 myTask1 的优先级高而不调用 IDLE

那怎么给自己的任务养一只看门狗呢?首先要在头文件中引入 #include "esp_task_wdt.h",然后在你的任务中创建看门狗 esp_task_wdt_add(NULL); 参数是任务的句柄,在任务里面用 NULL 就行

然后还要使用 esp_task_wdt_reset() 定期的喂狗

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_task_wdt.h"

void myTask1(void *pvParam){
    while(1){
        ;
    }
}

void myTask2(void *pvParam){

    esp_task_wdt_add(NULL);   //创建看门狗
    while(1){
        printf("Task2\n");
        //esp_task_wdt_reset();  //喂狗
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}

void app_main(void)
{
    TaskHandle_t pxTask = NULL;
    xTaskCreate(myTask1, "myTask1", 1024, NULL, 0, &pxTask);
    xTaskCreate(myTask2, "myTask2", 1024, NULL, 0, NULL);
}

如果取消喂狗的操作,会触发看门狗

(10335) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
(10335) task_wdt:  - myTask2 (CPU 0)
(10335) task_wdt: Tasks currently running:
(10335) task_wdt: CPU 0: myTask1
(10335) task_wdt: Print CPU 0 (current core) backtrace

Queue传递数据

通过队列传递数据,队列是一个先入先出的结果

比如下面这个函数,sendTask 往队列里面放数据,recvTask 从队列里面取数据

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_task_wdt.h"
#include "freertos/queue.h"  //队列所需头文件

void sendTask(void *pvParam){

    QueueHandle_t QHandle;  //创建一个队列句柄
    QHandle = (QueueHandle_t) pvParam; //取得队列句柄

    BaseType_t xStatus;  //定义一个变量,观察发送队列返回值,判断是否成功

    int i = 0; //定义发送的数据

    while(1){
        xStatus = xQueueSend(QHandle, &i, 0);  //参数:队列句柄、发送缓冲区地址、超时时间
        if(xStatus != pdPASS)
            printf("Send Fail!\n");
        else
            printf("Send Done!\n");
        i++;
        if (i==8) i=0; //到8归零

        vTaskDelay(1000/portTICK_PERIOD_MS);
    }


}

void recvTask(void *pvParam){

    QueueHandle_t QHandle;  //创建一个队列句柄
    QHandle = (QueueHandle_t) pvParam; //取得队列句柄

    BaseType_t xStatus;  //定义一个变量,观察接收队列返回值,判断是否成功

    int j = 0;

    while(1){
        if(uxQueueMessagesWaiting(QHandle) !=0){ //先判断队列句柄是否为空
            xStatus = xQueueReceive(QHandle, &j, 0); //参数:队列句柄、接收缓冲区地址、超时时间
            if(xStatus != pdPASS)
                printf("Recv Fail!\n");
            else
                printf("Recv j=%d Done!\n", j);

            vTaskDelay(1000/portTICK_PERIOD_MS);
        }else{
            printf("No Data!\n");
        }

    }
}

void app_main(void)
{

    QueueHandle_t QHandle;  //创建一个队列句柄
    QHandle = xQueueCreate(5, sizeof(int)); //创建队列,参数:队列长度、队列的宽度

    if (QHandle != NULL){
        printf("Create queue successfully!\n");

        xTaskCreate(sendTask, "sendTask", 1024*5, (void *)QHandle, 1, NULL);
        xTaskCreate(recvTask, "recvTask", 1024*5, (void *)QHandle, 1, NULL);
    }
    else{
        printf("Can't create queue!\n");
    }


}
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_task_wdt.h"
#include "freertos/queue.h"  //队列所需头文件

typedef struct A_STRUCT{
    char id;
    char data;
} xStruct;

void sendTask(void *pvParam){

    QueueHandle_t QHandle;  //创建一个队列句柄
    QHandle = (QueueHandle_t) pvParam; //取得队列句柄

    BaseType_t xStatus;  //定义一个变量,观察发送队列返回值,判断是否成功

    xStruct myStruct = {1,66}; //定义发送的数据

    while(1){
        xStatus = xQueueSend(QHandle, &myStruct, 0);  //参数:队列句柄、发送缓冲区地址、超时时间
        if(xStatus != pdPASS)
            printf("Send Fail!\n");
        else
            printf("Send Done!\n");
        myStruct.id++;
        if (myStruct.id==8) myStruct.id=0; //到8归零

        vTaskDelay(1000/portTICK_PERIOD_MS);
    }


}

void recvTask(void *pvParam){

    QueueHandle_t QHandle;  //创建一个队列句柄
    QHandle = (QueueHandle_t) pvParam; //取得队列句柄

    BaseType_t xStatus;  //定义一个变量,观察接收队列返回值,判断是否成功

    xStruct myStruct = {0,0}; //定义接收的数据

    while(1){
        if(uxQueueMessagesWaiting(QHandle) !=0){ //先判断队列句柄是否为空
            xStatus = xQueueReceive(QHandle, &myStruct, 0); //参数:队列句柄、接收缓冲区地址、超时时间
            if(xStatus != pdPASS)
                printf("Recv Fail!\n");
            else
                printf("Recv myStruct.id=%d myStruct.data=%d Done!\n", myStruct.id, myStruct.data);

            vTaskDelay(1000/portTICK_PERIOD_MS);
        }else{
            printf("No Data!\n");
        }

    }
}

void app_main(void)
{

    QueueHandle_t QHandle;  //创建一个队列句柄
    QHandle = xQueueCreate(5, sizeof(int)); //创建队列,参数:队列长度、队列的宽度

    if (QHandle != NULL){
        printf("Create queue successfully!\n");

        xTaskCreate(sendTask, "sendTask", 1024*5, (void *)QHandle, 1, NULL);
        xTaskCreate(recvTask, "recvTask", 1024*5, (void *)QHandle, 1, NULL);
    }
    else{
        printf("Can't create queue!\n");
    }


}
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_task_wdt.h"
#include "freertos/queue.h"  //队列所需头文件

typedef struct A_STRUCT{
    char id;
    char data;
} xStruct;

void sendTask(void *pvParam){

    QueueHandle_t QHandle;  //创建一个队列句柄
    QHandle = (QueueHandle_t) pvParam; //取得队列句柄

    BaseType_t xStatus;  //定义一个变量,观察发送队列返回值,判断是否成功

    char *pStrToSend;  //定义发送的数据
    int i=0;

    while(1){
        pStrToSend = (char *)malloc(50);  //分配内存
        snprintf(pStrToSend, 50, "Hello From Queue %d\r\n" ,i);
        i++;
        xStatus = xQueueSend(QHandle, &pStrToSend, 0);  //参数:队列句柄、发送缓冲区地址、超时时间
        if(xStatus != pdPASS)
            printf("Send Fail!\n");
        else
            printf("Send Done!\n");

        vTaskDelay(1000/portTICK_PERIOD_MS);
    }

}

void recvTask(void *pvParam){

    QueueHandle_t QHandle;  //创建一个队列句柄
    QHandle = (QueueHandle_t) pvParam; //取得队列句柄

    BaseType_t xStatus;  //定义一个变量,观察接收队列返回值,判断是否成功

    char *pStrToRecv; //定义接收的数据

    while(1){
        if(uxQueueMessagesWaiting(QHandle) !=0){ //先判断队列句柄是否为空
            xStatus = xQueueReceive(QHandle, &pStrToRecv, 0); //参数:队列句柄、接收缓冲区地址、超时时间
            if(xStatus != pdPASS)
                printf("Recv Fail!\n");
            else
                printf("Recv %s Done!\n", pStrToRecv);
            free(pStrToRecv);

            vTaskDelay(1000/portTICK_PERIOD_MS);
        }else{
            printf("No Data!\n");
        }

    }
}

void app_main(void)
{
    QueueHandle_t QHandle;  //创建一个队列句柄
    QHandle = xQueueCreate(5, sizeof(char *)); //创建队列,参数:队列长度、队列的宽度

    if (QHandle != NULL){
        printf("Create queue successfully!\n");

        xTaskCreate(sendTask, "sendTask", 1024*5, (void *)QHandle, 1, NULL);
        xTaskCreate(recvTask, "recvTask", 1024*5, (void *)QHandle, 1, NULL);
    }
    else{
        printf("Can't create queue!\n");
    }
}

Queue多进单出

多个任务往一个队列里面放数据,一个任务从队列里面取数据,这时候要格外重视任务优先级,在这段代码中,我们设置 recvTask 从队列中取值,他的优先级更高,保证他取完值才会交给其他任务来运行,另外两个任务优先级相同,一次往队列里面放值

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_task_wdt.h"
#include "freertos/queue.h"  //队列所需头文件

void sendTask1(void *pvParam){

    QueueHandle_t QHandle;  //创建一个队列句柄
    QHandle = (QueueHandle_t) pvParam; //取得队列句柄
    BaseType_t xStatus;  //定义一个变量,观察接收队列返回值,判断是否成功
    int i=111;

    while(1){

        xStatus = xQueueSend(QHandle, &i, 0);  //参数:队列句柄、发送缓冲区地址、超时时间
        if(xStatus != pdPASS)
            printf("Send Fail!\n");
        else
            printf("Send %d Done!\n", i);

        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}

void sendTask2(void *pvParam){

    QueueHandle_t QHandle;  //创建一个队列句柄
    QHandle = (QueueHandle_t) pvParam; //取得队列句柄
    BaseType_t xStatus;  //定义一个变量,观察接收队列返回值,判断是否成功
    int i=222;

    while(1){

        xStatus = xQueueSend(QHandle, &i, 0);  //参数:队列句柄、发送缓冲区地址、超时时间
        if(xStatus != pdPASS)
            printf("Send Fail!\n");
        else
            printf("Send %d Done!\n", i);

        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}

void recvTask(void *pvParam){

    QueueHandle_t QHandle;  //创建一个队列句柄
    QHandle = (QueueHandle_t) pvParam; //取得队列句柄
    BaseType_t xStatus;  //定义一个变量,观察接收队列返回值,判断是否成功
    int i; //定义接收的数据

    while(1){
        xStatus = xQueueReceive(QHandle, &i, portMAX_DELAY); //参数:队列句柄、接收缓冲区地址、超时时间
                                                                 //portMAX_DELAY表示无限等待,阻塞task
        if(xStatus != pdPASS)
            printf("Recv Fail!\n");
        else
            printf("Recv %d Done!\n", i);
    }
}

void app_main(void)
{
    QueueHandle_t QHandle;  //创建一个队列句柄
    QHandle = xQueueCreate(5, sizeof(char *)); //创建队列,参数:队列长度、队列的宽度
    if (QHandle != NULL){
        printf("Create queue successfully!\n");

        xTaskCreate(sendTask1, "sendTask1", 1024*5, (void *)QHandle, 1, NULL);
        xTaskCreate(sendTask2, "sendTask2", 1024*5, (void *)QHandle, 1, NULL);
        xTaskCreate(recvTask, "recvTask", 1024*5, (void *)QHandle, 2, NULL);
    }
    else{
        printf("Can't create queue!\n");
    }
}

Queue集合

在应用设计中,会出现多个 task 分别写各自的 Queue,然后一个 task 分别从各个队列中读取数据,freertos 提供了一个队列集合的概念,把这些队列全部放在一个集合中,提供一个函数接口(xQueueSelectFromSet)从集合中取有数据的对应的队列的句柄,然后再读取数据

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_task_wdt.h"
#include "freertos/queue.h"  //队列所需头文件

void sendTask1(void *pvParam){
    QueueHandle_t QHandle;  //创建一个队列句柄
    QHandle = (QueueHandle_t) pvParam; //取得队列句柄
    BaseType_t xStatus;  //定义一个变量,观察接收队列返回值,判断是否成功
    int i=111;
    while(1){

        xStatus = xQueueSend(QHandle, &i, 0);  //参数:队列句柄、发送缓冲区地址、超时时间
        if(xStatus != pdPASS)
            printf("Send Fail!\n");
        else
            printf("Send %d Done!\n", i);

        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}

void sendTask2(void *pvParam){

    QueueHandle_t QHandle;  //创建一个队列句柄
    QHandle = (QueueHandle_t) pvParam; //取得队列句柄
    BaseType_t xStatus;  //定义一个变量,观察接收队列返回值,判断是否成功
    int i=222;

    while(1){

        xStatus = xQueueSend(QHandle, &i, 0);  //参数:队列句柄、发送缓冲区地址、超时时间
        if(xStatus != pdPASS)
            printf("Send Fail!\n");
        else
            printf("Send %d Done!\n", i);
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}

void recvTask(void *pvParam){

    QueueSetHandle_t QueueSet;  //创建一个队列句柄
    QueueSet = (QueueSetHandle_t) pvParam; //取得队列句柄
    QueueSetMemberHandle_t QueueData;
    BaseType_t xStatus;  //定义一个变量,观察接收队列返回值,判断是否成功

    int i; //定义接收的数据

    while(1){
        QueueData = xQueueSelectFromSet(QueueSet, portMAX_DELAY);  //从队列集合中取有数据的队列
        xStatus = xQueueReceive(QueueData, &i, portMAX_DELAY);     //参数:队列句柄、接收缓冲区地址、超时时间
                                                                   //portMAX_DELAY表示无限等待,阻塞task
        if(xStatus != pdPASS)
            printf("Recv Fail!\n");
        else
            printf("Recv %d Done!\n", i);
    }
}

void app_main(void)
{
    QueueHandle_t QHandle1;  //创建一个队列句柄
    QHandle1 = xQueueCreate(5, sizeof(char *)); //创建队列,参数:队列长度、队列的宽度

    QueueHandle_t QHandle2;  //创建一个队列句柄
    QHandle2 = xQueueCreate(5, sizeof(char *)); //创建队列,参数:队列长度、队列的宽度

    QueueSetHandle_t QueueSet;   //创建队列集合(两个队列长度的和)      
    QueueSet = xQueueCreateSet(10);    

    xQueueAddToSet(QHandle1, QueueSet);  //把QHandle1加到QueueSet中
    xQueueAddToSet(QHandle2, QueueSet);  //把QHandle2加到QueueSet中

    if ((QHandle1 != NULL) && (QHandle2 != NULL) && (QueueSet != NULL)){
        printf("Create queue successfully!\n");
        xTaskCreate(sendTask1, "sendTask1", 1024*5, (void *)QHandle1, 1, NULL);
        xTaskCreate(sendTask2, "sendTask2", 1024*5, (void *)QHandle2, 1, NULL);
        xTaskCreate(recvTask, "recvTask", 1024*5, (void *)QueueSet, 2, NULL);
    }
    else{
        printf("Can't create queue!\n");
    }
}

Queue MailBox

队列会把数据从一个 task 发送到另一个 task 或中断,但是 mailbox 会保存数据,被任何 task 或中断读取数据

发送方会覆盖 mailbox 的值,接收方只能读取 mailbox 的值

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_task_wdt.h"
#include "freertos/queue.h"  //队列所需头文件

void writeTask(void *pvParam){

    QueueHandle_t MailBox;  //创建一个队列句柄
    MailBox = (QueueHandle_t) pvParam; //取得队列句柄
    BaseType_t xStatus;  //定义一个变量,观察接收队列返回值,判断是否成功

    int i=1;

    while(1){

        xStatus = xQueueOverwrite(MailBox, &i);  //参数:队列句柄、发送缓冲区地址
        if(xStatus != pdPASS)
            printf("Send Fail!\n");
        else
            printf("Write %d Done!\n", i);

        i++;

        vTaskDelay(6000/portTICK_PERIOD_MS);
    }
}

void readTask(void *pvParam){

    QueueHandle_t MailBox;  //创建一个队列句柄
    MailBox = (QueueHandle_t) pvParam; //取得队列句柄
    BaseType_t xStatus;  //定义一个变量,观察接收队列返回值,判断是否成功

    int i;

    while(1){

        xStatus = xQueuePeek(MailBox, &i, 0);  //参数:队列句柄、发送缓冲区地址
        if(xStatus != pdPASS)
            printf("Read Fail!\n");
        else
            printf("Read %d Done!\n", i);

        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}


void app_main(void)
{

    QueueHandle_t MailBox;  //创建一个队列句柄
    MailBox = xQueueCreate(1, sizeof(int)); //创建队列,参数:队列长度、队列的宽度

    if (MailBox != NULL){
        printf("Create queue successfully!\n");
        xTaskCreate(writeTask, "writeTask", 1024*5, (void *)MailBox, 1, NULL);
        xTaskCreate(readTask, "readTask1", 1024*5, (void *)MailBox, 2, NULL);  //共用readTask函数体创建三个不同的任务
        xTaskCreate(readTask, "readTask2", 1024*5, (void *)MailBox, 2, NULL);
        xTaskCreate(readTask, "readTask3", 1024*5, (void *)MailBox, 2, NULL);
    }
    else{
        printf("Can't create queue!\n");
    }

}

原文: https://www.yuque.com/hxfqg9/iot/xrlkh8y5xopen1v6