队列的作用
我们假设有这么一段代码
1 #include<stdio.h>
2 int a =0;
3 void aa();
4 void bb();
5 void main()
6 {
7 aa();
8 bb();
9 printf("a= %d\n",a);
10 }
11 void aa()
12 {
13 a++;
14 }
15 void bb()
16 {
17 a++;
18 }
这段代码运行结果如图:
从代码可以看出运行结果和我们想要的结果相当。这段代码运行的本质是这样的:首先寄存器会去读a的值并复制给R0,然后再进行加法操作,即R0=R0+1 ,最后再计算出的R0的值重新赋值给a,调用一次aa函数后a的值为1,再次调用bb后a的值为2。
但是我们的FreeRTOS系统是多任务的操作系统,它会进行函数的切换的,当aa函数执行完第二步时,CPU分配给函数aa的时间到了,然后执行bb,执行完bb后的a是1,然后再次回到aa执行第三步,第三步会把bb执行结果的a=1给覆盖掉,最后输出的结果是aa函数的a=1,造成数据的丢失。引入队列可以高效的解决这个互斥问题。
队列的核心
解决互斥
在源码中,队列通过关闭中断来解决这个问题。即当aa在执行的时候,不允许任何任务打断它。
taskENTER_CRITICAL();
数据存放
在队列中使用环形缓冲区可以循环的进行数据存放。假设当前数组的长度Len为4,即有0,1,2,3这四项,一开始数组是空的,在第0项时,读位置r和写位置w都为等于0。当要写入第一个数据时是这样的——buffer[w]=value,然后下一个时,w=w+1,这是正常的计算方式。直到最后一项时再重新回到第0项,此时应该循环,但是w=w+1却无法循环,所以其计算方式应该是w=(w+1)%Len,这样就可以形成一个循环,也被我们称为环形缓冲区。
在源码中是这样计算buffer的,buffer = 长度+数据大小
xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize )
队列 = 头部+buffer
pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes );
头部就是队列的结构体 QueueDefinition ,里面有队列所携带的东西
休眠与唤醒
我们假设函数A里面有个FLAG=1,循环一百万次,还有个函数B,它需要等A里面的FLAG变量变成0了再去做某件事情,但是A循环一百万次B也去判断一百万次的话就太浪费CPU资源了。这时候A就应该主动告知,即当条件不允许时B休眠,但条件允许时A唤醒B。
在队列结构体中有这么两个链表
List_t xTasksWaitingToSend; /*写队列不成功挂起*/
List_t xTasksWaitingToReceive; /*读队列不成功挂起*/
当A循环第一时,B会判断条件是否成熟,不成熟的话就会把链表放入 第二个链表中,等待A的唤醒。
队列的读写
队列的写
写队列时,若是不成功是否愿意等待,若是不愿意等待则返回ERR,若愿意则下面操作
xQueueSendToBack
xQueueGenericSend
/* 如果不成功: */
vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait );
// 1. 当前任务记录在队列的链表里: pxQueue->xTasksWaitingToSend
vListInsert( pxEventList, &( pxCurrentTCB->xEventListItem ) );
// 2. 把当前任务从ready list放到delayed list
prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );
当写队列成功时,就去 xTasksWaitingToReceive 这个链表唤醒队列,xTasksWaitingToReceive会移除第一个任务(FIFO原则),然后再吧任务由原来的delayed list 状态移到ready list。
队列的读
读队列时的操作方式和写队列一样的操作方式。先去判断有无数据,当无数据时判断是否愿意等待,若是不愿意等待则返回ERR,若是愿意等待则进入休眠状态:1、把队列放入xTasksWaitingToReceive。2、把队列由原来ready list放入delayed list
当有数据或者被唤醒时,复制数据,然后再去xTasksWaitingToSend队列唤醒队列,具体操作是:1、把xTasksWaitingToSend中第一个移除(FIFO原则)
2、把它从delayed list放入ready list