基于esp32-idf学习FreeRtos笔记

学习目标:

  • 一周掌握 FreeRtos 入门知识

学习内容:

为什么要学习FreeRTOS

1. 只需要3个c文件就能实现FreeRtos的基本功能,分别是task.c   queue.c    list.c.文件小非常适合嵌入式操作系统,并且已经移植到很多设备平台上,使用性很广.

2. Free=免费开源,Rtos=实时操作系统

3. Esp32,非常适合用于开发,集成了很多stm32没有的外设,例如wifi 蓝牙一块芯片就可以完成很多功能,而且性能也比高于stm32,芯片价格也比stm32便宜,相信以后会是esp32的天下,并且esp32-idf已经移植好FreeRtos,所以基于esp32-idf学习FreeRtos是比较好的选择.


以下为学习笔记,供大家学习:

一. 创建Mytask的任务函数方法

传入函数的参数为无类型指针方便后期传入内容定义:

void MytTask(void* param)
{
    while(1)//任务内容是无限循环的
    {
        printf("Hello World !");//任务内容是打印Hello World!
        vTaskDelay(1000/portTICK_PERIOD_MS);
        //延时1000ms=1s,并将操作交还系统执行其他任务实现非阻塞延时
    }

}

在主函数void app_main(void)里创建任务xTaskCreate用于调用任务函数:                                         xTaskCreate: 创建函数,参数依次为 函数指针 , 函数名称 , 分配的内存 , 传递的参数 , 优先级别和句柄,主题函数为无限循环.

void app_main(void)
{
    xTaskCreate(MyTask,"mytask1",1024,NULL,1,NULL);//创建任务函数
    //1:pxtaskcode任务函数
    //2:PcName任务名字
    //3:usStackDepth任务堆栈
    //4:pyParameters任务传入参数
    //5:usPriorit任务优先级,最低优先级为0=空闲任务,可以设置0-31
    //6:pxCreattedTask任务句柄任务创建成功后会返回这个句柄,其他api任务接口可调用这个句柄          
}

xTaskDelete: (句柄)删除函数,也可以在主体函数中用xTaskDelete(NULL)删除函数

void app_main(void)
{
    TaskHandle_t MyHandle=NULL;//创造一个TaskHandle_t类型的变量;
    xTaskCreate(myTask,"mytask1",1024,NULL,1,&MyHandle);//创建任务函数
    //1:pxtaskcode任务函数
    //2:PcName任务名字
    //3:usStackDepth任务堆栈
    //4:pyParameters任务传入参数
    //5:usPriorit任务优先级,最低优先级为0=空闲任务,可以设置0-31
    //6:pxCreattedTask任务句柄任务创建成功后会返回这个句柄,其他api任务接口可调用这个句柄 

    if(MyHandle!=NULL)//判读任务创建函数是否执行
    vTaskDelete(MyHandle);//删除任务    
}

但是如果任务创建完立马执行删除任务,会导致创建的任务还没来得及执行就被删除了,及没有执行或者没有执行完毕,所以要在vTaskDelete()函数上加延时

portTICK_PERIOD_MS是在FreeRTOS.h里定义的系统时钟一般为1;

vTaskDelay()以毫秒为单位执行的

void app_main(void)
{
    TaskHandle_t MyHandle=NULL;//创造一个TaskHandle_t类型的变量;
    xTaskCreate(MyTask,"Mytask1",1024,NULL,1,&MyHandle);//创建任务函数
    //1:pxtaskcode任务函数
    //2:PcName任务名字
    //3:usStackDepth任务堆栈
    //4:pyParameters任务传入参数
    //5:usPriorit任务优先级,最低优先级为0=空闲任务,可以设置0-31
    //6:pxCreattedTask任务句柄任务创建成功后会返回这个句柄,其他api任务接口可调用这个句柄 

     vTaskDelay(1000/portTICK_PERIOD_MS);//添加延时保证任务执行完才删除任务

    if(MyHandle!=NULL)//判读任务创建函数是否执行
    vTaskDelete(MyHandle);//删除任务    
}

将延时时间设置为8000及8000ms=8s可以使任务执行8次

void app_main(void)
{
    TaskHandle_t MyHandle=NULL;//创造一个TaskHandle_t类型的变量;
    xTaskCreate(myTask,"Mytask1",1024,NULL,1,&MyHandle);//创建任务函数
    //1:pxtaskcode任务函数
    //2:PcName任务名字
    //3:usStackDepth任务堆栈
    //4:pyParameters任务传入参数
    //5:usPriorit任务优先级,最低优先级为0=空闲任务,可以设置0-31
    //6:pxCreattedTask任务句柄任务创建成功后会返回这个句柄,其他api任务接口可调用这个句柄 

     vTaskDelay(8000/portTICK_PERIOD_MS);//设置为8s延时,上面的任务每个为1s即以执行8次任务函数

    if(MyHandle!=NULL)//判读任务创建函数是否执行
    vTaskDelete(MyHandle);//删除任务    
}

或者可以使用第二种任务删除的方法:

在创建任务内容的函数里添加vTaskDelete函数可以删除当前task任务:

创建任务的函数句柄不需要设置值,可直接设置为NULL.

void MyTask(void* param)
{
   // while(1)//不需要循环因为执行完需要删除任务
    {
        printf("Hello World !");
        vTaskDelay(1000/portTICK_PERIOD_MS);
 
    }

    vTaskDelete(NULL);//删除当前任务
}
void app_main(void)
{
    xTaskCreate(MyTask,"mytask1",1024,NULL,1,NULL);//创建任务函数 
}

二. 传入数据

Task Input Parameter输入参数:4种 分别为 整数,数组,结构体,字符串

任务函数xTaskCreate传入的第4个参数pvParameters是void类型指针即可以接收任何类型的参数

想要将参数传入任务中需要定义一个变量TaskNum;将变量转换为void指针变量.再传入xTaskCreate函数中;

void MyTask(void* param)
{
    int*Pint;//创建一个int类型的指针
    Pint=(int*)param;//将传入函数的参数赋值给Pint
    printf("%d \n",*Pint);//打印Pint指针的内容用于验证参数是否传入任务函数中
    vTaskDelay(1000/portTICK_PERIOD_MS);
    vTaskDelete(NULL);//删除当前任务
}

int TestNum=1;//创建一个int类型变量用于传入任务函数;
void app_main(void)
{
    xTaskCreate(MyTask,"mytask1",1024,(void*)&TestNum,1,NULL);
    //创建任务函数 并将TestNum参数传入函数中
}

如果显示stack overflow即栈溢出可以将任务分配的内存调整为2048,如果没有输出可能是printf里面没加\n,没加\n是不会输出东西的.

传入数组                  注意传入数组则参数不需要加&取地址符,因为数组将首地址传入函数的

void MyTask(void* param)
{
    int* pArryAddr;//创建一个int类型的指针
    pArryAddr=(int*)param;//将传入函数的参数赋值给 pArryAddr
    printf("%d \n",*pArryAddr);//打印Pint指针的内容用于验证参数是否传入任务函数中
    printf("%d \n",*(pArryAddr+1));//打印Pint指针的内容用于验证参数是否传入任务函数中
    printf("%d \n",*(pArryAddr+2));//打印Pint指针的内容用于验证参数是否传入任务函数中

    vTaskDelay(1000/portTICK_PERIOD_MS);
    vTaskDelete(NULL);//删除当前任务
}

int TestNum[]={1,2,3};//创建一个int类型数组用于传入任务函数;
void app_main(void)
{
    xTaskCreate(MyTask,"mytask1",2024,(void*)TestNum,1,NULL);
    //创建任务函数 并将TestNum参数传入函数中
}

传入结构体

typedef struct A_STRUCT
{
    int inum1;
    int inum2;
}xStruct;
xStruct xStrtest={1,2};//创建结构体

void MyTask(void* param)
{
    xStruct *pStrtest;//创建一个xStruct类型的结构体指针
    pStrtest=(xStruct*)param;//将传入函数的参数赋值给Pint
    printf("%d \n",pStrtest->inum1);//打印pStrtest指针的内容用于验证参数是否传入任务函数中
    printf("%d \n",pStrtest->inum2);//打印pStrtest指针的内容用于验证参数是否传入任务函数中

    vTaskDelay(1000/portTICK_PERIOD_MS);
    vTaskDelete(NULL);//删除当前任务
}


void app_main(void)
{
    xTaskCreate(MyTask,"mytask1",2048,(void*)&xStrtest,1,NULL);//创建任务函数并传入结构体
}

传入字符串: 注意字符串本身就是地址所以传入参数是也是不需要取地址的:

static const char *TastTxt="Hello World !"//创建字符串指针

void MyTask(void* param)
{
    char *pcTxtmytask=(char *)param;//创建一个char类型的指针
    
    printf("%s \n",pcTxtmytask);//打印pStrtest指针的内容用于验证参数是否传入任务函数中
    
    vTaskDelay(1000/portTICK_PERIOD_MS);
    vTaskDelete(NULL);//删除当前任务
}


void app_main(void)
{
    xTaskCreate(MyTask,"mytask1",2048,(void*)pcTxtmytask,1,NULL);//创建任务函数并传入结构体
}

三. 任务优先级

任务优先级在FreeRTOSConfig.h里由 configMAX_PRIORITIES定义.

如果设置的优先级大于 configMAX_PRIORITIES-1会默认用 configMAX_PRIORITIES-1代替.

不建议修改FreeRTOSConfig.h里由 configMAX_PRIORITIES里的值,因为会重新编译文件,占用更多内存.

uxTaskPriorityGet()函数可以获取任务优先级用法如下:

void app_main(void)
{
    UBaseType_t iPriority=0;//定义一个UBaseType_t类型的变量用于存放任务优先级
    TaskHandle_t pxtask=NULL;//定义一个任务句柄
    xTaskCreate(MyTask,"mytask1",2048,NULL,1,&pxtask);//创建任务函数并传入结构体
    iPriority=uxTaskPriorityGet(pxtask);//读取任务函数的优先级赋值给iPriority
    printf("%d\n",iPriority);
}

同样优先级的任务会以时间片的形似同时执行任务.谁先创建谁先运行.

如果打印结果是1,2,2,1是因为ESP32双核运行导致的。在SDK配置编辑器(menuconfig)中勾选 "Run FreeRTOS only on first core" 就好了。

void MyTask1(void* param)
{
    while(1)
    {
        printf("1\n");
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}

void MyTask2(void* param)
{
    while(1)
    {
        printf("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);
}
结果
1
2
1
2
1

vTaskPrioritySet();可以设置任务的优先级

void MyTask1(void* param)
{
    while(1)
    {
        printf("1\n");
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}

void MyTask2(void* param)
{
    while(1)
    {
        printf("2\n");
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}


void app_main(void)
{
    TaskHandle_t pxtask=NULL;//定义一个任务句柄
    xTaskCreate(MyTask1,"mytask1",2048,NULL,2,NULL);
    xTaskCreate(MyTask2,"mytask2",2048,NULL,1,&pxtask);
    vTaskPrioritySet(pxtask,3);//可以更改或者设置任务优先级
}
结果
1
2
2
1
2

四. vTaskSuspend():任务挂起

void MyTask1(void* param)
{
    while(1)
    {
        printf("1\n");
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}

void MyTask2(void* param)
{
    while(1)
    {
        printf("2\n");
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}


void app_main(void)
{
    TaskHandle_t pxtask=NULL;//定义一个任务句柄
    xTaskCreate(MyTask1,"mytask1",2048,NULL,1,NULL);
    xTaskCreate(MyTask2,"mytask2",2048,NULL,1,&pxtask);
    vTaskDelay(2000/portTICK_PERIOD_MS);
    vTaskSuspend(pxtask);
}
结果
1
2
1
2
1
1
1
1

vTaskResume():任务恢复

void MyTask1(void* param)
{
    while(1)
    {
        printf("1\n");
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}

void MyTask2(void* param)
{
    while(1)
    {
        printf("2\n");
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}


void app_main(void)
{
    TaskHandle_t pxtask=NULL;//定义一个任务句柄
    xTaskCreate(MyTask1,"mytask1",2048,NULL,1,NULL);
    xTaskCreate(MyTask2,"mytask2",2048,NULL,1,&pxtask);
    vTaskSuspend(pxtask);//任务挂起
    vTaskDelay(2000/portTICK_PERIOD_MS);
    vTaskResume(pxtask);//任务恢复
  
}
结果
1
1
1
2

vTaskSuspendAll()挂起所有任务

vTaskResumeAll()恢复所有任务

如果在一段代码中不希望这段代码被其他任务切换就可以调用上面的函数,但是在这段代码中就不能再调用其他FreeRtos函数了即挂起后不可以执行FreeRTOS自身的函数


五. 任务列表vTaskList():

首先需要在系统设置menuconfig中设置或者在FreeRTOSconfig.h中将以下参数设置为1.基于esp32-idf学习FreeRtos笔记_第1张图片

 然后创建建一个char类型的数组用于存放列表信息再调用vTaskList(数组)函数.

void MyTask1(void* param)
{
    while(1)
    {
        printf("1\n");
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}

void MyTask2(void* param)
{
    while(1)
    {
        printf("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);
    static char pcWriteBuffer[512]={0};
    vTaskList(pcWriteBuffer);
    printf("------------------------\n");
    printf("name----state----priority----stack----num\n");
    printf("%s\n",pcWriteBuffer);

}

state说明                                                                                                                                            X=运行状态 executing                                                                                                                      B=阻塞状态 Blocked state                                                                                                                  R=就绪状态 Ready state                                                                                                                    S=挂起状态 Suspended state                                                                                                            D=删除状态 Deleted state


六. 获取任务堆栈大小   uxTaskGetStackHighWaterMark(任务句柄):

首先创建一个UBaseType_t类型的变量

变量=uxTaskGetStackHighWaterMark(任务句柄)

如果这个返回值越接近小说明任务越接近堆栈溢出,需加大任务内存

vTaskDelay也会占用内存,对于嵌入式开发所以内存都非常珍贵,所以要合理分配.

void MyTask1(void* param)
{
    while(1)
    {
        printf("1\n");
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}

void app_main(void)
{
    TaskHandle_t pxTask1;
    xTaskCreate(MyTask1,"mytask1",2048,NULL,1,&pxTask1);
    UBaseType_t iStack;
    while(1)
    {
        iStack=uxTaskGetStackHighWaterMark(pxTask1);
        printf("task1 stack=%d\n",iStack);
        vTaskDelay(3000/portTICK_PERIOD_MS);
    }
}


七. 看门狗

有两种Interrupt watchdog中断看门狗和Task Watchdog Timer任务看门狗  

Interrupt watchdog中断看门狗:用于控制中断的使用,如果在中断中运行比较长的程序,长时间禁止中断都会触发中断看门狗.例如wifi task ,idle task 长时间没被cpu调度运行.

可以再中断看门狗得程序里写入重启系统的命令用于保证产品不好被卡住,可以从错误中恢复.在系统设置menuconfig中可以设置是否开启,以及触发的时间.

基于esp32-idf学习FreeRtos笔记_第2张图片

Task Watchdog Timer任务看门狗

针对任务,某些任务需要周期性运行例如idle Task.如果5s内idle Task没有被触发就会触发任务看门狗,任务看门狗也可以通过函数重启系统.

如果在自定的任务中想启用任务看门狗需要先添加esp_task_wdt.h头文件,然后调用esp_task_wdt_add(任务句柄,在本task内可填NULL会自动调用本task的句柄).最后在任务结尾添加esp_task_wdt_reset();喂狗,告诉看门狗程序被执行.否则会触发任务看门狗.

如果想要不触发任务看门狗可以在Task中用vTaskDelay阻塞函数喂狗.或者改变任务优先级为0,保证idle Task任务被触发.或者在menuconfig中关闭任务看门狗(不推荐这样做).


八. 队列

传达数据Quesue:通过队列传送

通过队列传送整型需要在头文件包含freertos/queue.h文件然后创建一个句柄QueueHandle_t QHandle;,然后用xQueueCreate(5,sizeof(int))函数的返回值赋值给创建的句柄QHandle.用于判断是否创建成功,然后在接收跟发送Task的输入参数中输入(void*)QHandle.用于通过指针传输数据.

#include "freertos/queue.h"//需要包含的头文件

void MyTask1(void* param)
{
    QueueHandle_t QHandle; //创建一个句柄
    QHandle=(QueueHandle_t)param;//将传入的句柄赋值给创建的变量

    BaseType_t xStatus;//定义一个变量用于存放xQueueSend()的返回值
    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;

        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}
 
void MyTask2(void* param)
{
    QueueHandle_t QHandle; //创建一个句柄
    QHandle=(QueueHandle_t)param;//将传入的队列句柄赋值给创建的变量
    BaseType_t xStatus;//定义一个变量用于存放xQueueReceive()的返回值
    int j=0;//需要接收的数据

    while(1)
    {
        xStatus=xQueueReceive(QHandle,&j,0);
        if(xStatus != pdPASS)//判读发送是否成功
            printf("receive fail\n");
        else
            printf("receive done j=%d\n",j);

        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}
 
 
void app_main(void)
{
    QueueHandle_t QHandle;//定义一个队列句柄
    
    QHandle = xQueueCreate(5,sizeof(int));
    if(QHandle != NULL)
    {
        printf("Success\n");
        xTaskCreate(MyTask1,"mytask1",1024*5,(void*)QHandle,1,NULL);
        xTaskCreate(MyTask2,"mytask2",1024*5,(void*)QHandle,1,NULL);
    }
    else
    printf("Can't create");
}

uxQueueMessagesWaiting(QHandle任务函数传入的队列句柄)用于判断队列中是否有数值和 返回数据的长度;传入参数为QHandle任务函数的传入值;返回值是数据的长度.

传达结构体时需要先创建结构体,然后取结构体的地址传入xQueueSend()函数,在接收的task中创建.结构体变量用于接收同样取结构体的地址传入xQueueReceive()函数,在xQueueCreate (5,sizeof(xStruct))函数中修改队列宽度为结构体宽度.

#include "freertos/queue.h"//需要包含的头文件

typedef struct A_STRUCT
{
    char id;
    char data;
}xStruct;//创建结构体

void MyTask1(void* param)
{
    QueueHandle_t QHandle; //创建一个句柄
    QHandle=(QueueHandle_t)param;//将传入的句柄赋值给创建的变量

    BaseType_t xStatus;//定义一个变量用于存放xQueueSend()的返回值
    xStruct xUSB={1,2};
    while(1)
    {
        xStatus=xQueueSend(QHandle,&xUSB,0);//执行队列发送函数
        if(xStatus!=pdPASS)//判读发送是否成功
            printf("send fail\n");
        else
            printf("send done\n");

        xUSB.id++;
        if (xUSB.id == 8)
            xUSB.id=0;

        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}
 
void MyTask2(void* param)
{
    QueueHandle_t QHandle; //创建一个句柄
    QHandle=(QueueHandle_t)param;//将传入的队列句柄赋值给创建的变量
    BaseType_t xStatus;//定义一个变量用于存放xQueueReceive()的返回值
    xStruct xUSB = {0,0};//需要接收的数据

    while(1)
    {
        xStatus=xQueueReceive(QHandle,&xUSB,0);
        if(xStatus != pdPASS)//判读发送是否成功
            printf("receive fail\n");
        else
            printf("receive done id=%d,data=%d\n",xUSB.id,xUSB.data);

        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}
 
 
void app_main(void)
{
    QueueHandle_t QHandle;//定义一个队列句柄
    
    QHandle = xQueueCreate(5,sizeof(xStruct));//修改队列宽度
    if(QHandle != NULL)
    {
        printf("Success\n");
        xTaskCreate(MyTask1,"mytask1",1024*5,(void*)QHandle,1,NULL);
        xTaskCreate(MyTask2,"mytask2",1024*5,(void*)QHandle,1,NULL);
    }
    else
    printf("Can't create");
}

传达指针时需要先创建这个指针,并用malloc给这个指针分配内存,再用snprintf()函数将要写入的内容传入这个指针.在接收Task中同样也要创建一个指针用于接收,并传入xQueueReceive()函数中同时记得在后面用free()函数释放内存.同时记得在xQueueCreate (5,sizeof(指针)函数中修改队列宽度为指针宽度.

#include "freertos/queue.h"

void MyTask1(void* param)
{
    QueueHandle_t QHandle; //创建一个句柄
    QHandle=(QueueHandle_t)param;//将传入的句柄赋值给创建的变量

    BaseType_t xStatus;//定义一个变量用于存放xQueueSend()的返回值
    char i=0;
    char *Pointer;//需要发送的数据

    while(1)
    {
        Pointer=(char*)malloc(50);//创建指针的大小
        snprintf(Pointer,50,"Today is %d day!\n",i);//将可变参数(...)按照 format 格式化成字符串,并将字符串复制到 str 中
        i++;
        xStatus=xQueueSend(QHandle,&Pointer,0);//执行队列发送函数
        if(xStatus!=pdPASS)//判读发送是否成功
            printf("send fail\n");
        else
            printf("send done\n");

        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}
 
void MyTask2(void* param)
{
    QueueHandle_t QHandle; //创建一个句柄
    QHandle=(QueueHandle_t)param;//将传入的队列句柄赋值给创建的变量
    BaseType_t xStatus;//定义一个变量用于存放xQueueReceive()的返回值
    char *Pointer;//需要接收的数据

    while(1)
    {
        xStatus=xQueueReceive(QHandle,&Pointer,0);
        if(xStatus != pdPASS)//判读发送是否成功
            printf("receive fail\n");
        else
            printf("%s\n",Pointer);

        free(Pointer);//释放内存
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}
 
 
void app_main(void)
{
    QueueHandle_t QHandle;
    

    QHandle = xQueueCreate(5,sizeof(char*));
    if(QHandle != NULL)
    {
        printf("Success\n");
        xTaskCreate(MyTask1,"mytask1",1024*5,(void*)QHandle,1,NULL);
        xTaskCreate(MyTask2,"mytask2",1024*5,(void*)QHandle,1,NULL);
    }
    else
    printf("Can't create");
}

多个任务发送同个队列且一个任务接收的情况如何设置:

首先创建一个接收任务,并且接收任务的优先级要比发送的要高,这样就能在发送完数据后及时接收数据,在xQueueReceive(QHandle,&i,portMAX_DELAY)函数中的第3个参数写入portMAX_DELAY用于阻塞等待接收.一旦有数据就会触发接收,同时可以删除下面的vTaskDelay()函数.因为上面对portMAX_DELAY可以实现阻塞.

#include "freertos/queue.h"

void MyTask1(void* param)
{
    QueueHandle_t QHandle; //创建一个句柄
    QHandle=(QueueHandle_t)param;//将传入的句柄赋值给创建的变量
    BaseType_t xStatus;//定义一个变量用于存放xQueueSend()的返回值
    int i=1;
    
    while(1)
    {        
        xStatus=xQueueSend(QHandle,&i,0);//执行队列发送函数
        if(xStatus!=pdPASS)//判读发送是否成功
            printf("send fail\n");
        else
            printf("send done\n");
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}
 
void MyTask2(void* param)
{
    QueueHandle_t QHandle; //创建一个句柄
    QHandle=(QueueHandle_t)param;//将传入的句柄赋值给创建的变量
    BaseType_t xStatus;//定义一个变量用于存放xQueueSend()的返回值
    int i=2;
    
    while(1)
    {        
        xStatus=xQueueSend(QHandle,&i,0);//执行队列发送函数
        if(xStatus!=pdPASS)//判读发送是否成功
            printf("send fail\n");
        else
            printf("send done\n");
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}




void MyReceive(void* param)
{
    QueueHandle_t QHandle; //创建一个句柄
    QHandle=(QueueHandle_t)param;//将传入的队列句柄赋值给创建的变量
    BaseType_t xStatus;//定义一个变量用于存放xQueueReceive()的返回值
    int i=0;//需要接收的数据

    while(1)
    {
        xStatus=xQueueReceive(QHandle,&i,portMAX_DELAY);
        if(xStatus != pdPASS)//判读发送是否成功
            printf("receive fail\n");
        else
            printf("%d\n",i);
    }
}
 
 
void app_main(void)
{
    QueueHandle_t QHandle;
    

    QHandle = xQueueCreate(5,sizeof(int));
    if(QHandle != NULL)
    {
        printf("Success\n");
        xTaskCreate(MyTask1,"mytask1",1024*5,(void*)QHandle,1,NULL);
        xTaskCreate(MyTask2,"mytask2",1024*5,(void*)QHandle,1,NULL);
        xTaskCreate(MyReceive,"myreceive",1024*5,(void*)QHandle,2,NULL);
    }
    else
        printf("Can't create");
}

队列集合:

多个Task分别写各自的队列,然后一个Task读取所以队列的情况如何设置:

首先要分别创建各自发送任务的队列句柄并传入创建任务的函数,再创建一个QueueSetHandle_t类型的句柄用于存放队列集合再用xQueueCreateSet(所有队列的长度)赋值给创建的句柄,然后用xQueueAddToSet(需要集合的句柄,集合后的句柄)函数将要集合的句柄集合起来,再将这个句柄传入创建接收任务的接收参数中.并且在接收任务中创建一个QueueSetMemberHandle_t的句柄用于接收数据,再用xQueueSelectFromSet(接收到的句柄,portMAX_DELAY)函数赋值给创建的接收句柄用于取出有数据的队列值,再通过xQueueReceive()函数接收取出的值.

#include "freertos/queue.h"

void MyTask1(void* param)
{
    QueueHandle_t QHandle; //创建一个句柄
    QHandle=(QueueHandle_t)param;//将传入的句柄赋值给创建的变量
    BaseType_t xStatus;//定义一个变量用于存放xQueueSend()的返回值
    int i=1;
    
    while(1)
    {        
        xStatus=xQueueSend(QHandle,&i,0);//执行队列发送函数
        if(xStatus!=pdPASS)//判读发送是否成功
            printf("send fail\n");
        else
            printf("send done\n");
        i++;
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}
 
void MyTask2(void* param)
{
    QueueHandle_t QHandle; //创建一个句柄
    QHandle=(QueueHandle_t)param;//将传入的句柄赋值给创建的变量
    BaseType_t xStatus;//定义一个变量用于存放xQueueSend()的返回值
    int i=2;
    
    while(1)
    {        
        xStatus=xQueueSend(QHandle,&i,0);//执行队列发送函数
        if(xStatus!=pdPASS)//判读发送是否成功
            printf("send fail\n");
        else
            printf("send done\n");
        i++;
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}

void MyReceive(void* param)
{
    QueueHandle_t QHandle; //创建一个句柄
    QHandle=(QueueHandle_t)param;//将传入的队列句柄赋值给创建的变量
    BaseType_t xStatus;//定义一个变量用于存放xQueueReceive()的返回值

    QueueSetMemberHandle_t QueueDate;//创建一个用于接收队列集合数据的变量
    int i;

    while(1)
    {
        QueueDate=xQueueSelectFromSet(QHandle,portMAX_DELAY);//取出有数据的队列
        xStatus=xQueueReceive(QueueDate,&i,portMAX_DELAY);//取出QueueDate里的数据
        if(xStatus != pdPASS)//判读发送是否成功
            printf("receive fail\n");
        else
            printf("%d\n",i);
    }
}
 
void app_main(void)
{
    QueueHandle_t QHandle1;
    QHandle1 = xQueueCreate(5,sizeof(int)); //创建各自的队列句柄
    QueueHandle_t QHandle2;
    QHandle2 = xQueueCreate(5,sizeof(int)); //创建各自的队列句柄

    QueueHandle_t QHandleSet;
    QHandleSet = xQueueCreate(10,sizeof(int));//创建队列集合
    xQueueAddToSet(QHandle1, QHandleSet);//将任务一的队列集合到集合里
    xQueueAddToSet(QHandle2, QHandleSet);//将任务二的队列集合到集合里

    xTaskCreate(MyTask1,"mytask1",1024*5,(void*)QHandle1,1,NULL);
    xTaskCreate(MyTask2,"mytask2",1024*5,(void*)QHandle2,1,NULL);
    xTaskCreate(MyReceive,"myreceive",1024*5,(void*)QHandleSet,2,NULL);
}

一个任务发送但多个任务接收的情况如何设置:

首先创建一个写任务,调用xQueueOverwrite(Mailbox,&i)将i的值发送出去.并且这个任务的优先级要比接收的优先级要高,且这个Mailbox内容的数量只能为1,所以创建的队列数量也为1,接收函数用xQueuePeek(Mailbox,&i,0);该函数可以读取队列的内容但不改变删除队列,将Mailbox中的内容赋值给i,即接收传入队列的数据.

#include "freertos/queue.h"

void MyWriteTask(void* param)
{
    QueueHandle_t Mailbox; //创建一个句柄
    Mailbox=(QueueHandle_t)param;//将传入的队列句柄赋值给创建的变量
    BaseType_t xStatus;//定义一个变量用于存放xQueueReceive()的返回值
    int i=0;//需要接收的数据
    xQueueOverwrite(Mailbox,&i);

    while(1)
    {
        xQueueOverwrite(Mailbox,&i);
        xStatus=xQueueOverwrite(Mailbox,&i);
        if(xStatus != pdPASS)//判读发送是否成功
            printf("Send Fail\n");
        else
            printf("Senf Success\n");
        i++;
        vTaskDelay(6000/portTICK_PERIOD_MS);
    }
}
 

void MyTask(void* param)
{
    QueueHandle_t Mailbox; //创建一个句柄
    Mailbox=(QueueHandle_t)param;//将传入的句柄赋值给创建的变量
    BaseType_t xStatus;//定义一个变量用于存放xQueueSend()的返回值
    int i;
    
    while(1)
    {        
        xStatus=xQueuePeek(Mailbox,&i,0);//执行队列发送函数
        if(xStatus!=pdPASS)//判读发送是否成功
            printf("read fail\n");
        else
            printf("read i=%d\n",i);
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}
 
 
void app_main(void)
{
    QueueHandle_t Mailbox;
    Mailbox = xQueueCreate(1,sizeof(int));//Mailbox数量只有1

    if(Mailbox != NULL)
    {
        printf("Success\n");
        xTaskCreate(MyWriteTask,"mywritetask",1024*5,(void*)Mailbox,2,NULL);
        xTaskCreate(MyTask,"mytask1",1024*5,(void*)Mailbox,1,NULL);
        xTaskCreate(MyTask,"mytask2",1024*5,(void*)Mailbox,1,NULL);
        xTaskCreate(MyTask,"mytask3",1024*5,(void*)Mailbox,1,NULL);
    }
    else
        printf("Can't create");
}

九. 软件定时器

FreeRTOS中的软件定时器与硬件和平台无关,在不同平台代码都是一样的,且硬件定时器有数量限制,但软件定时器是由TIMER_TASK_STACK_DEPTH堆栈设置定时器的数量,可以设置有很多个.  创建软件定时器首先需要包含FreeRTOS.h和timers.h这两个头文件,再定义一个定时器句柄TimerHandle_t xTimer1然后用

TimerHandle_t xTimerCreate函数
(
const char *pcTimerName,                               //定时器名称      "Timer1"
const TickType_t xTimerPeriod,                        //定时的时间以pdMS_TO_TICKS(ms为单位)
const UBaseType_t uxAutoReload,                  //定时器重装设置pdTRUE为启动pdFALSE为关闭
void * const pvTimerID,                                     //定时器的标识符id      (void*)id
TimerCallbackFunction_t pxCallbackFunction  //定时器回调函数 即要到时间要执行的内容
);进行创建定时器赋值给xTimer1.
创建回调函数返回值为void vTimer1CallBack(TimerHandle_t xTimer)内容为定时器要执行的内容
再创建xTimeriStart(xTimer1(定时器句柄),0(等待时间))用于启动定时器.
要想停止定时器可以调用xTimerStop (xTimer1(定时器句柄),0(等待时间))来停止指定的定时器.
要想在回调函数里获得timer的名字可以调用pcTimerGetName(xTimer句柄)来获取.
要想在回调函数里获得timer的id可以调用pvTimerGetTimerID(xTimer句柄)来获取.
多个定时器可以共用一个回调函数.
xTimerReset(xTimer( 定时器句柄),0(等待时间))可以重置定时器定时.相当于模拟看门狗喂狗功能.

xTimerChangePeriod(xTimer(定时器句柄),pdMS_TO_TICKS(ms为单位)修改的定时时间,0(等待时间))可以修改定时器的定时时间.

#include "freertos/timers.h"//包含头文件
int id1=1;//创建定时器id变量
int id2=2;//创建定时器id变量

 void TimerCallBack(TimerHandle_t xTimer)//创建定时器到时任务
{
    const char *strName;
    strName = pcTimerGetName(xTimer);//读取定时器名字
    int *id;
    id=(int*)pvTimerGetTimerID(xTimer);//读取定时器id

    printf("timer name=%s,id=%d\n",strName,*id);
}
 
void app_main(void)
{
    TimerHandle_t xTimer1;//定义计数器句柄
    TimerHandle_t xTimer2;//定义计数器句柄
    xTimer1 = xTimerCreate("Timer1",pdMS_TO_TICKS(1000),pdTRUE,(void*)&id1,TimerCallBack);//定义计数器
    xTimer2 = xTimerCreate("Timer2",pdMS_TO_TICKS(2000),pdTRUE,(void*)&id2,TimerCallBack);//定义计数器
    xTimerStart(xTimer1,0);//开始计数器
    xTimerStart(xTimer2,0);//开启计数器
    
    vTaskDelay(pdMS_TO_TICKS(6000));
    xTimerChangePeriod(xTimer1,pdMS_TO_TICKS(6000),0);//更改计数周期

    vTaskDelay(pdMS_TO_TICKS(11000));
    xTimerReset(xTimer1,0);//重新计数

    vTaskDelay(pdMS_TO_TICKS(13000));
    xTimerStop(xTimer1,0);//停止计数器
}

十. 二进制信号量 Binary Semaphore

信号量可以用于控制task同步,启停.信号量要么1要么0.
vSemaphoreCreateBinary(void)函数为旧的二进制信号创建函数为了向下兼容而保留
所以最好使用新的xSemaphoreCreateBinary()函数来创建信号量.
使用二进制信号量要先包含"freertos/semphr.h"头文件,然后创建一个SemaphoreHandle_t类型的句柄来获取xSemaphoreCreateBinary()函数的返回值.
然后创建xSemaphoreGive(二进制句柄),用于释放创建的信号量.
在Task里用xSemaphoreTask(二进制句柄,等待时间)来获取信号量执行任务.
 
#include "freertos/semphr.h"//包含头文件
int iCount = 0;
SemaphoreHandle_t semphrHandle;
void myTask1(void*pvParam)
{
    while (1)
    {
        xSemaphoreTake(semphrHandle,portMAX_DELAY);//获取信号量
        for(int i=0;i<10;i++)
        {
            iCount++;
            printf("myTask1 iCount = %d!\n",iCount);
            vTaskDelay(pdMS_TO_TICKS(1000));
        }
        xSemaphoreGive(semphrHandle);//释放信号量
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}
void myTask2(void*pvParam)
{
    while (1)
    {
        xSemaphoreTake(semphrHandle,portMAX_DELAY);//获取信号量
        for(int i=0;i<10;i++)
        {
            iCount++;
            printf("myTask2 iCount = %d!\n",iCount);
            vTaskDelay(pdMS_TO_TICKS(1000));
        }
        xSemaphoreGive(semphrHandle);//释放信号量
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
} 

void app_main(void)
{
    semphrHandle = xSemaphoreCreateBinary();//创建信号量
    xSemaphoreGive(semphrHandle);//释放信号量
    xTaskCreate(myTask1,"mytask1",1024*5,NULL,1,NULL);
    xTaskCreate(myTask2,"mytask2",1024*5,NULL,1,NULL);
}

十一. 计数型信号量 Count Semaphore

使用计数型信号量要先包含"freertos/semphr.h"头文件,然后创建一个SemaphoreHandle_t类型的句柄来获 xSpemaphoreCreateCounting(最大信号量,初始信号量)创建计数型信号量函数的返回值,其他步骤同二进制信号量.
uxSemaphoreGetCount(信号量句柄)函数可以获取当前信号量大小.
xSemaphoreTask(二进制句柄,等待时间)函数可以获得信号量,如果成功返回pdTRUE,如果信号量为
零则返回pdFAIL.
#include "freertos/semphr.h"//包含头文件

SemaphoreHandle_t semphrHandle;
void myTask1(void*pvParam)
{
    int emptySpace=0;
    BaseType_t iResult;
    while (1)
    {
        emptySpace=uxSemaphoreGetCount(semphrHandle);//获取当前信号量值
        printf("剩余%d车位\n",emptySpace);
        iResult=xSemaphoreTake(semphrHandle,0);//取得当前信号量,如果信号量为0则获取失败
        if(iResult == pdPASS)
            printf("允许进入\n");
        else
            printf("停车位已满\n");

        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}
void myTask2(void*pvParam)
{
    while (1)
    {
        vTaskDelay(pdMS_TO_TICKS(6000));
        xSemaphoreGive(semphrHandle);
        printf("已出库");
    }
} 

void app_main(void)
{
    semphrHandle = xSemaphoreCreateCounting(5,5);//创建计数型信号量
    xSemaphoreGive(semphrHandle);
    xTaskCreate(myTask1,"mytask1",1024*5,NULL,1,NULL);
    xTaskCreate(myTask2,"mytask2",1024*5,NULL,1,NULL);
}

十二.互斥量 Mutex

互斥量与二进制信号量很相似,最大区别是获得互斥量的任务会继承企图获得互斥量任务的优先级别.
使用互斥量要先包含"freertos/semphr.h"头文件,然后创建一个SemaphoreHandle_t类型的句柄来获取xSemaphoreCreateMutex()函数的返回值即互斥量.
#include"freertos/semphr.h"
SemaphoreHandle_t mutexHandle;
void myTask1(void*pvParam)
{
    BaseType_t iRet;
    while(1)
    {
        printf("Task1 is begin!\n");
        iRet = xSemaphoreTake(mutexHandle,1000);//创建互斥信号量
        if(iRet==pdPASS)//判断是否创建成功
        {
            printf("Task1 Start!\n");
            
            for(int i=0;i<50;i++)
            {
                printf("Task1 i=%d\n",i);
                vTaskDelay(pdMS_TO_TICKS(1000));
            }
            printf("Task1 End!\n");
            xSemaphoreGive(mutexHandle);//释放信号量
            vTaskDelay(pdMS_TO_TICKS(5000));
        }
        else
        {
            printf("Task3 File!\n");
            vTaskDelay(pdMS_TO_TICKS(1000));
        }
    }

}
void myTask2(void*pvParam)
{
    printf("Task2 is begin!\n");
    vTaskDelay(pdMS_TO_TICKS(1000));
    while(1)
    {
        ;
    }
} 
void myTask3(void*pvParam)
{
    BaseType_t iRet;
    printf("Task3 is begin!\n");
    vTaskDelay(pdMS_TO_TICKS(1000));
    while (1)
    {
        iRet = xSemaphoreTake(mutexHandle,1000);//获得斥信号量
        if(iRet==pdPASS)//判断是否创建成功
        {
            printf("Task3 Start!\n");
            for(int i=0;i<10;i++)
            {
                printf("Task3 i=%d\n",i);
                vTaskDelay(pdMS_TO_TICKS(1000));
            }
            printf("Task3 End!\n");
            xSemaphoreGive(mutexHandle);//释放信号量
            vTaskDelay(pdMS_TO_TICKS(5000));
        }
        else
        {
            printf("Task3 File!\n");
            vTaskDelay(pdMS_TO_TICKS(1000));
        }
    }
   
} 


void app_main(void)
{
    mutexHandle = xSemaphoreCreateMutex();//创建斥信号量
    vTaskSuspendAll();//挂起所有任务
    xTaskCreate(myTask1,"mytask1",1024*5,NULL,1,NULL);
    xTaskCreate(myTask2,"mytask2",1024*5,NULL,2,NULL);
    xTaskCreate(myTask3,"mytask3",1024*5,NULL,3,NULL);
    xTaskResumeAll();//恢复所有任务
}

十二.递归互斥量 Recursive Mutex

递归互斥量可以由拥有者多次获取,但是也要求拥有者释放相同次数。比如,一个递归互斥量被获取了5次,那么同样需要释放5次,未释放完全其他任务无法获取.
使用互斥量要先包含"freertos/semphr.h"头文件,然后创建一个SemaphoreHandle_t类型的句柄来获取xSemaphoreCreateRecursiveMutex()函数的返回值即递归互斥量.
#include "freertos/semphr.h"//包含头文件
SemaphoreHandle_t mutexHandle;

void myTask1(void*pvParam)
{
    while(1)
    {
        printf("-----------------------------------------\n");
        printf("Task1 is begin!\n");
        xSemaphoreTakeRecursive(mutexHandle,portMAX_DELAY);//创建递归互斥信号量A
        printf("Task1 take A!\n");
        for(int i=0;i<10;i++)
        {
            printf("Task1 i=%d for A!\n", i);
            vTaskDelay(pdMS_TO_TICKS(1000));
        }

        xSemaphoreTakeRecursive(mutexHandle,portMAX_DELAY);//创建递归互斥信号量B
        printf("Task1 take B!\n");
        for(int i=0;i<10;i++)
        {
            printf("Task1 i=%d for B!\n", i);
            vTaskDelay(pdMS_TO_TICKS(1000));
        }  
        printf("Task1 give B!\n");
        xSemaphoreGiveRecursive(mutexHandle);//释放递归互斥信号量B
        vTaskDelay(pdMS_TO_TICKS(3000));//延时3s

        printf("Task1 give A!\n");
        xSemaphoreGiveRecursive(mutexHandle);//释放递归互斥信号量A
        vTaskDelay(pdMS_TO_TICKS(3000));//延时3s
    }

}
void myTask2(void*pvParam)
{
    vTaskDelay(pdMS_TO_TICKS(1000));
    while(1)
    {
        printf("-----------------------------------------\n");
        printf("Task2 is begin!\n");
        xSemaphoreTakeRecursive(mutexHandle,portMAX_DELAY);//创建递归互斥信号量
        printf("Task2 take!\n");
        for(int i=0;i<10;i++)
        {
            printf("Task2 i=%d!\n", i);
            vTaskDelay(pdMS_TO_TICKS(1000));
        }  
        printf("Task2 give!\n");
        xSemaphoreGiveRecursive(mutexHandle);//释放递归互斥信号量
        vTaskDelay(pdMS_TO_TICKS(3000));//延时3s
    }
} 



void app_main(void)
{
    mutexHandle = xSemaphoreCreateRecursiveMutex();//创建递归互斥信号量
    vTaskSuspendAll();//挂起所有任务
    xTaskCreate(myTask2,"mytask2",1024*5,NULL,1,NULL);
    xTaskCreate(myTask1,"mytask1",1024*5,NULL,1,NULL);
    
    xTaskResumeAll();//恢复所有任务
}

十三.事件组等待功能 Event Group Wait

事件组储存在类型 EventGroupHandle_t中
事件组的位数为  8需设置configUSE_16_BIT_TICKS is set to 1.
事件组的位数为24需设置 configUSE_16_BIT_TICKS is set to 0.
首先要包含"freertos/event_groups.h"文件,然后用
EventGroupHandle_t xGreatedEventGroup;//定义一个事件组变量
创建事件组函数xGreatedEventGroup=xEventGroupCreate()创建事件组.
再使用xEventGroupWaitBits
(
const EventGroupHandle_t xEventGroup,//事件组  
const EventBits_t uxBitsToWaitFor,//设置需要判断的位数例如0和2位0101对应0x05
const BaseType_t xClearOnExit,//是否清零事件组的位数
const BaseType_t xWaitForAllBits,//设置是否同时满足还是只需要一个位为1就可触发
TickType_t xTicksToWait //阻塞时间
);//设置事件组等待功能 
使用xEventGroupSetBits(xGreatedEventGroup(事件组变量),BIT(位));设置对应的Bit位置1.
#include "freertos/event_groups.h"//包含头文件“event_groups.h”
EventGroupHandle_t xGreatedEventGroup;
#define BIT_0 ( 1 << 0 )
#define BIT_4 ( 1 << 4 )


void myTask1(void*pvParam)
{
    while(1)
    {
        printf("-----------------------------------------\n");
        printf("Task1 is begin to wait!\n");
        xEventGroupWaitBits(
                 xGreatedEventGroup, /* The event group being tested. */
                 BIT_0 | BIT_4, /* The bits within the event group to wait for. */
                 pdTRUE, /* BIT_0 and BIT_4 should be cleared before returning. */
                 pdFALSE, /* Don't wait for both bits, either bit will do. */
                 portMAX_DELAY);/* Wait for either bit to be set. */
        printf("-----------------------------------------\n");
        printf("In task1 bit 0 or bit 1 is set!\n");
        vTaskDelay(pdMS_TO_TICKS(1000));//延时1s
    }

}
void myTask2(void*pvParam)
{
    vTaskDelay(pdMS_TO_TICKS(1000));
    while(1)
    {
        printf("-----------------------------------------\n");
        printf("Task2 is begin to be set bit0!\n");
        xEventGroupSetBits(xGreatedEventGroup,BIT_0);
        vTaskDelay(pdMS_TO_TICKS(10000));//延时5s

        printf("-----------------------------------------\n");
        printf("Task2 is begin to be set bit4!\n");
        xEventGroupSetBits(xGreatedEventGroup,BIT_4);
        vTaskDelay(pdMS_TO_TICKS(10000));//延时5s
    }
} 



void app_main(void)
{
    xGreatedEventGroup = xEventGroupCreate();//创建事件组
    if(xGreatedEventGroup == NULL)//判读创建是否成功
    {
        printf("Event group fail!\n");
    }
    else
    {
        vTaskSuspendAll();//挂起所有任务

        xTaskCreate(myTask2,"mytask2",1024*5,NULL,1,NULL);
        xTaskCreate(myTask1,"mytask1",1024*5,NULL,1,NULL);
        
        xTaskResumeAll();//恢复所有任务
    }
}

十四.事件组同步功能 Event Group Sync

Event Group sync和wait的功能很相似,区别在于sync会在设置bit后阻塞自身直到设置的所有bit置1后才开始运行task后续代码
在需要同时运行的task中用xEventGroupSync
(
xEventBits,//事件组
TASK_2_BIT, //设置置1的bit位
ALL_SYNC_BITS, //设置需要同时置一的bit位,都置一才运行后续代码
portMAX_DELAY //等待时间,超时则执行后续代码
);//设置同时运行功能.
#include "freertos/event_groups.h"//包含头文件“event_groups.h”
EventGroupHandle_t xEventBits;
#define TASK_0_BIT ( 1 << 0 )
#define TASK_1_BIT ( 1 << 1 )
#define TASK_2_BIT ( 1 << 2 )
#define ALL_SYNC_BITS ( TASK_0_BIT | TASK_1_BIT | TASK_2_BIT )

void myTask0(void*pvParam)
{
    while(1)
    {
        printf("-----------------------------------------\n");
        printf("Task0 is begin to wait!\n");
        vTaskDelay(pdMS_TO_TICKS(1000));//延时1s
        printf("-----------------------------------------\n");
        printf("Task0 set bit0!\n");
        xEventGroupSync(xEventBits, 
                        TASK_0_BIT, /* The bit to set. */
                        ALL_SYNC_BITS, /* The bits to wait for. */
                        portMAX_DELAY );/* Timeout value. */
        printf("task0 sync!\n");
        vTaskDelay(pdMS_TO_TICKS(10000));//延时10s
    }
}
void myTask1(void*pvParam)
{
    while(1)
    {
        printf("-----------------------------------------\n");
        printf("Task1 is begin to wait!\n");
        vTaskDelay(pdMS_TO_TICKS(3000));//延时3s
        printf("-----------------------------------------\n");
        printf("Task1 set bit1!\n");
        xEventGroupSync(xEventBits, 
                        TASK_1_BIT, /* The bit to set. */
                        ALL_SYNC_BITS, /* The bits to wait for. */
                        portMAX_DELAY );/* Timeout value. */
        printf("task1 sync!\n");
        vTaskDelay(pdMS_TO_TICKS(10000));//延时10s
    }

}
void myTask2(void*pvParam)
{
    while(1)
    {
        printf("-----------------------------------------\n");
        printf("Task2 is begin to wait!\n");
        vTaskDelay(pdMS_TO_TICKS(5000));//延时5s
        printf("-----------------------------------------\n");
        printf("Task2 set bit0!\n");
        xEventGroupSync(xEventBits, 
                        TASK_2_BIT, /* The bit to set. */
                        ALL_SYNC_BITS, /* The bits to wait for. */
                        portMAX_DELAY );/* Timeout value. */
        printf("task2 sync!\n");
        vTaskDelay(pdMS_TO_TICKS(10000));//延时10s
    }
} 



void app_main(void)
{
    xEventBits = xEventGroupCreate();//创建事件组
    if(xEventBits == NULL)//判读创建是否成功
    {
        printf("Event group fail!\n");
    }
    else
    {
        vTaskSuspendAll();//挂起所有任务

        xTaskCreate(myTask0,"mytask2",1024*5,NULL,1,NULL);
        xTaskCreate(myTask1,"mytask1",1024*5,NULL,1,NULL);
        xTaskCreate(myTask2,"mytask1",1024*5,NULL,1,NULL);
        
        xTaskResumeAll();//恢复所有任务
    }
}

十五.通知同步功能 Noticfication Sync

通过函数 ulTaskNotifyTake()阻塞任务再通过ulTaskNotifyGive()释放任务.
static TaskHandle_t xTask1 = NULL;//创建任务句柄

void myTask1(void*pvParam)
{
    while(1)
    {
        printf("-----------------------------------------\n");
        printf("Task1 wait for notification!\n");
        ulTaskNotifyTake( pdTRUE, portMAX_DELAY );
        printf("-----------------------------------------\n");
        printf("Task1 get notification!\n");
    }

}
void myTask2(void*pvParam)
{
    while(1)
    {
        vTaskDelay(pdMS_TO_TICKS(5000));//延时5s
        printf("-----------------------------------------\n");
        printf("Task2 notification Task1!\n");
        xTaskNotifyGive(xTask1);//
    }
} 



void app_main(void)
{
    
    vTaskSuspendAll();//挂起所有任务

    xTaskCreate(myTask1,"mytask1",1024*5,NULL,1,&xTask1);
    xTaskCreate(myTask2,"mytask1",1024*5,NULL,1,NULL);
        
    xTaskResumeAll();//恢复所有任务

}

十六.通知值 Noticfication Value

通过控制通知的值从而经过不同的处理流程
首先在任务中创建需要传递的通知值变量uint32_t ulNotifiedValue;
通过使用函数xTaskNotifyWait
(
uint32_t ulBitsToClearOnEntry,//清除在进入函数前的通知值
uint32_t ulBitsToClearOnExit,//清除退出函数后的通知值
uint32_t *pulNotificationValue,//需要传达的通知值指针
TickType_t xTicksToWait //阻塞时间
);//创建通知等待任务.
在其他任务中通过函数
xTaskNotify
(
TaskHandle_t xTaskToNotify,//需要通知的任务句柄
uint32_t ulValue,//需要传递的通知值
eNotifyAction eAction
//可写入4个参数分别是
1:eNoAction
通知事件但不改变事件的值
2:eSetBits
通知事件只改变对应bit上的位
3:eIncrement
被通知任务的通知值增加 1,这种情况下,参数 ulValue 未使用, ulTaskNotifyGive()函数跟这个等价。
4:eSetValueWithOverwrite
通知事件,无论前面是否存在事件挂起,都将事件的值更改为 ulValue.
5:eSetValueWithoutOverwrite
通知事件,如果通知任务还没取走上一个通知,又接收到了一个通知,则这次通知值未能更新并返回 pdFALSE,且不更改事件值,否则返回 pdPASS。且将事件的值更改为 ulValue
);//通知任务
static TaskHandle_t xTask1 = NULL;

void myTask1(void *pvParam)
{
    uint32_t ulNotifiedValue;
    while (1)
    {
        printf("-----------------------------------------\n");
        printf("Task1 wait for notification!\n");
        xTaskNotifyWait(0x00,             /* Don't clear any notification bits on entry. */
                        ULONG_MAX,        /* Reset the notification value to 0 on exit. */
                        &ulNotifiedValue, /* Notified value pass out in ulNotifiedValue. */
                        portMAX_DELAY);   /* Block indefinitely. */
        /* Process any events that have been latched in the notified value. */
        if ((ulNotifiedValue & 0x01) != 0)
        {
            /* Bit 0 was set - process whichever event is represented by bit 0. */
            printf("bit0 is notification!\n");
        }
        if ((ulNotifiedValue & 0x02) != 0)
        {
            /* Bit 1 was set - process whichever event is represented by bit 1. */
            printf("bit1 is notification!\n");
        }
        if ((ulNotifiedValue & 0x04) != 0)
        {
            /* Bit 2 was set - process whichever event is represented by bit 2. */
            printf("bit2 is notification!\n");
        }
    }
}
void myTask2(void *pvParam)
{
    while (1)
    {

        printf("-----------------------------------------\n");
        vTaskDelay(pdMS_TO_TICKS(5000)); //延时5s
        printf("Task2 notification Task1!\n");
        xTaskNotify( xTask1, 0x01, eSetValueWithOverwrite );
        vTaskDelay(pdMS_TO_TICKS(3000)); //延时3s
        xTaskNotify( xTask1, 0x02, eSetValueWithOverwrite );
        vTaskDelay(pdMS_TO_TICKS(1000)); //延时1s
        xTaskNotify( xTask1, 0x04, eSetValueWithOverwrite );
        vTaskDelay(pdMS_TO_TICKS(1000)); //延时1s 
    }
}

void app_main(void)
{

    vTaskSuspendAll(); //挂起所有任务

    xTaskCreate(myTask1, "mytask1", 1024 * 5, NULL, 1, &xTask1);
    xTaskCreate(myTask2, "mytask1", 1024 * 5, NULL, 1, NULL);

    xTaskResumeAll(); //恢复所有任务
}

十七.流数据 Stream Buffer

流数据是实时获取来自不同数据源的海量数据
使用 流数据要先包含#include "freertos/stream_buffer.h"头文件,然后创建一个StreamBufferHandle_t类型的句柄来获 xStreamBufferCreate(数据大小, 接收到解除任务阻塞的数据大小)创建的流数据.
#include 
#include "freertos/stream_buffer.h"
StreamBufferHandle_t StreamBufferHandle;

void myTask1(void *pvParam)
{
    char tx_buf[150];//创建数据变量
    int i = 0;
    int str_len = 0;//创建数据大小变量
    int send_bytes = 0;//创建xStreamBufferSend函数的返回值即实际发送的数据大小
    while (1)
    {
        vTaskDelay(pdMS_TO_TICKS(3000)); //先延时可以让task2先执行,先阻塞任务,如果把这个放到task1尾部,则task1会先发送流数据,由于buffer里已经有数据了task2会先接收数据执行后续代码再阻塞任务.
        i++;
        str_len = sprintf(tx_buf, "Hello I am supperman %d", i); //初始化流数据
        send_bytes = xStreamBufferSend(StreamBufferHandle,//流数据句柄
                                       (void *)tx_buf,//需要发送的数据指针
                                       str_len,//发送数据的大小
                                       portMAX_DELAY); 
        printf("--------------\n");
        printf("Send: str_len = %d, send_bytes = %d!\n", str_len, send_bytes);
       
    }
}
void myTask2(void *pvParam)
{
    char rx_buf[150];//创建接收数据
    int xReceivedBytes;创建xStreamBufferReceive函数的返回值即实际接收的数据大小
    while (1)
    {
        memset(rx_buf, 0, sizeof(rx_buf));//初始化rx_buf即将所以数组覆盖为0
        xReceivedBytes = xStreamBufferReceive(StreamBufferHandle,//流数据句柄
                                             (void *)rx_buf,//需要接收的数据指针
                                             sizeof(rx_buf),//接收数据的大小
                                             portMAX_DELAY);
        printf("-----------------------------------------\n");
        printf("Receive: xReceivedBytes = %d, data = %s!\n", xReceivedBytes, rx_buf);
    }
}

void app_main(void)
{
    StreamBufferHandle = xStreamBufferCreate(1000, 100);//创建流数据1000为流数据的大小100为接收到解除任务阻塞的数据大小
    if (StreamBufferHandle != NULL)
    {
        vTaskSuspendAll(); //挂起所有任务

        xTaskCreate(myTask1, "mytask1", 1024 * 5, NULL, 1, NULL);
        xTaskCreate(myTask2, "mytask2", 1024 * 5, NULL, 1, NULL);

        xTaskResumeAll(); //恢复所有任务
    }
    else
    {
        printf("StreamBuffer create fail !\n ");
    }    
}

十八.流数据可用空间 Stream Buffer SpacesAvailable

流数据是实时获取来自不同数据源的海量数据,那么如何确认流数据的的大小呢,这时候我们需要使用一个task3来检测流数据的变化情况.先将stream buffer设置成一个比较大的空间,再通过task3用函数xStreamBufferSpacesAvailable(流数据句柄)来确定流数据的剩余空间进而调整流数据的大小;函数的返回值就是使用后的剩余空间.
#include 
#include "freertos/stream_buffer.h"
StreamBufferHandle_t StreamBufferHandle;

void myTask1(void *pvParam)
{
    char tx_buf[150];
    int i = 0;
    int str_len = 0;
    int send_bytes = 0;
    while (1)
    {
        vTaskDelay(pdMS_TO_TICKS(3000)); //延时3s 
        i++;
        str_len = sprintf(tx_buf, "Hello I am supperman %d", i); //初始化流数据
        send_bytes = xStreamBufferSend(StreamBufferHandle,
                                       (void *)tx_buf,
                                       str_len,
                                       portMAX_DELAY); //发送流数据
        printf("--------------\n");
        printf("Send: str_len = %d, send_bytes = %d!\n", str_len, send_bytes);
       
    }
}

void myTask2(void *pvParam)
{
    char rx_buf[150];//创建接收数据
    int xReceivedBytes;
    while (1)
    {
        memset(rx_buf, 0, sizeof(rx_buf));
        xReceivedBytes = xStreamBufferReceive(StreamBufferHandle,
                                             (void *)rx_buf,
                                             sizeof(rx_buf),
                                             portMAX_DELAY);
        printf("-----------------------------------------\n");
        printf("Receive: xReceivedBytes = %d, data = %s!\n", xReceivedBytes, rx_buf);
    }
}

void myTask3(void *pvParam)
{
    int buf_space = 0;
    int min_space = 1000;

    while (1)
    {
        buf_space = xStreamBufferSpacesAvailable(StreamBufferHandle);//判断流数据使用了多少内存
        if(buf_space < min_space)
            min_space = buf_space;//输出最大的使用空间
        printf("-----------------------------------------\n");
        printf("buf_space = %d!,min_space = %d!\n", buf_space, min_space);
        vTaskDelay(pdMS_TO_TICKS(3000)); //延时3s 
    }
}

void app_main(void)
{
    StreamBufferHandle = xStreamBufferCreate(1000, 100);
    if (StreamBufferHandle != NULL)
    {
        vTaskSuspendAll(); //挂起所有任务

        xTaskCreate(myTask1, "mytask1", 1024 * 5, NULL, 1, NULL);
        xTaskCreate(myTask2, "mytask2", 1024 * 5, NULL, 1, NULL);
        xTaskCreate(myTask3, "mytask3", 1024 * 5, NULL, 1, NULL);
        xTaskResumeAll(); //恢复所有任务
    }
    else
    {
        printf("StreamBuffer create fail !\n ");
    }    
}

十九.消息数据 Message Buffer

消息数据跟流数据而非常的相似,但也有不同,首先消息数据每次只能接收一条数据,而流数据每次可以接收函数中设置对应大小的数据.
使用 流数据要先包含#include "freertos/message_buffer.h"头文件,然后创建一个MessageBufferHandle_t类型的句柄来获 xMessage BufferCreate(数据大小)创建的流数据.
其余函数改成对应消息数据的函数即可.
#include 
#include "freertos/message_buffer.h"
MessageBufferHandle_t MessageBufferHandle;

void myTask1(void *pvParam)
{
    char tx_buf[150];
    int i = 0;
    int str_len = 0;
    int send_bytes = 0;
    for(int i=0; i<3 ;i++)
    {
        str_len = sprintf(tx_buf, "Hello I am supperman %d", i); //初始化流数据
        send_bytes = xMessageBufferSend(MessageBufferHandle,
                                       (void *)tx_buf,
                                       str_len,
                                       portMAX_DELAY); //发送流数据
        printf("--------------\n");
        printf("Send: i = %d, send_bytes = %d!\n", i, send_bytes);
    }
    vTaskDelete(NULL); //延时3s 
}

void myTask2(void *pvParam)
{
    char rx_buf[200];//创建接收数据
    int xReceivedBytes = 0;

    vTaskDelay(pdMS_TO_TICKS(3000)); //延时3s 
    while (1)
    {
        memset(rx_buf, 0, sizeof(rx_buf));
        xReceivedBytes = xMessageBufferReceive(MessageBufferHandle,
                                             (void *)rx_buf,
                                             sizeof(rx_buf),
                                             portMAX_DELAY);
        printf("-----------------------------------------\n");
        printf("Receive: xReceivedBytes = %d, data = %s!\n", xReceivedBytes, rx_buf);
        vTaskDelay(pdMS_TO_TICKS(1000)); //延时1s 
    }
}



void app_main(void)
{
    MessageBufferHandle = xMessageBufferCreate(1000);
    if (MessageBufferHandle != NULL)
    {
        vTaskSuspendAll(); //挂起所有任务

        xTaskCreate(myTask1, "mytask1", 1024 * 5, NULL, 1, NULL);
        xTaskCreate(myTask2, "mytask2", 1024 * 5, NULL, 1, NULL);

        xTaskResumeAll(); //恢复所有任务
    }
    else
    {
        printf("StreamBuffer create fail !\n ");
    }    
}

除了每次只能接收一条数据外Message Buffer如果发送的数据大于接收数据函数设定的大小,就会一直返回0且不会接收到数据即接收到的数据为空,而Stream Buffer会依次接收设定大小的内容直到所有需要接收的内容都被接收.

你可能感兴趣的:(学习)