4.2.20 空閒任務呼叫1--prvCheckTasksWaitingTermination
刪除所有終止的任務, 釋放資源。簡單描述就是清空xTasksWaitingTermination列表,釋放資源,遞減uxCurrentNumberOfTasks和uxDeletedTasksWaitingCleanUp。
介面:
static void prvCheckTasksWaitingTermination( void )
介面程式碼如下:
1 static void prvCheckTasksWaitingTermination( void ) 2 { 3 /** THIS FUNCTION IS CALLED FROM THE RTOS IDLE TASK **/ 4 5 #if ( INCLUDE_vTaskDelete == 1 ) 6 { 7 TCB_t * pxTCB; 8 9 /* uxDeletedTasksWaitingCleanUp is used to prevent taskENTER_CRITICAL() 10 * being called too often in the idle task. */ 11 while( uxDeletedTasksWaitingCleanUp > ( UBaseType_t ) 0U ) 12 { 13 #if ( configNUMBER_OF_CORES == 1 ) 14 { 15 taskENTER_CRITICAL(); 16 { 17 { 18 /* 從終止列表中取出任務 */ 19 pxTCB = listGET_OWNER_OF_HEAD_ENTRY( ( &xTasksWaitingTermination ) ); 20 /* 將任務從終止列表中移出 */ 21 ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); 22 --uxCurrentNumberOfTasks; 23 --uxDeletedTasksWaitingCleanUp; 24 } 25 } 26 taskEXIT_CRITICAL(); 27 28 prvDeleteTCB( pxTCB ); // 釋放任務資源 29 } 30 #endif /* #if( configNUMBER_OF_CORES == 1 ) */ 31 } 32 } 33 #endif /* INCLUDE_vTaskDelete */ 34 }
4.2.21 空閒任務呼叫2--prvGetExpectedIdleTime
這個用於低功耗,主要作用是獲取期望睡眠的tick時間。
介面:
static TickType_t prvGetExpectedIdleTime( void )
返回:實際返回的是最近喚醒任務的剩餘tick數,即最多能夠睡眠這麼多tick數後就要甦醒了,因為有任務延遲結束需要喚醒了。
介面程式碼如下:
1 static TickType_t prvGetExpectedIdleTime( void ) 2 { 3 TickType_t xReturn; 4 UBaseType_t uxHigherPriorityReadyTasks = pdFALSE; 5 6 /* uxHigherPriorityReadyTasks takes care of the case where 7 * configUSE_PREEMPTION is 0, so there may be tasks above the idle priority 8 * task that are in the Ready state, even though the idle task is 9 * running. */ 10 /* uxHigherPriorityReadyTasks用於configUSE_PREEMPTION為0的情況, 因為有可能 11 * 存在空閒任務在執行時, 也有高於空閒任務優先順序的任務處於就緒態。如果是搶佔 12 * 式的排程的話, 則不可能會有更高優先順序的任務就緒, 否則根本輪不到空閒任務 13 * 執行。 */ 14 #if ( configUSE_PORT_OPTIMISED_TASK_SELECTION == 0 ) 15 { 16 if( uxTopReadyPriority > tskIDLE_PRIORITY ) 17 { 18 uxHigherPriorityReadyTasks = pdTRUE; 19 } 20 } 21 #else 22 { 23 const UBaseType_t uxLeastSignificantBit = ( UBaseType_t ) 0x01; 24 25 /* When port optimised task selection is used the uxTopReadyPriority 26 * variable is used as a bit map. If bits other than the least 27 * significant bit are set then there are tasks that have a priority 28 * above the idle priority that are in the Ready state. This takes 29 * care of the case where the co-operative scheduler is in use. */ 30 if( uxTopReadyPriority > uxLeastSignificantBit ) 31 { 32 uxHigherPriorityReadyTasks = pdTRUE; 33 } 34 } 35 #endif /* if ( configUSE_PORT_OPTIMISED_TASK_SELECTION == 0 ) */ 36 37 /* 這裡一樣, 搶佔式的話當前任務就是空閒任務, 所以下面的判斷除了最後的else, 38 * 其他都只有非搶佔式的才有可能進, 搶佔式的就直接看最後的else即可。 */ 39 if( pxCurrentTCB->uxPriority > tskIDLE_PRIORITY ) 40 { 41 xReturn = 0; 42 } 43 else if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > 1U ) 44 { 45 /* There are other idle priority tasks in the ready state. If 46 * time slicing is used then the very next tick interrupt must be 47 * processed. */ 48 /* 和空閒任務同優先順序的話, 應該也是先執行其他任務最後執行空閒任務 */ 49 xReturn = 0; 50 } 51 else if( uxHigherPriorityReadyTasks != pdFALSE ) 52 { 53 /* There are tasks in the Ready state that have a priority above the 54 * idle priority. This path can only be reached if 55 * configUSE_PREEMPTION is 0. */ 56 xReturn = 0; 57 } 58 else 59 { 60 /* 這裡算出來的值是最近喚醒的任務的剩餘tick數 */ 61 xReturn = xNextTaskUnblockTime; 62 xReturn -= xTickCount; 63 } 64 65 return xReturn; 66 }
4.2.22 空閒任務呼叫3--vPortSuppressTicksAndSleep
這個介面就是用於進入低功耗模式的,由於這個和具體的平臺相關,所以定義在port.c中,這裡只看cortex-m3和m4核的程式碼。
這個介面個人認為非常複雜,裡面有一些計算流程本人也不是很明白,還需要以後反覆開發和閱讀才能理解。
介面:
__weak void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime )
__weak:如果沒有__weak修飾的相同介面定義了,則用新定義的介面,否則就使用該介面,類似與多型,需要編譯器支援,非c標準。
引數1:xExpectedIdleTime,4.2.21的介面計算返回的tick數。
前置介面1:eSleepModeStatus eTaskConfirmSleepModeStatus( void ),用於確定是否真的需要進入低功耗模式。
返回:eSleepModeStatus,eAbortSleep:不進入,eStandardSleep:進入但休眠時間不高於xExpectedIdleTime,eNoTasksWaitingTimeout:進入但只能依靠外部中斷喚醒。
1 eSleepModeStatus eTaskConfirmSleepModeStatus( void ) 2 { 3 #if ( INCLUDE_vTaskSuspend == 1 ) 4 /* The idle task exists in addition to the application tasks. */ 5 const UBaseType_t uxNonApplicationTasks = configNUMBER_OF_CORES; 6 #endif /* INCLUDE_vTaskSuspend */ 7 8 eSleepModeStatus eReturn = eStandardSleep; 9 10 /* This function must be called from a critical section. */ 11 12 /* 此時排程器暫停, 有必要檢查一下是否有任務就緒了, 或者排程延遲了, 13 * 或者有tick中斷處理被延遲了 */ 14 if( listCURRENT_LIST_LENGTH( &xPendingReadyList ) != 0U ) 15 { 16 /* A task was made ready while the scheduler was suspended. */ 17 eReturn = eAbortSleep; 18 } 19 else if( xYieldPendings[ portGET_CORE_ID() ] != pdFALSE ) 20 { 21 /* A yield was pended while the scheduler was suspended. */ 22 eReturn = eAbortSleep; 23 } 24 else if( xPendedTicks != 0U ) 25 { 26 /* A tick interrupt has already occurred but was held pending 27 * because the scheduler is suspended. */ 28 eReturn = eAbortSleep; 29 } 30 31 #if ( INCLUDE_vTaskSuspend == 1 ) 32 else if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == ( uxCurrentNumberOfTasks - uxNonApplicationTasks ) ) 33 { 34 /* If all the tasks are in the suspended list (which might mean they 35 * have an infinite block time rather than actually being suspended) 36 * then it is safe to turn all clocks off and just wait for external 37 * interrupts. */ 38 /* 如果所有的任務都在掛起列表中(即使是那些等待事件且無限延遲的任務有), 39 * 那麼可以停止時鐘並等待外部中斷 */ 40 eReturn = eNoTasksWaitingTimeout; 41 } 42 #endif /* INCLUDE_vTaskSuspend */ 43 else 44 { 45 mtCOVERAGE_TEST_MARKER(); 46 } 47 48 return eReturn; 49 }
前置介面2:void vTaskStepTick( TickType_t xTicksToJump )
引數:xTicksToJump--休眠的tick數,用於更新xTickCount值。
1 void vTaskStepTick( TickType_t xTicksToJump ) 2 { 3 TickType_t xUpdatedTickCount; 4 5 /* Correct the tick count value after a period during which the tick 6 * was suppressed. Note this does *not* call the tick hook function for 7 * each stepped tick. */ 8 xUpdatedTickCount = xTickCount + xTicksToJump; 9 configASSERT( xUpdatedTickCount <= xNextTaskUnblockTime ); 10 11 if( xUpdatedTickCount == xNextTaskUnblockTime ) 12 { 13 /* Arrange for xTickCount to reach xNextTaskUnblockTime in 14 * xTaskIncrementTick() when the scheduler resumes. This ensures 15 * that any delayed tasks are resumed at the correct time. */ 16 /* 有任務到達喚醒時間了, 使xPendedTicks自增, 讓xTaskIncrementTick() 17 * 介面去實現任務的喚醒 */ 18 configASSERT( uxSchedulerSuspended != ( UBaseType_t ) 0U ); 19 configASSERT( xTicksToJump != ( TickType_t ) 0 ); 20 21 /* Prevent the tick interrupt modifying xPendedTicks simultaneously. */ 22 taskENTER_CRITICAL(); 23 { 24 xPendedTicks++; 25 } 26 taskEXIT_CRITICAL(); 27 xTicksToJump--; // xPendedTicks已自增了, xTicksToJump就需要少算一個 28 } 29 else 30 { 31 mtCOVERAGE_TEST_MARKER(); 32 } 33 34 // 更新xTickCount值 35 xTickCount += xTicksToJump; 36 }
系統滴答時鐘的幾個暫存器需要展示一下:
介面程式碼如下:
1 __weak void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) 2 { 3 uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickDecrementsLeft; 4 TickType_t xModifiableIdleTime; 5 6 /* Make sure the SysTick reload value does not overflow the counter. */ 7 /* xMaximumPossibleSuppressedTicks是最多可以休眠的tick數, 在 8 * vPortSetupTimerInterrupt中有初始化。 */ 9 if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ) 10 { 11 xExpectedIdleTime = xMaximumPossibleSuppressedTicks; 12 } 13 14 /* Enter a critical section but don't use the taskENTER_CRITICAL() 15 * method as that will mask interrupts that should exit sleep mode. */ 16 /* taskENTER_CRITICAL()介面修改的是basepri, 而__disable_irq()修改的是 17 * primask, 不然某些中斷被遮蔽後這些中斷觸發就無法退出休眠模式了. */ 18 __disable_irq(); 19 __dsb( portSY_FULL_READ_WRITE ); 20 __isb( portSY_FULL_READ_WRITE ); 21 22 /* If a context switch is pending or a task is waiting for the scheduler 23 * to be unsuspended then abandon the low power entry. */ 24 /* 檢查是否真的需要進入低功耗 */ 25 if( eTaskConfirmSleepModeStatus() == eAbortSleep ) 26 { 27 /* Re-enable interrupts - see comments above the __disable_irq() 28 * call above. */ 29 __enable_irq(); 30 } 31 else 32 { 33 /* Stop the SysTick momentarily. The time the SysTick is stopped for 34 * is accounted for as best it can be, but using the tickless mode will 35 * inevitably result in some tiny drift of the time maintained by the 36 * kernel with respect to calendar time. */ 37 /* SysTick停止時間會計入總時長, 使用無滴答模式會導致核心時間與日曆時間 38 * 存在微小偏差. */ 39 /* portNVIC_SYSTICK_CTRL_REG: 0xE000E010, SYSTICK Control and Status Register. 40 * portNVIC_SYSTICK_CLK_BIT_CONFIG: ( 1UL << 2UL ), 41 * portNVIC_SYSTICK_INT_BIT: ( 1UL << 1UL ), *0xE000E010=0x06. 42 * 即使用內部時鐘, 使能該中斷並禁止systick, 可以按自己需求修改, 但bit0不能置位 */ 43 portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT ); 44 45 /* Use the SysTick current-value register to determine the number of 46 * SysTick decrements remaining until the next tick interrupt. If the 47 * current-value register is zero, then there are actually 48 * ulTimerCountsForOneTick decrements remaining, not zero, because the 49 * SysTick requests the interrupt when decrementing from 1 to 0. */ 50 /* portNVIC_SYSTICK_CURRENT_VALUE_REG: 0xE000E018, 跟定時器計數的暫存器差不多. */ 51 ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; 52 53 if( ulSysTickDecrementsLeft == 0 ) 54 { 55 ulSysTickDecrementsLeft = ulTimerCountsForOneTick; 56 } 57 58 /* Calculate the reload value required to wait xExpectedIdleTime 59 * tick periods. -1 is used because this code normally executes part 60 * way through the first tick period. But if the SysTick IRQ is now 61 * pending, then clear the IRQ, suppressing the first tick, and correct 62 * the reload value to reflect that the second tick period is already 63 * underway. The expected idle time is always at least two ticks. */ 64 /* 計算過載的計數值, 這裡分兩種情況: 1、第一個tick中斷未到, 計數已到一定值, 65 * 2、第一個tick中斷已觸發, 但處於pend態, 第二個tick計數已到一定值, 66 * 無論哪種情況都需要減去一個tick數, 再計算剩下的tick的計數值. */ 67 ulReloadValue = ulSysTickDecrementsLeft + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); 68 69 /* 如果tick中斷已pend了, 就清除中斷, 再減去一個tick的計數值(即上面計算的值 70 * 多算了一個tick計數值), 這樣計算出來的休眠時間就比較準確. */ 71 if( ( portNVIC_INT_CTRL_REG & portNVIC_PEND_SYSTICK_SET_BIT ) != 0 ) 72 { 73 portNVIC_INT_CTRL_REG = portNVIC_PEND_SYSTICK_CLEAR_BIT; 74 ulReloadValue -= ulTimerCountsForOneTick; 75 } 76 77 /* 這是一個補償值, 即停止tick中斷後, 執行到這一條語句的大致tick計數值, 78 * 這需要根據自己的工程去估算, 在vPortSetupTimerInterrupt中去修改. */ 79 if( ulReloadValue > ulStoppedTimerCompensation ) 80 { 81 ulReloadValue -= ulStoppedTimerCompensation; 82 } 83 84 /* Set the new reload value. */ 85 /* portNVIC_SYSTICK_LOAD_REG: 0xE000E014, 過載暫存器, 從這個值減到0就觸發tick中斷 */ 86 portNVIC_SYSTICK_LOAD_REG = ulReloadValue; 87 88 /* Clear the SysTick count flag and set the count value back to 89 * zero. */ 90 /* 清除當前計數值, 一旦啟動系統時鐘, 這個暫存器的值就變成reload的值, 91 * 再遞減至0, 然後觸發中斷. */ 92 portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; 93 94 /* Restart SysTick. 使能系統定時器 */ 95 portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; 96 97 /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can 98 * set its parameter to 0 to indicate that its implementation contains 99 * its own wait for interrupt or wait for event instruction, and so wfi 100 * should not be executed again. However, the original expected idle 101 * time variable must remain unmodified, so a copy is taken. */ 102 /* configPRE_SLEEP_PROCESSING()可以自己實現休眠等待, 並將xModifiableIdleTime 103 * 設定成0, 則表示已經完成了休眠, 系統就不會執行wfi指令. */ 104 xModifiableIdleTime = xExpectedIdleTime; 105 configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); 106 107 if( xModifiableIdleTime > 0 ) 108 { 109 __dsb( portSY_FULL_READ_WRITE ); 110 __wfi(); // 等待中斷指令, 即進入休眠模式 111 __isb( portSY_FULL_READ_WRITE ); 112 } 113 114 /* 到這裡休眠就結束了, 和之前的configPRE_SLEEP_PROCESSING 115 * 是一對, 表示使用者自己實現的休眠和結束 */ 116 configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); 117 118 /* Re-enable interrupts to allow the interrupt that brought the MCU 119 * out of sleep mode to execute immediately. See comments above 120 * the __disable_irq() call above. */ 121 /* 使能中斷, 將會立即進入中斷處理例程, 可能是外部中斷也可能是tick中斷. */ 122 __enable_irq(); 123 __dsb( portSY_FULL_READ_WRITE ); 124 __isb( portSY_FULL_READ_WRITE ); 125 126 /* Disable interrupts again because the clock is about to be stopped 127 * and interrupts that execute while the clock is stopped will increase 128 * any slippage between the time maintained by the RTOS and calendar 129 * time. */ 130 /* 後面要再次處理系統tick值, 如果期間出現中斷的話, 會導致RTOS維護的時間 131 * 和日曆時間出現偏差, 所以直接禁止中斷可以防止這種偏差出現 */ 132 __disable_irq(); 133 __dsb( portSY_FULL_READ_WRITE ); 134 __isb( portSY_FULL_READ_WRITE ); 135 136 /* Disable the SysTick clock without reading the 137 * portNVIC_SYSTICK_CTRL_REG register to ensure the 138 * portNVIC_SYSTICK_COUNT_FLAG_BIT is not cleared if it is set. Again, 139 * the time the SysTick is stopped for is accounted for as best it can 140 * be, but using the tickless mode will inevitably result in some tiny 141 * drift of the time maintained by the kernel with respect to calendar 142 * time */ 143 /* *0xE000E010=0x06, 與前面功效一樣, 主要用於禁止systick. 144 * 雖然下面進一步校準了tick值, 但使用低功耗模式不可避免得會導致系統時鐘 145 * 和日曆時鐘出現細微的偏差 */ 146 portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT ); 147 148 /* Determine whether the SysTick has already counted to zero. */ 149 /* 檢查tick計數是否有減到0, bit16是讀清的 */ 150 if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) 151 { 152 uint32_t ulCalculatedLoadValue; 153 154 /* The tick interrupt ended the sleep (or is now pending), and 155 * a new tick period has started. Reset portNVIC_SYSTICK_LOAD_REG 156 * with whatever remains of the new tick period. */ 157 /* 需要恢復原先的tick計數, 所以需要減去目前已經計的數(休眠中觸發 158 * tick中斷後的時長), ulCalculatedLoadValue的值就是恢復後的計數值 159 * 需要繼續計的剩下的值, 注意這裡是已經減到0, 又重新開始倒數計時了. 160 * 簡單的假設, 原先一個tick需要500, 即499, 休眠總數是800, 這次休眠 161 * 了900, 那麼當前計數暫存器的值就是700, 800-700=100, 那麼當前一個 162 * tick需要計的剩下的數就是499-100=399, 這樣就像是在休眠時tick仍在 163 * 計數一樣, 雖然實際休眠時tick被禁止了 */ 164 ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); 165 166 /* Don't allow a tiny value, or values that have somehow 167 * underflowed because the post sleep hook did something 168 * that took too long or because the SysTick current-value register 169 * is zero. */ 170 /* 處理下ulCalculatedLoadValue值可能溢位的問題, 因為ulReloadValue必然是大於 171 * 一個tick的計數值, 而如果喚醒的中斷處理了太長時間, 或者使用者的hook執行了太 172 * 長時間導致當前計數值正好為0或變得特別小, 那麼計算的值就會溢位(減出負數). 173 * 如果小於ulStoppedTimerCompensation, 則正好減去了當前處理的時間(大約). */ 174 if( ( ulCalculatedLoadValue <= ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) 175 { 176 ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); 177 } 178 179 /* 過載計數值 */ 180 portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue; 181 182 /* As the pending tick will be processed as soon as this 183 * function exits, the tick value maintained by the tick is stepped 184 * forward by one less than the time spent waiting. */ 185 /* 休眠前已經少算了一個tick, 如果是pend了, 在函式退出後會立即處理 */ 186 ulCompleteTickPeriods = xExpectedIdleTime - 1UL; 187 } 188 else 189 { 190 /* Something other than the tick interrupt ended the sleep. */ 191 192 /* Use the SysTick current-value register to determine the 193 * number of SysTick decrements remaining until the expected idle 194 * time would have ended. */ 195 /* 不是tick中斷喚醒的, 計算思路就相對簡單一些 */ 196 ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; 197 #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG != portNVIC_SYSTICK_CLK_BIT ) 198 { 199 /* If the SysTick is not using the core clock, the current- 200 * value register might still be zero here. In that case, the 201 * SysTick didn't load from the reload register, and there are 202 * ulReloadValue decrements remaining in the expected idle 203 * time, not zero. */ 204 /* tick使用非核心時鐘的特殊處理, 暫時不是很理解 */ 205 if( ulSysTickDecrementsLeft == 0 ) 206 { 207 ulSysTickDecrementsLeft = ulReloadValue; 208 } 209 } 210 #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ 211 212 /* Work out how long the sleep lasted rounded to complete tick 213 * periods (not the ulReload value which accounted for part 214 * ticks). */ 215 /* 再次假設, 原先一個tick需要500, 休眠總數是900(2個tick), 這次休眠300 216 * 那麼ulCompletedSysTickDecrements=(2*500)-(900-300)=400 */ 217 ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - ulSysTickDecrementsLeft; 218 219 /* How many complete tick periods passed while the processor 220 * was waiting? */ 221 /* ulCompleteTickPeriods = 400 / 500 = 0 */ 222 ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick; 223 224 /* The reload value is set to whatever fraction of a single tick 225 * period remains. */ 226 /* 結果 = (0+1)*500-400=100. 簡單計算下, 休眠總數是900, 說明睡眠前的當前值是400, 227 * 已經計數了100, 這次休眠300, 總計是400, 即還剩下100. 結果是相符的, 關鍵是這個 228 * 演算法是怎麼得來的, 我一時也不是很清楚, 但是結果是準確的 */ 229 portNVIC_SYSTICK_LOAD_REG = ( ( ulCompleteTickPeriods + 1UL ) * ulTimerCountsForOneTick ) - ulCompletedSysTickDecrements; 230 } 231 232 /* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD_REG again, 233 * then set portNVIC_SYSTICK_LOAD_REG back to its standard value. If 234 * the SysTick is not using the core clock, temporarily configure it to 235 * use the core clock. This configuration forces the SysTick to load 236 * from portNVIC_SYSTICK_LOAD_REG immediately instead of at the next 237 * cycle of the other clock. Then portNVIC_SYSTICK_LOAD_REG is ready 238 * to receive the standard value immediately. */ 239 /* 這裡當前計數暫存器的值已經更新了, 所以再設定reload值是安全的 */ 240 portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; 241 portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; 242 #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG == portNVIC_SYSTICK_CLK_BIT ) 243 { 244 portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; 245 } 246 #else 247 { 248 /* The temporary usage of the core clock has served its purpose, 249 * as described above. Resume usage of the other clock. */ 250 portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT; 251 252 if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) 253 { 254 /* The partial tick period already ended. Be sure the SysTick 255 * counts it only once. */ 256 portNVIC_SYSTICK_CURRENT_VALUE_REG = 0; 257 } 258 259 portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; 260 portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; 261 } 262 #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ 263 264 /* Step the tick to account for any tick periods that elapsed. */ 265 vTaskStepTick( ulCompleteTickPeriods ); 266 267 /* Exit with interrupts enabled. */ 268 __enable_irq(); 269 } 270 }
這一篇內容較多,也較難,日後如果有問題的地方,筆者再回來更新。好了,下篇再見!