4.2.9 週期任務用的延遲--xTaskDelayUntil
介面:BaseType_t xTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement )
形參1:pxPreviousWakeTime,上一次喚醒時間,第一次需要用介面xTaskGetTickCount()獲取;
形參2:xTimeIncrement,想要延遲的時間。
返回值:用於判讀任務是否確實需要delay。
1 BaseType_t xTaskDelayUntil( TickType_t * const pxPreviousWakeTime, 2 const TickType_t xTimeIncrement ) 3 { 4 TickType_t xTimeToWake; 5 BaseType_t xAlreadyYielded, xShouldDelay = pdFALSE; 6 7 configASSERT( pxPreviousWakeTime ); 8 configASSERT( ( xTimeIncrement > 0U ) ); 9 10 vTaskSuspendAll(); 11 { 12 /* Minor optimisation. The tick count cannot change in this 13 * block. */ 14 const TickType_t xConstTickCount = xTickCount; 15 16 configASSERT( uxSchedulerSuspended == 1U ); 17 18 /* Generate the tick time at which the task wants to wake. */ 19 /* 計算任務下次喚醒的時間 */ 20 xTimeToWake = *pxPreviousWakeTime + xTimeIncrement; 21 22 if( xConstTickCount < *pxPreviousWakeTime ) 23 { 24 /* The tick count has overflowed since this function was 25 * lasted called. In this case the only time we should ever 26 * actually delay is if the wake time has also overflowed, 27 * and the wake time is greater than the tick time. When this 28 * is the case it is as if neither time had overflowed. */ 29 /* tick值已經溢位了, 只有xTimeToWake也溢位並大於xConstTickCount 30 * 時, 才需要延遲 */ 31 /* 假設tick是8位的, 當前值為0x5(從FF加到5), pxPreviousWakeTime為0xFD, 32 * 當xTimeIncrement為1時, xTimeToWake未溢位, 當xTimeIncrement為0x15時, 33 * xTimeToWake溢位。 顯然當xTimeIncrement為1時, xTimeToWake比 34 * pxPreviousWakeTime和xConstTickCount都大, 但不需要延遲, 因為tick已過, 35 * 當xTimeIncrement為0x15時, xTimeToWake比pxPreviousWakeTime小但比xConstTickCount 36 * 大, tick要等到0x12才到延遲時間, 所以需要進行延遲. */ 37 if( ( xTimeToWake < *pxPreviousWakeTime ) && ( xTimeToWake > xConstTickCount ) ) 38 { 39 xShouldDelay = pdTRUE; 40 } 41 else 42 { 43 mtCOVERAGE_TEST_MARKER(); 44 } 45 } 46 else 47 { 48 /* The tick time has not overflowed. In this case we will 49 * delay if either the wake time has overflowed, and/or the 50 * tick time is less than the wake time. */ 51 /* tick沒有溢位, 那麼xTimeToWake溢位或比xConstTickCount小, 52 * 必然是需要延遲的. */ 53 if( ( xTimeToWake < *pxPreviousWakeTime ) || ( xTimeToWake > xConstTickCount ) ) 54 { 55 xShouldDelay = pdTRUE; 56 } 57 else 58 { 59 mtCOVERAGE_TEST_MARKER(); 60 } 61 } 62 63 /* Update the wake time ready for the next call. */ 64 *pxPreviousWakeTime = xTimeToWake; 65 66 if( xShouldDelay != pdFALSE ) 67 { 68 /* prvAddCurrentTaskToDelayedList() needs the block time, not 69 * the time to wake, so subtract the current tick count. */ 70 /* 延遲列表記錄的是阻塞時間而不是喚醒時間, 所以需要減去tick. */ 71 prvAddCurrentTaskToDelayedList( xTimeToWake - xConstTickCount, pdFALSE ); 72 } 73 else 74 { 75 mtCOVERAGE_TEST_MARKER(); 76 } 77 } 78 xAlreadyYielded = xTaskResumeAll(); 79 80 /* Force a reschedule if xTaskResumeAll has not already done so, we may 81 * have put ourselves to sleep. */ 82 if( xAlreadyYielded == pdFALSE ) 83 { 84 taskYIELD_WITHIN_API(); 85 } 86 else 87 { 88 mtCOVERAGE_TEST_MARKER(); 89 } 90 91 return xShouldDelay; 92 }
4.2.10 成對的資訊獲取介面,一般的和帶有FromISR字尾的
這類介面比較簡單,主要用於獲取TCB中的某些引數,這裡挑選任務優先順序獲取介面為例,重點在於帶有FromISR的介面。一般的介面很簡單,這裡就直接放出來即可。
1 UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask ) 2 { 3 TCB_t const * pxTCB; 4 UBaseType_t uxReturn; 5 6 taskENTER_CRITICAL(); 7 { 8 /* If null is passed in here then it is the priority of the task 9 * that called uxTaskPriorityGet() that is being queried. */ 10 pxTCB = prvGetTCBFromHandle( xTask ); 11 uxReturn = pxTCB->uxPriority; 12 } 13 taskEXIT_CRITICAL(); 14 15 return uxReturn; 16 }
1 UBaseType_t uxTaskPriorityGetFromISR( const TaskHandle_t xTask ) 2 { 3 TCB_t const * pxTCB; 4 UBaseType_t uxReturn; 5 UBaseType_t uxSavedInterruptStatus; 6 7 /* RTOS ports that support interrupt nesting have the concept of a 8 * maximum system call (or maximum API call) interrupt priority. 9 * Interrupts that are above the maximum system call priority are keep 10 * permanently enabled, even when the RTOS kernel is in a critical section, 11 * but cannot make any calls to FreeRTOS API functions. If configASSERT() 12 * is defined in FreeRTOSConfig.h then 13 * portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion 14 * failure if a FreeRTOS API function is called from an interrupt that has 15 * been assigned a priority above the configured maximum system call 16 * priority. Only FreeRTOS functions that end in FromISR can be called 17 * from interrupts that have been assigned a priority at or (logically) 18 * below the maximum system call interrupt priority. FreeRTOS maintains a 19 * separate interrupt safe API to ensure interrupt entry is as fast and as 20 * simple as possible. More information (albeit Cortex-M specific) is 21 * provided on the following link: 22 * https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ 23 /* 檢查呼叫帶有FromISR字尾的中斷優先順序是否高於 24 * configMAX_SYSCALL_INTERRUPT_PRIORITY, 是的話會進入assert */ 25 portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); 26 27 /* MISRA Ref 4.7.1 [Return value shall be checked] */ 28 /* More details at: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/MISRA.md#dir-47 */ 29 /* coverity[misra_c_2012_directive_4_7_violation] */ 30 /* 獲取當前中斷遮蔽優先順序, 並設定中斷遮蔽優先順序為configMAX_SYSCALL_INTERRUPT_PRIORITY 31 * 相當於進入了臨界區並返回了當前中斷遮蔽優先順序 */ 32 uxSavedInterruptStatus = ( UBaseType_t ) taskENTER_CRITICAL_FROM_ISR(); 33 { 34 /* If null is passed in here then it is the priority of the calling 35 * task that is being queried. */ 36 pxTCB = prvGetTCBFromHandle( xTask ); 37 uxReturn = pxTCB->uxPriority; 38 } 39 /* 將儲存的中斷遮蔽優先順序設定回去, 相當於退出臨界區 */ 40 taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus ); 41 42 return uxReturn; 43 }
4.2.11 設定任務優先順序--vTaskPrioritySet
介面雖然較長,但註釋較多,且邏輯較為簡單,直接看程式碼裡的註釋即可。
1 void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority ) 2 { 3 TCB_t * pxTCB; 4 UBaseType_t uxCurrentBasePriority, uxPriorityUsedOnEntry; 5 BaseType_t xYieldRequired = pdFALSE; 6 7 configASSERT( uxNewPriority < configMAX_PRIORITIES ); 8 9 /* Ensure the new priority is valid. */ 10 if( uxNewPriority >= ( UBaseType_t ) configMAX_PRIORITIES ) 11 { 12 uxNewPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U; 13 } 14 else 15 { 16 mtCOVERAGE_TEST_MARKER(); 17 } 18 19 taskENTER_CRITICAL(); 20 { 21 /* If null is passed in here then it is the priority of the calling 22 * task that is being changed. */ 23 pxTCB = prvGetTCBFromHandle( xTask ); 24 25 #if ( configUSE_MUTEXES == 1 ) 26 { 27 uxCurrentBasePriority = pxTCB->uxBasePriority; 28 } 29 #else 30 { 31 uxCurrentBasePriority = pxTCB->uxPriority; 32 } 33 #endif 34 35 if( uxCurrentBasePriority != uxNewPriority ) 36 { 37 /* The priority change may have readied a task of higher 38 * priority than a running task. */ 39 if( uxNewPriority > uxCurrentBasePriority ) 40 { 41 /* 提升優先順序 */ 42 #if ( configNUMBER_OF_CORES == 1 ) 43 { 44 if( pxTCB != pxCurrentTCB ) 45 { 46 /* The priority of a task other than the currently 47 * running task is being raised. Is the priority being 48 * raised above that of the running task? */ 49 /* 設定的優先順序比當前執行的任務高, 需要yield */ 50 if( uxNewPriority > pxCurrentTCB->uxPriority ) 51 { 52 xYieldRequired = pdTRUE; 53 } 54 else 55 { 56 mtCOVERAGE_TEST_MARKER(); 57 } 58 } 59 else 60 { 61 /* The priority of the running task is being raised, 62 * but the running task must already be the highest 63 * priority task able to run so no yield is required. */ 64 } 65 } 66 #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ 67 } 68 else if( taskTASK_IS_RUNNING( pxTCB ) == pdTRUE ) 69 { 70 /* Setting the priority of a running task down means 71 * there may now be another task of higher priority that 72 * is ready to execute. */ 73 /* 降低當前執行任務的優先順序, 則更高優先順序的就緒任務需要搶佔 */ 74 #if ( configUSE_TASK_PREEMPTION_DISABLE == 1 ) 75 if( pxTCB->xPreemptionDisable == pdFALSE ) 76 #endif 77 { 78 xYieldRequired = pdTRUE; 79 } 80 } 81 else 82 { 83 /* Setting the priority of any other task down does not 84 * require a yield as the running task must be above the 85 * new priority of the task being modified. */ 86 } 87 88 /* Remember the ready list the task might be referenced from 89 * before its uxPriority member is changed so the 90 * taskRESET_READY_PRIORITY() macro can function correctly. */ 91 /* 獲取任務當前的優先順序, 有可能是繼承來的優先順序 */ 92 uxPriorityUsedOnEntry = pxTCB->uxPriority; 93 94 #if ( configUSE_MUTEXES == 1 ) 95 { 96 /* Only change the priority being used if the task is not 97 * currently using an inherited priority or the new priority 98 * is bigger than the inherited priority. */ 99 /* 只有非繼承來的優先順序, 或提升優先順序才可以直接修改pxTCB->uxPriority */ 100 if( ( pxTCB->uxBasePriority == pxTCB->uxPriority ) || ( uxNewPriority > pxTCB->uxPriority ) ) 101 { 102 pxTCB->uxPriority = uxNewPriority; 103 } 104 else 105 { 106 mtCOVERAGE_TEST_MARKER(); 107 } 108 109 /* The base priority gets set whatever. */ 110 pxTCB->uxBasePriority = uxNewPriority; 111 } 112 #else /* if ( configUSE_MUTEXES == 1 ) */ 113 { 114 pxTCB->uxPriority = uxNewPriority; 115 } 116 #endif /* if ( configUSE_MUTEXES == 1 ) */ 117 118 /* Only reset the event list item value if the value is not 119 * being used for anything else. */ 120 /* xEventListItem的值未被使用, 才允許更新這個值 */ 121 if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == ( ( TickType_t ) 0U ) ) 122 { 123 listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxNewPriority ) ); 124 } 125 else 126 { 127 mtCOVERAGE_TEST_MARKER(); 128 } 129 130 /* If the task is in the blocked or suspended list we need do 131 * nothing more than change its priority variable. However, if 132 * the task is in a ready list it needs to be removed and placed 133 * in the list appropriate to its new priority. */ 134 /* 如果任務處於就緒列表, 則需要將其放到修改後的優先順序就緒列表 */ 135 if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxPriorityUsedOnEntry ] ), &( pxTCB->xStateListItem ) ) != pdFALSE ) 136 { 137 /* The task is currently in its ready list - remove before 138 * adding it to its new ready list. As we are in a critical 139 * section we can do this even if the scheduler is suspended. */ 140 if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) 141 { 142 /* It is known that the task is in its ready list so 143 * there is no need to check again and the port level 144 * reset macro can be called directly. */ 145 portRESET_READY_PRIORITY( uxPriorityUsedOnEntry, uxTopReadyPriority ); 146 } 147 else 148 { 149 mtCOVERAGE_TEST_MARKER(); 150 } 151 152 prvAddTaskToReadyList( pxTCB ); 153 } 154 else 155 { 156 #if ( configNUMBER_OF_CORES == 1 ) 157 { 158 mtCOVERAGE_TEST_MARKER(); 159 } 160 #endif 161 } 162 163 if( xYieldRequired != pdFALSE ) 164 { 165 /* The running task priority is set down. Request the task to yield. */ 166 /* 開始yield, 但是在退出臨界區後才會進入sv中斷? */ 167 taskYIELD_TASK_CORE_IF_USING_PREEMPTION( pxTCB ); 168 } 169 else 170 { 171 mtCOVERAGE_TEST_MARKER(); 172 } 173 174 /* Remove compiler warning about unused variables when the port 175 * optimised task selection is not being used. */ 176 ( void ) uxPriorityUsedOnEntry; 177 } 178 } 179 taskEXIT_CRITICAL(); 180 }
補個流程圖
4.2.12 掛起任務--vTaskSuspend
還是直接看程式碼註釋,解釋得比較清楚了,介面功能可以顧名思義,邏輯也還好不復雜。
1 void vTaskSuspend( TaskHandle_t xTaskToSuspend ) 2 { 3 TCB_t * pxTCB; 4 5 taskENTER_CRITICAL(); 6 { 7 /* If null is passed in here then it is the running task that is 8 * being suspended. */ 9 pxTCB = prvGetTCBFromHandle( xTaskToSuspend ); 10 11 /* Remove task from the ready/delayed list and place in the 12 * suspended list. */ 13 if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) 14 { 15 taskRESET_READY_PRIORITY( pxTCB->uxPriority ); 16 } 17 else 18 { 19 mtCOVERAGE_TEST_MARKER(); 20 } 21 22 /* Is the task waiting on an event also? */ 23 /* 任務掛起, 任務就不管是否在等待事件 */ 24 if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) 25 { 26 ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); 27 } 28 else 29 { 30 mtCOVERAGE_TEST_MARKER(); 31 } 32 33 vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) ); 34 35 /* 如果在等待通知, 也直接退出等待 */ 36 #if ( configUSE_TASK_NOTIFICATIONS == 1 ) 37 { 38 BaseType_t x; 39 40 for( x = ( BaseType_t ) 0; x < ( BaseType_t ) configTASK_NOTIFICATION_ARRAY_ENTRIES; x++ ) 41 { 42 if( pxTCB->ucNotifyState[ x ] == taskWAITING_NOTIFICATION ) 43 { 44 /* The task was blocked to wait for a notification, but is 45 * now suspended, so no notification was received. */ 46 pxTCB->ucNotifyState[ x ] = taskNOT_WAITING_NOTIFICATION; 47 } 48 } 49 } 50 #endif /* if ( configUSE_TASK_NOTIFICATIONS == 1 ) */ 51 } 52 taskEXIT_CRITICAL(); 53 54 #if ( configNUMBER_OF_CORES == 1 ) 55 { 56 UBaseType_t uxCurrentListLength; 57 58 if( xSchedulerRunning != pdFALSE ) 59 { 60 /* Reset the next expected unblock time in case it referred to the 61 * task that is now in the Suspended state. */ 62 /* 如果下個解除阻塞的時間對應的任務正好是掛起的任務, 63 * 無論如何重置一下下個解除阻塞的時間 */ 64 taskENTER_CRITICAL(); 65 { 66 prvResetNextTaskUnblockTime(); 67 } 68 taskEXIT_CRITICAL(); 69 } 70 else 71 { 72 mtCOVERAGE_TEST_MARKER(); 73 } 74 75 if( pxTCB == pxCurrentTCB ) 76 { 77 if( xSchedulerRunning != pdFALSE ) 78 { 79 /* The current task has just been suspended. */ 80 /* 當前執行的任務掛起, 需要yield */ 81 configASSERT( uxSchedulerSuspended == 0 ); 82 portYIELD_WITHIN_API(); 83 } 84 else 85 { 86 /* The scheduler is not running, but the task that was pointed 87 * to by pxCurrentTCB has just been suspended and pxCurrentTCB 88 * must be adjusted to point to a different task. */ 89 90 /* Use a temp variable as a distinct sequence point for reading 91 * volatile variables prior to a comparison to ensure compliance 92 * with MISRA C 2012 Rule 13.2. */ 93 uxCurrentListLength = listCURRENT_LIST_LENGTH( &xSuspendedTaskList ); 94 95 if( uxCurrentListLength == uxCurrentNumberOfTasks ) 96 { 97 /* No other tasks are ready, so set pxCurrentTCB back to 98 * NULL so when the next task is created pxCurrentTCB will 99 * be set to point to it no matter what its relative priority 100 * is. */ 101 /* 所有建立的任務處於掛起狀態, 只能等新任務建立了 */ 102 pxCurrentTCB = NULL; 103 } 104 else 105 { 106 /* 切換下上下文, 因為掛起的是當前任務, 也有可能就緒列表為空, 107 * 會導致pxCurrentTCB = idle, 否則會選擇就緒列表中優先順序最高 108 * 任務, 這個介面在這裡只是設定了pxCurrentTCB引數, 並pend了 109 * yield, 因為當前排程器處於暫停狀態 */ 110 vTaskSwitchContext(); 111 } 112 } 113 } 114 else 115 { 116 mtCOVERAGE_TEST_MARKER(); 117 } 118 } 119 #endif /* #if ( configNUMBER_OF_CORES == 1 ) */ 120 }
這篇就到這裡了,內容也比較多,下篇開始vTaskResume和vTaskStartScheduler。
下篇再見!