STM32G0的Low-Power Run模式测试

有项目要做低功耗,试了一下STM32G030的LP Run模式。

LP Run模式进入方法

 手册上进LP Run模式的步骤:

STM32CubeMX里面直接把频率设到2M,这样不用在代码里改主频,直接就能进LP Run模式。


进LP Run就很简单了,直接调用库函数:

HAL_PWREx_EnableLowPowerRunMode();

寄存器PWR_SR2的值从0x0180变成了0x0380

所以可以加一个循环,等待模式切换成功:

while(!HAL_IS_BIT_SET(PWR->SR2, PWR_SR2_REGLPF));

关闭Flash

让代码在SRAM运行

如果说进行Low-Power Run是平平无奇,一帆风顺的话,关闭Flash就是天坑,一步一个坑。

先改sct文件,加一句*.o(RAMCODE):

; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************

LR_IROM1 0x08000000 0x00008000  {    ; load region size_region
  ER_IROM1 0x08000000 0x00008000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
   .ANY (+XO)
  }
  RW_IRAM1 0x20000000 0x00002000  {  ; RW data
  *.o(RAMCODE)
   .ANY (+RW +ZI)
  }
}

函数前加__attribute__((section("RAMCODE")))修饰符就会被加载到SRAM区域,例如:

__attribute__((section("RAMCODE"))) void sensor_loop(void)
{
    //...
}

 编译之后,map文件:

关闭Flash

进入Low-Power Run模式的代码改成:

HAL_PWREx_EnableFlashPowerDown(PWR_FLASHPD_LPRUN);
HAL_PWREx_EnableLowPowerRunMode();
while(!HAL_IS_BIT_SET(PWR->SR2, PWR_SR2_REGLPF));

当你写代码时坑就来了,不能调用库函数,因为只要调用就会跳转到Flash,除非把调用的函数也加到RAMCODE段。所以用到哪些库函数,都要自己改写。直接读写寄存是没问题的,一般HAL的宏函数喜欢写成全大写,或者以两条下划线开头,比如下面这些函数就都没有问题:

__HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_STOPF);
I2C_RESET_CR2(hi2c);

__HAL_TIM_SetCompare(&htim1, TIM_CHANNEL_1, pluse);

所以为了安全起见,上面进入Low-Power Run模式的代码也改成了不使用HAL函数的方案:

    PWR->CR1 |= PWR_FLASHPD_LPRUN;		//0x40007000
    PWR->CR1 |= PWR_FLASHPD_LPSLEEP;
    PWR->CR1 |= PWR_FLASHPD_STOP;
    SET_BIT(PWR->CR1, PWR_CR1_LPR);		//0x40007000
    while(!HAL_IS_BIT_SET(PWR->SR2, PWR_SR2_REGLPF));	//0x40007014

关闭中断

因为中断向量的代码在Flash段,所以只要中断,就会卡死。我的代码不用中断,我直接禁用:

__disable_irq();

禁用全局中断之后,要注意,系统滴答也没有了,要延时就用代码跑循环,HAL的库里在Low Power这一块就有这么干的,例如下面这个函数:

/**
  * @brief Exit Low-power Run mode.
  * @note  Before HAL_PWREx_DisableLowPowerRunMode() completion, the function checks that
  *        REGLPF has been properly reset (otherwise, HAL_PWREx_DisableLowPowerRunMode
  *        returns HAL_TIMEOUT status). The system clock frequency can then be
  *        increased above 2 MHz.
  * @retval HAL Status
  */
HAL_StatusTypeDef HAL_PWREx_DisableLowPowerRunMode(void)
{
  uint32_t wait_loop_index = ((PWR_REGLPF_SETTING_DELAY_6_US * SystemCoreClock) / 1000000U) + 1U;

  /* Clear LPR bit */
  CLEAR_BIT(PWR->CR1, PWR_CR1_LPR);

  /* Wait until REGLPF is reset */
  while (HAL_IS_BIT_SET(PWR->SR2, PWR_SR2_REGLPF))
  {
    if (wait_loop_index != 0U)
    {
      wait_loop_index--;
    }
    else
    {
      return HAL_TIMEOUT;
    }
  }

  return HAL_OK;
}

除法的问题

下一个问题就更麻烦了,因为Cortex-M0是没有除法器的,所以在做除法或取模时,实际上是调用__aeabi_uidiv函数,再次卡死。

例外是除以2的幂,或者除以10。不会调用函数。

我的代码是优化算法,把除法去掉了。如果实在去不掉,就自己在网上找除法的函数:

//gcc的除和模除算法,在这里实现为了避免memcode跳转到flash域
//num:被除数;den:除数;modwanted:是除法还是取模
__attribute__((section("RAMCODE"))) uint32_t __udivmodsi4(uint32_t num, uint32_t den, int modwanted)
{
  uint32_t bit = 1;
  uint32_t res = 0;

  while (den < num && bit && !(den & (1L<<31)))
    {
      den <<=1;
      bit <<=1;
    }
  while (bit)
    {
      if (num >= den)
	{
	  num -= den;
	  res |= bit;
	}
      bit >>=1;
      den >>=1;
    }
  if (modwanted) return num;
  return res;
}
__attribute__((section("RAMCODE"))) uint16_t __udivmodhi4(uint16_t num, uint16_t den, int modwanted)
{
  uint16_t bit = 1;
  uint16_t res = 0;

  while (den < num && bit && !(den & (1U<<15)))
    {
      den <<=1;
      bit <<=1;
    }
  while (bit)
    {
      if (num >= den)
	{
	  num -= den;
	  res |= bit;
	}
      bit >>=1;
      den >>=1;
    }
  if (modwanted) return num;
  return res;
}

常量的问题

如果是定义的一般的常量,编译器会直接当作宏来处理,这个没什么问题。

但是当算法中有查表的需求,写了一个数组,而且是只读的。那这个数组其实是在code段,也就是flash中,自然也会卡死。

而且在map中还找不到这个数组名,很迷。

所以解决的方法也很简单,就是把这个数组加长一点,然后在代码中写一下,编译器就会认为这个数组是RW,就会放在ram中。

这个问题应该有更优雅的解决办法,不过我没找到。

最终模式切换代码是这样的:

__attribute__((section("RAMCODE"))) void sensor_loop(void)
{
    //...

    //关闭全局中断
	__disable_irq();
	
	//关闭flash
	//HAL_PWREx_EnableFlashPowerDown(PWR_FLASHPD_LPRUN);
	PWR->CR1 |= PWR_FLASHPD_LPRUN;		//0x40007000
	PWR->CR1 |= PWR_FLASHPD_LPSLEEP;
	PWR->CR1 |= PWR_FLASHPD_STOP;
	
	//进入Low-Power Run
	//HAL_PWREx_EnableLowPowerRunMode();
	SET_BIT(PWR->CR1, PWR_CR1_LPR);		//0x40007000
	
	//等待电源切换完成
	while(!HAL_IS_BIT_SET(PWR->SR2, PWR_SR2_REGLPF));	//0x40007014

    while(1)
    {
        //...
    }

    //不会执行到这里
	CLEAR_BIT(PWR->CR1, PWR_CR1_LPR);	//退出Low-Power Run
	while (HAL_IS_BIT_SET(PWR->SR2, PWR_SR2_REGLPF));	//等待电源切换完成
	while (!HAL_IS_BIT_SET(PWR->SR2, PWR_SR2_FLASH_RDY));	//等待Flash就绪
	__enable_irq();	//打开全局中断
}

疑问

主频不设置成2M以下,好像也是能把电源切成Low-Power,估计可能运行不太稳定吧。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值