目录
本章基于FreeRtos V9.0.0版本分析
一、消息队列简介
1.1 消息队列功能
消息队列用于任务、中断间数据通信, 支持任务与任务间、中断和任务间传递信息,消息队列是一种异步的通信方式。
1.2 消息队列特点
FreeRTOS 中使用队列数据结构实现任务异步通信工作,具有如下特性:
(1)支持先进先出(FIFO)、后进先出(LIFO)、覆盖3种管理方式。
(2)读写队列均支持超时、阻塞机制。
(3)可以允许不同长度(不超过队列节点最大值)的任意类型消息。
(4) 一个任务能从任意一个消息队列收发消息,多个任务能从同一个消息队列收发消息。
(5) 当队列使用结束后,可以删除。
1.3 消息队列阻塞
(1)接收阻塞:当队列中的消息是空时,读取消息的任务将被阻塞,可以指定阻塞的任务时间 xTicksToWait(当xTicksToWait=0xffff时表示永久阻塞,即挂起)。
(2)接收唤醒:当队列中有新消息,被阻塞的接收任务会被唤醒(阻塞态->就绪态->运行态【优先级高于当前运行任务】)并处理新消息,当等待的时间超过了指定的阻塞时间,被阻塞的接收任务会被唤醒(阻塞态->就绪态->运行态【优先级高于当前运行任务】),但是跳过消息处理。
(3)发送阻塞:当队列中的消息满员时,发送任务将被阻塞,可以指定阻塞的任务时间xTicksToWait(当xTicksToWait=0xffff时表示永久阻塞,即挂起)。
(4)发送阻塞:当队列中有空闲位置,被阻塞的发送任务会被唤醒(阻塞态->就绪态->运行态【优先级高于当前运行任务】)并发送新消息;当等待的时间超过了指定的阻塞时间,被阻塞的发送任务会被唤醒(阻塞态->就绪态->运行态【优先级高于当前运行任务】),并跳过消息发送。
1.4 消息队列结构
(1)队列控制块结构体
/*-----------------------队列结构------------------------------------*/
typedef struct QueueDefinition
{
int8_t *pcHead; /*< 【1.1】指向队列存储区域的开始地址【1.2】互斥信号时为NULL*/
int8_t *pcTail; /*< 【2.1】指向队列存储区域的结尾地址【2.2】互斥信号时指向信号持有任务控制块*/
int8_t *pcWriteTo; /*< 【3】队列写指针,指向储存区下一个空闲的位置*/
union
{
int8_t *pcReadFrom; /*< 【4.1】队列读指针*/
UBaseType_t uxRecursiveCallCount; /*<【4.2】互斥信号,递归互斥次数 */
}u;
List_t xTasksWaitingToSend; /*<【5】消息发送阻塞的任务链表。按优先级高->低顺序存储.*/
List_t xTasksWaitingToReceive;/*<【6】消息读取阻塞的任务列表。按优先级高->低顺序存储*/
volatile UBaseType_t uxMessagesWaiting;/*<【7.1】当前队员数(待读取的)*/
UBaseType_t uxLength; /*<【7.2】队列长度(最大队员数). */
UBaseType_t uxItemSize; /*<【7.2】队列中每个队员大小(bytes)*/
volatile int8_t cRxLock; /*<【8.1】接收锁,队列被锁定时从队列中接收(从队列中移除)的项数。当队列未锁定时,设置为queueUNLOCKED. */
volatile int8_t cTxLock; /*<【8.1】发送锁,队列被锁定时传输到队列(添加到队列)的项数。当队列未锁定时,设置为queueUNLOCKED。 */
/*【9】用于静态队列禁止使用释放*/
#if((configSUPPORT_STATIC_ALLOCATION==1)&&(configSUPPORT_DYNAMIC_ALLOCATION==1))
uint8_t ucStaticallyAllocated;
#endif
#if ( configUSE_QUEUE_SETS == 1 )
struct QueueDefinition *pxQueueSetContainer;/*【10】队列集合指针*/
#endif
/*【11】可视化跟踪*/
#if ( configUSE_TRACE_FACILITY == 1)
UBaseType_t uxQueueNumber; // 数目
uint8_t ucQueueType; // 类型
#endif
} xQUEUE;
typedef xQUEUE Queue_t;
(2)队列存储结结构体
1.5 消息队列类型
FreeRtos中队列部分有6种类型,其中消息队列类型为 queueQUEUE_TYPE_BASE,本章只讲解消息队列,信号类型将在后面章节进行讲解。
#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 ) // 递归互斥信号
二、消息队列创建
消息队列创建有2种方式,静态创建和动态创建,与任务创建类似。静态创建即控制块和队列数据均通过全局变量存储,不能释放的;动态创建的控制块和队列数据均为动态内存,可以生灭;本章我们仅以动态创建为例进行分析。
// 动态新建消息队列(宏定义接口)
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
#define xQueueCreate( uxQueueLength, uxItemSize ) xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) )
#endif
// 静态新建消息队列(宏定义接口)
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
#define xQueueCreateStatic( uxQueueLength, uxItemSize, pucQueueStorage, pxQueueBuffer ) xQueueGenericCreateStatic( ( uxQueueLength ), ( uxItemSize ), ( pucQueueStorage ), ( pxQueueBuffer ), ( queueQUEUE_TYPE_BASE ) )
#endif
// 静态队列创建
QueueHandle_t xQueueGenericCreateStatic(
const UBaseType_t uxQueueLength, // 队列最大长度(队员最大数目)
const UBaseType_t uxItemSize, // 单个队员大小
uint8_t *pucQueueStorage, // 指向队列首地址
StaticQueue_t *pxStaticQueue, // 指向队列控制块
const uint8_t ucQueueType ) // 队列类型
// 动态队列创建
QueueHandle_t xQueueGenericCreate(
const UBaseType_t uxQueueLength, // 队列最大长度(队员最大数目)
const UBaseType_t uxItemSize, // 单个队员大小
const uint8_t ucQueueType ) // 队列类型
2.1 源码分析
队列创建过程:
1、分配动态内存,内存长度为:队列控制块+队列(队员最大个数×每个队员bytes大小)。
2、初始化队列控制块
(1)pcHead指向队列首地址,空队列则指向控制块本身,但消息队列不会是空队列;
(2)pcTail指向队列尾地址;
(3)pcWriteTo队列写指针,指向第一个队员;
(4)pcReadFrom队列读指针,指向最后一个队员;
(5)uxMessagesWaiting当前有效队列个数,清零。
(6)uxLength, 初始化为队列最大队员数
(7)uxItemSize,初始化为每个队员大小(bytes)
(8)cRxLock/cTxLock,队列接收/发送锁,默认为解锁状态;
(9)xTasksWaitingToReceive/xTasksWaitingToSend,初始化队列发送/接收任务的阻塞链表。
(10)监视功能和队列集功能初始化,选配。
// 动态队列创建,返回队列句柄QueueHandle_t
QueueHandle_t xQueueGenericCreate(
const UBaseType_t uxQueueLength, // 队列长度
const UBaseType_t uxItemSize, // 单个队员大小
const uint8_t ucQueueType ) // 队列类型
{
Queue_t *pxNewQueue;
size_t xQueueSizeInBytes;
uint8_t *pucQueueStorage;
// 断言
configASSERT( uxQueueLength > ( UBaseType_t ) 0 );
//【1】计算队列占用存储空间
if( uxItemSize == ( UBaseType_t ) 0 )
{
xQueueSizeInBytes = ( size_t ) 0;
}
else
{
xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize );
}
// 【2】申请动态存储空间(队列控制块+队列)
pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes );
if( pxNewQueue != NULL )
{
// 【3】指向队列首地址
pucQueueStorage=((uint8_t*)pxNewQueue)+sizeof(Queue_t);
// 动静态均允许
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
{
pxNewQueue->ucStaticallyAllocated = pdFALSE;
}
#endif
// 【4】初始化队列控制块
prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue );
}
return pxNewQueue;
}
// 初始化队列控制块
static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, const uint8_t ucQueueType, Queue_t *pxNewQueue )
{
/*主要应对编译器对未使用参数的告警*/
( void ) ucQueueType;
//【1】初始化pcHead,指向队首,空队列时指向控制块。
if( uxItemSize == ( UBaseType_t ) 0 )
{
pxNewQueue->pcHead = ( int8_t * ) pxNewQueue;// 空队列 但是pcHead不能为NULL,NULL表示互斥队列
}
else
{
pxNewQueue->pcHead = ( int8_t * ) pucQueueStorage; // 指向开始区域
}
//【2】更新最大队员个数uxLength,单个队员大小uxItemSize
pxNewQueue->uxLength = uxQueueLength;
pxNewQueue->uxItemSize = uxItemSize;
//【3】队列复位
(void)xQueueGenericReset(pxNewQueue, pdTRUE);
// 【4】可视化监视
#if (configUSE_TRACE_FACILITY == 1)
{
pxNewQueue->ucQueueType = ucQueueType;
}
#endif /* configUSE_TRACE_FACILITY */
// 【5】队列集容器
#if(configUSE_QUEUE_SETS == 1)
{
pxNewQueue->pxQueueSetContainer = NULL;
}
#endif /* configUSE_QUEUE_SETS */
traceQUEUE_CREATE( pxNewQueue );
}
/*队列复位*/
BaseType_t xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue )
{
Queue_t * const pxQueue = (Queue_t *) xQueue;
configASSERT( pxQueue );
taskENTER_CRITICAL();
{
// 【1】计算队尾地址
pxQueue->pcTail = pxQueue->pcHead + ( pxQueue->uxLength * pxQueue->uxItemSize );
// 【2】当前队员个数清0
pxQueue->uxMessagesWaiting = ( UBaseType_t ) 0U;
// 【3】队列写指针指向队首
pxQueue->pcWriteTo = pxQueue->pcHead;
// 【4】队列读指针指向队尾
pxQueue->u.pcReadFrom = pxQueue->pcHead + ((pxQueue->uxLength-( UBaseType_t )1U )*pxQueue->uxItemSize);
// 【5】收发解锁状态
pxQueue->cRxLock = queueUNLOCKED;
pxQueue->cTxLock = queueUNLOCKED;
// 【6.1】不是新建的队列,释放之前阻塞的任务
if( xNewQueue == pdFALSE )
{
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )
{
if(xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )
{
queueYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
// 【6.2】是新建的队列,初始化阻塞链表
else
{
vListInitialise( &( pxQueue->xTasksWaitingToSend ) );
vListInitialise( &( pxQueue->xTasksWaitingToReceive ) );
}
}
taskEXIT_CRITICAL();
return pdPASS;
}
2.2 逻辑图分析
三、发送消息
3.1 发送消息接口
(1)系统支持从多个线程、中断发送消息,源码具备线程和中断的发送接口。
// 线程发送消息
BaseType_t xQueueGenericSend( QueueHandle_t xQueue, // 消息队列
const void * const pvItemToQueue,// 新消息
TickType_t xTicksToWait, // 阻塞延时
const BaseType_t xCopyPosition // 发送方式
)
// 中断发送消息
BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue, // 消息队列
const void * const pvItemToQueue, // 新消息
BaseType_t * const pxHigherPriorityTaskWoken,// 高优先级任务解除阻塞标志
const BaseType_t xCopyPosition ) // 发送方式
(2)消息拷贝方式有三种:
FIFO:写指针pcWriteTo拷贝队列,存储到队列的头部,消息按照发送时间顺序排列。
LIFO:读指针pcReadFrom拷贝队列,存储到队列的尾部,会优先读取;适用于紧急消息。
覆盖方式:不会出现阻塞,且队列长度必须是1,常用于信号。
#define queueSEND_TO_BACK ( ( BaseType_t ) 0 ) // 发到尾部(FIFO 从头至尾 正方向)
#define queueSEND_TO_FRONT ( ( BaseType_t ) 1 ) // 发到头部(LIFO 从尾至头 反方向)
#define queueOVERWRITE ( ( BaseType_t ) 2 ) // 覆盖(覆盖方式 )
(3)通过宏定义分别设置各种拷贝方式的消息发送接口,防止应用使用方式设置错误。
/*-----------------------------------------------------------*/
/*---------------发送消息(线程)--------------------------------*/
/*-----------------------------------------------------------*/
// 发送消息(线程 FIFO方式)
#define xQueueSendToFront( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_FRONT )
// 发送消息(线程 LIFO方式)
#define xQueueSendToBack( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )
// 发送消息(线程 标准接口 默认方式 FIFO)
#define xQueueSend( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )
// 发送消息(线程 覆盖方式)
#define xQueueOverwrite( xQueue, pvItemToQueue ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), 0, queueOVERWRITE )
/*-----------------------------------------------------------*/
/*---------------发送消息(中断)--------------------------------*/
/*-----------------------------------------------------------*/
// 发送消息(中断 FIFO方式)
#define xQueueSendToFrontFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_FRONT )
// 发送消息(中断 LIFO方式)
#define xQueueSendToBackFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK )
// 发送消息(中断 标准接口 默认方式 FIFO)
#define xQueueSendFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK )
// 发送消息(中断 覆盖方式)
#define xQueueOverwriteFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueOVERWRITE )
3.2 发送消息代码分析
3.2.1 源码分析
(1)任务、中断都可以给消息队列发送消息,如果队列未满或者允许覆盖入队, 将消息拷贝到消息队尾,消息拷贝成功后,判断链表xTasksWaitingToReceive(接收任务阻塞链表),并释放出最高优先级的任务(【阻塞态或挂起态】->【就绪态或运行态】)。
(2)如果队列满,会根据用户指定的阻塞超时时间进行阻塞,在这段时间中,如果队列一直不允许入队,该任务将保持阻塞状态以等待队列允许入队。当其它任务从其等待的队列中读取入了数据(队列未满),该任务将自动由阻塞态转为就绪态。当任务等待的时间超过了指定的阻塞时间,即使队列中还不允许入队,任务也会自动从阻塞态转移为就绪态,此时发送消息的任务或者中断程序会收到一个错误码 errQUEUE_FULL。
(3)发送紧急消息(LIFO)的过程与发送消息几乎一样,唯一的不同是,当发送紧急消息时,发送
的位置是消息队列队头而非队尾,这样,接收者就能够优先接收到紧急消息,从而及时进
行消息处理。
(4)消息队列通过收发锁cRxLock、cTxLock来防止中断和任务对任务链表的操作竞争。在发送任务处理阻塞的过程中(即队列闭锁状态),可能会有中断接收消息,导致消息队列有空闲位。此时如果又有中断发送消息,必然会成功,但是由于阻塞任务闭锁了消息发送。所以中断跳过任务链表的操作。只对cTxLock进行累加。发送任务处理完阻塞切换后,会解锁队列闭锁。根据cTxLock的计数,补偿对任务链表的处理。
// 压入队员到队列
BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition )
{
BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired;
TimeOut_t xTimeOut;
Queue_t * const pxQueue = ( Queue_t * ) xQueue;
// 断言
configASSERT( pxQueue );
configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) );
configASSERT( !( ( xCopyPosition == queueOVERWRITE ) && ( pxQueue->uxLength != 1 ) ) );
// 【1】调度器状态判断
#if ((INCLUDE_xTaskGetSchedulerState == 1)||(configUSE_TIMERS == 1))
{
// 不能满足(调度器暂停且等待时间不为0)
configASSERT(!((xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED)&&( xTicksToWait != 0)));
}
#endif
for( ;; )
{
//【1】进入临界区
taskENTER_CRITICAL();
{
// 【2】队列有空闲、或覆盖类型
if((pxQueue->uxMessagesWaiting < pxQueue->uxLength)||(xCopyPosition == queueOVERWRITE ) )
{
// 监视
traceQUEUE_SEND( pxQueue );
//【2.1】直接拷贝到队列位置
xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );
//【2.2】队列集容器处理
#if (configUSE_QUEUE_SETS == 1 )
{
//【2.2.1】队列已经添加到集容器处】
if( pxQueue->pxQueueSetContainer != NULL )
{
// 消息存到收集器,一般是用队列存储信号
if( prvNotifyQueueSetContainer( pxQueue, xCopyPosition ) != pdFALSE )
{
/*队列是队列集的成员,发送到队列集会导致更高优先级的任务被解除阻塞。需要进行上下文切换。*/
queueYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
//【2.2.1】队列没有添加到集容器处
else
{
/*如果有一个任务正在等待数据到达队列,那么现在就解除阻塞。*/
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
{
if( xTaskRemoveFromEventList(&(pxQueue->xTasksWaitingToReceive)) != pdFALSE )
{
queueYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else if( xYieldRequired != pdFALSE )
{
queueYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
#else
{
/*如果有一个任务正在等待数据到达队列,那么现在就解除阻塞。*/
if( listLIST_IS_EMPTY(&( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
{
// 移除最高优先级的任务
if(xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
{
// 强制调度
queueYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
// 有优先级继承取消(互斥信号)
else if( xYieldRequired != pdFALSE )
{
queueYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_QUEUE_SETS */
//【退出临界区】
taskEXIT_CRITICAL();
return pdPASS;
}
// 没有空闲(满了)时会阻塞
else
{
// 队列一直满,发送失败
if( xTicksToWait == ( TickType_t ) 0 )
{
/* 队列已满或没有指定阻塞时间. */
taskEXIT_CRITICAL();
traceQUEUE_SEND_FAILED( pxQueue );
return errQUEUE_FULL;
}
// 否则设置超时判断的启动器
else if( xEntryTimeSet == pdFALSE )
{
vTaskSetTimeOutState( &xTimeOut );/* 队列已满或没有指定阻塞时间.设置超时结构 */
xEntryTimeSet = pdTRUE;
}
// 时间到
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
/*中断和其他任务可以发送和接收队列,现在临界区已经退出。*/
taskEXIT_CRITICAL();
// 暂停任务
vTaskSuspendAll();
// 队列加锁
prvLockQueue( pxQueue );
/* 检查是否超期.*/
if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )
{
// 满
if( prvIsQueueFull( pxQueue ) != pdFALSE )
{
traceBLOCKING_ON_QUEUE_SEND( pxQueue );
// 添加任务到发送等待队列
vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait );
// 解锁队列
prvUnlockQueue( pxQueue );
// 出让,重启任务
if( xTaskResumeAll() == pdFALSE )
{
portYIELD_WITHIN_API();
}
}
else
{
// 解锁队列
prvUnlockQueue( pxQueue );
( void ) xTaskResumeAll();
}
}
// 时间到 超期返回失败
else
{
// 解锁队列
prvUnlockQueue( pxQueue );
( void ) xTaskResumeAll();
traceQUEUE_SEND_FAILED( pxQueue );
return errQUEUE_FULL;
}
}
}
// 中断压入队列
BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue, const void * const pvItemToQueue, BaseType_t * const pxHigherPriorityTaskWoken, const BaseType_t xCopyPosition )
{
BaseType_t xReturn;
UBaseType_t uxSavedInterruptStatus;
Queue_t * const pxQueue = ( Queue_t * ) xQueue;
configASSERT( pxQueue );
configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) );
configASSERT( !( ( xCopyPosition == queueOVERWRITE ) && ( pxQueue->uxLength != 1 ) ) );
// 验证中断优先级(禁止更高优先级)
portASSERT_IF_INTERRUPT_PRIORITY_INVALID();
//【1】临界区进入(可嵌套)
uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
{
//【2】有空闲或者覆盖模式
if((pxQueue->uxMessagesWaiting < pxQueue->uxLength)||(xCopyPosition==queueOVERWRITE))
{
// 临时记录锁定状态
const int8_t cTxLock = pxQueue->cTxLock;
traceQUEUE_SEND_FROM_ISR( pxQueue );
// 拷贝条目到队列
( void ) prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );
// 解锁状态
if( cTxLock == queueUNLOCKED )
{
// 集容器
#if ( configUSE_QUEUE_SETS == 1 )
{
if( pxQueue->pxQueueSetContainer != NULL )
{
if( prvNotifyQueueSetContainer( pxQueue, xCopyPosition ) != pdFALSE )
{
/* The queue is a member of a queue set, and posting to the queue set caused a higher priority task to unblock. A context switch is required. */
if( pxHigherPriorityTaskWoken != NULL )
{
*pxHigherPriorityTaskWoken = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
{
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
{
/* The task waiting has a higher priority so
record that a context switch is required. */
if( pxHigherPriorityTaskWoken != NULL )
{
*pxHigherPriorityTaskWoken = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
#else /* configUSE_QUEUE_SETS */
{
// 有等待接收队列的任务
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
{
// 任务"阻塞态"-->就绪态
if( xTaskRemoveFromEventList( &(pxQueue->xTasksWaitingToReceive)) != pdFALSE )
{
/*等待任务优先级更高*/
if( pxHigherPriorityTaskWoken != NULL )
{
*pxHigherPriorityTaskWoken = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_QUEUE_SETS */
}
else
{
// 增加锁计数,以便解锁队列的任务知道在它被锁定时数据被发布
pxQueue->cTxLock = ( int8_t ) ( cTxLock + 1 );
}
xReturn = pdPASS;
}
else
{
traceQUEUE_SEND_FROM_ISR_FAILED( pxQueue );
xReturn = errQUEUE_FULL;
}
}
// 释放高优先级
portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );
return xReturn;
}
3.2.2 逻辑图分析
四、接收消息
4.1 消息接收接口
(1)系统支持线程、中断接收消息,源码具备线程和中断的接收接口。
// 线程接收消息
BaseType_t xQueueGenericReceive( QueueHandle_t xQueue, // 消息队列
void * const pvBuffer, // 消息接收缓存区
TickType_t xTicksToWait, // 消息接收阻塞延时
const BaseType_t xJustPeeking ) // 消息接收完成不删除标志
// 中断发送消息(接收完成后删除)
BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue, // 消息队列
void * const pvBuffer, // 消息接收缓存区
BaseType_t * const pxHigherPriorityTaskWoken )// 高优先级任务解除阻塞标志
// 中断接收消息(接收完成后不删除)
BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, // 消息队列
void * const pvBuffer ) // 消息接收缓存区
(2)消息接收方式有2种:接受完成后删除,接收完成后不删除。
接受完成后删除:接受完成后消息个数会减1,消息队列必然有空余位置,检查发送阻塞链表,释放最高优先级阻塞任务。
接收完成后不删除:消息个数不变,消息队列此时不为空,检查发接收塞链表,释放最高优先级阻塞任务。
(3)通过宏定义分别设置各种方式的消息接收接口,防止应用使用方式设置错误。
/*-----------------------------------------------------------*/
/*---------------接收消息(线程)--------------------------------*/
/*-----------------------------------------------------------*/
// 接收消息(线程 接收完成不删除)
#define xQueuePeek( xQueue, pvBuffer, xTicksToWait ) xQueueGenericReceive( ( xQueue ), ( pvBuffer ), ( xTicksToWait ), pdTRUE )
// 接收消息(线程 标准接口 接收完成删除)
#define xQueueReceive( xQueue, pvBuffer, xTicksToWait ) xQueueGenericReceive( ( xQueue ), ( pvBuffer ), ( xTicksToWait ), pdFALSE )
/*-----------------------------------------------------------*/
/*---------------接收消息(中断)--------------------------------*/
/*-----------------------------------------------------------*/
// 接收消息(中断 标准接口 接收完成删除)
BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue, void * const pvBuffer, BaseType_t * const pxHigherPriorityTaskWoken ) ;
// 接收消息(中断 接收完成不删除)
BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, void * const pvBuffer );
4.2 接收消息源码分析
4.2.1 源码分析
(1)任务接收消息时,可指定一个阻塞超时时间,队列中有消息的时候,任务可以读取到消息。如队列为空,该任务将切至阻塞或挂起状态以等待队列数据有效。当有其它任务或中断发送消息后,该阻塞任务将自动由阻塞态转为就绪态。当任务等待的时间超过了指定的阻塞时间,即使队列中尚无有效数据,任务也会自动从阻塞态转移为就绪态。
(2)消息读取后有删除和不删除2种处理方式;选择删除处理方式,消息个数会减1,消息队列必然有空余位置,此时可以检查发送阻塞链表,如果有被阻塞的任务,则将最高优先级的一个释放。
如果选择不删除处理方式,消息个数不变,检查发送阻塞链表是无意义的;但消息队列此时不为空,所以,可检查发接收塞链表,如果有被阻塞的任务,则将最高优先级的一个释放。
(3)接收消息任务在处理阻塞的过程中,会设置接收闭锁状态(即队列闭锁状态);此时,可能会有中断发送消息,导致队列有消息。此时如果又有中断发送消息,必然会成功,但是由于队列闭锁状态。所以中断跳过任务链表的操作。只对cRxLock进行累加。当发送任务处理完阻塞切换后,会解锁队列闭锁。根据cRxLock的计数,补偿对任务链表的处理。
// 任务读取队列消息
BaseType_t xQueueGenericReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait, const BaseType_t xJustPeeking )
{
BaseType_t xEntryTimeSet = pdFALSE;
TimeOut_t xTimeOut;
int8_t *pcOriginalReadPosition;
Queue_t * const pxQueue = ( Queue_t * ) xQueue;
configASSERT( pxQueue );
configASSERT( !( ( pvBuffer == NULL ) && (pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) );
// 调度器状态监测
#if ((INCLUDE_xTaskGetSchedulerState == 1)||( configUSE_TIMERS == 1 ) )
{
configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
}
#endif
for( ;; )
{
// 操作前进入临界区
taskENTER_CRITICAL();
{
const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting;
// 有消息
if( uxMessagesWaiting > ( UBaseType_t ) 0 )
{
pcOriginalReadPosition = pxQueue->u.pcReadFrom; //消息读指针
// 只读取一个消息
prvCopyDataFromQueue( pxQueue, pvBuffer );
// 消息读取后删除
if( xJustPeeking == pdFALSE )
{
traceQUEUE_RECEIVE( pxQueue );
// 更新个数
pxQueue->uxMessagesWaiting = uxMessagesWaiting - 1;
// 互斥相关
#if ( configUSE_MUTEXES == 1 )
{
if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
{
/*如果有必要,记录实现优先级继承所需的信息。*/
// 更新持有个数和持有者
pxQueue->pxMutexHolder = ( int8_t * ) pvTaskIncrementMutexHeldCount();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_MUTEXES */
// 有等待发送的任务(即当前任务)
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )
{
// 删除一条
// 事项链表条目移除
// 任务"阻塞-->就绪"
// 上下文切换
// 因为队列接收了一条消息后,不在是满员状态,可以唤醒发送任务
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )
{
queueYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
// 不删除
else
{
traceQUEUE_PEEK( pxQueue );
/* 读指针不变 */
pxQueue->u.pcReadFrom = pcOriginalReadPosition;
/*有等待接收的任务【就是当前任务】*/
if(listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive))==pdFALSE)
{
// 删除一条
// 事项链表条目移除
// 任务"阻塞-->就绪"
// 上下文切换
// 因为队列非空状态,可以唤醒接收任务
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
{
// 调度
queueYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
taskEXIT_CRITICAL();
return pdPASS;
}
else
{
// 【3.1】没有消息,等待时间为0 ,返回空
if( xTicksToWait == ( TickType_t ) 0 )
{
taskEXIT_CRITICAL();
traceQUEUE_RECEIVE_FAILED( pxQueue );
return errQUEUE_EMPTY;
}
// 【3.1】等待时间不为0,启动任务定时器
else if( xEntryTimeSet == pdFALSE )
{
/* 启动定时器时刻. */
vTaskSetTimeOutState( &xTimeOut );
xEntryTimeSet = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
// 退出临界区
taskEXIT_CRITICAL();
/* 暂停任务*/
vTaskSuspendAll();
/* 锁队列(收发均锁)*/
prvLockQueue( pxQueue );
// 时间没到
if(xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )
{
// 无队员
if( prvIsQueueEmpty( pxQueue ) != pdFALSE )
{
traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue );
// 互斥信号 优先级继承,防止死锁
#if ( configUSE_MUTEXES == 1 )
{
if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
{
taskENTER_CRITICAL();
{
vTaskPriorityInherit( ( void * ) pxQueue->pxMutexHolder );
}
taskEXIT_CRITICAL();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif
// 事项条目更新至等待链表,任务更新至阻塞链表
vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait );
// 解除闭锁 恢复调度 上限文切换
prvUnlockQueue( pxQueue );
if( xTaskResumeAll() == pdFALSE )
{
portYIELD_WITHIN_API();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
// 有队员,解锁队列,恢复调度
prvUnlockQueue( pxQueue );
( void ) xTaskResumeAll();
}
}
// 时间到 解除阻塞,返回失败
else
{
// 时间到,无队员,解锁队列,恢复调度
prvUnlockQueue( pxQueue );
( void ) xTaskResumeAll();
// 失败
if( prvIsQueueEmpty( pxQueue ) != pdFALSE )
{
traceQUEUE_RECEIVE_FAILED( pxQueue );
return errQUEUE_EMPTY;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
}
/*-----------------------------------------------------------*/
// 中断读取队列消息
BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue, void * const pvBuffer, BaseType_t * const pxHigherPriorityTaskWoken )
{
BaseType_t xReturn;
UBaseType_t uxSavedInterruptStatus;
Queue_t * const pxQueue = ( Queue_t * ) xQueue;
configASSERT( pxQueue );
configASSERT( !( ( pvBuffer == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) );
portASSERT_IF_INTERRUPT_PRIORITY_INVALID();
// 进入临界区
uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
{
// 队员数
const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting;
// 有队员
if( uxMessagesWaiting > ( UBaseType_t ) 0 )
{
const int8_t cRxLock = pxQueue->cRxLock;
traceQUEUE_RECEIVE_FROM_ISR( pxQueue );
prvCopyDataFromQueue( pxQueue, pvBuffer );
pxQueue->uxMessagesWaiting = uxMessagesWaiting - 1;
// 消息非闭锁
if( cRxLock == queueUNLOCKED )
{
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )
{
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )
{
/* The task waiting has a higher priority than us so force a context switch. */
if( pxHigherPriorityTaskWoken != NULL )
{
*pxHigherPriorityTaskWoken = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
pxQueue->cRxLock = ( int8_t ) ( cRxLock + 1 );
}
xReturn = pdPASS;
}
else
{
xReturn = pdFAIL;
traceQUEUE_RECEIVE_FROM_ISR_FAILED( pxQueue );
}
}
// 退出临界区
portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );
return xReturn;
}
/*-----------------------------------------------------------*/
// 中断读取队列消息(读完不清除)
BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, void * const pvBuffer )
{
BaseType_t xReturn;
UBaseType_t uxSavedInterruptStatus;
int8_t *pcOriginalReadPosition;
Queue_t * const pxQueue = ( Queue_t * ) xQueue;
configASSERT( pxQueue );
configASSERT( !( ( pvBuffer == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) );
configASSERT( pxQueue->uxItemSize != 0 ); /* Can't peek a semaphore. */
portASSERT_IF_INTERRUPT_PRIORITY_INVALID();
// 嵌套进入临界区
uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
{
if( pxQueue->uxMessagesWaiting > ( UBaseType_t ) 0 )
{
traceQUEUE_PEEK_FROM_ISR( pxQueue );
pcOriginalReadPosition = pxQueue->u.pcReadFrom;
prvCopyDataFromQueue( pxQueue, pvBuffer );
pxQueue->u.pcReadFrom = pcOriginalReadPosition;
xReturn = pdPASS;
}
else
{
xReturn = pdFAIL;
traceQUEUE_PEEK_FROM_ISR_FAILED( pxQueue );
}
}
// 退出临界区
portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );
return xReturn;
}
4.2.2 逻辑图分析
五、队列集合模式【选配】
在【三、发送消息】、【四、接收消息】章节中主要解析单个消息队列的操作,队列集合模式主要是在任务、中断中接收多种类型的多个队列(也包含信号类);队列集合模式通过宏定义configUSE_QUEUE_SETS投退。
5.1 队列集合创建与队列注册
队列集合使用前需要先创建,然后将其他创建好的队列注册到队列集合中才能有效;这样在队列发送消息时,会将队列句柄发送到队列集合中。
(1) 队列创建接口:可见队列集合类型为queueQUEUE_TYPE_SET ,消息为队列句柄(即队列指针)。
// 创建队列集合 返回句柄
QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQueueLength )
{
QueueSetHandle_t pxQueue;
pxQueue = xQueueGenericCreate( uxEventQueueLength, sizeof(Queue_t*), queueQUEUE_TYPE_SET );
return pxQueue;
}
#endif /* configUSE_QUEUE_SETS */
(2) 队列注册接口:队列必须在消息个数为空(没有亟待处理的消息)时,才能注册消息集合。
// 添加队列至集合【必须是空状态才能添加】
BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet )
{
BaseType_t xReturn;
taskENTER_CRITICAL();
{
// 已添加
if((( Queue_t * ) xQueueOrSemaphore )->pxQueueSetContainer != NULL )
{
xReturn = pdFAIL;
}
// 当前未完成处理的条目
else if( ( ( Queue_t * ) xQueueOrSemaphore )->uxMessagesWaiting != ( UBaseType_t ) 0 )
{
xReturn = pdFAIL;
}
// 正常添加
else
{
( ( Queue_t * ) xQueueOrSemaphore )->pxQueueSetContainer = xQueueSet;
xReturn = pdPASS;
}
}
taskEXIT_CRITICAL();
return xReturn;
}
(3) 创建、注册、发送消息过程图:
5.2 队列集合模式下消息发送
消息发送与【三、发送消息】中类似,与但队列收发的区别在于队列集合模式下不再使用队列的接收阻塞任务链表,改用队列集合pxQueueSetContainer的接收阻塞任务链表。并且将消息队列的句柄作为队员压入消息集合。逻辑图解析参照【三、发送消息】章节。
/*-----------------------------------------------------------*/
// 向队列集合发送消息,通知队列集容器
static BaseType_t prvNotifyQueueSetContainer( const Queue_t * const pxQueue, const BaseType_t xCopyPosition )
{
Queue_t *pxQueueSetContainer = pxQueue->pxQueueSetContainer;// 收集器
BaseType_t xReturn = pdFALSE;
configASSERT( pxQueueSetContainer );
// 必须有足够的容量
configASSERT( pxQueueSetContainer->uxMessagesWaiting < pxQueueSetContainer->uxLength );
// 【1】集中器未存满
if( pxQueueSetContainer->uxMessagesWaiting < pxQueueSetContainer->uxLength )
{
const int8_t cTxLock = pxQueueSetContainer->cTxLock;
traceQUEUE_SEND( pxQueueSetContainer );
/* 【2】写入队员(队员为队列指针)*/
xReturn = prvCopyDataToQueue( pxQueueSetContainer, &pxQueue, queueSEND_TO_BACK );// 消息存至收集器
// 闭锁状态
if( cTxLock == queueUNLOCKED )
{
// 集线器处任务等待状态
if(listLIST_IS_EMPTY(&(pxQueueSetContainer->xTasksWaitingToReceive)) == pdFALSE )
{
if(xTaskRemoveFromEventList( &( pxQueueSetContainer->xTasksWaitingToReceive)) != pdFALSE )
{
/* 等待的任务优先级更高。 */
xReturn = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
pxQueueSetContainer->cTxLock = ( int8_t ) ( cTxLock + 1 );
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
return xReturn;
}
5.3 队列集合模式下消息接收
队列集合消息接收后固定删除,支持中断和任务接收,接收逻辑与【四、接收消息】中一致,只不过接收的对象是队列集和,接收的消息为队列句柄。如果接收任务阻塞,则阻塞任务事项条目被插入到队列集合的接收阻塞任务链表。
// 从队列集合读取消息(任务中,读取后删除)
QueueSetMemberHandle_t xQueueSelectFromSet( QueueSetHandle_t xQueueSet, TickType_t const xTicksToWait )
{
QueueSetMemberHandle_t xReturn = NULL;
( void ) xQueueGenericReceive( ( QueueHandle_t ) xQueueSet, &xReturn, xTicksToWait, pdFALSE );
return xReturn;
}
/*-----------------------------------------------------------*/
// 从队列集合读取消息(中断中,读取后删除)
QueueSetMemberHandle_t xQueueSelectFromSetFromISR( QueueSetHandle_t xQueueSet )
{
QueueSetMemberHandle_t xReturn = NULL;
( void ) xQueueReceiveFromISR( ( QueueHandle_t ) xQueueSet, &xReturn, NULL );
return xReturn;
}
5.4 队列集合取消注册
取消队列对队列集合的注册,即队列中队列集合指针pxQueueSetContainer 归0,但是该操作必须在队列成员为空的情况下进行,即没有等待处理的消息。
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ))
/*-----------------------------------------------------------*/
// 移除队列集和【必须是空状态才能移除】
BaseType_t xQueueRemoveFromSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet )
{
BaseType_t xReturn;
Queue_t * const pxQueueOrSemaphore = ( Queue_t * ) xQueueOrSemaphore;
if( pxQueueOrSemaphore->pxQueueSetContainer != xQueueSet )
{
xReturn = pdFAIL;
}
// 当前未完成处理的条目
else if( pxQueueOrSemaphore->uxMessagesWaiting != ( UBaseType_t ) 0 )
{
xReturn = pdFAIL;
}
else
{
taskENTER_CRITICAL();
{
pxQueueOrSemaphore->pxQueueSetContainer = NULL;
}
taskEXIT_CRITICAL();
xReturn = pdPASS;
}
return xReturn;
}
/*-----------------------------------------------------------*/
六、队列注册【选配】
队列注册就是使用一个数组管理各个队列的句柄;可以通过注册表获取队列句柄和名称。
6.1 注册表结构
/* 注册表长度 */
#define configQUEUE_REGISTRY_SIZE 10
#if ( configQUEUE_REGISTRY_SIZE > 0 )
/* 注册表结构体 */
typedef struct QUEUE_REGISTRY_ITEM
{
const char *pcQueueName;// 队列名称
QueueHandle_t xHandle; // 队列句柄
} xQueueRegistryItem;
typedef xQueueRegistryItem QueueRegistryItem_t;
/* 注册表数组 */
PRIVILEGED_DATA QueueRegistryItem_t xQueueRegistry[ configQUEUE_REGISTRY_SIZE ];
#endif
6.2 注册表接口
/*-----------------注册队列----------------------------------*/
void vQueueAddToRegistry( QueueHandle_t xQueue, const char *pcQueueName )
{
UBaseType_t ux;
/* 看看注册表中是否有空白区域。 NULL名称表示空闲槽位. */
for( ux = ( UBaseType_t ) 0U; ux < ( UBaseType_t ) configQUEUE_REGISTRY_SIZE; ux++ )
{
/* 如果队列没有在注册表中,则替换现有条目. */
if( xQueueRegistry[ ux ].pcQueueName == NULL )
{
xQueueRegistry[ ux ].pcQueueName = pcQueueName;
xQueueRegistry[ ux ].xHandle = xQueue;
traceQUEUE_REGISTRY_ADD( xQueue, pcQueueName );
break;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
/*------------- 获取消息队列的名字(根据句柄)----------------------*/
//
const char *pcQueueGetName( QueueHandle_t xQueue )
{
UBaseType_t ux;
const char *pcReturn = NULL;
for( ux = ( UBaseType_t ) 0U; ux < ( UBaseType_t ) configQUEUE_REGISTRY_SIZE; ux++ )
{
if( xQueueRegistry[ ux ].xHandle == xQueue )
{
pcReturn = xQueueRegistry[ ux ].pcQueueName;
break;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
return pcReturn;
}
/*----------------取消注册(根据句柄)--------------------------------*/
void vQueueUnregisterQueue( QueueHandle_t xQueue )
{
UBaseType_t ux;
for( ux = ( UBaseType_t ) 0U; ux < ( UBaseType_t ) configQUEUE_REGISTRY_SIZE; ux++ )
{
if( xQueueRegistry[ ux ].xHandle == xQueue )
{
xQueueRegistry[ ux ].pcQueueName = NULL;
xQueueRegistry[ ux ].xHandle = ( QueueHandle_t ) 0;
break;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
七、队列删除
队列的删除比较简单,一是取消注册,二是将动态内存释放,但是删除队列前先确认队列为空,防止丢消息。
void vQueueDelete( QueueHandle_t xQueue )
{
Queue_t * const pxQueue = ( Queue_t * ) xQueue;
configASSERT( pxQueue );
traceQUEUE_DELETE( pxQueue );
//【1】取消注册
#if ( configQUEUE_REGISTRY_SIZE > 0 )
{
vQueueUnregisterQueue( pxQueue );
}
#endif
//【2.1】动态消息队列,释放内存
#if(( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ))
{
vPortFree( pxQueue );
}
//【2.2】动静态
#elif(( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ))
{
if( pxQueue->ucStaticallyAllocated == ( uint8_t ) pdFALSE )
{
vPortFree( pxQueue );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#else
{
( void ) pxQueue;
}
#endif /* configSUPPORT_DYNAMIC_ALLOCATION */
}