出隊操作類似於入隊的操作。先整明白入隊的操作,這個就好說了。
分析:
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是指獲取信號量。
留白