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 }
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 }
这一篇内容较多,也较难,日后如果有问题的地方,笔者再回来更新。好了,下篇再见!