Freertos消息队列接收源码xQueueGenericReceive分析

来源:互联网 发布:app store付费软件 编辑:程序博客网 时间:2024/06/05 15:17

Freertos消息队列接收源码xQueueGenericReceive分析

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


/* This function relaxes the coding standard somewhat to allow return
statements within the function itself.  This is done in the interest
of execution time efficiency. */


for( ;; )
{
taskENTER_CRITICAL();
{
const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting;//获取有效消息条数


/* Is there data in the queue now?  To be running the calling task
must be the highest priority task wanting to access the queue. */
if( uxMessagesWaiting > ( UBaseType_t ) 0 )//有有效消息
{
/* Remember the read position in case the queue is only being
peeked. */
pcOriginalReadPosition = pxQueue->u.pcReadFrom;//拿初始读取位置


prvCopyDataFromQueue( pxQueue, pvBuffer );//从队列中拷贝数据


if( xJustPeeking == pdFALSE )/*需要不需要删除队列中接收到的信息,false是需要删除,true是不需要删除*/
{
traceQUEUE_RECEIVE( pxQueue );


/* Actually removing data, not just peeking. */
pxQueue->uxMessagesWaiting = uxMessagesWaiting - 1;/*目前队列中的有效items数减少一个*/


#if ( configUSE_MUTEXES == 1 )
{
if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
{
/* Record the information required to implement
priority inheritance should it become necessary. */
//申请互斥锁,互斥锁资源占用计数值uxMutexesHeld加1
pxQueue->pxMutexHolder = ( int8_t * ) pvTaskIncrementMutexHeldCount(); /*lint !e961 Cast is not redundant as TaskHandle_t is a typedef. */
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_MUTEXES */


if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )//队列中有列表项因等待发送而排队
{
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )
//从对应事件表或状态表删除并加入就绪表或挂起就绪表,并酌情调度,有tickless的系统,还要刷新最新任务解锁时间
{
queueYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
traceQUEUE_PEEK( pxQueue );


/* The data is not being removed, so reset the read
pointer. */
pxQueue->u.pcReadFrom = pcOriginalReadPosition;//拷贝完数据后,队列读取位置恢复为进入该函数时的初始位置


/* The data is being left in the queue, so see if there are
any other tasks waiting for the data. */
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
//由于数据并没有被拿走,所以如果有任务还在请求队列数据的,仍然可以拿数据
{
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
//从对应事件表或状态表删除并加入就绪表或挂起就绪表,并酌情调度,有tickless的系统,还要刷新最新任务解锁时间
{
/* The task waiting has a higher priority than this task. */
queueYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}


taskEXIT_CRITICAL();
return pdPASS;
}
else //没有有效消息
{
if( xTicksToWait == ( TickType_t ) 0 )//不设置超时,直接返回队列空错误
{
/* The queue was empty and no block time is specified (or
the block time has expired) so leave now. */
taskEXIT_CRITICAL();
traceQUEUE_RECEIVE_FAILED( pxQueue );
return errQUEUE_EMPTY;
}
else if( xEntryTimeSet == pdFALSE )
{
/* The queue was empty and a block time was specified so
configure the timeout structure. */
vTaskSetTimeOutState( &xTimeOut );//记录当前系统节拍溢出次数和当前节拍数
xEntryTimeSet = pdTRUE;
}
else
{
/* Entry time was already set. */
mtCOVERAGE_TEST_MARKER();
}
}
}
taskEXIT_CRITICAL();


/* Interrupts and other tasks can send to and receive from the queue
now the critical section has been exited. */


vTaskSuspendAll();//挂起调度
prvLockQueue( pxQueue );//开读写事务锁


/* Update the timeout state to see if it has expired yet. */
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
{
/* Try again. */
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 ) ) );


/* RTOS ports that support interrupt nesting have the concept of a maximum
system call (or maximum API call) interrupt priority.  Interrupts that are
above the maximum system call priority are kept permanently enabled, even
when the RTOS kernel is in a critical section, but cannot make any calls to
FreeRTOS API functions.  If configASSERT() is defined in FreeRTOSConfig.h
then portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion
failure if a FreeRTOS API function is called from an interrupt that has been
assigned a priority above the configured maximum system call priority.
Only FreeRTOS functions that end in FromISR can be called from interrupts
that have been assigned a priority at or (logically) below the maximum
system call interrupt priority.  FreeRTOS maintains a separate interrupt
safe API to ensure interrupt entry is as fast and as simple as possible.
More information (albeit Cortex-M specific) is provided on the following
link: http://www.freertos.org/RTOS-Cortex-M3-M4.html */
portASSERT_IF_INTERRUPT_PRIORITY_INVALID();


uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
{
const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting;


/* Cannot block in an ISR, so check there is data available. */
if( uxMessagesWaiting > ( UBaseType_t ) 0 )
{
const int8_t cRxLock = pxQueue->cRxLock;


traceQUEUE_RECEIVE_FROM_ISR( pxQueue );


prvCopyDataFromQueue( pxQueue, pvBuffer );
pxQueue->uxMessagesWaiting = uxMessagesWaiting - 1;


/* If the queue is locked the event list will not be modified.
Instead update the lock count so the task that unlocks the queue
will know that an ISR has removed data while the queue was
locked. */
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
{
/* Increment the lock count so the task that unlocks the queue
knows that data was removed while it was locked. */
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;
}
/*-----------------------------------------------------------*/




static void prvCopyDataFromQueue( Queue_t * const pxQueue, void * const pvBuffer )//从队列中拷贝数据
{
if( pxQueue->uxItemSize != ( UBaseType_t ) 0 )//数据项长度为0不拷贝
{
pxQueue->u.pcReadFrom += pxQueue->uxItemSize;//后移一个列表项
if( pxQueue->u.pcReadFrom >= pxQueue->pcTail ) /*lint !e946 MISRA exception justified as use of the relational operator is the cleanest solutions. */
{
pxQueue->u.pcReadFrom = pxQueue->pcHead;//循环队列
}
else
{
mtCOVERAGE_TEST_MARKER();
}
//读取实际的数据
( void ) memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->u.pcReadFrom, ( size_t ) pxQueue->uxItemSize ); /*lint !e961 !e418 MISRA exception as the casts are only redundant for some ports.  Also previous logic ensures a null pointer can only be passed to memcpy() when the count is 0. */
}
}


BaseType_t xTaskRemoveFromEventList( const List_t * const pxEventList )//从事件表删除任务项
{
TCB_t *pxUnblockedTCB;
BaseType_t xReturn;


/* THIS FUNCTION MUST BE CALLED FROM A CRITICAL SECTION.  It can also be
called from a critical section within an ISR. */


/* The event list is sorted in priority order, so the first in the list can
be removed as it is known to be the highest priority.  Remove the TCB from
the delayed list, and add it to the ready list.


If an event is for a queue that is locked then this function will never
get called - the lock count on the queue will get modified instead.  This
means exclusive access to the event list is guaranteed here.


This function assumes that a check has already been made to ensure that
pxEventList is not empty. */
pxUnblockedTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxEventList );//从事件表头获取任务
configASSERT( pxUnblockedTCB );
( void ) uxListRemove( &( pxUnblockedTCB->xEventListItem ) );//删除该任务在事件表中的列表项


if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )//没有挂起调度器
{
( void ) uxListRemove( &( pxUnblockedTCB->xStateListItem ) );//删除该任务在状态表中的列表项并导入就绪表
prvAddTaskToReadyList( pxUnblockedTCB );
}
else
{
/* The delayed and ready lists cannot be accessed, so hold this task
pending until the scheduler is resumed. */
vListInsertEnd( &( xPendingReadyList ), &( pxUnblockedTCB->xEventListItem ) );//若挂起了调度器,就将任务的事件列表项插入挂起就绪表
}


if( pxUnblockedTCB->uxPriority > pxCurrentTCB->uxPriority )//最高就绪优先级发生变更,需要请求任务调度
{
/* Return true if the task removed from the event list has a higher
priority than the calling task.  This allows the calling task to know if
it should force a context switch now. */
xReturn = pdTRUE;


/* Mark that a yield is pending in case the user is not using the
"xHigherPriorityTaskWoken" parameter to an ISR safe FreeRTOS function. */
xYieldPending = pdTRUE;
}
else
{
xReturn = pdFALSE;
}


#if( configUSE_TICKLESS_IDLE != 0 )//由于事件列表发生变更,有可能导致任务从延时表中剥离,tickless模式下需要刷新最新任务解锁时间
{
/* 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;
}






BaseType_t xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, TickType_t * const pxTicksToWait )//检查任务超时
{
BaseType_t xReturn;


configASSERT( pxTimeOut );
configASSERT( pxTicksToWait );


taskENTER_CRITICAL();
{
/* Minor optimisation.  The tick count cannot change in this block. */
const TickType_t xConstTickCount = xTickCount;//记录当前系统时钟节拍


#if( INCLUDE_xTaskAbortDelay == 1 )//如果终止延时被使能,直接返回TRUE,表示超时
if( pxCurrentTCB->ucDelayAborted != pdFALSE )
{
/* The delay was aborted, which is not the same as a time out,
but has the same result. */
pxCurrentTCB->ucDelayAborted = pdFALSE;
xReturn = pdTRUE;
}
else
#endif


#if ( INCLUDE_vTaskSuspend == 1 )
if( *pxTicksToWait == portMAX_DELAY )//如果延时值为最大值,即死等,直接返回FALSE
{
/* If INCLUDE_vTaskSuspend is set to 1 and the block time
specified is the maximum block time then the task should block
indefinitely, and therefore never time out. */
xReturn = pdFALSE;
}
else
#endif
//当前溢出次数不等于进入点溢出次数,且当前节拍依然大于进入点节拍,则延时已超过最大值portMAX_DELAY,直接返回TRUE
if( ( xNumOfOverflows != pxTimeOut->xOverflowCount ) && ( xConstTickCount >= pxTimeOut->xTimeOnEntering ) ) /*lint !e525 Indentation preferred as is to make code within pre-processor directives clearer. */
{
/* The tick count is greater than the time at which
vTaskSetTimeout() was called, but has also overflowed since
vTaskSetTimeOut() was called.  It must have wrapped all the way
around and gone past again. This passed since vTaskSetTimeout()
was called. */
xReturn = pdTRUE;
}
//超时时间未到返回FALSE
else if( ( ( TickType_t ) ( xConstTickCount - pxTimeOut->xTimeOnEntering ) ) < *pxTicksToWait ) /*lint !e961 Explicit casting is only redundant with some compilers, whereas others require it to prevent integer conversion errors. */
{
/* Not a genuine timeout. Adjust parameters for time remaining. */
*pxTicksToWait -= ( xConstTickCount - pxTimeOut->xTimeOnEntering );
vTaskSetTimeOutState( pxTimeOut );//由于更新了等待时间pxTicksToWait,需要更新等待初始点为当前点
xReturn = pdFALSE;
}
else
{
xReturn = pdTRUE;
}
}
taskEXIT_CRITICAL();


return xReturn;
}




#if ( configUSE_MUTEXES == 1 )


void vTaskPriorityInherit( TaskHandle_t const pxMutexHolder )//优先级反转
{
TCB_t * const pxTCB = ( TCB_t * ) pxMutexHolder;


/* If the mutex was given back by an interrupt while the queue was
locked then the mutex holder might now be NULL. */
if( pxMutexHolder != NULL )
//是否有任务获取了当前互斥信号量,该变量用于存储获取了该互斥信号量的任务控制块
{
/* If the holder of the mutex has a priority below the priority of
the task attempting to obtain the mutex then it will temporarily
inherit the priority of the task attempting to obtain the mutex. */
if( pxTCB->uxPriority < pxCurrentTCB->uxPriority )
//获取了该互斥信号量的任务的优先级低于当前任务优先级就进行优先级反转
{
/* Adjust the mutex holder state to account for its new
priority.  Only reset the event list item value if the value is
not being used for anything else. */
//获取占用信号量的任务的事件列表项值,并使用当前任务优先级更新它
//在该列表项值没有被使用于其他用途时才更新(如用于pxCoRoutine,或记录任务控制块地址)
if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL )
{
listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxCurrentTCB->uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
}
else
{
mtCOVERAGE_TEST_MARKER();
}


/* If the task being modified is in the ready state it will need
to be moved into a new list. */
//检查占有信号量的任务控制块的状态列表项pvContainer,看其是否归属于就绪表,即其是否处于就绪态
if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ pxTCB->uxPriority ] ), &( pxTCB->xStateListItem ) ) != pdFALSE )
{
if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )//若是则从就绪表移除
{
//若移除后该优先级在就绪表中无任务,则重置该优先级就绪标志
taskRESET_READY_PRIORITY( pxTCB->uxPriority );
}
else
{
mtCOVERAGE_TEST_MARKER();
}


/* Inherit the priority before being moved into the new list. */
pxTCB->uxPriority = pxCurrentTCB->uxPriority;//提升优先级并加入就绪表
prvAddTaskToReadyList( pxTCB );
}
else
{
/* Just inherit the priority. */
pxTCB->uxPriority = pxCurrentTCB->uxPriority;//占用信号量的任务不是就绪态,则仅更新其优先级
//任务基优先级uxBasePriority,仅在prvInitialiseNewTask,vTaskPrioritySet,vTaskGetInfo中赋值,
//该变量用来保存任务在创建时登记的优先级,即任务的原始优先级
}


traceTASK_PRIORITY_INHERIT( pxTCB, pxCurrentTCB->uxPriority );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}


#endif /* configUSE_MUTEXES */
原创粉丝点击