本帖最后由 keer_zu 于 2021-11-16 09:05 编辑
4.低功耗tickless模式
通常情况下,FreeRTOS回调空闲任务钩子函数(需要设计者自己实现),在空闲任务钩子函数中设置微处理器进入低功耗模式来达到省电的目的。因为系统要响应系统节拍中断事件,因此使用这种方法/会周期性的退出、再进入低功耗状态。如果系统节拍中断频率过快,则大部分电能和CPU时间会消耗在进入和退出低功耗状态上。
FreeRTOS的tickless空闲模式会在空闲周期时停止周期性系统节拍中断。停止周期性系统节拍中断可以使微控制器长时间处于低功耗模式。移植层需要配置外部唤醒中断,当唤醒事件到来时,将微控制器从低功耗模式唤醒。微控制器唤醒后,会重新使能系统节拍中断。由于微控制器在进入低功耗后,系统节拍计数器是停止的,但我们又需要知道这段时间能折算成多少次系统节拍中断周期,这就需要有一个不受低功耗影响的外部时钟源,即微处理器处于低功耗模式时它也在计时的,这样在重启系统节拍中断时就可以根据这个外部计时器计算出一个调整值并写入RTOS 系统节拍计数器变量中。
空闲任务的源代码如下所示,其中宏portTASK_FUNCTION翻译出来为:void prvIdleTask(void * pvParameters)。
static portTASK_FUNCTION( prvIdleTask,pvParameters )
{
/*防止编译器警告 */
(void ) pvParameters;
for(;; )
{
/*检查是否有任务删除了自己,如果有的话,空闲任务负责删除这个任务的TCB和堆栈空间 */
prvCheckTasksWaitingTermination();
#if( configUSE_PREEMPTION == 0 )
{
/*如果我们没有使用抢占式调度,我们会强制任务切换,看看是否有其它任务变得有效.
如果使用抢占式调度,是不需要这样的,因为任务变得有效后会抢占空闲任务.*/
taskYIELD();
}
#endif/* configUSE_PREEMPTION */
#if( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) )
{
/* 当使用抢占式内核,相同优先级的任务使用时间片方式获得CPU权限.如果有任务与空闲
任务共享一个优先级,那么空闲任务不必等到时间片耗尽再进行任务切换.
如果空闲优先级下的就绪列表中有多个任务,则执行用户任务*/
if(listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) >( UBaseType_t ) 1 )
{
taskYIELD();
}
}
#endif/* ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 )) */
#if( configUSE_IDLE_HOOK == 1 )
{
externvoid vApplicationIdleHook( void );
/*调用用户定义函数.这样允许设计者在不增加任务开销的情况下实现后台功能
注意:这个函数中绝对不允许调用任务可能引起阻塞的函数.*/
vApplicationIdleHook();
}
#endif/* configUSE_IDLE_HOOK */
#if( configUSE_TICKLESS_IDLE != 0 )
{
TickType_txExpectedIdleTime;
/*如果每次执行空闲任务都挂起调度器,起然后再解除调度器,这很难让人满意,因此这里
执行两次同样的比较(xExpectedIdleTime和configEXPECTED_IDLE_TIME_BEFORE_SLEEP),
第一次比较是测试一下是否达到预期的空闲时间,并不会挂起调度器.*/
xExpectedIdleTime= prvGetExpectedIdleTime();
if(xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
{
vTaskSuspendAll();
{
/*现在调度器被挂起,需要再次采样空闲时间,这次空闲时间可以使用了*/
configASSERT(xNextTaskUnblockTime >= xTickCount );
xExpectedIdleTime= prvGetExpectedIdleTime();
if(xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
{
portSUPPRESS_TICKS_AND_SLEEP(xExpectedIdleTime );
}
}
(void ) xTaskResumeAll();
}
}
#endif/* configUSE_TICKLESS_IDLE */
}
}
|