一、消息队列的基本概念
1、产生的原因:
由于使用全局变量处理数据时并不是很安全的,于是产生了消息队列。消息队列可以理解为带中断保护的全局数组,它是用来存放数据的,我们将其设计为队列的结构体。
下图是队列接受和发送数据的框图:
下图是写队列和读队列的函数内部:带有中断保护,管理范围内的终端不会再响应,并且不会进行任务切换(PendSV任务切换中断也被关闭)
2、定义:
消息队列是任务到任务、任务到中断、中断到任务数据交流的一种机制(进行消息传递)FreeRTOS基于队列, 实现了多种功能,其中包括队列集、互斥信号量、计数型信号量、二值信号量、 递归互斥信号量,因此很有必要深入了解 FreeRTOS 的队列 。在消息队列中可以存储数量有限、大小固定的数据,可以将队列理解为一个全局数组。队列中的每一个数据叫做“队列项目”,队列能够存储“队列项目”的最大数量称为队列的长度。【简单的说:队列就是任务之间或者任务与中断时间传递消息和数据的的载体。】
如下图所示:队列长度和队列项目大小需要用户设定,然后会根据设定的分配内存空间。
3、FreeRtos的队列特点:
用户可以设置阻塞时间:
若阻塞时间为0:直接返回不会等待
若阻塞时间为0~port_MAX_DELAY (最大值0xffffffff):等待设定的阻塞时间(一段时间内阻塞),若在该时间内还无法入队,超时后直接返回不再等待
若阻塞时间为port_MAX_DELAY (最大值0xffffffff):代表死等,一直等到入列为止
4、队列的入队和出队:
队列满了,此时写不进去数据:
①将该任务的状态列表项挂载在pxDelayedTaskList(阻塞状态);
②将该任务的事件列表项挂载在xTasksWaitingToSend(等待发送);
队列为空,此时读不出数据:
①将该任务的状态列表项挂载在pxDelayedTaskList(阻塞状态);
②将该任务的事件列表项挂载在xTasksWaitingToReceive(等待接收);
若多个任务在写入消息给一个“满队列”时,这些队列全都处于阻塞状态,当有队列空间时,那些任务会被写入,那些任务会进入就绪态:
①优先级最高的任务
②优先级相同时,阻塞时间最长的任务进入就绪态
5、队列的基本操作:
FIFO先进先出
二、队列的结构体
1、队列结构体:
里面有一个联合体,我们知道后面学的各种信号量都是基于队列实现的,那这个联合体就根据不同的结构,使用不同的成员。
联合体的使用如下:
队列结构体整体示意图:主要有两部分组成:队列结构体/队列控制块和队列项(存储数据的)构成。
三、队列相关的API函数
1、使用队列的主要流程:
创建队列 -> 写队列 ->读队列
2、创建队列的API函数:
①动态创建:自动分配堆栈空间
函数:#define xQueueCreate(xQueueLength, uxItemSize)
本质:xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), (queueQUEUE_TYPE_BASE ))
形参:xQueueLength表示队列长度,uxItemSize表示队列项目的大小(字节为单位)
②静态创建:用户分配堆栈空间
③FreeRtos基于队列实现了很多功能,每一种功能对应一种类型:
3、写入消息的API函数
写队列的所有函数:
写队列函数的形参:
从上图可见每个写入的函数都是使用的xQueueGenericSend( ),但是写入的位置不同。
写入消息入口参数:
xQueueGenericSend(QueueHandle_t xQueue,
const void *const pvltemToQueue,
TickType_t xTicksToWait,
const BaseType_t xCopyPosition);
4、读取消息的API函数:
读队列的所有函数:
①xQueueReceive( )函数:
②xQueuePeek( )函数: