4.低功耗设计-stm32低功耗模式

       在之前的低功耗软件设计中也提到过一部分的stm32降功耗的方法,freeRtos系统帮我们写好的一个睡眠模式tickless,当我们的系统进入空闲任务后,就会自动睡眠,来达到降功耗的目的,打开方式也比较简单,在FreeRtosConfig中将宏定义 configUSE_TICKLESS_IDLE 配置为 1。就是这么简单,我当前用的片子是stm32L452,初步使用了这个方法,在72Mhz主频下,功耗在2ma,如果你想验证,直接通过ST提供给我们的CubeMX生成一下代码就可以了,在低功耗测试中,我们经常要更改主频或者管脚,使用cubMX生成代码是真的很方便。

在这个试验中我们会遇到一个问题,就是CubeMx生成的代码,打开宏定义之后不能进入睡眠。这个原因是因为CubeMX自动生成了一个任务,导致我们进入空闲任务后仍然无法获取到空闲时间,导致无法休眠, defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);屏蔽掉这个任务就可以了。

上面我们说的都是tickless这个模式,但是作为低功耗产品来说,这个功耗还是很大怎么办,那么就要我们就要将MCU切到其他模式下,进一步的降低功耗。

  说道这里就要说一下stm32的低功耗模式了,之前我们的stm32F系列四种模式,L系列为5种,L系列近几年发展很快,模式多,电源管理及低功耗外设,还有漏电流比F系列小很多。

 

每种模式肯定功耗不一样啊,但是要根据产品的需求来选择合适的模式。

 

上面这两个图比较重要,这样我们就可以知道哪种功耗最低,怎么进入模式,怎么退出模式,有没有唤醒延时,唤醒后,RAM数据是不是还存在等等,都是重要的评估标准。

有的模式任意中断都可以唤醒,有的则必须外部中断或者特定中断唤醒,有的无唤醒延时等。

 

不管哪种模式其实最终通过关闭相应的时钟来达到降低功耗的目的,mcu的主要功耗来源也是时钟,还有管脚上的一些漏电流。闲置的IO都设置为模拟输入配置,理论上可以达到IO零消耗。之前测试过,在F系列上将不用管脚配置一下,电流下降很多,L系列这方面做的很好了,即使不配置功耗也不是很高。

说道这里我们肯定要了解我们的MCU的时钟情况,CubeMx的作用就来了,以stm32L452RE为例:

 

通过上面的图我们可以很清晰的看到时钟来源,有外部的8Mhz晶振,和内部的HSI MSI LSI晶振,外部晶振肯定稳定性更高些,也是我们经常用到的,我们的低功耗应用中,MSI LSI HSI等几种将会经常被提及。

下面主要说两种模式,低功耗运行模式和低功耗stop2模式

 

低功耗运行模式首先在几种模式中功耗算低的,并且内核和外设都可以保持运行状态

Stop模式,内核停止,Vcore范围内的时钟停止,PLL,MSI,HSI,HSE都被禁止,SRAM和寄存器的值保留,功耗够低。

 

低功耗运行模式在freeRtos中的应用也很简单分为几部

  1. freeRtosConfig.h中添加三个宏定义

#define configUSE_TICKLESS_IDLE 1 (使能低功耗模式)

#define configPRE_SLEEP_PROCESSING(x)          OS_PreSleepProcessing(x) //睡眠入口

#define configPOST_SLEEP_PROCESSING(x)         OS_PostSleepProcessing(x)//唤醒后操作

  1. 添加用户函数
void OS_PreSleepProcessing(uint32_t *ulExpectedIdleTime)

{

    *ulExpectedIdleTime = 0;  //屏蔽掉默认的wfi指令执行方式
       
    HAL_PWREx_EnableLowPowerRunMode();

}

void OS_PostSleepProcessing(uint32_t *ulExpectedIdleTime)

{

HAL_PWREx_DisableLowPowerRunMode();

}

低功耗运行模式就完了,比之前的tickless模式功耗要低一点点。

 

 

停机模式的使用耗费了很多的经历,过程中出现了很多的问题,比如说默认使用的HSE时钟,滴答定时器在stop模式唤醒后变慢。

最开始的时候我就是按照上面的步骤在入口函数中添加睡眠指令HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);

在唤醒出口函数中添加对时钟重新的配置。

Stop模式后,PLL时钟被停掉了,我们开始的时候用的PLL时钟,stop模式唤醒后,默认的使用内部MSI时钟。

通过这个思路我开始用的HSE时钟,唤醒后我重新SystemClock_Config(),一下应该就可以了,但是还是慢,网上也都说需要重新配下时钟,但是没有人说怎么配置。。无语啊。

那么我换条思路,不唤醒后默认使用MSI时钟吗??那么我开始的时候就配置为MSI时钟,使用48Mhz,这样配置后,确实不变慢了,也足以说明,确实唤醒后使用的MSI时钟,但是这个时候出现了一个问题,我板卡重启后,板卡无法运行,只有debug下能运行,感觉像是只能RAM运行,falsh中找不到时钟配置似的,还有个问题就是停机模式下确实功耗降低了,但是在1ma左右,没有达到理想值ua级别。

这下回到的原点

freeRtos的时钟来源从时钟树上可以看到来自AHB,我们的时钟最开始配置的HSE,也就是滴答时钟应该也变慢了,唤醒中断本身就变慢了,我们重新配置时钟,然后又进入stop,所以感觉像是重配没有管用。那么我们将唤醒中断改为LPTIM或者RTC,这样我们就没有什么问题了。St官方给我们提供了参考历程,使用的RTC唤醒,他使用的stm32L476,经过芯片等配置的更改,可以跑到我的L452上了,测试确实是好用的,功耗在ua。唤醒后时钟没有问题。官方默认的时钟使用的MSI,应该考虑到切换时钟会存在时间的开销和电流的消耗。

官方的例程使用的是无系统的,我想使用系统,同样也是上面的操作,增加两个用户函数,在里面实现睡眠动作和唤醒后的操作。

void OS_PreSleepProcessing(uint32_t *ulExpectedIdleTime)

{

  TickType_t tick;

 *ulExpectedIdleTime = 0;  //屏蔽掉默认的wfi指令执行方式

 tick = prvGetExpectedIdleTime();//获取freertos空闲时间

 HAL_RTCEx_DeactivateWakeUpTimer(&RTCHandle);//使用RTC唤醒

 HAL_RTCEx_SetWakeUpTimer_IT(&RTCHandle,tick, RTC_WAKEUPCLOCK_RTCCLK_DIV16);//设置唤醒时间

 HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);//进入stop模式

}


void OS_PostSleepProcessing(uint32_t *ulExpectedIdleTime)

{
  SYSCLKConfig_STOP(); //恢复PLL时钟

}

这样我们的睡眠和唤醒就正常了,我们使用 prvGetExpectedIdleTime(),动态获取空闲时间,避免我们在睡眠过程中,错过任务操作,睡眠和唤醒时间由实时系统说了算。

 

St的历程跑的很好,我们在移植的过程中仿佛并不是那么顺利,会遇到第一个问题。

 

  if( HAL_RTC_Init(&RTCHandle) != HAL_OK)

  {

    /* Initialization Error */

   }

移植到我们的板子后,RTC时钟初始化不通过,st历程好好的,其实st也做操作了

void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc)

{

  RCC_OscInitTypeDef RCC_OscInitStruct;

  RCC_PeriphCLKInitTypeDef  PeriphClkInitStruct;

  /*##-1- Configure the RTC clock source ######################################*/

  /* -a- Enable LSI Oscillator */

  RCC_OscInitStruct.OscillatorType =  RCC_OSCILLATORTYPE_LSI;

  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;

  RCC_OscInitStruct.LSIState = RCC_LSI_ON;

  if(HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)

  {

    while(1);

  }

  /* -b- Select LSI as RTC clock source */

  PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;

  PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;

  if(HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)

  {

    while(1);

  }

  /* Enable RTC Clock */

  __HAL_RCC_RTC_ENABLE();

  /*##-3- Configure the NVIC for RTC Alarm ###################################*/

  HAL_NVIC_SetPriority(RTC_WKUP_IRQn, 0x0, 0);

  HAL_NVIC_EnableIRQ(RTC_WKUP_IRQn);

}

void HAL_RTC_MspDeInit(RTC_HandleTypeDef *hrtc)

{

  /*##-1- Reset peripherals ##################################################*/

  __HAL_RCC_RTC_DISABLE();

}

增加这两个函数后,我们就发现可以运行通过。但是又出现了一个问题

我调用了delay_ms的一个延时,却出现了问题,整个程序进入硬件中断。

在freeRtosConfig.h中添加这个宏定义即可解决。

#define INCLUDE_xTaskGetSchedulerState          1

 

其他的函数可以看一下我提供的源代码,这里不着重的说了。

 

在我们的实际应用中一定要尽量的使系统多处在空闲任务中,这样才能长时间进入模式

为了满足我们的项目需求,我们还可以通过标志位来切换到不同的模式中,例如:

 

 

网上搜索下载的文档及官方测试代码还有修改的代码,将全部开放下载,可以在下面链接中下载到。绝对超值。

上面的方法目前还没有长期测试,只为大家提供一点思路。如有问题可留言讨论。

注:

当你要包含新的hal库文件时,除了添加相应的.c文件,引用.h文件外,一般还要在stm32l4xx_hal_conf.h中打开相应的

宏定义。

另外使用低功耗的模式的时候,要注意FreeRTOSConfig.h文件中的宏定义,有的时候打开了不必要的宏定义,会使低功耗模式下,任务出现无法调度的情况。(低功耗模式正常,但是任务无法调度),同时系统时钟中断,RTC_WKUP_IRQHandler等中断的实现也要小心修改

 

 

  • 6
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值