低功耗的含义不必过多解释,一听就能懂。
低功耗对电池供电产品尤其重要。
STM32的有三种低功耗模式,即睡眠模式、停止模式和待机模式。
在我的印象中,停止不就是关机吗?但并不是。
在系统或电源复位以后,微控制器处于运行状态。当CPU不需继续运行时,可以利用多种低功耗模式来节省功耗,例如等待某个外部事件时。用户需要根据最低电源消耗、最快速启动时间和可用的唤醒源等条件,选定一个最佳的低功耗模式。
● 睡眠模式
仅Cortex™-M3内核停止,其他所有外设包括Cortex-M3核心的外设,如NVIC、系统时
钟(SysTick)等仍在运行。
● 停止模式
所有的时钟都已停止。
● 待机模式
1.8V电源关闭。
WFI:Wait For Interrupt,WFE:Wait For Event
WFI(Wait for interrupt)和WFE(Wait for event)是两个让ARM核进入low-power模式的指令,由ARM architecture定义,由ARM core实现。
WFI = wait for interrupt 等待中断,即下一次中断发生前都在此hold住不干活。
WFE = wait for event 等待事件,即下一次事件发生前都在此hold住不干活。
这是ARM里CMSIS内核中的指令:
cmsis_arm.cc里有如下说明 /** \brief Wait For Interrupt \details Wait For Interrupt is a hint instruction that suspends execution until one of a number of events occurs. */ #define __WFI __wfi /** \brief Wait For Event \details Wait For Event is a hint instruction that permits the processor to enter a low-power state until one of a number of events occurs. */ #define __WFE __wfe
WFI 或 WFE 命令,它们实质上都是内核指令,在库文件 core_cmInstr.h 中把这些指令封装成了函数。
对于这两个指令,我们应用时一般只需要知道,调用它们都能进入低功耗模式。__wfi及__wfe 是编译器内置的函数,函数内部使用调用了相应的汇编指令。其中 WFI指令决定了它需要用中断唤醒,而WFE 则决定了它可用事件来唤醒,关于它们更详细的区别可查阅《cortex-CM3/CM4权威指南》了解。
注:非低功耗状态下被称为运行模式。
run、sleep、stop、standby
从表中可以看到,这三种低功耗模式层层递进,运行的时钟或芯片功能越来越少,因而功耗越来越低。
此外,在运行模式下,可以通过以下方式中的一种降低功耗:
● 降低系统时钟
● 关闭APB和AHB总线上未被使用的外设时钟。
在运行模式下,通过对预分频寄存器进行编程,可以降低任意一个系统时钟(SYSCLK、
HCLK、PCLK1、PCLK2)的速度。进入睡眠模式前,也可以利用预分频器来降低外设的时钟。
在睡眠模式中,仅关闭了内核时钟,内核停止运行,但其片上外设,CM3核心的外设全都还照常运行,在软件上表现为不再执行新的代码。
这个状态会保留睡眠前的内核寄存器、内存的数据。
唤醒后 ,若由中断唤醒,先进入中断,退出中断服务程序后,接着执行 WFI 指令后的程序;若由事件唤醒,直接接着执行 WFE 后的程序。
唤醒延迟:无延迟。
停止模式是在Cortex™-M3的深睡眠模式基础上结合了外设的时钟控制机制,在停止模式下电压调节器可运行在正常或低功耗模式。此时在1.8V供电区域的的所有时钟都被停止,PLL、HSI和HSE RC振荡器的功能被禁止,SRAM和寄存器内容被保留下来。
在停止模式下,所有的I/O引脚都保持它们在运行模式时的状态。
在停止模式中,进一步关闭了其它所有的时钟,于是所有的外设都停止了工作,但由于其 1.8V 区域的部分电源没有关闭,还保留了内核的寄存器、内存的信息,所以从停止模式唤醒,并重新开启时钟后,还可以从上次停止处继续执行代码。
唤醒后,若由中断唤醒,先进入中断,退出中断服务程序后,接着执行 WFI 指令后的程序;若由事件唤醒,直接接着执行 WFE 后的程序。停止模式唤醒后,STM32 会使用 HSI(f1 的 HSI 为 8M,f4 为 12M)作为系统时钟。所以,有必要在唤醒以后,在程序上重新配置系统时钟,将时钟切换回 HSE。
唤醒延迟 :基础延迟为 HSI 振荡器的启动时间,若调压器工作在低功耗模式,还需要加上调压器从低功耗切换至正常模式下的时间,若 FLASH 工作在掉电模式,还需要加上 FLASH 从掉电模式唤醒的时间。
待机模式可实现系统的最低功耗。该模式是在Cortex-M3深睡眠模式时关闭电压调节器。整个1.8V供电区域被断电。PLL、HSI和HSE振荡器也被断电。SRAM和寄存器内容丢失。只有备份的寄存器和待机电路维持供电。
它除了关闭所有的时钟,还把 1.8V 区域的电源也完全关闭了,也就是说,从待机模式唤醒后,由于没有之前代码的运行记录,只能对芯片复位,重新检测 boot 条件,从头开始执行程序。
在待机模式下,所有的I/O引脚处于高阻态,除了以下的引脚:
● 复位引脚(始终有效)
● 当被设置为防侵入或校准输出时的TAMPER引脚
● 被使能的唤醒引脚
功耗越低的低功耗,其唤醒条件越严苛。
- 睡眠模式下,任一一个中断都是可以唤醒的(针对调用 WFI 命令进入的睡眠);
- 停止模式下,是任一一个外部中断才能唤醒,注意是任一外部中断,不是任一中断;
- 待机模式又有所不同,外部中断并不能唤醒待机模式,比较常见的唤醒有:1.WKUP 引脚上升沿(按下 PA0,使之出现上升沿,只要 PA0 出现一个上升沿即可唤醒单片机,而不管这个上升沿持续多长时间,软件上只需要在进入待机模式之前,将 PA0 配置为唤醒功能即可);
2.NRST 引脚复位(即按下复位按键),这种方式是让单片机重新复位了,这是硬件上的唤醒;
3. 单片机系统重新上电,这跟第 2 点是一样的,都是硬件复位。
低功耗模式下的自动唤醒(AWU)
RTC可以在不需要依赖外部中断的情况下唤醒低功耗模式下的微控制器(自动唤醒模式)。RTC提供一个可编程的时间基数,用于周期性从停止或待机模式下唤醒。通过对备份区域控制寄存器(RCC_BDCR)的RTCSEL[1:0]位的编程,三个RTC时钟源中的二个时钟源可以选作实现此功能。
● 低功耗32.768kHz外部晶振(LSE)
该时钟源提供了一个低功耗且精确的时间基准。(在典型情形下消耗小于1µA)
● 低功耗内部RC振荡器(LSI RC)
使用该时钟源,节省了一个32.768kHz晶振的成本。但是RC振荡器将少许增加电源消耗。
为了用RTC闹钟事件将系统从停止模式下唤醒,必须进行如下操作:
● 配置外部中断线17为上升沿触发。
● 配置RTC使其可产生RTC闹钟事件。
如果要从待机模式中唤醒,不必配置外部中断线17。
更高端的单片机比如F4有自动唤醒,而F1没有直接的自动唤醒,但可以通过RTC的闹钟事件来实现自动唤醒。
相关HAL函数
stm32f1xx_hal_pwr.h /** @addtogroup PWR_Exported_Functions_Group1 Initialization and de-initialization functions * @{ */ /* Initialization and de-initialization functions *******************************/ void HAL_PWR_DeInit(void); void HAL_PWR_EnableBkUpAccess(void); void HAL_PWR_DisableBkUpAccess(void); /** * @} */ /** @addtogroup PWR_Exported_Functions_Group2 Peripheral Control functions * @{ */ /* Peripheral Control functions ************************************************/ void HAL_PWR_ConfigPVD(PWR_PVDTypeDef *sConfigPVD); /* #define HAL_PWR_ConfigPVD 12*/ void HAL_PWR_EnablePVD(void); void HAL_PWR_DisablePVD(void); /* WakeUp pins configuration functions ****************************************/ void HAL_PWR_EnableWakeUpPin(uint32_t WakeUpPinx); void HAL_PWR_DisableWakeUpPin(uint32_t WakeUpPinx); /* Low Power modes configuration functions ************************************/ void HAL_PWR_EnterSTOPMode(uint32_t Regulator, uint8_t STOPEntry); void HAL_PWR_EnterSLEEPMode(uint32_t Regulator, uint8_t SLEEPEntry); void HAL_PWR_EnterSTANDBYMode(void); void HAL_PWR_EnableSleepOnExit(void); void HAL_PWR_DisableSleepOnExit(void); void HAL_PWR_EnableSEVOnPend(void); void HAL_PWR_DisableSEVOnPend(void); void HAL_PWR_PVD_IRQHandler(void); void HAL_PWR_PVDCallback(void);
Regulator是电压调节器的选项;STOPEntry是关于用WFI还是WFE的选项。
/* Includes ------------------------------------------------------------------*/ #include "MyApplication.h" /* Private define-------------------------------------------------------------*/ /* Private variables----------------------------------------------------------*/ /* Private function prototypes------------------------------------------------*/ static void Sleep_Mode(void); //睡眠模式 static void Stop_Mode(void); //停机模式 static void Standby_Mode(void); //待机模式 /* Public variables-----------------------------------------------------------*/ LowPowerConsumption_t LowPowerConsumption = { FALSE, FALSE, FALSE, Sleep_Mode, Stop_Mode, Standby_Mode }; /* * @name Sleep_Mode * @brief 睡眠模式 * @param None * @retval None */ static void Sleep_Mode(void) { /***Note:任意中断都可以将系统从睡眠模式中唤醒***/ //关闭Systick与定时器中断,否则,进入睡眠模式后立马被唤醒 HAL_SuspendTick(); HAL_TIM_Base_Stop_IT(&htim6); //关闭外部中断0至2,只允许触摸按键4中断退出睡眠模式 HAL_NVIC_DisableIRQ(EXTI0_IRQn); HAL_NVIC_DisableIRQ(EXTI1_IRQn); HAL_NVIC_DisableIRQ(EXTI2_IRQn); HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON,PWR_SLEEPENTRY_WFI); //恢复中断 HAL_ResumeTick(); HAL_TIM_Base_Start_IT(&htim6); HAL_NVIC_EnableIRQ(EXTI0_IRQn); HAL_NVIC_EnableIRQ(EXTI1_IRQn); HAL_NVIC_EnableIRQ(EXTI2_IRQn); } /* * @name Stop_Mode * @brief 停机模式 * @param None * @retval None */ static void Stop_Mode(void) { /***Note:外部中断可将系统从停机模式中唤醒***/ //关闭外部中断0至2,只允许触摸按键4中断退出停机模式 HAL_NVIC_DisableIRQ(EXTI0_IRQn); HAL_NVIC_DisableIRQ(EXTI1_IRQn); HAL_NVIC_DisableIRQ(EXTI2_IRQn); HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON,PWR_STOPENTRY_WFI); //退出停止模式时,HSI RC振荡器被选为系统时钟 //系统时钟需要重新初始化 SystemClock_Config(); //恢复中断 HAL_NVIC_EnableIRQ(EXTI0_IRQn); HAL_NVIC_EnableIRQ(EXTI1_IRQn); HAL_NVIC_EnableIRQ(EXTI2_IRQn); } /* * @name Standby_Mode * @brief 待机模式 * @param None * @retval None */ static void Standby_Mode(void) { HAL_PWR_EnterSTANDBYMode(); } /******************************************************** End Of File ********************************************************/