本文基于韦东山FreeRTOS和STM32cubeMX记录
队列通常可以作用与"任务到任务","任务到中断", "中断到任务"直接直接传输信息。
一、队列的特性
1.常规操作
- 队列可以有若干项,这被称为队列的长度
- 每个数据大小固定
- 在创建队列的时候就要指定队列长度和数据大小
- 数据保持FIFO的原则,即first in first out,也就是我们常说的先入先出,后入后出
- 也可以强制写队列头部,覆盖头部数据;

2.传输数据的两种方法
使用队列传输数据时,我们通常使用拷贝或者引用两种方法
- 拷贝:把数据,变量的值复制进入队列;
- 引用:把数据,变量的地址复制进队列;
在FreeRTOS中采用的拷贝这种方法,因为他相对于引用来说更加简单:
- 局部变量的值可以发送到队列中,后续即使函数退出、局部变量被回收,也
- 不会影响队列中的数据
- 无需分配 buffer 来保存数据,队列中有 buffer
- 局部变量可以马上再次使用
- 发送任务、接收任务解耦:接收任务不需要知道这数据是谁的、也不需要发 送任务来释放数据
- 如果数据实在太大,你还是可以使用队列传输它的地址
- 队列的空间有 FreeRTOS 内核分配,无需任务操心
- 对于有内存保护功能的系统,如果队列使用引用方法,也就是使用地址,必 须确保双方任务对这个地址都有访问权限。使用拷贝方法时,则无此限制:内核有足够的权限,把数据复制进队列、再把数据复制出队列。
3.队列的阻塞访问
只要知道队列句柄,那么谁都可以读队列,写队列。任务、ISR都可以。且可以多个任务读写队列。
任务读写队列时,若干读写不成功,则阻塞;也可以指定超时时间。某个任务读队列时,如果队列没有数据,则该任务可以进入阻塞状态:还可以指定阻塞 的时间。如果队列有数据了,则该阻塞的任务会变为就绪态。如果一直都没有数据,则时间 到之后它也会进入就绪态。
- 优先级最高的任务
- 如果大家的优先级相同,那等待时间最久的任务会进入就绪态
- 优先级最高的任务
- 如果大家的优先级相同,那等待时间最久的任务会进入就绪态
二、队列函数
使用队列的流程:创建队列,写队列,读队列,删除队列
1.创建队列
队列的创建也分为动态分配内存和静态分配内存两种方法。
- 动态分配内存:xQueueCreate,队列的内存在函数内部动态分配
函数原型如下:
QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize );
参数说明:
- uxQueueLength:队列长度,最多能存放多少个数据
- uxItemSize:每个数据的大小:以字节为单位
- 返回值:非0:成功,返回句柄;NULL:失败,因为内存不足
- 静态分配内存:xQueueCreateStatic,队列的内存要事先分配好
函数原型如下:
QueueHandle_t xQueueCreateStatic(
UBaseType_t uxQueueLength,
UBaseType_t uxItemSize,
uint8_t *pucQueueStorageBuffer,
StaticQueue_t *pxQueueBuffer
);
参数说明:
- uxQueueLength:队列长度,最多能存放多少个数据(item)
- uxItemSize:每个数据(item)的大小:以字节为单位
- pucQueueStorageBuffer:如果 uxItemSize 非 0,pucQueueStorageBuffer 必须指向一个 uint8_t 数组, 此数组大小至少为"uxQueueLength * uxItemSize"
- pxQueueBuffer:必须执行一个 StaticQueue_t 结构体,用来保存队列 的数据结构
- 返回值:非 0:成功,返回句柄,以后使用句柄来操作队列 ;NULL:失败,因为 pxQueueBuffer 为 NULL
2.复位队列
队列刚被创建时,里面没有数据;使用过程中可以调用xQueueReset()把队列恢复为初始状态
函数原型为:
/* pxQueue : 复位哪个队列;
* 返回值: pdPASS(必定成功)
*/
BaseType_t xQueueReset( QueueHandle_t pxQueue);
3.删除队列
删除队列的函数为vQueueDelete(),只能删除使用动态方式创建的队列,他会释放内存。
函数原型如下:
void vQueueDelete( QueueHandle_t xQueue );
4.写队列
可以把数据写到队列头部,也可以写到队列尾部,此函数有两个版本:在任务中调用,在ISR中使用。
函数原型如下:
/* 等同于xQueueSendToBack
* 往队列尾部写入数据,如果没有空间,阻塞时间为xTicksToWait
*/
BaseType_t xQueueSend(
QueueHandle_t xQueue,
const void *pvItemToQueue,
TickType_t xTicksToWait
);
/*
* 往队列尾部写入数据,如果没有空间,阻塞时间为xTicksToWait
*/
BaseType_t xQueueSendToBack(
QueueHandle_t xQueue,
const void *pvItemToQueue,
TickType_t xTicksToWait
);
/*
* 往队列尾部写入数据,此函数可以在中断函数中使用,不可阻塞
*/
BaseType_t xQueueSendToBackFromISR(
QueueHandle_t xQueue,
const void *pvItemToQueue,
BaseType_t *pxHigherPriorityTaskWoken
);
/*
* 往队列头部写入数据,如果没有空间,阻塞时间为xTicksToWait
*/
BaseType_t xQueueSendToFront(
QueueHandle_t xQueue,
const void *pvItemToQueue,
= TickType_t xTicksToWait
);
/*
* 往队列头部写入数据,此函数可以在中断函数中使用,不可阻塞
*/
BaseType_t xQueueSendToFrontFromISR(
QueueHandle_t xQueue,
const void *pvItemToQueue,
BaseType_t *pxHigherPriorityTaskWoken
);
参数说明:
- xQueue:队列句柄
- pvItemToQueue:数据指针,这个数据会被复制进队列,复制多大的数据则是在创建队列时指定的
- xTicksToWait:如果队列满则无法写入新数据,可以让任务进入阻塞状态, xTicksToWait 表示阻塞的最大时间(Tick Count)。 如果被设为 0,无法写入数据时函数会立刻返回; 如果被设为 portMAX_DELAY,则会一直阻塞直到有空间可写
- 返回值:pdPASS:数据成功写入了队列;errQUEUE_FULL:写入失败,队列满
5.读队列
读到最后一个数据后,队列中的该数据会被移除。这个函数也分为在任务中使用和在ISR中使用。
函数原型如下:
BaseType_t xQueueReceive( QueueHandle_t xQueue,
void * const pvBuffer,
TickType_t xTicksToWait );
BaseType_t xQueueReceiveFromISR(
QueueHandle_t xQueue,
void *pvBuffer,
BaseType_t *pxTaskWoken
);
参数说明如下:
- xQueue:队列句柄
- pvBuffer:buffer的指针,队列数据会复制到这个buffer;复制多大的数据则是在创建队列时指定的
- xTicksToWait:如果队列空则无法读出新数据,可以让任务进入阻塞状态, xTicksToWait 表示阻塞的最大时间(Tick Count)。 如果被设为 0,无法读出数据时函数会立刻返回; 如果被设为 portMAX_DELAY,则会一直阻塞直到有数据可读
- 返回值:pdPASS:数据成功读出了队列;errQUEUE_EMPTY:读出失败,队列空
6.查询
可以查询队列中有多少数据,有多少空闲空间。函数原型如下:
/*
* 返回队列中可用数据的个数
*/
UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue );
/*
* 返回队列中可用空间的个数
*/
UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue );
7.覆盖/偷看
/* 覆盖队列
* xQueue: 写哪个队列
* pvItemToQueue: 数据地址
* 返回值: pdTRUE表示成功, pdFALSE表示失败
*/
BaseType_t xQueueOverwrite(
QueueHandle_t xQueue,
const void * pvItemToQueue
);
BaseType_t xQueueOverwriteFromISR(
QueueHandle_t xQueue,
const void * pvItemToQueue,
BaseType_t *pxHigherPriorityTaskWoken
);
/* 偷看队列
* xQueue: 偷看哪个队列
* pvItemToQueue: 数据地址, 用来保存复制出来的数据
* xTicksToWait: 没有数据的话阻塞一会
* 返回值: pdTRUE表示成功, pdFALSE表示失败
*/
BaseType_t xQueuePeek(
QueueHandle_t xQueue,
void * const pvBuffer,
TickType_t xTicksToWait
);
BaseType_t xQueuePeekFromISR(
QueueHandle_t xQueue,
void *pvBuffer,
);
三、队列集
1.创建队列集
函数原型如下:
QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQueueLength )
参数说明:
- uxQueueLength:队列集长度,最多能存放多少个数据(队列句柄)
- 返回值:非 0:成功,返回句柄,以后使用句柄来操作队列 ;NULL:失败,因为内存不足
2.将队列传入队列集
函数原型如下:
BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet );
参数说明:
- xQueueOrSemaphore:队列句柄,这个队列要加入队列集
- xQueueSet: 队列集句柄
- 返回值:pdTRUE:成功 ;pdFALSE:失败
3.读取队列集
QueueSetMemberHandle_t xQueueSelectFromSet( QueueSetHandle_t xQueueSet, TickType_t const xTicksToWait );
参数说明:
- xQueue:队列集句柄
- xTicksToWait:如果队列集空则无法读出数据,可以让任务进入阻塞状态, xTicksToWait 表示阻塞的最大时间(Tick Count)。如果被设为 0,无 法读出数据时函数会立刻返回;如果被设为 portMAX_DELAY,则 会一直阻塞直到有数据可写
- 返回值:NULL:失败;队列句柄:成功