队列
信号量均是用队列实现的,包括二值信号量,计数信号量,互斥信号量,递归互斥信号量
队列结构体
typedef struct QueueDefinition
{
int8_t *pcHead; /* 指向队列存储区开始地址 */
int8_t *pcTail; /* 指向队列存储区末尾 */
int8_t *pcWriteTo; /* 指向存储区下一个空闲区域 */
union /* 用一个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; /* 队列上锁后,出队的队列项数量;当队列没有上锁,此字段为queueUNLOCKED */
volatile int8_t cTxLock; /* 队列上锁后,入队的队列项数量;当队列没有上锁,此字段为queueUNLOCKED */
#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
uint8_t ucStaticallyAllocated; /* 静态存储的话,设置为pdTURE */
#endif
#if ( configUSE_QUEUE_SETS == 1 ) //队列集
struct QueueDefinition *pxQueueSetContainer;
#endif
#if ( configUSE_TRACE_FACILITY == 1 ) //跟踪调试
UBaseType_t uxQueueNumber;
uint8_t ucQueueType;
#endif
} xQUEUE;
typedef xQUEUE Queue_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 );
if( uxItemSize == ( UBaseType_t ) 0 )
{
/* 队列项大小为0,不需要存储区 */
xQueueSizeInBytes = ( size_t ) 0;
}
else
{
/* 分配相应大小的存储区 */
xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize );
}
pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes );
/* 申请内存成功 */
if( pxNewQueue != NULL )
{
pucQueueStorage = ( ( uint8_t * ) pxNewQueue ) + sizeof( Queue_t );
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
{
/* 动态方法创建,这个字段赋为pdFALSE */
pxNewQueue->ucStaticallyAllocated = pdFALSE;
}
#endif /* configSUPPORT_STATIC_ALLOCATION */
/* 初始化一个新的队列,为队列中的成员变量赋值,处理发送和接收列表 */
prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue );
}
return pxNewQueue;
}
队列上锁
#define prvLockQueue( pxQueue ) \
taskENTER_CRITICAL(); \
{ \
if( ( pxQueue )->cRxLock == queueUNLOCKED ) \
{ \
( pxQueue )->cRxLock = queueLOCKED_UNMODIFIED; \
} \
if( ( pxQueue )->cTxLock == queueUNLOCKED ) \
{ \
( pxQueue )->cTxLock = queueLOCKED_UNMODIFIED; \
} \
} \
taskEXIT_CRITICAL()
将cRxLock和cTxLock设置为queueLOCKED_UNMODIFIED
队列解锁
static void prvUnlockQueue( Queue_t * const pxQueue )
{
/* 此函数必须在任务调度器挂起后调用 */
/* 上锁计数器会记录上锁期间入队或出队的队列项数量 */
/* 上锁后队列项可以加入或移出队列,但是相应的事件列表不会更新 */
/* 解锁时要更新相应的事件列表 */
taskENTER_CRITICAL();
{
int8_t cTxLock = pxQueue->cTxLock;
/* 判断数据在上锁期间是否被加入到队列中 */
while( cTxLock > queueLOCKED_UNMODIFIED )
{
/**********************************************************/
/**********************省略队列集代码************************/
/**********************************************************/
#else /* configUSE_QUEUE_SETS */
{
/* 将任务从事件列表中移除 */
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
{
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
{
/* 从列表中移除任务优先级更高,进行一次任务切换 */
vTaskMissedYield();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
break;
}
}
#endif /* configUSE_QUEUE_SETS */
--cTxLock;
}
pxQueue->cTxLock = queueUNLOCKED;
}
taskEXIT_CRITICAL();
/* 处理cRxLock */
taskENTER_CRITICAL();
{
int8_t cRxLock = pxQueue->cRxLock;
while( cRxLock > queueLOCKED_UNMODIFIED )
{
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )
{
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )
{
vTaskMissedYield();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
--cRxLock;
}
else
{
break;
}
}
pxQueue->cRxLock = queueUNLOCKED;
}
taskEXIT_CRITICAL();
}
向队列发送消息
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 ) ) );
#if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) )
{
configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
}
#endif
for( ;; )
{
/* 关中断,进入临界区 */
taskENTER_CRITICAL();
{
/* 查看队列中是否还有剩余空间,如果采用的覆写方式入队则不用在乎队列是否是满的 */
if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) )
{
traceQUEUE_SEND( pxQueue );
/* 将数据拷贝到队列中 */
xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );
/**********************************************************/
/**********************省略队列集代码************************/
/**********************************************************/
#else /* configUSE_QUEUE_SETS */
{
/* 判断是否有任务在等待队列消息而阻塞 */
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
{
/* 有的话移除任务,取消阻塞:将TCB中的事件等待列表项和状态等待列表项从当前所在列表中移除,添加任务到就绪列表 */
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();
}
}
taskEXIT_CRITICAL();
return pdPASS;
}
/* 如果队列满了 */
else
{
if( xTicksToWait == ( TickType_t ) 0 )
{
taskEXIT_CRITICAL();
/* 阻塞时间为0则直接返回 */
traceQUEUE_SEND_FAILED( pxQueue );
return errQUEUE_FULL;
}
else if( xEntryTimeSet == pdFALSE )
{
/* 阻塞时间不为0则初始化超时时间结构体 */
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 );
/* 唤醒所有,开启OS时钟计数 */
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 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 ) //如果队列中有数据
{
/* 读取数据到pvBuffer中,并更新u.pcReadFrom的值 */
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 ) //如果此队列是互斥信号量
{
/* 将pxMutexHolder标记为获取到互斥信号量的任务TCB */
/* 将任务TCB中互斥信号量的数量加一 ( pxCurrentTCB->uxMutexesHeld )++; */
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 );
/* 不删除队列项,恢复u.pcReadFrom */
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 //队列中无数据
{
if( xTicksToWait == ( TickType_t ) 0 )
{
/* 队列为空且等待时间为0,直接返回 */
taskEXIT_CRITICAL();
traceQUEUE_RECEIVE_FAILED( pxQueue );
return errQUEUE_EMPTY;
}
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();
}
}
}
}
取消事件列表任务阻塞 xTaskRemoveFromEventList
BaseType_t xTaskRemoveFromEventList( const List_t * const pxEventList )
{
TCB_t *pxUnblockedTCB;
BaseType_t xReturn;
//获取列表中第一个列表项的所属任务TCB
pxUnblockedTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxEventList );
configASSERT( pxUnblockedTCB );
/* 将要解除阻塞的任务事件列表项移出 */
( void ) uxListRemove( &( pxUnblockedTCB->xEventListItem ) );
if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) //任务调度器未被挂起
{
/* 将任务TCB的状态列表项从延时列表中移出,将其添加到就绪列表中 */
( void ) uxListRemove( &( pxUnblockedTCB->xStateListItem ) );
prvAddTaskToReadyList( pxUnblockedTCB );
}
else
{
/* 将任务事件列表项添加到等待唤醒列表中 */
vListInsertEnd( &( xPendingReadyList ), &( pxUnblockedTCB->xEventListItem ) );
}
if( pxUnblockedTCB->uxPriority > pxCurrentTCB->uxPriority )
{
/* 如果解除阻塞的优先级较现在运行的任务优先级高 */
xReturn = pdTRUE;
/* 标志一下,之后调用一次任务切换 */
xYieldPending = pdTRUE;
}
else
{
xReturn = pdFALSE;
}
#if( configUSE_TICKLESS_IDLE != 0 )
{
/* If a task is blocked on a kernel object then xNextTaskUnblockTime
might be set to the blocked task's time out time. If the task is
unblocked for a reason other than a timeout xNextTaskUnblockTime is
normally left unchanged, because it is automatically reset to a new
value when the tick count equals xNextTaskUnblockTime. However if
tickless idling is used it might be more important to enter sleep mode
at the earliest possible time - so reset xNextTaskUnblockTime here to
ensure it is updated at the earliest possible time. */
prvResetNextTaskUnblockTime();
}
#endif
return xReturn;
}