• FreeRTOS学习笔记-队列


    关键函数说明

    头文件

    #include "freertos/queue.h"
    
    • 1

    创建队列

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

    向队列写入数据

    //向队列最前端写入,等同于先入后出
    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:队列满
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    等待时间为:

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

    从队列接收数据

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

    等待时间为:

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

    snprintf()函数

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

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

    队列集合

    //创建队列集
    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 句柄
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    队列邮箱

    //写队列邮箱
    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:队列邮箱为空。
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    课程示例

    通过队列发送与接收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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    效果

    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);//修改接收检测时间
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    效果

    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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    效果

    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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    效果

    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 )

    在这里插入图片描述

    #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
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76

    效果
    任务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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

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

    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”中选择有数据的队列,并接收队列中的数据。
    在这里插入图片描述

    #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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77

    效果

    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拿到指令并执行不同到操作。
    在这里插入图片描述

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

    • 邮箱是只有一个元素长度的队列
    • 邮箱读写方式与队列不同
    • 邮箱写函数 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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79

    效果

    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个函数体,执行相同的函数,通过打印任务名可做区分。

  • 相关阅读:
    python pip本地安装第三方包以及其他常用命令
    【LeetCode每日一题】——236.二叉树的最近公共祖先
    Adobe研究人员研发新AI模型LRM:实现从2D样本瞬时生成3D图像
    【工具】java判断某个时间是否在时间范围区间内
    2.js获取单选按钮(radio)的value值和点击事件
    day15文件操作
    Windows10中使用VS2022和Cmake编译构建C++开源日志库-spdlog
    NSTextField如何实现字体居中
    java计算机毕业设计河南口腔医疗机构线上服务系统MyBatis+系统+LW文档+源码+调试部署
    【老生谈算法】matlab实现蚁群算法解决旅行商问题——蚁群算法
  • 原文地址:https://blog.csdn.net/ai_ljh/article/details/126764365