STM32L431 低功耗设计 笔记
STM32L4七大工作模式
参照《STM32L4A6xG Datasheet》描述了7种工作模式
睡眠模式 Sleep mode
- 在睡眠模式下,仅CPU核在睡觉,因此程序处于睡觉前状态暂停。程序的运行态主要由CPU核寄存器、RAM内的数据相关。所谓CPU睡眠指CPU不会再进行取指、译码、执行的动作,CPU睡眠后相关寄存器保持不变,RAM内数据保持不变。
- 当发生唤醒事件时,所有外设都可以继续运行,并且可以唤醒CPU。程序从执行对应的中断/事件处理代码,然后从睡眠前的状态继续执行。
低功耗运行/睡眠模式
低功耗运行模式Low-power run (LPRun): 这个模式下,CPU可以运行程序,但是跑的较慢。
- 该模式可通过低功耗稳压器Low-Power Regulator为内核逻辑电路提供的电压工作来实现,显然降低工作电压可显著降低功耗。
- 降低CPU的工作频率,CPU频率限制为2 MHz。独立时钟外设可以由HSI16提供时钟。
低功耗睡眠模式Low-power sleep (LPSleep):仅低功耗运行模式可进入该模式。仅CPU时钟停止,当被唤醒时,系统将恢复为低功耗运行模式LPRun。
停止模式 STOP
提供三种停止模式:停止0,停止1和停止2模式
- 停止模式可实现最低功耗,同时保留SRAM和寄存器的内容。由供电的所有时钟都停止运行,PLL,MSI RC,HSI16
- RC和HSE晶体振荡器被禁用,但LSE或LSI时钟处于运行状态。
- RTC可以保持运行状态(是通过软件配置可实现带RTC的停止模式,以及不带RTC的停止模式)。
- 另外某些具有唤醒功能的外设可以在停止模式下启用HSI16 RC振荡器,以检测其唤醒事件。
差异点:
- 三种模式的内部供电电源不一样,Stop0由MR(Main Regulator)提供,而Stop1/Stop2则由LPR(Low PowerRegulator提供)。
- 唤醒源Stop2支持的唤醒源相比Stop0/Stop1少一些
- 所需功耗则是Stop0>Stop1>Stop2,Stop0由MR供电,所以三种模式里功耗相对略大一些。
- 唤醒时间则是Stop0<Stop1<Stop2。
相同点
- CPU、FLASH都处于停止运行状态,而SRAM仍然处于运行,这意味着内存里的内容会保持住。LSE(Low Speed External)/LSI(Low Speed Internal)时钟运行为唤醒源提供运行时钟。
待机/关机模式
区别基本就是 唤醒源不一样,具体看下面图。
待机模式Standby mode:
- 待机模式用于通过BOR实现低功耗。内部稳压器已关闭,因此由供电的所有电路都被关闭, PLL,MSI RC,HSI16 RC和HSE晶体振荡器等时钟电路也处于关闭状态。
- RTC可以设置为运行状态(因此与Stop模式类似,可通过软件配置实现带RTC的待机模式,不带RTC的待机模式)。
关机模式 Shutdown mode:
- 关机模式可实现最低功耗。内部稳压器已关闭,因此电源已关闭。PLL,HSI16,MSI,LSI和HSE振荡器也被关闭。
- 进入待机模式后,除了备份域和待机电路中的寄存器外,SRAM1和寄存器内容都会丢失。但SRAM2可配置为数据保持状态。
- RTC可以保持活动状态(同样可以通过软件配置成带RTC的关机模式,不带RTC的关机模式)。BOR在关机模式下不可用。在此模式下无法监视电源电压,因此不支持切换到备份域。除了备份域中的寄存器外,SRAM1,SRAM2和寄存器内容都会丢失。
- 当发生外部复位也即NRST引脚检测到复位事件,WKUP引脚事件(可配置成上升或下降沿触发模式)或RTC事件(警报,定期唤醒,时间戳,篡改)时,设备退出关机模式。唤醒后的系统时钟为MSI,频率为4MHz。
- 当发生外部复位(NRST引脚)、IWDG复位、WKUP唤醒引脚事件(上升沿/下降沿)或RTC事件(警报,定期唤醒,时间戳,篡改)或检测到故障时,设备退出待机模式。唤醒后的系统时钟由MSI提供,最高可为8 MHz。
STM32L4模式概述

设计思路
- 关闭所有开启的外设使能
- 把引脚设置成模拟输入或者浮空输入
- 关闭外设时钟
- 失能PVD、PVM以及VREFBUF。 L4参考手册P149
- 关闭或者挂起SysTick定时器。 L4参考手册P150
- 所有中悬挂断标志都要清除,挂起或者关闭SysTick定时器就不会产生中断标志。
- 当退出低功耗时,要重设时钟,可以通过STOPWUCK 来设置,就不用再次设置系统时钟了,使用外设要重新初始化,RTC和看门狗就不用了。
完成上面流程之后发现功耗还是很高总结一下耗电的来源
功耗主要来自这几个地方:
- HSE / LSE 的外部晶振功耗肯定比相对应的HSI / MSI / LSI要高
- 尽量关闭PLL,这个东西消耗电流达到了200uA
- 运行主频是很耗电的,够用就好,尽量使用HSE 4M的方式,实际中我使用8M进行分频的
- HCLK也很耗电,对其分频可以节省电能
- 电压也与功耗 有关,虽然关系不大,也是可见的关系,3.3V比1.8V的电压,功耗还是大了很多
- 外部功耗大硬件电路可以设计MOS管进行开关控制,用到时打开,休眠考虑关闭
- 启动方式可能会影响功耗?目前时从Flash中启动,从SRAM启动应该功耗会低一点。目前没做测试,有待更新
手册中查到的运行功耗:
运行模式下的电流消耗取决于几个参数。
- 执行的二进制代码(程序本身+编译器影响)
- 程序在内存中的位置
- 设备软件配置
- I/O引脚负载和开关率
- 溫度
- 从闪存或SRAM执行
- 当从Flash中执行时:ART加速器配置(Cache,预取)。
- 从SRAM执行时:SRAM1或SRAM2。
进入低功耗钱关闭不用的串口或者IIC或者SPI等通信设备。 如UART
HAL_UART_MspDeInit(&huart1);//该函数会关闭中断,清空内部寄存器,并且设置引脚为GPIO模式。所以可以达到降低功耗的目的。
在唤醒之后应该开启串口中断
HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART1_IRQn);
HAL_NVIC_SetPriority(USART2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART2_IRQn);
HAL_NVIC_SetPriority(USART3_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART3_IRQn);
低功耗模式
1.睡眠模式
在睡眠模式中,仅关闭了内核时钟,内核停止运行,但其片上外设,CM3 核心的外设全都还照常运行。有两种方式进入睡眠模式,它的进入方式决定了从睡眠唤醒的方式,分别是 WFI(wait for interrupt) 和 WFE(wait for event),即由等待“中断”唤醒和由“事件”唤醒。
- 立即睡眠: 在执行 WFI 或 WFE 指令时立即进入睡眠模式。
- 退出时睡眠: 在退出优先级最低的中断服务程序后才进入睡眠模式。
- 进入方式: 内核寄存器的 SLEEPDEEP=0 ,然后调用 WFI 或
WFE 指令即可进入睡眠模式;SLEEPONEXIT=1 时,进入“退出时睡眠”模式。 - 唤醒方式: 如果是使用 WFI 指令睡眠的,则可使用任意中断唤醒;如果是使用 WFE 指令睡眠的,则由事件唤醒。
- 睡眠时: 关闭内核时钟,内核停止,而外设正常运行,在软件上表现为不再执行新的代码。这个状态会保留睡眠前的内核寄存器、内存的数据。
- 唤醒延迟: 无延迟。
- 唤醒后: 若由中断唤醒,先进入中断,退出中断服务程序后,接着执行 WFI 指令后的程序;若由事件唤醒,直接接着执行 WFE 后的程序。
2.停止模式
在停止模式中,进一步关闭了其它所有的时钟,于是所有的外设都停止了工作,但由于其 1.8V 区域的部分电源没有关闭,还保留了内核的寄存器、内存的信息,所以从停止模式唤醒,并重新开启时钟后,还可以从上次停止处继续执行代码。停止模式可以由任意一个外部中断(EXTI)唤醒,在停止模式中可以选择电压调节器为开模式或低功耗模式。
-
调压器低功耗模式: 在停止模式下调压器可工作在正常模式或低功耗模式,可进一步降低功耗。
-
进入方式: 内核寄存器的 SLEEPDEEP=1 ,PWR_CR 寄存器中的 PDDS=0 ,然后调用 WFI 或 WFE
指令即可进入停止模式;PWR_CR 寄存器的 LPDS=0 时,调压器工作在正常模式, LPDS=1 时工作在低功耗模式。 -
唤醒方式: 如果是使用 WFI 指令睡眠的,可使用任意 EXTI 线的中断唤醒;如果是使用 WFE 指令睡眠的,可使用任意配置为事件模式的
EXTI 线事件唤醒。 -
停止时: 内核停止,片上外设也停止。这个状态会保留停止前的内核寄存器、内存的数据。
-
唤醒延迟: 基础延迟为 HSI 振荡器的启动时间,若调压器工作在低功耗模式,还需要加上调压器从低功耗切换至正常模式下的时间。
-
唤醒后: 若由中断唤醒,先进入中断,退出中断服务程序后,接着执行 WFI 指令后的程序;若由事件唤醒,直接接着执行 WFE 后的程序。唤醒后,STM32 会使用 HSI 作为系统时钟。
3.待机模式
待机模式,它除了关闭所有的时钟,还把 1.8V 区域的电源也完全关闭了,也就是说,从待机模式唤醒后,由于没有之前代码的运行记录,只能对芯片复位,重新检测 boot 条件,从头开始执行程序。它有四种唤醒方式,分别是 WKUP(PA0)引脚的上升沿,RTC 闹钟事件,NRST 引脚的复位和 IWDG(独立看门狗)复位。
- 进入方式: 内核寄存器的 SLEEPDEEP=1 ,PWR_CR 寄存器中的 PDDS=1 ,PWR_CR 寄存器中的唤醒状态位 WUF=0 ,然后调用 WFI 或 WFE 指令即可进入待机模式。
- 唤醒方式: 通过 WKUP 引脚的上升沿,RTC 闹钟、唤醒、入侵、时间戳事件或 NRST 引脚外部复位及 IWDG 复位唤醒。
- 待机时: 内核停止,片上外设也停止;内核寄存器、内存的数据会丢失;除复位引脚、RTC_AF1 引脚及 WKUP 引脚,其它 I/O 口均工作在高阻态。
- 唤醒延迟: 芯片复位的时间。
- 唤醒后: 相当于芯片复位,在程序表现为从头开始执行代码。
WFI与WFE命令
我们了解到进入各种低功耗模式时都需要调用 WFI 或 WFE 命令,它们实质上都是内核指令,在库文件 core_cm3.h 中把这些指令封装成了函数。
/** brief 等待中断
等待中断 是一个暂停执行指令
暂停至任意中断产生后被唤醒
*/
#define __WFI __wfi
/** brief 等待事件
等待事件 是一个暂停执行指令
暂停至任意事件产生后被唤醒
*/
#define __WFE __wfe
对于这两个指令,我们应用时一般只需要知道,调用它们都能进入低功耗模式,需要使用函数的格式“__WFI();”和“__WFE();”来调用(因为__wfi 及__wfe 是编译器内置的函数,函数内部调用了相应的汇编指令)。
其中 WFI 指令决定了它需要用中断唤醒,而 WFE 则决定了它可用事件来唤醒。
转载 Leung_ManWah 大佬文章 原文链接
代码实现
实验中仅使用串口1,通过串口发送字符5,启动STOP2模式,通过PC9引脚唤醒 ,系统时钟2M来达到最低功耗
STM32CubeMX设置
串口1设置
中断设置 高电平唤醒
编写进入低功耗前代码
printf("\r\n=== Power Stop ===\r\n");
HAL_DBGMCU_DisableDBGStopMode();
HAL_DBGMCU_DisableDBGSleepMode();
HAL_DBGMCU_DisableDBGStandbyMode();//关闭调试接口
/* 关闭串口1时钟 */
HAL_UART_DeInit(&huart1);
__HAL_RCC_USART1_CLK_DISABLE();
/* 设置所有引脚为模拟输入 */
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = GPIO_PIN_All;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA,&GPIO_InitStruct);
HAL_GPIO_Init(GPIOB,&GPIO_InitStruct);
HAL_GPIO_Init(GPIOC,&GPIO_InitStruct);
/* 关闭所有GPIO时钟 */
__HAL_RCC_GPIOA_CLK_DISABLE();
__HAL_RCC_GPIOB_CLK_DISABLE();
__HAL_RCC_GPIOC_CLK_DISABLE();//
/*打开中断引脚时钟*/
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = PWR_EN_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
HAL_GPIO_Init(PWR_EN_GPIO_Port, &GPIO_InitStruct);
/* EXTI interrupt init*/
HAL_NVIC_SetPriority(EXTI9_5_IRQn, 6, 0);
HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);
HAL_PWR_DisablePVD();//禁用电源电压检测器(PVD)
HAL_SYSCFG_DisableVREFBUF();//禁用内部电压基准缓冲器(VREFBUF)
HAL_SYSCFG_DisableIOAnalogSwitchBooster();//禁用I/O模拟量开关电压升压器。
__HAL_PWR_PVM3_EXTI_DISABLE_RISING_FALLING_EDGE();//禁用PVM3中断
__HAL_PWR_PVM4_EXTI_DISABLE_RISING_FALLING_EDGE();//禁用PVM4中断
// 唤醒后选择哪个启动时钟源
__HAL_RCC_WAKEUPSTOP_CLK_CONFIG(RCC_STOP_WAKEUPCLOCK_MSI);
// 降低核心工作电压,需要根据实际工作频率设置,建议在Cubemx内的RCC项目中配置
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);
HAL_SuspendTick(); // 暂停滴答时钟,防止通过滴答时钟中断唤醒
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU); // 清除唤醒标记
__HAL_RCC_PWR_CLK_ENABLE(); //运行电源管理时钟
// HAL_DBGMCU_EnableDBGStopMode(); //stop模式下进行调试
// HAL_PWR_EnableBkUpAccess();
HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI); //进入stop 2 mode
编写退出低功耗程序
SystemClock_Config(); // 刚从STOP模式唤醒时钟默认使用内部高速8M时钟,所以需要重新配置时钟
HAL_ResumeTick(); // 被唤醒后,恢复滴答时钟
MX_GPIO_Init();
MX_USART1_UART_Init();
HAL_UART_Receive_IT(&huart1, &usart1_buf.aRxBuff, 1);
// 获取重新配置后的时钟状态
SYSCLK_Frequency = HAL_RCC_GetSysClockFreq();
HCLK_Frequency = HAL_RCC_GetHCLKFreq();
PCLK1_Frequency = HAL_RCC_GetPCLK1Freq();
PCLK2_Frequency = HAL_RCC_GetPCLK2Freq();
SYSCLK_Source = __HAL_RCC_GET_SYSCLK_SOURCE();
HAL_Delay(200);
printf("\r\n 重新配置后的时钟状态:\r\n");
printf(" SYSCLK 频率:%d,\r\n HCLK 频率:%d,\r\n PCLK1 频率:%d,\r\n PCLK2 频率:%d,\r\n 时钟源:%d (0 表示 HSI,8 表示 PLLCLK)\n",SYSCLK_Frequency,HCLK_Frequency,PCLK1_Frequency,PCLK2_Frequency,SYSCLK_Source);
HAL_Delay(200);
下载程序 观察表电流1.01mA,串口发送‘5’成功进入低功耗,电流2.3uA左右,按下唤醒按键程序成功被唤醒。
下载程序之后
判断是否进入低功耗看是否程序停了下来。
判断是否达到低功耗的标准测功耗是否符合该模式下的功耗。
并不是进入了低功耗就代表达到了低功耗的标准。
如果做不到,请考虑是否将全部IO配置为模拟输入模式
调试问题
问题原因:低功耗模式进去有很快退出
查各种中断、事件看看有没有唤醒。
发现经常被唤醒的是系统滴答
void HAL_SuspendTick(void);
void HAL_ResumeTick(void);
调试模式时独立看门狗问题
/**
* @brief IWDG Peripherals Debug mode
*/
#if defined (DBGMCU_APB1_FZ_DBG_IWDG_STOP)
#define __HAL_DBGMCU_FREEZE_IWDG() SET_BIT(DBGMCU->APB1FZ, DBGMCU_APB1_FZ_DBG_IWDG_STOP)
#define __HAL_DBGMCU_UNFREEZE_IWDG() CLEAR_BIT(DBGMCU->APB1FZ, DBGMCU_APB1_FZ_DBG_IWDG_STOP)
#endif
__HAL_DBGMCU_FREEZE_IWDG() 冻结看门狗,此时在调试模式下看门狗就不会复位
__HAL_DBGMCU_UNFREEZE_IWDG()恢复看门狗
STOP模式可调试模式
/* DBGMCU Peripheral Control functions *****************************************/
void HAL_DBGMCU_EnableDBGSleepMode(void);
void HAL_DBGMCU_DisableDBGSleepMode(void);
void HAL_DBGMCU_EnableDBGStopMode(void);
void HAL_DBGMCU_DisableDBGStopMode(void);
void HAL_DBGMCU_EnableDBGStandbyMode(void);
void HAL_DBGMCU_DisableDBGStandbyMode(void);
如 HAL_DBGMCU_EnableDBGStopMode,就可以在stop模式下进行调试。
HAL_DBGMCU_EnableDBGSleepMode 在Sleep模式下进行调试
HAL_DBGMCU_EnableDBGStandbyMode 在待机模式下进行调试