• FreeRTOS入门教程(队列详细使用示例)



    前言

    上篇文章我们已经讲解了队列的概念和队列相关的API函数,那么本篇文章的话就开始带大家来学习使用队列。

    一、队列基本使用

    这个例子将会创建三个任务,其中两个任务用来发送数据到队列中,另一个任务用来从队列中读取数据

    void Task1Function(void * param)
    {
    	int val;
    	
    	while (1)
    	{
    		val = 100;
    		xQueueSend(xQueueCalcHandle, &val, 0);
    		
    		vTaskDelay(1000);
    	}
    }
    
    void Task2Function(void * param)
    {
    	int val;
    	
    	while (1)
    	{
    		val = 200;
    		xQueueSend(xQueueCalcHandle, &val, 0);
    		
    		vTaskDelay(1000);
    	}
    }
    
    void Task3Function(void * param)
    {
    	int val;
    	const TickType_t xTicksToWait = pdMS_TO_TICKS( 100UL );
    	BaseType_t xStatus;
    	
    	while (1)
    	{
    		xStatus = xQueueReceive(xQueueCalcHandle, &val, xTicksToWait);
    		if( xStatus == pdPASS )
    		{
    			/* 读到了数据 */
    			printf( "Received = %d\r\n", val );
    		} 
    		else
    		{
    			/* 没读到数据 */
    			printf( "Could not receive from the queue.\r\n" );
    		}		
    	}
    }
    
    
    xQueueCalcHandle = xQueueCreate(5, sizeof(int));
    
    xTaskCreate(Task1Function, "Task1", 100, NULL, 1, NULL);
    xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);
    xTaskCreate(Task3Function, "Task3", 100, NULL, 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

    运行效果:

    从运行效果中可以看出,当队列中有数据的时候就能够从队列中读取到数据,当队列中没有数据时,超时后会返回pdFALSE。

    在这里插入图片描述
    这里使用百问网的一张图片来描述这个过程:
    在这里插入图片描述

    二、如何分辨数据源

    通过上面这个实验我们完成了队列数据的发送和队列数据的接收,但是我们无法得知数据是哪个队列所发送的,那么下面这个实验就带大家来完成分辨数据源的实验。

    前面的实验中我们使用单独的一个int变量来代表数据,这样的话只能接收到对应的数据而无法分辨是谁发过来的数据,那么有什么办法来分辨是谁发来的数据呢?

    这里的解决方法是使用结构体:

    typedef enum
    {
    	Task1,
    	Task2
    }ID_t;
    
    typedef struct data
    {
    	ID_t id;
    	int data;
    }Data_t;
    
    static Data_t senddata[2] = {
    	{Task1, 10},
    	{Task2, 20}
    };
    
    void Task1Function(void * param)
    {
    	
    	while (1)
    	{
    		xQueueSend(xQueueCalcHandle, &senddata[0], 0);
    		
    		vTaskDelay(1000);
    	}
    }
    
    void Task2Function(void * param)
    {
    	
    	while (1)
    	{
    		xQueueSend(xQueueCalcHandle, &senddata[1], 0);
    		
    		vTaskDelay(1000);
    	}
    }
    
    void Task3Function(void * param)
    {
    	Data_t mydata;
    	const TickType_t xTicksToWait = pdMS_TO_TICKS( 100UL );
    	BaseType_t xStatus;
    	
    	while (1)
    	{
    		xStatus = xQueueReceive(xQueueCalcHandle, &mydata, xTicksToWait);
    		if( xStatus == pdPASS )
    		{
    			/* 读到了数据 */
    			if(mydata.id == Task1)
    			{
    				printf("this is Task1 data :%d\r\n", mydata.data);
    			}
    			else
    			{
    				printf("this is Task2 data :%d\r\n", mydata.data);
    			}
    		} 
    		else
    		{
    			/* 没读到数据 */
    			printf( "Could not receive from the queue.\r\n" );
    		}		
    	}
    }
    
    
    xQueueCalcHandle = xQueueCreate(5, sizeof(Data_t));
    
    xTaskCreate(Task1Function, "Task1", 100, NULL, 1, NULL);
    xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);
    xTaskCreate(Task3Function, "Task3", 100, NULL, 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

    当接收到数据时会先判断结构体中的id,通过id来判断是哪个任务发送过来的数据。

    运行效果:
    在这里插入图片描述

    三、传输大块数据

    FreeRTOS 中的队列通常使用数据拷贝来传递数据。这意味着当你将数据发送到队列或从队列接收数据时,队列会在内部复制数据的副本,而不是传递指向原始数据的指针。

    这种数据拷贝的方法确保了数据的安全性和一致性,因为多个任务可以独立访问它们自己的副本,而不会干扰其他任务。然而,需要注意的是,数据拷贝可能会引入一些性能开销,尤其是在处理大量数据时。

    那么当使用队列来传输大量数据时该怎么做呢?

    这里我们可以使用指针来解决这个问题,传递大块数据的时候我们可以使用指针来解决这个问题,在传输大块数据时,可以先得到数据的地址,将数据的地址作为数据传递过来,当接收到数据的地址时,就能够通过数据的地址来得到对应的数据了。

    示例:

    char pcbuffer[100] = "Hello World";
    
    void Task1Function(void * param)
    {
    	char* buffer;
    	while (1)
    	{
    		buffer = pcbuffer;
    		xQueueSend(xQueueCalcHandle, &buffer, 0);
    		
    		vTaskDelay(1000);
    	}
    }
    
    void Task2Function(void * param)
    {
    	char* buffer;
    	while (1)
    	{
    		buffer = pcbuffer;
    		xQueueSend(xQueueCalcHandle, &buffer, 0);
    		
    		vTaskDelay(1000);
    	}
    }
    
    void Task3Function(void * param)
    {
    	char* rebuffer;
    	const TickType_t xTicksToWait = pdMS_TO_TICKS( 100UL );
    	BaseType_t xStatus;
    	
    	while (1)
    	{
    		xStatus = xQueueReceive(xQueueCalcHandle, &rebuffer, xTicksToWait);
    		if( xStatus == pdPASS )
    		{
    			/* 读到了数据 */
    			printf("recv buffer : %s\r\n", rebuffer);
    		} 
    		else
    		{
    			/* 没读到数据 */
    			printf( "Could not receive from the queue.\r\n" );
    		}		
    	}
    }
    
    xQueueCalcHandle = xQueueCreate(5, sizeof(char*));
    if (xQueueCalcHandle == NULL)
    {
    	printf("can not create queue\r\n");
    }
    
    xTaskCreate(Task1Function, "Task1", 100, NULL, 1, NULL);
    xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);
    xTaskCreate(Task3Function, "Task3", 100, NULL, 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

    运行结果:

    在这里插入图片描述

    这里有几个点需要注意:

    1.
    由于队列传递数据是复制数据的副本,所有传输数据时并不会影响到原来的数据,但是在这里使用到了地址,当改变这个地址空间的数据后,原来的数据也会受到影响。

    2.
    由于传递的是地址空间,那么这里的话就必须保证这个数据是全局数据,因为局部数据会被释放,释放后就无法进行使用了,所有需要保证数据是全局数据。

    总结

    本篇文章就讲解到这里,本篇文章主要给大家讲解了队列的具体代码和使用方法。

  • 相关阅读:
    MySql和Oracle表分区
    知识图谱下的关联交易
    深入理解Spring Boot AOP:CGLIB代理与JDK动态代理的完全指南
    创客板Arduino之开关
    数据采集时使用HTTP代理IP效率不高怎么办?
    【Java基础】数组
    C#异步编程由浅入深(三)细说Awaiter
    java计算机毕业设计留守儿童帮扶网站MyBatis+系统+LW文档+源码+调试部署
    PyTorch入门之【dataset】
    【车载开发系列】UDS诊断服务入门知识
  • 原文地址:https://blog.csdn.net/m0_49476241/article/details/133616057