出队操作类似于入队的操作。先整明白入队的操作,这个就好说了。
分析:
1. QueueRecv和QueuePeek都是xQueueGenericReceive实现的 2. xQueueReceiveFromISR 3. xQueuePeekFromISR
函数在读取消息的时候是采用拷贝方式的,所以用户需要提供一个数组或缓冲区来保存读取到的数据,
所读取的数据长度是创建队列的时候所设定的每个队列项目的长度
BaseType_t xQueueReceive( QueueHandle_t xQueue, void * pvBuffer, TickType_t xTicksToWait );
BaseType_t xQueuePeek ( QueueHandle_t xQueue, void * pvBuffer, TickType_t xTicksToWait );
他们都用函数
xQueueGenericReceive( ) 来实现。
BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue, void * pvBuffer, BaseType_t * pxTaskWoken );
BaseType_t xQueuePeekFromISR ( QueueHandle_t xQueue, void * pvBuffer );
他们是各自实现的。。。
下面先分析xQueueGenericReceive() :
1 BaseType_t xQueueGenericReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait, const BaseType_t xJustPeeking ) 2 { 3 BaseType_t xEntryTimeSet = pdFALSE; 4 TimeOut_t xTimeOut; 5 int8_t *pcOriginalReadPosition; 6 Queue_t * const pxQueue = ( Queue_t * ) xQueue; 7 8 configASSERT( pxQueue ); 9 configASSERT( !( ( pvBuffer == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) ); 10 #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) 11 { 12 configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) ); 13 } 14 #endif 15 16 /* This function relaxes the coding standard somewhat to allow return 17 statements within the function itself. This is done in the interest 18 of execution time efficiency. 为了效率,直接在函数内返回*/ 20 for( ;; ) 21 { 22 taskENTER_CRITICAL(); 23 { 24 const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting; //队列中item个数 25 26 /* Is there data in the queue now? To be running the calling task 27 must be the highest priority task wanting to access the queue. */ 28 if( uxMessagesWaiting > ( UBaseType_t ) 0 ) //队列中有item 29 { 30 /* Remember the read position in case the queue is only being 31 peeked. */ 32 pcOriginalReadPosition = pxQueue->u.pcReadFrom; //记录队列的读指针 33 34 prvCopyDataFromQueue( pxQueue, pvBuffer ); //<-- 从队列拷贝出数据 ##3 见后 35 36 if( xJustPeeking == pdFALSE ) 【不是peek的方法】 37 { 38 traceQUEUE_RECEIVE( pxQueue ); 39 40 /* Actually removing data, not just peeking. */ 41 pxQueue->uxMessagesWaiting = uxMessagesWaiting - 1; //<-- 队列中item个数 减一 42 43 #if ( configUSE_MUTEXES == 1 ) //不关注是否是mutex
56 #endif /* configUSE_MUTEXES */
//有任务在等待发送消息,此时队列有空间了,可以发送 58 if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) 59 { 60 if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) 61 { 62 queueYIELD_IF_USING_PREEMPTION(); //置位PendSV 63 } 64 else 65 { 66 mtCOVERAGE_TEST_MARKER(); 67 } 68 } 69 else 70 { 71 mtCOVERAGE_TEST_MARKER(); 72 } 73 } 74 else 【peek】 75 { 76 traceQUEUE_PEEK( pxQueue ); 77 78 /* The data is not being removed, so reset the read 79 pointer. */ 80 pxQueue->u.pcReadFrom = pcOriginalReadPosition; 81 82 /* The data is being left in the queue, so see if there are 83 any other tasks waiting for the data.
peek之后的消息仍在队列中,所以看一下是否有任务在等待接收消息*/ 84 if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) 85 { 86 if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) 87 { 88 /* The task waiting has a higher priority than this task. */ 89 queueYIELD_IF_USING_PREEMPTION(); //置位PendSV 90 } 91 else 92 { 93 mtCOVERAGE_TEST_MARKER(); 94 } 95 } 96 else 97 { 98 mtCOVERAGE_TEST_MARKER(); 99 } 100 }/* end of peek? */ 101 102 taskEXIT_CRITICAL(); 103 return pdPASS; 104 } 105 else 【queue has no data】 106 { 107 if( xTicksToWait == ( TickType_t ) 0 ) 108 { 109 /* The queue was empty and no block time is specified (or 110 the block time has expired) so leave now. */ 111 taskEXIT_CRITICAL(); 112 traceQUEUE_RECEIVE_FAILED( pxQueue ); 113 return errQUEUE_EMPTY; //直接返回err 114 } 115 else if( xEntryTimeSet == pdFALSE ) 116 { 117 /* The queue was empty and a block time was specified so 118 configure the timeout structure. */ 119 vTaskSetTimeOutState( &xTimeOut ); //初始化时间结构体 120 xEntryTimeSet = pdTRUE; 121 } 122 else 123 { 124 /* Entry time was already set. */ 125 mtCOVERAGE_TEST_MARKER(); 126 } 127 } 128 } 129 taskEXIT_CRITICAL(); 130 131 /* Interrupts and other tasks can send to and receive from the queue 132 now the critical section has been exited. */ 133 134 vTaskSuspendAll(); //调度锁 135 prvLockQueue( pxQueue ); //队列锁 136 137 /* Update the timeout state to see if it has expired yet. */ 138 if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) 【阻塞时间没到】 139 { 140 if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) 【队列是空的】 141 { 142 traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue ); 143 144 #if ( configUSE_MUTEXES == 1 )
159 #endif
/* 将任务添加到队列的 xTasksWaitingToRCV 列表中 和 延时列表中,
并且将任务从就绪列表中移除。##0 见后*/
161 vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait ); 162 prvUnlockQueue( pxQueue ); 163 if( xTaskResumeAll() == pdFALSE ) 164 { 165 portYIELD_WITHIN_API(); 166 } 167 else 168 { 169 mtCOVERAGE_TEST_MARKER(); 170 } 171 } 172 else 【队列不是空的】 173 { 174 /* Try again. 从for(;;)那里重新来一遍*/ 175 prvUnlockQueue( pxQueue ); 176 ( void ) xTaskResumeAll(); 177 } 178 } 179 else 【 timeout 】 180 { 181 prvUnlockQueue( pxQueue ); //队列解锁 182 ( void ) xTaskResumeAll(); //恢复任务调度 183 184 if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) //队列仍是空的 185 { 186 traceQUEUE_RECEIVE_FAILED( pxQueue ); 187 return errQUEUE_EMPTY; //返回错误 188 } 189 else 190 { 191 mtCOVERAGE_TEST_MARKER(); 192 } 193 } 194 } 195 } 196 /*-----------------------------------------------------------*/
下面先分析xQueueGenericReceiveFromISR() :
1 BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue, void * const pvBuffer, BaseType_t * const pxHigherPriorityTaskWoken ) 2 { 3 BaseType_t xReturn; 4 UBaseType_t uxSavedInterruptStatus; 5 Queue_t * const pxQueue = ( Queue_t * ) xQueue; 6 7 configASSERT( pxQueue ); 8 configASSERT( !( ( pvBuffer == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) ); 9 10 /* RTOS ports that support interrupt nesting have the concept of a maximum 11 system call (or maximum API call) interrupt priority. Interrupts that are 12 above the maximum system call priority are kept permanently enabled, even 13 when the RTOS kernel is in a critical section, but cannot make any calls to 14 FreeRTOS API functions. If configASSERT() is defined in FreeRTOSConfig.h 15 then portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion 16 failure if a FreeRTOS API function is called from an interrupt that has been 17 assigned a priority above the configured maximum system call priority. 18 Only FreeRTOS functions that end in FromISR can be called from interrupts 19 that have been assigned a priority at or (logically) below the maximum 20 system call interrupt priority. FreeRTOS maintains a separate interrupt 21 safe API to ensure interrupt entry is as fast and as simple as possible. 22 More information (albeit Cortex-M specific) is provided on the following 23 link: http://www.freertos.org/RTOS-Cortex-M3-M4.html */ 24 portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); 25 26 uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); 记录BasePRI值,并屏蔽所有系统可管理的中断 27 { 28 const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting; //队列中item的数量 29 30 /* Cannot block in an ISR, so check there is data available. */ 31 if( uxMessagesWaiting > ( UBaseType_t ) 0 ) 32 { 33 const int8_t cRxLock = pxQueue->cRxLock; 34 35 traceQUEUE_RECEIVE_FROM_ISR( pxQueue ); 36 37 prvCopyDataFromQueue( pxQueue, pvBuffer ); //取item 38 pxQueue->uxMessagesWaiting = uxMessagesWaiting - 1; 39 40 /* If the queue is locked the event list will not be modified. 41 Instead update the lock count so the task that unlocks the queue 42 will know that an ISR has removed data while the queue was 43 locked. */ 44 if( cRxLock == queueUNLOCKED ) 【队列没加锁】 45 {
//这里看一下
// 如果(之前队列满了 && 有任务在等待发送到这个队列的话) -> 则切换任务 46 if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )
47 { 48 if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) ##1 见后 49 { 50 /* The task waiting has a higher priority than us so 51 force a context switch. */ 52 if( pxHigherPriorityTaskWoken != NULL ) 53 { 54 *pxHigherPriorityTaskWoken = pdTRUE; //置位任务切换标志 55 } 56 else 57 { 58 mtCOVERAGE_TEST_MARKER(); 59 } 60 } 61 else 62 { 63 mtCOVERAGE_TEST_MARKER(); 64 } 65 } 66 else 67 { 68 mtCOVERAGE_TEST_MARKER(); 69 } 70 } 71 else 【队列加锁时,RxLock计数值加一】 ##2 72 { 73 /* Increment the lock count so the task that unlocks the queue 74 knows that data was removed while it was locked. */ 75 pxQueue->cRxLock = ( int8_t ) ( cRxLock + 1 ); 76 } 77 78 xReturn = pdPASS; 79 } 80 else 【队列中没有item】 81 { 82 xReturn = pdFAIL; 83 traceQUEUE_RECEIVE_FROM_ISR_FAILED( pxQueue ); 84 } 85 } 86 portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); 87 88 return xReturn; 89 } 90 /*-----------------------------------------------------------*/
1 BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, void * const pvBuffer ) 2 { 3 BaseType_t xReturn; 4 UBaseType_t uxSavedInterruptStatus; 5 int8_t *pcOriginalReadPosition; 6 Queue_t * const pxQueue = ( Queue_t * ) xQueue; 7 8 configASSERT( pxQueue ); 9 configASSERT( !( ( pvBuffer == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) ); 10 configASSERT( pxQueue->uxItemSize != 0 ); /* Can't peek a semaphore. */ 11 12 /* RTOS ports that support interrupt nesting have the concept of a maximum 13 system call (or maximum API call) interrupt priority. Interrupts that are 14 above the maximum system call priority are kept permanently enabled, even 15 when the RTOS kernel is in a critical section, but cannot make any calls to 16 FreeRTOS API functions. If configASSERT() is defined in FreeRTOSConfig.h 17 then portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion 18 failure if a FreeRTOS API function is called from an interrupt that has been 19 assigned a priority above the configured maximum system call priority. 20 Only FreeRTOS functions that end in FromISR can be called from interrupts 21 that have been assigned a priority at or (logically) below the maximum 22 system call interrupt priority. FreeRTOS maintains a separate interrupt 23 safe API to ensure interrupt entry is as fast and as simple as possible. 24 More information (albeit Cortex-M specific) is provided on the following 25 link: http://www.freertos.org/RTOS-Cortex-M3-M4.html */ 26 portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); 27 28 uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); 29 { 30 /* Cannot block in an ISR, so check there is data available. */ 31 if( pxQueue->uxMessagesWaiting > ( UBaseType_t ) 0 ) 32 { 33 traceQUEUE_PEEK_FROM_ISR( pxQueue ); 34 35 /* Remember the read position so it can be reset as nothing is 36 actually being removed from the queue. */ 37 pcOriginalReadPosition = pxQueue->u.pcReadFrom; //记录读指针位置 38 prvCopyDataFromQueue( pxQueue, pvBuffer ); //读取一个item 39 pxQueue->u.pcReadFrom = pcOriginalReadPosition; //恢复读指针位置 40 41 xReturn = pdPASS; 42 } 43 else 44 { 45 xReturn = pdFAIL; 46 traceQUEUE_PEEK_FROM_ISR_FAILED( pxQueue ); 47 } 48 } 49 portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); 50 51 return xReturn; 52 } 53 /*-----------------------------------------------------------*/
##0 注意函数PlaceOnEventList 双加,加入到EventList 和 DelayList。
void vTaskPlaceOnEventList( List_t * const pxEventList, const TickType_t xTicksToWait ) { configASSERT( pxEventList ); /* THIS FUNCTION MUST BE CALLED WITH EITHER INTERRUPTS DISABLED OR THE SCHEDULER SUSPENDED AND THE QUEUE BEING ACCESSED LOCKED. */ /* Place the event list item of the TCB in the appropriate event list. This is placed in the list in priority order so the highest priority task is the first to be woken by the event. The queue that contains the event list is locked, preventing simultaneous access from interrupts. */ vListInsert( pxEventList, &( pxCurrentTCB->xEventListItem ) ); prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE ); 见时间管理 }
与这个函数操作相反的是, xTaskRemoveFromEventList
##1 xTaskRemoveFromEventList 见队列创建初始化章节。
##2 队列加锁,不能进行入队出队操作。只进行RxLock计数值加一。
在队列解锁时:判断任务调度与否 、 RxLock减一。
##3 prvCopyDataFromQueue 见信号量章节。
和CopyDataToQueue类似,CopyToQ是指释放信号量。
CopyFromQ是指获取信号量。
留白