1、现实生活中的类比:
(可能有点不到位的地方,希望大家可以纠正)
假设你开着共享车现在需要去停车场停车,这时候是不是有两种情况:
a、停车场有位置
b、停车场没有位置
a情况:
如果停车场有位置,你又想停车,这种情况是最简单的,也是最舒服的,直接停车啥也不管。
b情况:
在b情况下我们也有三种选择
1、不等直接走
2、等一会
3、往死里等
到这里我们就算是明白了大概的逻辑流程下面我们将进行代码的分析。
2、代码分析
2.1函数原型
参数1:是队列句柄——意思是要将数据发送到那个队列中
参数2:向队列发送的原始数据——就是你要发的内容是啥
参数3:发送等待时间——告诉RTOS你这个发送需要要不要等待,等待的时间是多少
参数4:写入队列中的方式——就是写在队头还是写在队尾还是占领别人的位置
2.2代码内容,分析在代码段中
a:变量的含义:
//xEntryTimeSet在后面用来获取当前溢出时间和当前计数值的标志为。
//xYieldRequired在后面用于接收 prvCopyDataToQueue函数的返回值
BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired;
//存放当前溢出时间和当前计数值的变量
TimeOut_t xTimeOut;
Queue_t * const pxQueue = ( Queue_t * ) xQueue;
b、对理解暂时没有用的代码:
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
c、核心代码段分析:
//死循环
for( ;; )
{
//进入临界区防止下面执行的代码受到其他任务和中断的干扰
taskENTER_CRITICAL();
{
//判断队列中是否还有空位或者是发送方法位覆写——也就是我上述逻辑
//中进入停车场面临的情况是有位置还是没有位置
if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) )
{
//运行到这里说明有位置
traceQUEUE_SEND( pxQueue );
//直接停车——将要写入的数据复制到队列中(采用对应的方法)
xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );
//============================以下内容暂时不分析======================================//
// #if ( configUSE_QUEUE_SETS == 1 )
{
// if( pxQueue->pxQueueSetContainer != NULL )
// {
if( prvNotifyQueueSetContainer( pxQueue, xCopyPosition ) != 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();
// }
// }
// }
//============================以上内容暂时不分析======================================//
#else /* configUSE_QUEUE_SETS */
{
//在我们还车后的操作——(看看有没有人需要该停车场的车,
//如果有很多人需要则选择一个有钱的(优先级高的,等待时间长的))
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 */
//退出临界区,上面的for循环第一句就是进入临界区
taskEXIT_CRITICAL();
//返回执行结果pdPASS(这是在停车场有空位的情况下)
return pdPASS;
}
//以下是停车场没有空位的情况
else
{ //如果没有等待时间则退出临界区,返回errQUEUE_FULL,
//表示队列满了,就是停车场没有位置并且我不想等
if( xTicksToWait == ( TickType_t ) 0 )
{ //退出临界区,上面的for循环第一句就是进入临界区
taskEXIT_CRITICAL();
traceQUEUE_SEND_FAILED( pxQueue );
return errQUEUE_FULL;
}
//该else if是获取想等的当时时间,比如时19:18想等车,
//等30分钟,该else if就是获取19:18的时间
//xYieldRequired开头声明的变量初始值为pdFALSE
else if( xEntryTimeSet == pdFALSE )
{
//该函数是获取当前的计数超时次数于当前的计数次数
vTaskSetTimeOutState( &xTimeOut );
//将xEntryTimeSet赋值为pdTRUE
//确保只运行一次vTaskSetTimeOutState,
//也就是只记录你当时想等停车位的时间+你想等多久
//如果到了就退出不等待了,
//除非等待时间等于portMAX_DELAY
xEntryTimeSet = pdTRUE;
}
else
{
//未实现不用管
mtCOVERAGE_TEST_MARKER();
}
}
}
//退出临界区,上面的for循环第一句就是进入临界区
taskEXIT_CRITICAL();
//以下是没有车位想等的情况————有两种等,
//一种死等,一种等一段时间
//挂起任务调度器
vTaskSuspendAll();
//上锁该队列
prvLockQueue( pxQueue );
//判断是否到达了预定的时间,比如19:18开始等,等30分钟
//如果到了19:48以后则不等了。如果是死等则要等到有空位置为止
//xTaskCheckForTimeOut该函数是将时间节点和等待时间作为参数进行传递
//如果运行该函数则会重新获取时间节点和等待时间。
//比如我一开始的时间节点为19:18,等待30分钟
//运行该函数时时间节点为19:38,则修改一开始的时间节点为19:38,
//并且将等待时间改为10分钟
if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )
{
//判断队列是否位满状态
if( prvIsQueueFull( pxQueue ) != pdFALSE )
{
//运行到这说明为满状态
traceBLOCKING_ON_QUEUE_SEND( pxQueue );
//将任务添加到发送等待列表中,值为xTicksToWait
vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait );
//队列解锁
prvUnlockQueue( pxQueue );
//恢复任务调度器
if( xTaskResumeAll() == pdFALSE )
{
portYIELD_WITHIN_API();
}
}
//如果时间没有到或者死等,则循环到第一条语句再次执行
else
{
/* Try again. */
prvUnlockQueue( pxQueue );
( void ) xTaskResumeAll();
}
}
//等待时间已过,但是还是没有位置,
else
{
//队列解锁,恢复任务调度器,
prvUnlockQueue( pxQueue );
( void ) xTaskResumeAll();
//则返回errQUEUE_FULL
traceQUEUE_SEND_FAILED( pxQueue );
return errQUEUE_FULL;
}
}