消息队列
- 传输的数据不定长
- 支持先进先出FIFO,同时支持后进先出LIFO
- 均支持超时机制。
- 每个消息队列都与消息空间在同一段连续的内存空间中
- 消息队列的大小是消息队列控制块大小+(单个消息空间大小 * 消息队列长度)
- 任务或者中断服务程序都可以给消息队列发送消息
- 超时发送,超时发送失败返回 errQUEUE_FULL
- 发送紧急消息,就是放在队列头部
- 支持 消息读超时
同步互斥的方法对比
包含头文件
#include
- 消息队列运作模型
读消息的3中情况
- 有就取出,没有 扭头就走
- 没有等一会儿
- 死等消息
发送消息
队列满了就等会儿,超时了就返回errQUEUE_FULL
消息队列的内存结构
数据结构
typedef struct QueueDefinition
{
int8_t *pcHead;
int8_t *pcTail;
int8_t *pcWriteTo;
union . */
{
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;
volatile int8_t cTxLock;
#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
uint8_t ucStaticallyAllocated;
#endif
#if ( configUSE_QUEUE_SETS == 1 )
struct QueueDefinition *pxQueueSetContainer;
#endif
#if ( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxQueueNumber;
uint8_t ucQueueType;
#endif
} xQUEUE;
消息队列函数
1. 创建消息队列
#define xQueueCreate( uxQueueLength, uxItemSize ) xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) )
QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength,
UBaseType_t uxItemSize );
返回NULL,创建失败,成功返回队列句柄
枚举值 |
说明 |
queueQUEUE_TYPE_BASE |
表示队列 |
queueQUEUE_TYPE_SET |
表示队列集合 。 |
queueQUEUE_TYPE_MUTEX |
表示互斥量。 |
queueQUEUE_TYPE_COUNTING_SEMAPHORE |
表示计数信号量。 |
queueQUEUE_TYPE_BINARY_SEMAPHORE |
表示二进制信号量。 |
queueQUEUE_TYPE_RECURSIVE_MUTEX |
表示递归互斥量。 |
2. 消息队列删除函数
void vQueueDelete( QueueHandle_t xQueue )
1. 发送消息函数
- 向队列尾部发送一个队列消息
- 消息以拷贝的形式入队,而不是以引用的形式
- 该xQueueCreate函数绝对不能在中断服务程序里面被调用,
- 中断中使用有中断保护功能的 xQueueSendFromISR()来代替
1.1 发送消息到–尾部
#define xQueueSend( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )
#define xQueueSendToBack( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )
BaseType_t xQueueSend(QueueHandle_t xQueue,
const void * pvItemToQueue,
TickType_t xTicksToWait);
参数 |
说明 |
xQueue |
队列句柄 |
pvItemToQueue |
指针,指向要发送到队列尾部的队列消息 |
xTicksToWait |
队列满时,等待队列空闲的最大超时时间,超时时间的单位为系统节拍周期,portMAX_DELAY 一直等指导成功 |
返回值 |
发送消息成功返回 pdTRUE,否则返回 errQUEUE_FULL表示队列已满。 |
1.2 发送消息到–头部
#define xQueueSendToFront( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_FRONT )
BaseType_t xQueueSendToFront( QueueHandle_t xQueue,
const void * pvItemToQueue,
TickType_t xTicksToWait );
参数 |
说明 |
xQueue |
队列句柄 |
pvItemToQueue |
指针,指向要发送到队列尾部的队列消息 |
xTicksToWait |
队列满时,等待队列空闲的最大超时时间,超时时间的单位为系统节拍周期,portMAX_DELAY 一直等指导成功 |
返回值 |
发送消息成功返回 pdTRUE,否则返回 errQUEUE_FULL表示队列已满。 |
1.3 通用版本
其他的非中断发送都是使用他的宏定义实现
BaseType_t xQueueGenericSend(
QueueHandle_t xQueue,
const void * const pvItemToQueue,
TickType_t xTicksToWait,
const BaseType_t xCopyPosition ) }
参数 |
说明 |
xQueue |
队列句柄 |
pvItemToQueue |
指针,指向要发送到队列尾部的队列消息 |
xTicksToWait |
队列满时,等待队列空闲的最大超时时间,超时时间的单位为系统节拍周期,portMAX_DELAY 一直等指导成功 |
xCopyPosition |
queueSEND_TO_BACK:发送到队尾;queueSEND_TO_FRONT:发送到队头;queueOVERWRITE:以覆盖的方式发送 |
返回值 |
发送消息成功返回 pdTRUE,否则返回 errQUEUE_FULL表示队列已满。 |
2. 中断保护版本的消息发送–中断版本
只能用于中断中执行,是不带阻塞机制的
2.1 发送消息到队列头
#define xQueueSendToFrontFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_FRONT )
BaseType_t xQueueSendToFrontFromISR(QueueHandle_t xQueue,
const void *pvItemToQueue,
BaseType_t *pxHigherPriorityTaskWoken);
参数 |
说明 |
xQueue |
队列句柄 |
pvItemToQueue |
指针,指向要发送到队列尾部的队列消息 |
pxHigherPriorityTaskWoken |
如果入队导致一个任务解锁,并且解锁的任务优先级高于当前被中断的任务,将*pxHigherPriorityTaskWoken设置成 pdTRUE,然后在中断退出前需要进行一次上下文切换, 去 执 行 被 唤醒 的 优 先 级 更高 的 任 务 。从FreeRTOS V7.3.0 起,pxHigherPriorityTaskWoken 作为一个可选参数,可以设置为 NULL。 |
返回值 |
发送消息成功返回 pdTRUE,否则返回 errQUEUE_FULL表示队列已满。 |
2.2 发送消息到队列尾–中断版本
#define xQueueSendToBackFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK )
BaseType_t xQueueSendFromISR(QueueHandle_t xQueue,
const void *pvItemToQueue,
BaseType_t *pxHigherPriorityTaskWoken);
参数 |
说明 |
xQueue |
队列句柄 |
pvItemToQueue |
指针,指向要发送到队列尾部的队列消息 |
pxHigherPriorityTaskWoken |
如果入队导致一个任务解锁,并且解锁的任务优先级高于当前被中断的任务,将*pxHigherPriorityTaskWoken设置成 pdTRUE,然后在中断退出前需要进行一次上下文切换, 去 执 行 被 唤醒 的 优 先 级 更高 的 任 务 。从FreeRTOS V7.3.0 起,pxHigherPriorityTaskWoken 作为一个可选参数,可以设置为 NULL。 |
返回值 |
发送消息成功返回 pdTRUE,否则返回 errQUEUE_FULL表示队列已满。 |
2.3 中断发送的通用版本–中断版本
BaseType_t xQueueGenericSendFromISR(
QueueHandle_t xQueue,
const void *pvItemToQueue,
BaseType_t *pxHigherPriorityTaskWoken,
BaseType_t xCopyPosition
);
参数 |
说明 |
xQueue |
队列句柄 |
pvItemToQueue |
指针,指向要发送到队列尾部的队列消息 |
pxHigherPriorityTaskWoken |
如果入队导致一个任务解锁,并且解锁的任务优先级高于当前被中断的任务,将*pxHigherPriorityTaskWoken设置成 pdTRUE,然后在中断退出前需要进行一次上下文切换, 去 执 行 被 唤醒 的 优 先 级 更高 的 任 务 。从FreeRTOS V7.3.0 起,pxHigherPriorityTaskWoken 作为一个可选参数,可以设置为 NULL。 |
xCopyPosition |
queueSEND_TO_BACK:发送到队尾;queueSEND_TO_FRONT:发送到队头;queueOVERWRITE:以覆盖的方式发送 |
返回值 |
发送消息成功返回 pdTRUE,否则返回 errQUEUE_FULL表示队列已满。 |
3. 消息读取函数-普通版本
这两个函数不能用于中断,是带有阻塞机制的
移除已经获取的消息
#define xQueueReceive( xQueue, pvBuffer, xTicksToWait ) xQueueGenericReceive( ( xQueue ), ( pvBuffer ), ( xTicksToWait ), pdFALSE )
获取消息但是不移除消息
#define xQueuePeek( xQueue, pvBuffer, xTicksToWait ) xQueueGenericReceive( ( xQueue ), ( pvBuffer ), ( xTicksToWait ), pdTRUE )
通用版本的实现
BaseType_t xQueueGenericReceive(
QueueHandle_t xQueue,
void *pvBuffer,
TickType_t xTicksToWait,
BaseType_t xJustPeek,
);
参数 |
说明 |
xQueue |
队列句柄 |
pvItemToQueue |
指针,指向接收到要保存的数据。 |
xTicksToWait |
队列空时,阻塞超时的最大时间。如果该参数设置为 0,函数立刻返回。超时时间的单位为系统节拍周期,常量 portTICK_PERIOD_MS 用于辅助计算真实的时间,单位为 ms。如果 INCLUDE_vTaskSuspend 设置成 1,并且指定延时为 portMAX_DELAY 将导致任务无限阻塞(没有超时)。 |
xJustPeek |
是否移除 pdFALSE 移除; pdTRUE 不移除 |
返回值 |
队列项接收成功返回 pdTRUE,否则返回 pdFALSE。 |
4. 消息读取函数-中断版本
4.1移除已经获取的消息
BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue,
void * const pvBuffer,
BaseType_t * const pxHigherPriorityTaskWoken )
参数 |
说明 |
xQueue |
队列句柄 |
pvItemToQueue |
指针,指向接收到要保存的数据。 |
pxHigherPriorityTaskWoken |
任务在往队列投递信息时,如果队列满,则任务将阻塞在该队列上。如果 xQueueReceiveFromISR()到账了一个任 务 解 锁 了 则将 *pxHigherPriorityTaskWoken 设 置为pdTRUE , 否 则 *pxHigherPriorityTaskWoken 的 值将不变。从 FreeRTOS V7.3.0 起,pxHigherPriorityTaskWoken作为一个可选参数,可以设置为 NULL。 |
返回值 |
队列项接收成功返回 pdTRUE,否则返回 pdFALSE。 |
4.2不会把消息从该队列中移除
BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue,
void * const pvBuffer )
消息队列使用注意事项
- 使用 xQueueSend()、xQueueSendFromISR()、xQueueReceive()等这些函数之前应先创建需消息队列,并根据队列句柄进行操作。
- 队列读取采用的是先进先出(FIFO)模式,会先读取先存储在队列中的数据。当然也 FreeRTOS 也支持后进先出(LIFO)模式,那读取的时候就会读取到后进队列的数据。
- 在获取队列中的消息时候,我们必须要定义一个存储读取数据的地方,并且该数据区域大小不小于消息大小,否则,很可能引发地址非法的错误。
- 无论是发送或者是接收消息都是以拷贝的方式进行,如果消息过于庞大,可以将消息的地址作为消息进行发送、接收
消息队列的内部机制
队列的 作用
- 解决互斥问题,
- 解决cpu空转的问题
队列的核心:关中断解决互斥问题,链表实现任务的休眠和唤醒,数组实现数据的保存
typedef struct QueueDefinition
{
int8_t * pcHead;
int8_t * pcWriteTo;
union
{
QueuePointers_t xQueue;
SemaphoreData_t xSemaphore;
} u;
List_t xTasksWaitingToSend;
List_t xTasksWaitingToReceive;
volatile UBaseType_t uxMessagesWaiting;
UBaseType_t uxLength;
UBaseType_t uxItemSize;
volatile int8_t cRxLock;
volatile int8_t cTxLock;
#if ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
uint8_t ucStaticallyAllocated;
#endif
#if ( configUSE_QUEUE_SETS == 1 )
struct QueueDefinition * pxQueueSetContainer;
#endif
#if ( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxQueueNumber;
uint8_t ucQueueType;
#endif
} xQUEUE;
消息队列的初始化
- 分配内存,内存包括消息的队列头的空间和保存消息的空间,消息的空间 = 消息的个数 * 单个消息的大小
- 初始化QueueDefinition 成员
发送消息函数
接收数据函数