• STM32实战总结:HAL之低功耗


    低功耗的含义不必过多解释,一听就能懂。

    低功耗对电池供电产品尤其重要。

    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内核中的指令:

    1. cmsis_arm.cc里有如下说明
    2. /**
    3. \brief Wait For Interrupt
    4. \details Wait For Interrupt is a hint instruction that suspends execution until one of a number of events occurs.
    5. */
    6. #define __WFI __wfi
    7. /**
    8. \brief Wait For Event
    9. \details Wait For Event is a hint instruction that permits the processor to enter
    10. a low-power state until one of a number of events occurs.
    11. */
    12. #define __WFE __wfe

    WFI 或 WFE 命令,它们实质上都是内核指令,在库文件 core_cmInstr.h 中把这些指令封装成了函数。

    对于这两个指令,我们应用时一般只需要知道,调用它们都能进入低功耗模式。__wfi及__wfe 是编译器内置的函数,函数内部使用调用了相应的汇编指令。其中 WFI指令决定了它需要用中断唤醒,而WFE 则决定了它可用事件来唤醒,关于它们更详细的区别可查阅《cortex-CM3/CM4权威指南》了解。

    注:非低功耗状态下被称为运行模式。

    run、sleep、stop、standby

    从表中可以看到,这三种低功耗模式层层递进,运行的时钟或芯片功能越来越少,因而功耗越来越低。

    此外,在运行模式下,可以通过以下方式中的一种降低功耗:

    ● 降低系统时钟

    ● 关闭APBAHB总线上未被使用的外设时钟。

    在运行模式下,通过对预分频寄存器进行编程,可以降低任意一个系统时钟(SYSCLK

    HCLKPCLK1PCLK2)的速度。进入睡眠模式前,也可以利用预分频器来降低外设的时钟。

    睡眠模式

    在睡眠模式中,仅关闭了内核时钟,内核停止运行,但其片上外设,CM3核心的外设全都还照常运行,在软件上表现为不再执行新的代码

    这个状态会保留睡眠前的内核寄存器、内存的数据。

    唤醒后 ,若由中断唤醒,先进入中断,退出中断服务程序后,接着执行 WFI 指令后的程序;若由事件唤醒,直接接着执行 WFE 后的程序。

    唤醒延迟:无延迟。

    停止模式

    停止模式是在Cortex™-M3的深睡眠模式基础上结合了外设的时钟控制机制,在停止模式下电压调节器可运行在正常或低功耗模式。此时在1.8V供电区域的的所有时钟都被停止,PLLHSI和HSE RC振荡器的功能被禁止,SRAM和寄存器内容被保留下来。

    在停止模式下,所有的I/O引脚都保持它们在运行模式时的状态。

    在停止模式中,进一步关闭了其它所有的时钟,于是所有的外设都停止了工作,但由于其 1.8V 区域的部分电源没有关闭,还保留了内核的寄存器、内存的信息,所以从停止模式唤醒,并重新开启时钟后,还可以从上次停止处继续执行代码

    唤醒后,若由中断唤醒,先进入中断,退出中断服务程序后,接着执行 WFI 指令后的程序;若由事件唤醒,直接接着执行 WFE 后的程序。停止模式唤醒后,STM32 会使用 HSI(f1 的 HSI 为 8M,f4 为 12M)作为系统时钟。所以,有必要在唤醒以后,在程序上重新配置系统时钟,将时钟切换回 HSE。

    唤醒延迟 :基础延迟为 HSI 振荡器的启动时间,若调压器工作在低功耗模式,还需要加上调压器从低功耗切换至正常模式下的时间,若 FLASH 工作在掉电模式,还需要加上 FLASH 从掉电模式唤醒的时间。

    待机模式

    待机模式可实现系统的最低功耗。该模式是在Cortex-M3深睡眠模式时关闭电压调节器。整个1.8V供电区域被断电。PLLHSIHSE振荡器也被断电。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函数 

     

    1. stm32f1xx_hal_pwr.h
    2. /** @addtogroup PWR_Exported_Functions_Group1 Initialization and de-initialization functions
    3. * @{
    4. */
    5. /* Initialization and de-initialization functions *******************************/
    6. void HAL_PWR_DeInit(void);
    7. void HAL_PWR_EnableBkUpAccess(void);
    8. void HAL_PWR_DisableBkUpAccess(void);
    9. /**
    10. * @}
    11. */
    12. /** @addtogroup PWR_Exported_Functions_Group2 Peripheral Control functions
    13. * @{
    14. */
    15. /* Peripheral Control functions ************************************************/
    16. void HAL_PWR_ConfigPVD(PWR_PVDTypeDef *sConfigPVD);
    17. /* #define HAL_PWR_ConfigPVD 12*/
    18. void HAL_PWR_EnablePVD(void);
    19. void HAL_PWR_DisablePVD(void);
    20. /* WakeUp pins configuration functions ****************************************/
    21. void HAL_PWR_EnableWakeUpPin(uint32_t WakeUpPinx);
    22. void HAL_PWR_DisableWakeUpPin(uint32_t WakeUpPinx);
    23. /* Low Power modes configuration functions ************************************/
    24. void HAL_PWR_EnterSTOPMode(uint32_t Regulator, uint8_t STOPEntry);
    25. void HAL_PWR_EnterSLEEPMode(uint32_t Regulator, uint8_t SLEEPEntry);
    26. void HAL_PWR_EnterSTANDBYMode(void);
    27. void HAL_PWR_EnableSleepOnExit(void);
    28. void HAL_PWR_DisableSleepOnExit(void);
    29. void HAL_PWR_EnableSEVOnPend(void);
    30. void HAL_PWR_DisableSEVOnPend(void);
    31. void HAL_PWR_PVD_IRQHandler(void);
    32. void HAL_PWR_PVDCallback(void);

    Regulator是电压调节器的选项;STOPEntry是关于用WFI还是WFE的选项。

    关键代码

    1. /* Includes ------------------------------------------------------------------*/
    2. #include "MyApplication.h"
    3. /* Private define-------------------------------------------------------------*/
    4. /* Private variables----------------------------------------------------------*/
    5. /* Private function prototypes------------------------------------------------*/
    6. static void Sleep_Mode(void); //睡眠模式
    7. static void Stop_Mode(void); //停机模式
    8. static void Standby_Mode(void); //待机模式
    9. /* Public variables-----------------------------------------------------------*/
    10. LowPowerConsumption_t LowPowerConsumption =
    11. {
    12. FALSE,
    13. FALSE,
    14. FALSE,
    15. Sleep_Mode,
    16. Stop_Mode,
    17. Standby_Mode
    18. };
    19. /*
    20. * @name Sleep_Mode
    21. * @brief 睡眠模式
    22. * @param None
    23. * @retval None
    24. */
    25. static void Sleep_Mode(void)
    26. {
    27. /***Note:任意中断都可以将系统从睡眠模式中唤醒***/
    28. //关闭Systick与定时器中断,否则,进入睡眠模式后立马被唤醒
    29. HAL_SuspendTick();
    30. HAL_TIM_Base_Stop_IT(&htim6);
    31. //关闭外部中断02,只允许触摸按键4中断退出睡眠模式
    32. HAL_NVIC_DisableIRQ(EXTI0_IRQn);
    33. HAL_NVIC_DisableIRQ(EXTI1_IRQn);
    34. HAL_NVIC_DisableIRQ(EXTI2_IRQn);
    35. HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON,PWR_SLEEPENTRY_WFI);
    36. //恢复中断
    37. HAL_ResumeTick();
    38. HAL_TIM_Base_Start_IT(&htim6);
    39. HAL_NVIC_EnableIRQ(EXTI0_IRQn);
    40. HAL_NVIC_EnableIRQ(EXTI1_IRQn);
    41. HAL_NVIC_EnableIRQ(EXTI2_IRQn);
    42. }
    43. /*
    44. * @name Stop_Mode
    45. * @brief 停机模式
    46. * @param None
    47. * @retval None
    48. */
    49. static void Stop_Mode(void)
    50. {
    51. /***Note:外部中断可将系统从停机模式中唤醒***/
    52. //关闭外部中断02,只允许触摸按键4中断退出停机模式
    53. HAL_NVIC_DisableIRQ(EXTI0_IRQn);
    54. HAL_NVIC_DisableIRQ(EXTI1_IRQn);
    55. HAL_NVIC_DisableIRQ(EXTI2_IRQn);
    56. HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON,PWR_STOPENTRY_WFI);
    57. //退出停止模式时,HSI RC振荡器被选为系统时钟
    58. //系统时钟需要重新初始化
    59. SystemClock_Config();
    60. //恢复中断
    61. HAL_NVIC_EnableIRQ(EXTI0_IRQn);
    62. HAL_NVIC_EnableIRQ(EXTI1_IRQn);
    63. HAL_NVIC_EnableIRQ(EXTI2_IRQn);
    64. }
    65. /*
    66. * @name Standby_Mode
    67. * @brief 待机模式
    68. * @param None
    69. * @retval None
    70. */
    71. static void Standby_Mode(void)
    72. {
    73. HAL_PWR_EnterSTANDBYMode();
    74. }
    75. /********************************************************
    76. End Of File
    77. ********************************************************/

     

  • 相关阅读:
    递归算法讲解,深度理解递归
    前端面试宝典React篇05 如何设计 React 组件?
    天佑药品销售管理系统
    国内低代码开发平台靠谱的都有哪些?
    CVPR 2022 | SharpContour:一种基于轮廓变形 实现高效准确实例分割的边缘细化方法
    还在失眠?试试镁吧
    java - 数据结构,时间复杂度和空间复杂度
    创建交换机、队列以及绑定关系
    wpf devexpress添加TreeListControl到项目
    Redis-Cluster集群、Redis持久化、Redis作MySQL的缓存服务器、配置gearman实现Redis和MySQL数据同步
  • 原文地址:https://blog.csdn.net/qq_28576837/article/details/128091232