基于源码详谈FreeRTOS机制之队列

队列的作用

我们假设有这么一段代码

  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

  • 17
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值