stm32L0 +freertos tickless低功耗

STM32 电子创客营 570次浏览 已收录 1个评论 扫描二维码

嵌入式实时操作系统运行中都会有一个系统时钟节拍,类似于人的心脏来驱动整个系统的运行。运行在arm cortex-m内核的单片机上的系统时钟用的最多的就是systick(属于arm内核带的)。使用stm32cubemx可以直接生成移植好的freertos工程,默认也会使用systick为系统时钟,时钟周期为1ms。

基于MCU的低功耗核心思想也很简单,就是让mcu尽可能的处于低功耗的状态。需要处理一些事情的时候唤醒做完了再进入低功耗。那么和freertos结合起来进行低功耗的处理又该如何处理?

首先freertos相比裸机就是有系统时钟节拍的概念,就按照默认的1ms时钟周期来举例:每1ms时间到就会执行一次任务调度器,任务调度器就会去查看是否有任务需要切换,是否阻塞状态的任务已经变成了就绪态。如果不需要切换或者全部任务都处于阻塞态的话,freertos就会切换到IDLE空闲任务中运行。

既然进了空闲任务就代表该做的事情做完了。所以处理低功耗的最简单的思路就是在空闲任务里让MCU进入低功耗状态,直到下次时钟节拍到来的时候又被唤醒起来。这样就是每1ms需要被唤醒一次。如下图所示:

stm32L0 +freertos tickless低功耗

红色的区域为时钟节拍到唤醒后运行的状态,绿色区域为进入IDLE空闲任务再进入低功耗的状态。

该思路应该是在freertos下处理低功耗最简单的方式,但是每1ms 单片机还是需要唤醒一下,可能大部分被唤醒以后所有任务还是处于阻塞状态,就又直接进入低功耗了。所以这次唤醒就是无意义的,是浪费电量的。

至此就又引出了更高级的玩法,让那些在唤醒以后又直接进低功耗的直接干脆就不要唤醒了,就等到需要任务切换的时候再次唤醒。这样子系统时钟节拍就已经不像原来这样有节奏的1ms来一次了,感觉像是没有tick时钟,所以这种模式就叫做tickless。示意图如下面这种样子:

stm32L0 +freertos tickless低功耗

可以看到mcu将大部分时间处于绿色的低功耗区域,只有再真正需要有任务切换的时候再唤醒起来。核心思路明白了,其实还有很多细节疑问,我怎么知道这次唤醒距离下次需要任务切换的时间间隔?原来的systick都是配置好的1ms中断一次,现在又该怎么处理?

实际上这两个问题在freertos下都不是问题:因为freertos已经帮我们做好了。它会计算下次需要唤醒的时间间隔wake_time,然后重新配置systick过 wake_time 时间以后再次唤醒。而真正需要我们做的就是,修改配置文件,使能tickless

在freertosConfig.h文件中添加如下宏定义:

#define configUSE_TICKLESS_IDLE 1
#define portSUPPRESS_TICKS_AND_SLEEP( xIdleTime ) vPortSuppressTicksAndSleep( xIdleTime )

关键性进入低功耗和配置systick的代码在port.c文件中。可以自行阅读参考。

烧录运行,再使用仪器测量下电流发现电流已经降了不少,可是仍然在ma级别。这个对于电池供电的设备想要运行一年肯定是不行的,所以还需要进一步优化。要优化就要先明白现在没有降下去的原因在哪里。打开port.c文件看针对低功耗的部分如何处理,如下为关键性的代码:

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

进入低功耗最关键的一句代码就是__wfi(); 查看stm32L0的参考手册描述:

stm32L0 +freertos tickless低功耗

WFI指令只是让单片机进入了sleep模式,在该模式下也只有ARM-COREX 内核的时钟关闭了,但是其他比如GPIO、ADC、UART这些外设时钟都是打开的,也就是还处于耗电状态。

stm32L0提供5种低功耗模式。其中综合考虑STOP模式可以做到ua级别,同时又可以保存ram内容,唤醒以后继续运行。所以接下来的主要工作就是要把原来进入sleep低功耗模式换成进入STOP模式。

低功耗模式确定了,还有个最关键也是最复杂的一个问题。当进入STOP模式以后systick时钟彻底停止了,这样tickless计算出来下次该唤醒的时间到了,该如何唤醒?

systick用不了了,那么只能寻找替代的定时器,该定时器还要满足一个要求就是在单片机进入到STOP模式以后还可以继续运行。好在stm32L0系列有一个LPTIMER,正好满足要求,在进入低功耗以后也可以把单片机唤醒。那接下来的思路就是彻底抛弃systick,使用LPTIMER来产生系统时钟节拍。

在stm32cubemx中使能LPTIMER并配置,记好要使能中断。

stm32L0 +freertos tickless低功耗

在clock configuration中还有个选择LPTIMER时钟源的地方,我这里配置为LSI

stm32L0 +freertos tickless低功耗

生成代码,修改stm32l0xx_it.c文件,注释掉原来sytick中断中的freertos处理代码:

void SysTick_Handler(void)
{
  /* USER CODE BEGIN SysTick_IRQn 0 */
  /* USER CODE END SysTick_IRQn 0 */
  HAL_IncTick();
  //osSystickHandler();
  /* USER CODE BEGIN SysTick_IRQn 1 */
  /* USER CODE END SysTick_IRQn 1 */
}

再添加如下代码,把os系统节拍处理放在LPTIMER的中断回调中:

void HAL_LPTIM_AutoReloadMatchCallback(LPTIM_HandleTypeDef *hlptim)
{
	osSystickHandler();		
}

这个步骤完成了,当然还有个地方要处理,原来进入低功耗模式部分的代码。实际上freertos是可以自定义低功耗处理部分的,记得前面在freertosConfig.h文件中添加了的宏定义改为2 :define configUSE_TICKLESS_IDLE 2

除此之外,还有自定义个 vPortSuppressTicksAndSleep 函数来替代原来port.c中移植好的。具体这个函数该怎么写freertos有参考: https://www.freertos.org/low-power-tickless-rtos.html

如下为我自己写的处理代码:

void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime )
{	
	lp_sleep(xExpectedIdleTime);
}	    
void lp_sleep(uint16_t period)
{
	uint32_t time_real_sleep;
	if(flag_allow_lp!=0)return ; //Check if allow enter low power 
	lp_stop_timer();
	__disable_irq();
	
	if( eTaskConfirmSleepModeStatus() == eAbortSleep )
	{
		lp_start_timer(MS_TICKS);
		__enable_irq();
	}
	else 
	{
		wakeup_from_lptim=false;
		lp_start_timer(period*MS_TICKS);
		HAL_ADC_Stop_DMA(&hadc);
		HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
		/*wakeup*/
		extern void SystemClock_Config(void);
		SystemClock_Config();
		__enable_irq();
		if(wakeup_from_lptim==true)
		{
				wakeup_from_lptim=false;
				time_real_sleep=period;
		}
		else 
		{
				time_real_sleep=(hlptim1.Instance->CNT)/MS_TICKS;
		}
		lp_stop_timer();
				
		vTaskStepTick(time_real_sleep);
		lp_start_timer(MS_TICKS);
	}
}

这里面有两个地方需要注意:假如开了ADC的话进入STOP之前关掉,还有进入STOP使用PWR_LOWPOWERREGULATOR_ON参数可以进一步降低功耗。

基于以上修改之后再次测量单片机低功耗时的电流可以做到30ua左右了。


转载请注明转自电子创客营:stm32L0 +freertos tickless低功耗! 了解我们点击这里

微信扫一扫关注我们的公众号:eemaker

stm32L0 +freertos tickless低功耗


喜欢 (5)or分享 (0)
电子创客营
关于作者:

您必须 登录 才能发表评论!

(1)个小伙伴在吐槽
  1. 电子创客营
    也可以使用RTC Periodic auto-wakeup功能来替代LPTIMER.LPTIMER只有在stm32低功耗系列中才有,所以F系列也只能使用RTC
    电子创客营2019-09-26 17:36 Windows 10 | Chrome 75.0.3770.100