FreeRTOS学习笔记-队列

FreeRTOS学习笔记-队列

  • 关键函数说明
    • 头文件
    • 创建队列
    • 向队列写入数据
    • 从队列接收数据
    • snprintf()函数
    • 队列集合
    • 队列邮箱
  • 课程示例
    • 通过队列发送与接收int型数据
    • 通过队列发送与接收结构体
    • 通过队列发送与接收指针
    • 队列多进单出( Multiple In Single Out )
    • 先入后出函数测试
    • 队列集合(Queue Set)
    • 队列邮箱

关键函数说明

头文件

#include "freertos/queue.h"

创建队列

QueueHandle_t xQueueCreate( 
			UBaseType_t uxQueueLength,//队列长度,相当于buff深度
			UBaseType_t uxItemSize //元素大小(Byte),相当于buff宽度 例如:用 sizeof(int)获取;
			);

向队列写入数据

//向队列最前端写入,等同于先入后出
BaseType_t xQueueSendToFront( 
			QueueHandle_t xQueue, //队列句柄
			const void * pvItemToQueue, //待复制的数据指针,复制的数据大小由创建队列时指定的宽度确定
			TickType_t xTicksToWait //等待时间
			);
//向队列后面写入,等同于先入先出
BaseType_t xQueueSendToBack( 
			QueueHandle_t xQueue,
			const void * pvItemToQueue,
			TickType_t xTicksToWait 
			);
//返回值:	
//		pdPASS:成功写入  
// 		errQUEUE_FULL:队列满

等待时间为:

  • 0 : 若队列满立即返回
  • portMAX_DELAY:INCLUDE_vTaskSuspend == 1 ,且队列满,将一直阻塞,直到队列非满
  • 其他值: 如队列满,将等待该时间后返回,单位为Ticks系统默认为10ms

从队列接收数据

BaseType_t xQueueReceive( 
		QueueHandle_t xQueue,//队列句柄
		void *pvBuffer,//接收数据存放位置
		TickType_t xTicksToWait //等待时间,与发送类似
	);
//返回值:
//pdPASS: 接收成功
//errQUEUE_EMPTY:队列为空

等待时间为:

  • 0 : 若队列空立即返回
  • portMAX_DELAY:INCLUDE_vTaskSuspend == 1 ,且队列空,将一直阻塞,直到队列非空
  • 其他值: 如队列空,将等待该时间后返回,单位为Ticks系统默认为10ms
UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue//队列句柄
 );//返回值: 当前队列有效数据个数
 

snprintf()函数

将可变参数 (…) 按照 format 格式化成字符串,并将字符串复制到 str 中,实际上后半部分跟 printf() 函数用法相同。

int snprintf(char *str,//目标字符串
			 size_t size, //拷贝字节数(Bytes) format长度大于该值将被截断,并在(size-1)位置添加“\0”
			 const char *format, //格式化成字符串
			 ...   //可变参数
 ) ;//返回值: 写入的字符串长度

队列集合

//创建队列集
QueueSetHandle_t xQueueCreateSet( 
		const UBaseType_t uxEventQueueLength //队列集长度 = 包含的每个队列的长度之和
		);//返回:队列集句柄
		
//添加队列到队列集
BaseType_t xQueueAddToSet( 
		QueueSetMemberHandle_t xQueueOrSemaphore,//要添加到队列集的队列或信号量,被强制转换成 QueueSetMemberHandle_t 类型
		QueueSetHandle_t xQueueSet //队列集
		);//返回值: pdPASS:成功;  pdFAIL:无法将队列或信号量添加到队列集中,因为它已经是另一个集的成员
//从队列集移除队列
BaseType_t xQueueRemoveFromSet( 
		QueueSetMemberHandle_t xQueueOrSemaphore,
		QueueSetHandle_t xQueueSet 
		);
		
//从队列集的成员中选择包含数据的队列 或 可用于获取的信号量
QueueSetMemberHandle_t xQueueSelectFromSet( 
		QueueSetHandle_t xQueueSet, //队列集
		const TickType_t xTicksToWait //等待超时  设为 portMAX_DELAY时, 若 INCLUDE_vTaskSuspend == 1 ,且一直未收到数据将一直阻塞
		);//返回值 NULL:在xTicksToWait参数指定的阻止时间过期之前,该集中包含的队列或信号量不可用。其他值:QueueSetMemberHandle_t 句柄

队列邮箱

//写队列邮箱
BaseType_t xQueueOverwrite( 
			QueueHandle_t xQueue, //队列邮箱句柄
			const void *pvItemToQueue //要写入的数据指针
			);//返回值:只能是 pdPASS
			
//清空队列邮箱
BaseType_t xQueueReset( QueueHandle_t xQueue );

//读队列邮箱,读取后不会从队列中移除该数据
BaseType_t xQueuePeek( 
			QueueHandle_t xQueue,//队列邮箱句柄
			void *pvBuffer, //读出的数据指针
			TickType_t xTicksToWait //超时,功能与队列读取函数相同
			);//返回值:pdPASS:成功;errQUEUE_EMPTY:队列邮箱为空。

课程示例

通过队列发送与接收int型数据

#include 
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "freertos/queue.h"

void mytask1(void *pvParameter)//发送任务
{
    QueueHandle_t p;
    p = (QueueHandle_t)pvParameter;
    int t1 = 5;
    while (1)
    {
        if (xQueueSendToBack(p, &t1, 0) == pdPASS)
        {
            printf("mytask1: send ok \n");
        }
        else
        {
            printf("mytask1: send error \n");
        }
        t1++;
        vTaskDelay(500 / portTICK_PERIOD_MS);
    }
}
void mytask2(void *pvParameter)//接收任务
{

    QueueHandle_t p;
    p = (QueueHandle_t)pvParameter;
    int t2 = 0;
    UBaseType_t cnt;
    while (1)
    {
        cnt = uxQueueMessagesWaiting(p);
        if (cnt > 0)
        {
            xQueueReceive(p, &t2, 0);
            printf("mytask2: t2= %d cnt= %d\n", t2, cnt-1);
        }
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

void app_main(void)
{

    QueueHandle_t que1 = xQueueCreate(5, sizeof(int));
    if (que1 != NULL)//队列创建成功时再创建收发任务
    {
        xTaskCreate(mytask1, "mytask1", 1024 * 5, (void *)que1, 1, NULL);//注意堆栈,设小了会导致任务启动/运行异常
        xTaskCreate(mytask2, "mytask2", 1024 * 5, (void *)que1, 1, NULL);
    }
}

效果

mytask1: send ok
mytask2: t2= 5 cnt= 0
mytask1: send ok
mytask2: t2= 6 cnt= 0
mytask1: send ok
mytask1: send ok
mytask2: t2= 7 cnt= 1
mytask1: send ok
mytask1: send ok
mytask2: t2= 8 cnt= 2
mytask1: send ok
mytask1: send ok
mytask2: t2= 9 cnt= 3
mytask1: send ok
mytask1: send ok
mytask2: t2= 10 cnt= 4
mytask1: send ok
mytask1: send error
mytask2: t2= 11 cnt= 4
mytask1: send ok
mytask1: send error
mytask2: t2= 12 cnt= 4
mytask1: send ok
mytask1: send error

队列总长度为5,发送快,接收慢,队列满时,发送出错。

修改接收任务检测时间为200ms:

void mytask2(void *pvParameter)
{

    QueueHandle_t p;
    p = (QueueHandle_t)pvParameter;
    int t2 = 0;
    UBaseType_t cnt;
    while (1)
    {
        cnt = uxQueueMessagesWaiting(p);
        if (cnt > 0)
        {
            xQueueReceive(p, &t2, 0);
            printf("mytask2: t2= %d cnt= %d\n", t2, cnt-1);
        }
        vTaskDelay(200 / portTICK_PERIOD_MS);//修改接收检测时间
    }
}

效果

mytask1: send ok
mytask2: t2= 5 cnt= 0
mytask1: send ok
mytask2: t2= 6 cnt= 0
mytask1: send ok
mytask2: t2= 7 cnt= 0
mytask1: send ok
mytask2: t2= 8 cnt= 0
mytask1: send ok
mytask2: t2= 9 cnt= 0
mytask1: send ok
mytask2: t2= 10 cnt= 0
mytask1: send ok
mytask2: t2= 11 cnt= 0
mytask1: send ok
mytask2: t2= 12 cnt= 0
mytask1: send ok
mytask2: t2= 13 cnt= 0
mytask1: send ok
mytask2: t2= 14 cnt= 0
mytask1: send ok
mytask2: t2= 15 cnt= 0
mytask1: send ok

接收快,发送慢,每次发送都正常。
因为在接收任务中添加了uxQueueMessagesWaiting函数,用于统计队列有效元素个数,没有数据时不会去接收,因此未报错。

通过队列发送与接收结构体

#include 
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "freertos/queue.h"

typedef struct a_struct
{
    int id;
    int data;
} xStruct;

void mytask1(void *pvParameter)
{
    QueueHandle_t p;
    p = (QueueHandle_t)pvParameter;
    xStruct t1;
    t1.id = 0;
    t1.data = 999;
    while (1)
    {
        if (xQueueSendToBack(p, &t1, 0) == pdPASS)
        {
            printf("mytask1: send ok \n");
        }
        else
        {
            printf("mytask1: send error \n");
        }
        t1.id++;
        t1.data--;
        vTaskDelay(500 / portTICK_PERIOD_MS);
    }
}
void mytask2(void *pvParameter)
{

    QueueHandle_t p;
    p = (QueueHandle_t)pvParameter;
    xStruct t2;
    t2.id = 0;
    t2.data = 0;
    UBaseType_t cnt;
    while (1)
    {
        cnt = uxQueueMessagesWaiting(p);
        if (cnt > 0)
        {
            xQueueReceive(p, &t2, 0);
            printf("mytask2: t2= { %d , %d }cnt= %d\n", t2.id, t2.data, cnt - 1);
        }
        vTaskDelay(200 / portTICK_PERIOD_MS);
    }
}

void app_main(void)
{

    QueueHandle_t que1 = xQueueCreate(5, sizeof(xStruct));
    if (que1 != NULL)
    {
        xTaskCreate(mytask1, "mytask1", 1024 * 5, (void *)que1, 1, NULL);
        xTaskCreate(mytask2, "mytask2", 1024 * 5, (void *)que1, 1, NULL);
    }
}

效果

mytask1: send ok
mytask2: t2= { 0 , 999 }cnt= 0
mytask1: send ok
mytask2: t2= { 1 , 998 }cnt= 0
mytask1: send ok
mytask2: t2= { 2 , 997 }cnt= 0
mytask1: send ok
mytask2: t2= { 3 , 996 }cnt= 0
mytask1: send ok
mytask2: t2= { 4 , 995 }cnt= 0
mytask1: send ok
mytask2: t2= { 5 , 994 }cnt= 0
mytask1: send ok
mytask2: t2= { 6 , 993 }cnt= 0
mytask1: send ok
mytask2: t2= { 7 , 992 }cnt= 0
mytask1: send ok
mytask2: t2= { 8 , 991 }cnt= 0

通过队列发送与接收指针

#include 
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "freertos/queue.h"

void mytask1(void *pvParameter)
{
    QueueHandle_t p;
    p = (QueueHandle_t)pvParameter;
    char *pt1;
    
    int i = 0;
    while (1)
    {
        pt1 = (char *)malloc(50); //每次循环都分配新的地址
        snprintf(pt1, 50, "hello world %d", i);
        if (xQueueSendToBack(p, &pt1, 0) == pdPASS)
        {
            printf("mytask1: send ok pt1_addr= %p \n", pt1);
        }
        else
        {
            printf("mytask1: send error \n");
        }
        i++;
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}
void mytask2(void *pvParameter)
{

    QueueHandle_t p;
    p = (QueueHandle_t)pvParameter;
    char *pt2;
    UBaseType_t cnt;
    while (1)
    {
        cnt = uxQueueMessagesWaiting(p);
        if (cnt > 0)
        {
            xQueueReceive(p, &pt2, 0);//在队列中接收的实际只是一个char指针,即发送任务中分配的地址
            printf("mytask2: pt2_addr= %p  pt2= %s | cnt= %d\n", pt2, pt2, cnt - 1);
            free(pt2); //释放发送函数分配的内存
        }
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

void app_main(void)
{

    QueueHandle_t que1 = xQueueCreate(5, sizeof(char *));
    if (que1 != NULL)
    {
        xTaskCreate(mytask1, "mytask1", 1024 * 5, (void *)que1, 1, NULL);
        xTaskCreate(mytask2, "mytask2", 1024 * 5, (void *)que1, 1, NULL);
    }
}

效果

mytask1: send ok pt1_addr= 0x3ffb94d0
mytask2: pt2_addr= 0x3ffb94d0 pt2= hello world 0 | cnt= 0
mytask1: send ok pt1_addr= 0x3ffb4a48
mytask2: pt2_addr= 0x3ffb4a48 pt2= hello world 1 | cnt= 0
mytask1: send ok pt1_addr= 0x3ffb4a48
mytask2: pt2_addr= 0x3ffb4a48 pt2= hello world 2 | cnt= 0
mytask1: send ok pt1_addr= 0x3ffb4a48
mytask2: pt2_addr= 0x3ffb4a48 pt2= hello world 3 | cnt= 0

  • demo中故意把指针地址打印出来,是为了说明队列里面传递的只是一个地址,并非真正的数据拷贝;
  • 因为任务2中对任务1分配的char指针进行了free()操作,因此后面分配的地址可能重复。

屏蔽free()函数效果如下,这样会占用不必要的内存,需谨慎使用。

mytask1: send ok pt1_addr= 0x3ffb94d0
mytask2: pt2_addr= 0x3ffb94d0 pt2= hello world 0 | cnt= 0
mytask1: send ok pt1_addr= 0x3ffb4a48
mytask2: pt2_addr= 0x3ffb4a48 pt2= hello world 1 | cnt= 0
mytask1: send ok pt1_addr= 0x3ffb4a80
mytask2: pt2_addr= 0x3ffb4a80 pt2= hello world 2 | cnt= 0
mytask1: send ok pt1_addr= 0x3ffb4ab8
mytask2: pt2_addr= 0x3ffb4ab8 pt2= hello world 3 | cnt= 0
mytask1: send ok pt1_addr= 0x3ffb4af0
mytask2: pt2_addr= 0x3ffb4af0 pt2= hello world 4 | cnt= 0
mytask1: send ok pt1_addr= 0x3ffb4b28
mytask2: pt2_addr= 0x3ffb4b28 pt2= hello world 5 | cnt= 0

队列多进单出( Multiple In Single Out )

FreeRTOS学习笔记-队列_第1张图片

#include 
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "freertos/queue.h"

void mytask1(void *pvParameter) //发送任务1
{
    QueueHandle_t p;
    p = (QueueHandle_t)pvParameter;
    int t1 = 111;
    while (1)
    {
        if (xQueueSendToBack(p, &t1, 0) == pdPASS)
        {
            printf("mytask1: send ok \n");
        }
        else
        {
            printf("mytask1: send error \n");
        }
        t1++;
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}
void mytask2(void *pvParameter) //发送任务2
{
    QueueHandle_t p;
    p = (QueueHandle_t)pvParameter;
    int t2 = 222;
    while (1)
    {
        if (xQueueSendToBack(p, &t2, 0) == pdPASS)
        {
            printf("mytask2: send ok \n");
        }
        else
        {
            printf("mytask2: send error \n");
        }
        t2++;
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}
void mytask3(void *pvParameter) //接收任务
{

    QueueHandle_t p;
    p = (QueueHandle_t)pvParameter;
    int t3 = 0;
    UBaseType_t cnt;
    while (1)
    {
        cnt = uxQueueMessagesWaiting(p);
        if (cnt > 0)
        {
            xQueueReceive(p, &t3, 0);
            printf("mytask3: t3= %d cnt= %d\n", t3, cnt - 1);
        }
        vTaskDelay(200 / portTICK_PERIOD_MS);
    }
}

void app_main(void)
{

    QueueHandle_t que1 = xQueueCreate(5, sizeof(int));
    if (que1 != NULL) //队列创建成功时再创建收发任务
    {
        xTaskCreate(mytask1, "mytask1", 1024 * 5, (void *)que1, 1, NULL); //注意堆栈,设小了会导致任务启动/运行异常
        xTaskCreate(mytask2, "mytask2", 1024 * 5, (void *)que1, 1, NULL);
        xTaskCreate(mytask3, "mytask3", 1024 * 5, (void *)que1, 2, NULL);
    }
}

效果
任务1和2往同一个队列里面写数据,任务3接收。

mytask1: send ok
mytask3: t3= 111 cnt= 0
mytask2: send ok
mytask3: t3= 222 cnt= 0
mytask1: send ok
mytask2: send ok
mytask3: t3= 112 cnt= 1
mytask3: t3= 223 cnt= 0
mytask1: send ok
mytask2: send ok
mytask3: t3= 113 cnt= 1
mytask3: t3= 224 cnt= 0
mytask1: send ok
mytask2: send ok

先入后出函数测试

#include 
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "freertos/queue.h"

void mytask1(void *pvParameter)//发送任务
{
    QueueHandle_t p;
    p = (QueueHandle_t)pvParameter;
    int t1 = 5;
    while (1)
    {
        //if (xQueueSendToBack(p, &t1, 0) == pdPASS)//先入先出
        if (xQueueSendToFront(p, &t1, 0) == pdPASS)//先入后出
        {
            printf("mytask1: send ok \n");
        }
        else
        {
            printf("mytask1: send error \n");
        }
        t1++;
        vTaskDelay(500 / portTICK_PERIOD_MS);
    }
}
void mytask2(void *pvParameter)//接收任务
{

    QueueHandle_t p;
    p = (QueueHandle_t)pvParameter;
    int t2 = 0;
    UBaseType_t cnt;
    while (1)
    {
        cnt = uxQueueMessagesWaiting(p);
        if (cnt > 0)
        {
            xQueueReceive(p, &t2, 0);
            printf("mytask2: t2= %d cnt= %d\n", t2, cnt-1);
        }
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

void app_main(void)
{

    QueueHandle_t que1 = xQueueCreate(5, sizeof(int));
    if (que1 != NULL)//队列创建成功时再创建收发任务
    {
        xTaskCreate(mytask1, "mytask1", 1024 * 5, (void *)que1, 1, NULL);//注意堆栈,设小了会导致任务启动/运行异常
        xTaskCreate(mytask2, "mytask2", 1024 * 5, (void *)que1, 1, NULL);
    }
}

效果
先进入队列的数据反而没有按顺序打印,反而先接收了最新写入队列的数据。

mytask1: send ok
mytask2: t2= 5 cnt= 0
mytask1: send ok
mytask2: t2= 6 cnt= 0
mytask1: send ok
mytask1: send ok
mytask2: t2= 8 cnt= 1
mytask1: send ok
mytask1: send ok
mytask2: t2= 10 cnt= 2
mytask1: send ok
mytask1: send ok
mytask2: t2= 12 cnt= 3
mytask1: send ok
mytask1: send ok
mytask2: t2= 14 cnt= 4
mytask1: send ok
mytask1: send error

先入先出效果对比:
队列溢出前,按先入先出的顺序,依次接收。

mytask1: send ok
mytask2: t2= 5 cnt= 0
mytask1: send ok
mytask2: t2= 6 cnt= 0
mytask1: send ok
mytask1: send ok
mytask2: t2= 7 cnt= 1
mytask1: send ok
mytask1: send ok
mytask2: t2= 8 cnt= 2
mytask1: send ok
mytask1: send ok
mytask2: t2= 9 cnt= 3
mytask1: send ok
mytask1: send ok
mytask2: t2= 10 cnt= 4
mytask1: send ok
mytask1: send error

队列集合(Queue Set)

每个发送任务具有自己独立的队列,而接收任务从多个队列,即“Queue Set”中选择有数据的队列,并接收队列中的数据。
FreeRTOS学习笔记-队列_第2张图片

#include 
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "freertos/queue.h"

void mytask1(void *pvParameter) //发送任务1
{
    QueueHandle_t p;
    p = (QueueHandle_t)pvParameter;
    int t1 = 111;
    while (1)
    {
        if (xQueueSendToBack(p, &t1, 0) == pdPASS)
        {
            printf("mytask1: send ok \n");
        }
        else
        {
            printf("mytask1: send error \n");
        }
        t1++;
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}
void mytask2(void *pvParameter) //发送任务2
{
    QueueHandle_t p;
    p = (QueueHandle_t)pvParameter;
    char t2 = 222;
    while (1)
    {
        if (xQueueSendToBack(p, &t2, 0) == pdPASS)
        {
            printf("mytask2: send ok \n");
        }
        else
        {
            printf("mytask2: send error \n");
        }
        t2++;
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}
void mytask3(void *pvParameter) //接收任务
{
    QueueSetHandle_t pset;
    pset = (QueueSetHandle_t)pvParameter;
    QueueHandle_t p;
    int t3;
    while (1)
    {
        p = (QueueHandle_t)xQueueSelectFromSet(pset, portMAX_DELAY);
        xQueueReceive(p, &t3, 0);
        printf("mytask3: t3= %d \n", t3);
        vTaskDelay(200 / portTICK_PERIOD_MS);
    }
}

void app_main(void)
{
    QueueHandle_t que1 = xQueueCreate(5, sizeof(int));
    QueueHandle_t que2 = xQueueCreate(8, sizeof(char));
    QueueSetHandle_t que_set1 = xQueueCreateSet(13); //队列集长度 = 包含的每个队列的长度之和
    if ((que1 != NULL) && (que1 != NULL))            //队列创建成功时再创建收发任务
    {

        xQueueAddToSet(que1, que_set1);
        xQueueAddToSet(que2, que_set1);

        xTaskCreate(mytask1, "mytask1", 1024 * 5, (void *)que1, 1, NULL); //注意堆栈,设小了会导致任务启动/运行异常
        xTaskCreate(mytask2, "mytask2", 1024 * 5, (void *)que2, 1, NULL);
        xTaskCreate(mytask3, "mytask3", 1024 * 5, (void *)que_set1, 2, NULL);
    }
}

效果

mytask1: send ok
mytask3: t3= 111
mytask2: send ok
mytask3: t3= 222
mytask3: t3= 112
mytask1: send ok
mytask2: send ok
mytask3: t3= 223
mytask3: t3= 113
mytask1: send ok
mytask2: send ok
mytask3: t3= 224

从队列集中接收数据正常,但有个遗留问题还不知如何解决,就是从程序中怎么确定当前接收的队列是que1还是que2。

队列邮箱

队列是用于数据传递,从一个或多个任务发送数据到另一个任务;
队列邮箱(Mailbox)用于控制程序执行流程,写任务向Mailbox写入控制命令,各个读任务从Mailbox拿到指令并执行不同到操作。
FreeRTOS学习笔记-队列_第3张图片

邮箱基于队列创建,与队列主要差异如下:

  • 邮箱是只有一个元素长度的队列
  • 邮箱读写方式与队列不同
  • 邮箱写函数 xQueueOverwrite();
  • 邮箱读函数 xQueuePeek() 执行后不会从队列删除已经读取的数据
  • 要清空邮箱,需采用:xQueueReset() 函数
#include 
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "freertos/queue.h"

void send_task(void *pvParameter) //发送任务1
{
    QueueHandle_t p;
    p = (QueueHandle_t)pvParameter;
    int t1;
    while (1)
    {
        t1 = 1; // mode A
        xQueueOverwrite(p, &t1);
        vTaskDelay(2000 / portTICK_PERIOD_MS);

        t1 = 5; // mode B
        xQueueOverwrite(p, &t1);
        vTaskDelay(2000 / portTICK_PERIOD_MS);

        t1 = 8; // mode C
        xQueueOverwrite(p, &t1);
        vTaskDelay(2000 / portTICK_PERIOD_MS);

        xQueueReset(p); //清空队列内容
        vTaskDelay(2000 / portTICK_PERIOD_MS);
    }
}
void receive_task(void *pvParameter) //接收任务
{
    QueueHandle_t p;
    p = (QueueHandle_t)pvParameter;
    char t2 = 222;
    char *task_name;
    task_name = pcTaskGetName(NULL); //获取当前任务名

    while (1)
    {
        if (xQueuePeek(p, &t2, 0) == pdPASS) // portMAX_DELAY
        {
            switch (t2)
            {
            case 1:
                printf("%s: mode A \n", task_name);
                break;
            case 5:
                printf("%s: mode B \n", task_name);
                break;
            case 8:
                printf("%s: mode C \n", task_name);
                break;
            default:
                printf("%s: error \n", task_name);
                break;
            }
        }
        else
        {
            printf("%s: Mailbox empty \n", task_name);
        }
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

void app_main(void)
{
    QueueHandle_t Mailbox1 = xQueueCreate(1, sizeof(int));

    if (Mailbox1 != NULL) //邮箱创建成功时再创建收发任务
    {
        xTaskCreate(send_task, "send_task", 1024 * 5, (void *)Mailbox1, 1, NULL);        //注意堆栈,设小了会导致任务启动/运行异常
        xTaskCreate(receive_task, "receive_task1", 1024 * 5, (void *)Mailbox1, 2, NULL); //接收优先级高
        xTaskCreate(receive_task, "receive_task2", 1024 * 5, (void *)Mailbox1, 2, NULL); //接收任务2和3与任务1共用1个函数体,功能相同
        xTaskCreate(receive_task, "receive_task3", 1024 * 5, (void *)Mailbox1, 2, NULL);
    }
}

效果

receive_task2: mode A
receive_task1: mode A
receive_task3: mode A
receive_task1: mode A
receive_task2: mode A
receive_task3: mode A
receive_task1: mode A
receive_task2: mode A
receive_task3: mode B
receive_task1: mode B
receive_task2: mode B
receive_task3: mode B
receive_task1: mode B
receive_task2: mode B
receive_task3: mode C
receive_task1: mode C
receive_task2: mode C
receive_task3: mode C
receive_task1: mode C
receive_task2: mode C
receive_task3: Mailbox empty
receive_task1: Mailbox empty
receive_task2: Mailbox empty
receive_task3: Mailbox empty
receive_task1: Mailbox empty
receive_task2: Mailbox empty
receive_task3: mode A
receive_task1: mode A
receive_task2: mode A
receive_task3: mode A
receive_task1: mode A
receive_task2: mode A
receive_task3: mode B

1个发送任务进行系统控制,每隔2s切换模式A、B、C,并在最后2s清除邮箱,3个接收任务公用1个函数体,执行相同的函数,通过打印任务名可做区分。

你可能感兴趣的:(ESP32,esp32,freeRTOS)