目录
FreeRTOS 队列
队列是任务跟任务之间、任务跟中断之间进行数据交流的一种机制。
队列图示如下:
任务往队列写入数据,称为入队。从队列中读取数据,称为出队。入队和出队函数的内部都调用了临界段保护函数,禁止了任务的调度,保护了任务在读写时的数据,防止多个任务同时访问队列数据造成冲突。
FreeRTOS 队列大小
队列的存放数据的空间是有限的,空间大小可以在创建队列时指定。队列空间可以看成由多个存放数据的小盒子组成,这些小盒子称为“队列项目”,队列项目的大小单位为字节,小盒子的总数就是队列长度(如下图)。在创建队列时,就要指定队列项目的大小和队列长度。
FreeRTOS 队列特点
1)队列通常采用数据存取机制是:先入队的数据先出队,也就是“先进先出”(FIFO,First in First out)。当然也可以配置成“后进先出”(LIFO,Last in First out)的方式,慢入队的数据先出队。
2)任务在入队时,是把要发送的数据/地址进行拷贝,拷贝到队列中进行存放。出队时,任务每读取一次数据/地址(即其中一个队列项目的内容被读取),则被读取的队列项目会被清空。
3)队列,是独立于任务外的一个数据结构,不属于任意一个任务,一旦被创建就独立于任务外,任何任务都可以向队列发送消息或读取消息。
4)
入队阻塞:
任务往一个已存满消息的队列发送消息时,任务会阻塞,等队列出现空位后立马退出阻塞,并写入数据。
阻塞时间由入队函数的参数 xTicksToWait 决定:
1. 为 0 时,代表直接返回不等到;
2. 为 port_MAX_DELAY 时,代表死等,一直等到可以队列有空闲位置为止;
出队阻塞:
任务从一个空的队列读取消息时,任务会阻塞,等队列中出现有可读取的消息后立马退出阻塞,并进行读取。
阻塞时间由入队函数的参数 xTicksToWait 决定:
1. 为 0 时,代表直接返回不等到;
2. 为 port_MAX_DELAY 时,代表死等,一直等到可以队列有空闲位置为止;
补充:
Q:当多个任务都往一个“满队列”写入消息时,这些任务都会进入阻塞态,多个任务在等待同一个队列的空间,队列出现空闲空间时,哪个任务优先退出阻塞进入就绪态?
A:优先级最高的任务。如果大家的优先级相同,那等待时间最久的任务会进入就绪态。
FreeRTOS 队列的使用流程
创建队列:创建队列需要调用队列创建的 API 函数,有两种创建方式,分别是动态创建和静态创建。注意,创建队列前先定义一个全局变量,用来接收队列句柄。
↓
入队:任务调用入队的 API 函数往队列中填充消息。
↓
出队:任务调用出队的 API 函数从队列中读取消息。
FreeRTOS 队列的 API 函数
1、队列创建函数
(1)动态创建
xQueueCreate ( uxQueueLength, /* 队列长度 */
uxItemSize /* 队列项目大小,单位:字节 */ )
/* 返回值:
成功返回队列句柄。
失败返回 NULL。 */
(2)静态创建
QueueHandle_t xQueueCreateStatic( UBaseType_t uxQueueLength,
UBaseType_t uxItemSize,
uint9_t * pucQueueStorageBuffer,
StaticQueue_t * pxQueueBuffer );
/* 形参:
uxQueueLength:队列长度。
uxItemSize:队列项目大小,单位为字节。
pucQueueStorageBuffer:是一个指向 uint_t 类型数组的指针。数组大小至少有 uxItiemSize 个字节。当 uxItiemSize 为0时,该参数可以为 NULL。
pxQueueBuffer:是一个指向 StaticQueue_t 类型变量的指针。变量是用来存放队列这个数据结构的。 */
/* 返回值:
成功返回 pdTRUE。
失败返回 errQUEUE_FULL。 */
动态和静态的区别:
队列所需的内存空间由 FreeRTOS 从 FreeRTOS 管理的堆中分配,而静态创建需要用户自行分配内存
2、入队函数
(1)任务级:在任务函数中调用
往队列的尾部写入消息:
BaseType_t xQueueSend( QueueHandle_t xQueue,
const void * pvItemToQueue,
TickType_t xTicksToWait );
/* 形参:
xQueue:队列句柄。
pvItemToQueue:指针,指向要发送到队列尾部的队列消息。
xTicksToWait:队列满时,等待队列空闲的最大超时时间。如果 xTicksToWait 设置为 0,则队列满时函数立即返回。如果宏 INCLUDE_vTaskSuspend 置 1,且 xTicksToWait 设置为 portMAX_DELAY,则队列满时任务进入阻塞状态,会一直死等,直到队列有空闲位置。 */
/* 返回值:
成功返回 pdTRUE。
失败返回 errQUEUE_FULL。 */
往队列的头部写入消息:
xQueueSendToBack ( xQueue,
pvItemToQueue,
xTicksToWait );
覆盖队列里的消息:
xQueueOverwrite ( xQueue,
pvItemToQueue );
(2)中断级:在中断服务函数中调用
在中断中往队列的尾部写入消息:xQueueSendFromISR( )
在中断中往队列的头部写入消息:xQueueSendToFrontFromISR( )
在中断中覆写队列消息(只用于队列长度为 1 的情况):xQueueOverwriteFromISR( )
3、出队函数
(1)任务级:在任务函数中调用
从队列头部读取一个队列项目的消息,读取完后移除消息,腾出空闲位置
BaseType_t xQueueReceive ( QueueHandle_t xQueue,
void * const pvBuffer,
TickType_t xTicksToWait )
/* 形参:
xQueue :待读取的队列。
pvBuffer :信息读取缓冲区。
xTicksToWait :阻塞超时时间。 */
/* 返回值:
读取成功返回 pdTRUE。
读取失败返回 pdFALSE。 */
从队列头部读取一个队列项目的消息,但读取成功后不移除消息
BaseType_t xQueuePeek ( QueueHandle_t xQueue,
void * const pvBuffer,
TickType_t xTicksToWait )
/* 形参:
xQueue :待读取的队列。
pvBuffer :信息读取缓冲区。
xTicksToWait :阻塞超时时间。 */
/* 返回值:
读取成功返回 pdTRUE。
读取失败返回 pdFALSE。 */
(2)中断级:在中断服务函数中调用
在中断中从队列头部读取消息,并删除消息:xQueueReceiveFromISR( )
在中断中从队列头部读取消息:xQueuePeekFromISR( )
4、队列删除函数
函数:vQueueDelete ( xQueue );
参数:xQueue 是待删除队列的句柄。
队列被删除后,这个队列的所有信息都会被系统回收清空,而且不能再次使用这个消息队列了。vQueueDelete()也可用于删除信号量。如果删除消息队列时,有任务正在等待消息,则不应 该进行删除操作。