一、什么是消息队列
队列是不同任务、中断中数据传递的一种机制,又称消息队列。就类似于全局变量,将数据传输到不同的任务中。但全局变量没有写保护,容易造成数据受损。而消息队列中加入了进入临界区的写保护。
队列类似于数组,可以存储数量有限,大小固定的数据。队列中的每个数据叫做队列项目,能存储最大队列项目的数量交队列的长度。
二、队列的特点
1、入队出对的方式通常为先进先出,也可配置为后进先出;
2、数据的传递可以采用值传递,也可以是传递指针;
3、入队出队阻塞:当队列满了无法入队或队列空了无法出队时,称为入队和出队阻塞。可在任务向队列发送消息的时候设置阻塞时间。
如果阻塞时间为0,直接继续执行下面代码,不等待;
如果阻塞时间为0~FFFFFFFF,等待设定的时间,如果在这段时间内还无法入队/出队,则不等待;
如果阻塞时间为FFFFFFFF,一直等到可以入队/出队为止。
4、在等待(即阻塞)过程中,该任务的状态列表项被挂在到阻塞列表中(pxDelayedTaskLits),事件列表项被挂在在等待接收(xTasksWaitingToReceive)/发送列表(xTasksWaitingToSend)中;
5、多个任务写入满队列时,优先级高的优先写入,如果优先级相同,等待久的优先写入。
三、队列的相关API函数
1、队列结构体
typedef struct QueueDefinition
{
int8_t *pcHead; /*< 指向队列存储区的开头。 */
int8_t *pcTail; /*< 指向队列存储区的末尾字节。比实际需要的队列项多分配一个字节,用作标记。 */
int8_t *pcWriteTo; /*< 指向存储区的下一个可用位置。 */
union /* 使用联合体是为了确保两个互斥的结构成员不会同时出现(浪费 RAM)。 */
{
int8_t *pcReadFrom; /*< 当结构用作队列时,指向最后读取的队列项的位置。 */
UBaseType_t uxRecursiveCallCount;/*< 当结构用作递归互斥锁时,维护互斥锁递归“获取”的次数。 */
} u;
List_t xTasksWaitingToSend; /*< 阻塞在此队列上等待发送的任务列表。按优先级排序。 */
List_t xTasksWaitingToReceive; /*< 阻塞在此队列上等待读取的任务列表。按优先级排序。 */
volatile UBaseType_t uxMessagesWaiting; /*< 当前队列中的项数。 */
UBaseType_t uxLength; /*< 队列的长度,定义为它可以容纳的项数,而不是字节数。 */
UBaseType_t uxItemSize; /*< 队列将容纳的每个项的大小。 */
volatile int8_t cRxLock; /*< 存储在队列被锁定时从队列接收的项数(从队列中移除)。当队列未被锁定时,设置为 queueUNLOCKED。 */
volatile int8_t cTxLock; /*< 存储在队列被锁定时传输到队列的项数(添加到队列中)。当队列未被锁定时,设置为 queueUNLOCKED。 */
#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
uint8_t ucStaticallyAllocated; /*< 如果队列的内存是静态分配的,则设置为 pdTRUE,以确保不会尝试释放内存。 */
#endif
#if ( configUSE_QUEUE_SETS == 1 )
struct QueueDefinition *pxQueueSetContainer;
#endif
#if ( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxQueueNumber;
uint8_t ucQueueType;
#endif
} xQUEUE;
2、创建队列
函数 | 描述 |
xQueueCreate() | 动态方式创建队列 |
xQueueCreateStatic() | 静态方式创建队列 |
#define xQueueCreate( uxQueueLength, uxItemSize )
xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) )
创建的函数使用宏定义,返回值是成功返队列句柄,失败返回NULL。
创建的函数一共有三个参数。第一个是队列的长度,第二个是队列项目的大小,第三个是队列的类型,队列有如下类型:
#define queueQUEUE_TYPE_BASE ( ( uint8_t ) 0U ) /*< 基本队列类型。 */
#define queueQUEUE_TYPE_SET ( ( uint8_t ) 0U ) /*< 队列集合类型。 */
#define queueQUEUE_TYPE_MUTEX ( ( uint8_t ) 1U ) /*< 互斥锁队列类型。 */
#define queueQUEUE_TYPE_COUNTING_SEMAPHORE ( ( uint8_t ) 2U ) /*< 计数信号量队列类型。 */
#define queueQUEUE_TYPE_BINARY_SEMAPHORE ( ( uint8_t ) 3U ) /*< 二值信号量队列类型。 */
#define queueQUEUE_TYPE_RECURSIVE_MUTEX ( ( uint8_t ) 4U ) /*< 递归互斥锁队列类型。 */
3、写入消息
前四个为任务级,后四个为中断级。
函数 | 描述 |
xQueueSend() | 往队列的尾部写入消息 |
xQueueSendToBack() | 同xQueueSend() |
xQueueSendToFront() | 往队列的头部写入消息 |
xQueueOverwrite() | 覆盖队列消息(只用于队列长度为1的情况) |
xQueueSendFromISR() | 在中断中往队列的尾部写入消息 |
xQueueSendToBackFromISR() | 同xQueueSendFromISR() |
xQueueSendToFrontFromISR() | 在中断中往队列的头部写入消息 |
xQueueOverwriteFromISR() | 在中断中覆盖队列消息(只用于队列长度为1的情况) |
#define xQueueSend( xQueue, pvItemToQueue, xTicksToWait )
xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )
队列写入消息都调用的是此函数,只是第四个参数不同。
函数写入成功返回pdTRUE,写入失败返回errQUEUE_FULL。
第一个参数表示要写入的队列,第二个参数表示要写入的消息,第三个参数表示阻塞时间,第四个参数是写入方式,有下面三种:
#define queueSEND_TO_BACK ( ( BaseType_t ) 0 ) /*< 将项目发送到队列的后面。 */
#define queueSEND_TO_FRONT ( ( BaseType_t ) 1 ) /*< 将项目发送到队列的前面。 */
#define queueOVERWRITE ( ( BaseType_t ) 2 ) /*< 覆盖队列中已有的项目。 */
4、读取消息
前两个为任务级,后两个为中断级。
函数 | 描述 |
xQueueReceive() | 从队列头部读取消息,删除消息 |
xQueuePeek() | 从队列头部读取消息 |
xQueueReceiveFromISR() | 在中断中从队列头部读取消息,删除消息 |
xQueuePeekFromISR() | 在中断中从队列头部读取消息 |
#define xQueueReceive( xQueue, pvBuffer, xTicksToWait )
xQueueGenericReceive( ( xQueue ), ( pvBuffer ), ( xTicksToWait ), pdFALSE )
该函数有三个参数,分别是待读取的队列,信息读取缓冲区,阻塞时间。
读取成功返回pdTRUE,失败返回pdFALSE