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
/* 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(); //进入临界区
{
/* Is there room on the queue now? The running task must be the
highest priority task wanting to access the queue. If the head item
in the queue is to be overwritten then it does not matter if the
queue is full. */
if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) )
{ //如果等待进入队列的数据还有空位或者入队方式为覆写则直接写入队列
traceQUEUE_SEND( pxQueue ); //freertos里面的跟踪调试工具,用于调试
xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition ); //将消息写入队列
#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. */
queueYIELD_IF_USING_PREEMPTION(); //任务切换
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{ //不是队列集的一部分则判断是不是有任务在等待队列的消息而进入阻塞
/* If there was a task waiting for data to arrive on the
queue then unblock it now. */
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
{ //阻塞的任务挂载在xTasksWaitingToReceive列表,判断有没有阻塞的任务
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
{ //xTaskRemoveFromEventList用于将阻塞列表中的任务从xTasksWaitingToReceive中移除并把任务挂载到就绪列表中
/* The unblocked task has a priority higher than
our own so yield immediately. Yes it is ok to
do this from within the critical section - the
kernel takes care of that. */
queueYIELD_IF_USING_PREEMPTION(); //判断是不是要进行任务切换
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else if( xYieldRequired != pdFALSE ) //判断是不是要进行任务切换
{
/* This path is a special case that will only get
executed if the task was holding multiple mutexes
and the mutexes were given back in an order that is
different to that in which they were taken. */
queueYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
#else /* configUSE_QUEUE_SETS */
{ //不是队列集的一部分判断是不是有任务在等待队列消息而阻塞(与上面一样)
/* If there was a task waiting for data to arrive on the
queue then unblock it now. */
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
{
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
{
/* The unblocked task has a priority higher than
our own so yield immediately. Yes it is ok to do
this from within the critical section - the kernel
takes care of that. */
queueYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else if( xYieldRequired != pdFALSE )
{
/* This path is a special case that will only get
executed if the task was holding multiple mutexes and
the mutexes were given back in an order that is
different to that in which they were taken. */
queueYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_QUEUE_SETS */
taskEXIT_CRITICAL(); //退出临界区
return pdPASS; //表示写入完成
}
else
{ //没有位置并且不是覆写模式
if( xTicksToWait == ( TickType_t ) 0 ) //等待时间为0表示不等待
{
/* The queue was full and no block time is specified (or
the block time has expired) so leave now. */
taskEXIT_CRITICAL(); //退出临界区
/* Return to the original privilege level before exiting
the function. */
traceQUEUE_SEND_FAILED( pxQueue ); //跟踪调试
return errQUEUE_FULL; //返回队列已满
}
else if( xEntryTimeSet == pdFALSE ) //等待时间不为0并且xEntryTimeSet没有初始化
{
/* The queue was full and a block time was specified so
configure the timeout structure. */
vTaskSetTimeOutState( &xTimeOut ); //初始化xEntryTimeSet
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(); //任务调度器上锁,代码执行到这里说明队列已满而且等待时间不为0
prvLockQueue( pxQueue ); //给队列上锁
/* Update the timeout state to see if it has expired yet. */
if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) //更新时间状态并检查是否有超时发生
{
if( prvIsQueueFull( pxQueue ) != pdFALSE ) //上面的结果等于pdFALSE说明等待时间还没到,检查队列是不是满的
{
traceBLOCKING_ON_QUEUE_SEND( pxQueue ); //跟踪
vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait ); //到这里说明队列还是满的并且等待时间还没到
//将等待的任务添加到队列的xTasksWaitingToSend列表中
/* Unlocking the queue means queue events can effect the //如果等待时间为portMAX_DELAY则添加到xSuspendedTaskList列表中
event list. It is possible that interrupts occurring now
remove this task from the event list again - but as the
scheduler is suspended the task will go onto the pending
ready last instead of the actual ready list. */
prvUnlockQueue( pxQueue ); //前面的操作完成将队列解锁
/* Resuming the scheduler will move tasks from the pending
ready list into the ready list - so it is feasible that this
task is already in a ready list before it yields - in which
case the yield will not cause a context switch unless there
is also a higher priority task in the pending ready list. */
if( xTaskResumeAll() == pdFALSE ) //调用xTaskResumeAll恢复任务调度器
{
portYIELD_WITHIN_API();
}
}
else
{ //到这里说明等待时间还没到但是队列有位置了,那就再试一次
/* Try again. */
prvUnlockQueue( pxQueue );
( void ) xTaskResumeAll();
}
}
else
{ //到这里说明阻塞时间到了那么就直接解锁队列并恢复任务调度器
/* The timeout has expired. */
prvUnlockQueue( pxQueue );
( void ) xTaskResumeAll();
traceQUEUE_SEND_FAILED( pxQueue );
return errQUEUE_FULL; //直接返回队列已满
}
}
}
freeRtos中入队列最底层函数xQueueGenericSend解析
最新推荐文章于 2024-05-01 21:08:10 发布