FreeRTOS Tickless 有助于降低功耗

一般前后台程序就是查询,Delay,中断.一直在一个主频下,或者轮询的延迟时候休眠一下,等待标志等等.当然,在RTOS中可能没那么简单,任务多,不知道哪个完成哪个怎么的,要做到低功耗,就得靠RTOS有一定的能力.
实现OS的低功耗,一种是靠IDLE任务,一种是用户适当停止当前任务.而这个适当,没那么简单.
Tickless Idle 的设计思想在于尽可能得在 MCU 空闲时使其进入低功耗模式,这是FreeRTOS的低功耗使用方法,他只关断CPU状态.
合理的进入低功耗模式,更不能是进入低功耗几个周期又出来,这样很没意义,FreeRTOS是基于SysTick来运作的,当然你也可以是其他时钟,只是配置起来麻烦了一些, 任务调度器可以预期到下一个周期性任务的触发时间,但是突发任务却无法预测,要用户有自己的中断来触发唤醒,调整SysTick中断触发时间,可以少进入几次中断,少唤醒几次,也就是Tick less,从而更长的时间停留在低功耗模式中,这时候SysTick的RELOAD是变化的.
MCU可能被两种不同的情况唤醒,动态调整过的系统时钟中断或者突发性的外部事件,无论是哪一种情况,都可以通过运行在低功耗模式下的某种定时器来计算出 MCU 处于低功耗模式下的时间,这样在MCU唤醒后对系统时间进行软件补偿,不至于系统的一些时间周期错误.比如vTaskDelay可能因此误以为足够延迟等等.
进入prvIdleTask,也就是IDLE任务后就立马有一个判断.

具体先使用下面代码计算用户所需空闲等待时间.

xExpectedIdleTime = prvGetExpectedIdleTime();

然后判断延迟的时间是否值得进入低功耗:

if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP )

如果合理,挂起调度器,因为要马上进入低功耗了.

vTaskSuspendAll();

再判断一次,免得因为进入过程插入的时间周期导致进入空闲时候不合理.

xExpectedIdleTime = prvGetExpectedIdleTime();

如果依然要进入低功耗,就进入吧.否则直接恢复调度器,如果真的进入低功耗,就要等唤醒才能执行下一句,也是恢复调度器.

 xTaskResumeAll();

首先进入低功耗的函数是portSUPPRESS_TICKS_AND_SLEEP,这其实是一个define.

	extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime );
	#define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) vPortSuppressTicksAndSleep( xExpectedIdleTime )

对应函数默认是weak的,也就是我们自己可以写一个函数来代替他.

首先有这么一个判断.最大的Idle时间不能超过系统最大Idle数值.否则还是多Tick几次.

		if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks )
		{
			xExpectedIdleTime = xMaximumPossibleSuppressedTicks;
		}

然后去掉SysTick的使能.
portNVIC_SYSTICK_CTRL &= ~portNVIC_SYSTICK_ENABLE;
计算一下需要等多久才唤醒.

		ulReloadValue = portNVIC_SYSTICK_CURRENT_VALUE + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) );
		if( ulReloadValue > ulStoppedTimerCompensation )
		{
			ulReloadValue -= ulStoppedTimerCompensation;
		}

然后判断是否可以休眠.

if( eTaskConfirmSleepModeStatus() == eAbortSleep )

如果不可以就重新开始计时.

			/* Restart from whatever is left in the count register to complete
			this tick period. */
			portNVIC_SYSTICK_LOAD = portNVIC_SYSTICK_CURRENT_VALUE;
			/* Restart SysTick. */
			portNVIC_SYSTICK_CTRL |= portNVIC_SYSTICK_ENABLE;
			/* Reset the reload register to the value required for normal tick
			periods. */
			portNVIC_SYSTICK_LOAD = ulTimerCountsForOneTick - 1UL;
			/* Re-enable interrupts - see comments above __disable_irq() call
			above. */
			__enable_irq();

可以低功耗的话设置新的重载,进入低功耗.

			/* Set the new reload value. */
			portNVIC_SYSTICK_LOAD = ulReloadValue;
			/* Clear the SysTick count flag and set the count value back to
			zero. */
			portNVIC_SYSTICK_CURRENT_VALUE = 0UL;
			/* Restart SysTick. */
			portNVIC_SYSTICK_CTRL |= portNVIC_SYSTICK_ENABLE;

进入休眠的地方有两个钩子,一个进入前钩子,一个退出后钩子,可以使用关闭一些外设等等.

			configPRE_SLEEP_PROCESSING( &xModifiableIdleTime );
			if( xModifiableIdleTime > 0 )
			{
				__dsb( portSY_FULL_READ_WRITE );
				__wfi();
				__isb( portSY_FULL_READ_WRITE );
			}
			configPOST_SLEEP_PROCESSING( &xExpectedIdleTime );

进入后立马判断是不是因为够钟才清醒的.

			if( ( ulSysTickCTRL & portNVIC_SYSTICK_COUNT_FLAG ) != 0 )

如果不是的话,就是意外突发事件.如果是正常,重新补偿时间就够了.

				uint32_t ulCalculatedLoadValue;
				/* The tick interrupt has already executed, and the SysTick
				count reloaded with ulReloadValue.  Reset the
				portNVIC_SYSTICK_LOAD with whatever remains of this tick
				period. */
				ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE );
				/* Don't allow a tiny value, or values that have somehow
				underflowed because the post sleep hook did something
				that took too long. */
				if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) )
				{
					ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL );
				}
				portNVIC_SYSTICK_LOAD = ulCalculatedLoadValue;
				/* The tick interrupt handler will already have pended the tick
				processing in the kernel.  As the pending tick will be
				processed as soon as this function exits, the tick value
				maintained by the tick is stepped forward by one less than the
				time spent waiting. */
				ulCompleteTickPeriods = xExpectedIdleTime - 1UL;

完全退出休眠,重启SysTick到正常状态.

			portNVIC_SYSTICK_CURRENT_VALUE = 0UL;
			portENTER_CRITICAL();
			{
				portNVIC_SYSTICK_CTRL |= portNVIC_SYSTICK_ENABLE;
				vTaskStepTick( ulCompleteTickPeriods );
				portNVIC_SYSTICK_LOAD = ulTimerCountsForOneTick - 1UL;
			}
			portEXIT_CRITICAL();

这样,通过RTOS不断进入WFI,WFE状态,可以在IDLE时候节省能源.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
FreeRTOS是一个流行的实时操作系统内核,可以用于嵌入式系统的开发。tickless例程是FreeRTOS中的一个特性,用于优化系统的功耗。 传统的实时操作系统通常使用系统节拍(tick)来进行任务调度和时间管理。系统节拍是指操作系统定时中断的时间间隔,通过这个时间间隔,系统可以周期性地进行任务切换和时间管理。然而,这种方式会导致系统在任务比较空闲时也会定时唤醒,造成功耗的浪费。 tickless例程是为了解决这个问题而设计的。它通过根据任务调度情况动态地调整系统节拍的时间间隔,从而避免了不必要的定时唤醒,减小系统的功耗。 tickless例程的原理是在任务调度之前,通过检查系统的任务队列,判断下一个任务的执行时间,然后根据这个执行时间来动态地调整系统节拍的时间间隔。如果下一个任务的执行时间比当前时间间隔长,就延长时间间隔,从而延迟唤醒操作。如果下一个任务的执行时间比当前时间间隔短,就缩短时间间隔,从而提前唤醒操作。通过这种方式,系统可以在任务需要执行之前唤醒,避免了不必要的定时唤醒。 tickless例程的使用可以明显降低系统的功耗,特别是在任务比较空闲的情况下。但是需要注意的是,tickless例程可能会增加系统的复杂性和调试难度,因为系统的调度和时间管理变得更加动态和复杂。 总而言之,tickless例程是FreeRTOS中用于优化系统功耗的一个特性,通过动态地调整系统节拍的时间间隔来避免不必要的定时唤醒。它可以降低系统的功耗,但可能会增加系统的复杂性和调试难度。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值