任务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);
}
}
如此一来,就解决了,打印内容如下: