freeRTOS原始碼解析4--tasks.c 7

freeManX1807發表於2024-10-05

4.2.20 空閒任務呼叫1--prvCheckTasksWaitingTermination

刪除所有終止的任務, 釋放資源。簡單描述就是清空xTasksWaitingTermination列表,釋放資源,遞減uxCurrentNumberOfTasks和uxDeletedTasksWaitingCleanUp。
介面:
static void prvCheckTasksWaitingTermination( void )
介面程式碼如下:

freeRTOS原始碼解析4--tasks.c 7
 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 }
prvCheckTasksWaitingTermination

4.2.21 空閒任務呼叫2--prvGetExpectedIdleTime

這個用於低功耗,主要作用是獲取期望睡眠的tick時間。
介面:
static TickType_t prvGetExpectedIdleTime( void )

返回:實際返回的是最近喚醒任務的剩餘tick數,即最多能夠睡眠這麼多tick數後就要甦醒了,因為有任務延遲結束需要喚醒了。
介面程式碼如下:

freeRTOS原始碼解析4--tasks.c 7
 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 }
prvGetExpectedIdleTime

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 }

  系統滴答時鐘的幾個暫存器需要展示一下:

介面程式碼如下:

freeRTOS原始碼解析4--tasks.c 7
  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 }
vPortSuppressTicksAndSleep

  這一篇內容較多,也較難,日後如果有問題的地方,筆者再回來更新。好了,下篇再見!

相關文章