freeRTOS源码解析4--tasks.c 7

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 }
prvCheckTasksWaitingTermination

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 }
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 }

 

  系统滴答时钟的几个寄存器需要展示一下:

 

 

接口代码如下:

  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

 

  这一篇内容较多,也较难,日后如果有问题的地方,笔者再回来更新。好了,下篇再见!