FreeRtos中使用队列在不同任务中传输数据的两种方式:拷贝变量的内存地址和拷贝变量本身的值存在的问题

任务A、B之间通过队列来进行数据的传输与通信,任务A发送数据可以类比为工人,任务B 接受数据可以类比为客人,由工人向客人之间的数据传递。

xQueueSend 函数将数据发送到队列时,FreeRTOS 会将数据拷贝到队列的内部缓冲区。这意味着在任务之间传递数据时,实际传递的是数据的副本,而不是原始数据的引用。

创建一个队列,队列里面有多个item,每个item的大小只有4字节,当想把字符串str1传给客户的时候,把str1的地址放入队列,当客户从队列里面得到这个地址的时候,把它作为一个地址去访问字符串str1,队列里面传的是值,是把这个值拷贝进队列。

static int sum=0;
static QueueHandle_t xQueuecalHandle;//创建队列句柄
void Task1Function(void*param)
{
	volatile int i=0;
	MyStruct *myStructPtr = &myInstance;
	while(1)
	{
		for(i=0;i<100000;i++)
		{
			sum++;
			
		}
		xQueueSend(xQueuecalHandle,&sum,portMAX_DELAY);
	}
}


void Task2Function(void*param)
{
	int val;
	MyStruct *receivedStructPtr;
	while(1)
	{
		flagCalcEnd=0;
		xQueueReceive(xQueuecalHandle,&val,portMAX_DELAY);
		flagCalcEnd=1;
		printf("sum=%d\r\n",val);
	}
}

以上代码是首先在任务1中通过输入sum的地址到xQueueSend函数将sum的值拷贝进队列中,然后在任务2中通过输入val的地址到xQueueReceive函数中将队列中的值拷贝到val的地址,从而通过队列实现了任务1到任务2的传递,但是在数据多,数据量大的时候使用这个值的拷贝机制就会比较复杂。

如果使用拷贝变量的内存地址,如直接将包含多个数据的结构体的地址拷贝到队列里,接收到结构体地址可以直接指向到结构体的数据。

typedef struct 
{
   volatile int data;
    
} MyStruct;

MyStruct myInstance={0};

void Task1Function(void*param)
{
	volatile int i=0;
	MyStruct *myStructPtr = &myInstance;
	
	while(1)
	{
		for(i=0;i<100000;i++)
		{
			myInstance.data++;
		}
		
		xQueueSend(xQueuecalHandle,&myStructPtr,portMAX_DELAY);
		
	}
}
void Task2Function(void*param)
{
	int val;
	MyStruct *receivedStructPtr;
	while(1)
	{
		flagCalcEnd=0;
		xQueueReceive(xQueuecalHandle,&receivedStructPtr,portMAX_DELAY);
		flagCalcEnd=1;
		printf("receivedStructPtr->data=%d\r\n",receivedStructPtr->data);
	}
}

以上代码在任务1里面创建了一个结构体指针指向前面定义的结构体,通过将该指针myStructPtr的地址输入到xQueueSend函数中,使得myStructPtr指针指向的地址值拷贝到队列中,即将结构体myInstance的地址拷贝到队列中了。在任务2中xQueueReceive函数接受到队列传输过来的结构体地址值,通过receivedStructPtr指针输入到接受函数,将结构体的地址值赋给该指针,从而该指针指向到结构体myInstance,再通过该指针就可以读出结构体内保存的数据了。

但是以上代码打印出来会出数据不对,如下:

很可能是由于任务1在发送数据到队列时还在修改myInstance.data,导致任务2读取的值不一致。这是因为在任务1中,or循环中不断增加myInstance.data的值,而在此过程中调用了xQueueSend。由于任务调度的不确定性,任务2可能会在任务1仍在增加myInstance.data的过程中读取数据,导致读取的数据并不是期望的结果。

从图中可以看到从任务2切换到任务1取数据的过程 过程存在一定间隔约3ms,在这过程中,任务1继续执行佳佳,改变了结构体内的数值从而导致等到打印的时候数据就不能保证是100000整数倍。添加的vTaskDelay(1);让任务一发送完后立马主动让出CPU资源,使得任务2得以取出数据和及时打印数据,使得任务1里面的佳佳操作没有被执行

调用vTaskDelay()来解决这个问题,在FreeRTOS中,如果一个任务长时间运行而不调用 vTaskDelay(),它可能会占用所有CPU时间,使其他任务无法执行。

修改任务1代码如下,任务2代码无修改:

typedef struct 
{
   volatile int data;
    
} MyStruct;

MyStruct myInstance={0};
SemaphoreHandle_t xMutex;
void Task1Function(void*param)
{
	volatile int i=0;
	MyStruct *myStructPtr = &myInstance;
	
	while(1)
	{
		
		printf("xSemaphoreTake is ok");
		for(i=0;i<100000;i++)
		{
			myInstance.data++;
		
		}
			
		xQueueSend(xQueuecalHandle,&myStructPtr,portMAX_DELAY);
		vTaskDelay(1);
	}
}
void Task2Function(void*param)
{
	int val;
	MyStruct *receivedStructPtr;
	while(1)
	{
		flagCalcEnd=0;
		xQueueReceive(xQueuecalHandle,&receivedStructPtr,portMAX_DELAY);
		flagCalcEnd=1;
		printf("receivedStructPtr->data=%d\r\n",receivedStructPtr->data);
	}
}

如此一来,就解决了,打印内容如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值