FreeRTOS学习笔记七【队列-上】
目的
- 如何创建队列。
- 队列如何管理它的数据。
- 如何将数据发送到队列。
- 如何从队列接收数据。
- 什么是阻塞队列。
- 如何让多个队列阻止。
- 如何覆盖队列中的数据。
- 如何清空队列。
- 入队列和出队时对任务优先级的影响。
队列的特征
数据存储
队列是一种先进先出(FIFO)的缓冲区,数据从队尾入队,从队头出队。通常有两种方法实现队列:
- 数据采用副本的方式
采用副本方式的队列在入队时将数据逐字节复制到队列中。 - 数据采用引用的方式
采用引用方式的队列在入队时只保存数据的指针,而不是数据本身。
在FreeRTOS中使用的是副本方式的队列,这个方式比引用方式更加强大与简单。理由如下:
- 自动变量(栈变量)可以直接入队,即使该变量已经不存在了也依然可是使用它的值。
- 可以直接将数据入队,而不需要用户预先分配存储区域保存数据。
- 数据入队后,它的存储区域可以立即使用,或者可以立即修改它的值。
- 入队任务和出队任务完全解耦合,应用程序无需关心哪些任务拥有数据或何时需要释放数据的内存。
- 副本方式的队列依然可以通过引用方式使用,如,当数据太大时,也可以将指向数据的指针入队。
- 使用副本方式的队列时,完全由FreeRTOS负责管理(分配、释放)存储数据的内存。
- 在内存受保护的系统中,任务可以访问的RAM受限,这时如果使用引用方式的队列,必须将数据存放在两个任务都可以访问的RAM中才可以实现队列,而副本方式的队列没有这个限制。
多个任务访问
队列本身也是对象,任何任务都可以通过队列的句柄访问它。
出队时阻塞任务
当任务从队列中读取数据时,而队列里没有数据,此时该任务将会阻塞,直到由队列中有数据(其他任务将数据写入队列)时该任务才会进入就绪态。一个队列可以有多个任务从中获取数据,因此它可以阻塞多个任务,这种情况下每次只有一个任务在数据到达时进入就绪态,被阻塞的任务中始终是优先级高的任务先进入就绪态,如果所有任务优先级相同,那么阻塞时间长的任务先进入就绪态。
入队时阻塞任务
如果队列已满,此时入队,任务将会阻塞,同样的一个队列也可以阻塞多个任务,当有空间可以使数据入队时,只有一个任务可以进入就绪态,被阻塞的任务中始终是优先级高的任务先进入就绪态,如果所有任务优先级相同,那么阻塞时间长的任务先进入就绪态。
多个队列阻塞任务
队列可以分组,同样的任务需要等待数据在队列集合上的任意队列可用时才会进入就绪态。
队列的使用
xQueueCreate()
使用队列前必须先创建它,QueueHandle_t是队列句柄的变量类型,调用xQueueCreate()可以创建一个队列,创建成功时返回指向队列的QueueHandle_t指针,如果没有足够队空间,则创建失败返回NULL。下面是它的原型:
QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize );
参数:
- uxQueueLength
队列可以保存数据的最大项数。 - uxItemSize
每项数据的字节数。
返回值:
- 非NULL,表示已成功创建队列。 返回的值为创建的队列的句柄。
- NULL,则队列创建失败,因为没有足够的堆内存来分配队列数据结构和存储区域。
后面使用过程中可以用xQueueReset()函数将队列清空到初始状态。
xQueueSendToBack() 、 xQueueSendToFront()
xQueueSendToBack()用于将数据发送到队尾部,xQueueSendToFront()用于将数据发送到队头xQueueSen()与xQueueSendToBack()等效,效果完全相同。
注意:不要在中断服务函数中调用xQueueSendToFront()或xQueueSendToBack()。 中断安全版本应该使用xQueueSendToFrontFromISR()和QueueSendToBackFromISR(),这两个函数在后面介绍。函数原型:
BaseType_t xQueueSendToFront( QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait );
BaseType_t xQueueSendToFront( QueueHandle_t xQueue, const void</