当前位置:   article > 正文

MCU低功耗模式浅析

低功耗模式

1、低功耗简介

MCU的低功耗有三种,分别是待机模式、睡眠模式以及停止模式。

在系统或电源复位以后,微控制器处于运行状态。运行状态下的 HCLK 为 CPU 提供时钟,内核执行程序代码。

当 CPU 不需继续运行时,可以利用多个低功耗模式来节省功耗,例如等待某个外部事件时。用户需要根据最低电源消耗,最快速启动时间和可用的唤醒源等条件,选定一个最佳的低功耗模式。

2、低功耗特性详解

1、睡眠模式(CM3内核停止,外设仍然运行)

2、停止模式(所有时钟都停止)

3、待机(standby)模式(1.8V内核电源关闭)

首先简单看一下不同频率的单片机的正常工作下的电流消耗:

停机和待机模式下针对有没有看门狗则是有不同的情况:

睡眠模式下的电流消耗:

低功耗进入以及唤醒条件:

(1)睡眠模式:在睡眠模式中,仅关闭了内核时钟,内核停止运行,但其片上外设,CM4核心的外设全都还照常运行,在软件上表现为不再执行新的代码。这个状态会保留睡眠前的内核寄存器、内存的数据。唤醒后,若由中断唤醒,先进入中断,退出中断服务程序后,接着执行 WFI指令后的程序;若由事件唤醒,直接接着执行 WFE 后的程序。唤醒延迟:无延迟。

事件是中断的触发源,开放了对应的中断屏蔽位,则事件可以触发相应的中断。在STM32中,中断与事件不是等价的,一个中断肯定对应一个事件,但一个事件不一定对应一个中断。

当外部有信号输入时,如果通过了事件屏蔽寄存器,那么事件信号就进入脉冲触发器,引发一个脉冲信号,直接传递给相应的外设,用于触发,这就是一个纯硬件的过程,理解DMA的应该知道,这个方式不需要CPU参与,但是这也有它的缺点,如功能比较单一,仅能提供信号,不能提供信息,也就是只能产生指定功能的事件。如果通过中断屏蔽寄存器,就被直接送到CPU中,产生中断,如进入上面的入口函数开始处理。从这就可看出,事件是单纯硬件触发执行的过程,与CPU本身设计支持有关,而中断中则可以软件实现各种功能,而低功耗模式的事件唤醒就是stm32支持的事件之一。

(2)停止模式:在停止模式中,进一步关闭了其它所有的时钟,于是所有的外设都停止了工作,但由于其 1.2V 区域的部分电源没有关闭,还保留了内核的寄存器、内存的信息,所以从停止模式唤醒,并重新开启时钟后,还可以从上次停止处继续执行代码。唤醒后,若由中断唤醒,先进入中断,退出中断服务程序后,接着执行 WFI指令后的程序;若由事件唤醒,直接接着执行 WFE 后的程序。停止模式唤醒后,STM32会使用 HSI(f1的HSI为8M,f4为12M)作为系统时钟。所以,有必要在唤醒以后,在程序上重新配置系统时钟,将时钟切换回HSE。唤醒延迟 :基础延迟为 HSI振荡器的启动时间,若调压器工作在低功耗模式,还需要加上调压器从低功耗切换至正常模式下的时间,若 FLASH 工作在掉电模式,还需要加上 FLASH 从掉电模式唤醒的时间。

(3)待机模式:它除了关闭所有的时钟,还把 1.2V区域的电源也完全关闭了,也就是说,从待机模式唤醒后的代码执行等同于复位后的执行,进入Standby模式后,只能有Wake-up脚和RTC唤醒(正常唤醒),特别是唤醒后,程序将从最开始运行,也就是相当于软件复位。

3、待机模式以及睡眠模式验证

3.1、睡眠模式

关于睡眠模式,网上普遍介绍的比较简单,只需要调用一下下面的函数即可进入睡眠模式:HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON,PWR_SLEEPENTRY_WFI);

PWR_MAINREGULATOR_ON是授权进入调节模式,PWR_SLEEPENTRY_WFI则是配置唤醒模式,于是我在测试ADC以及PWM的demo里面加入了一段代码:

  1. void sleep(void)
  2. {
  3. printf("Start of sleep mode!\r\n");
  4. HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON,PWR_SLEEPENTRY_WFE);
  5. printf("End of sleep mode!\r\n");
  6. }

结果发现,根本没有进去的迹象,串口打印根本没有停止在Start of Sleep mode这里,仿佛整个代码压根没进入过睡眠模式。

突然想到睡眠模式会被任何中断唤醒,于是想到需要关闭所有中断,我在demo中使用的中断有,Tim定时器中断、串口接收中断。于是我试着关闭了这两个中断,后面又想到串口中断需要重复使能才存在,就不需要关闭使能。但是发现依旧没有达到睡眠的效果,整个单片机的运行没有任何异常。

  1. void sleep(void)
  2. {
  3. printf("Start of sleep mode!\r\n");
  4. HAL_TIM_Base_Stop_IT(&htim1);
  5. HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON,PWR_SLEEPENTRY_WFE);
  6. printf("End of sleep mode!\r\n");
  7. HAL_UART_Receive_IT(&huart1,(uint8_t*)&aRxBuffer,1); HAL_TIM_Base_Start_IT(&htim1);
  8. }

最后发现了还有一个中断被我忽略了,那就是Tick中断,SysTick 定时器属于内核外设,一般用于提供系统时基,CM3 内核产品均存在此定时器,如此通过校准值寄存器可以使系统在不同的CM3产品上运行时均产生恒定的 SysTick 中断频率。所以也就是说哪怕没有配置任何中断,你的工程也是会有Tick中断的,这是你单片机运行的时基。那么就很清晰了:

  1. void sleep(void)
  2. {
  3. printf("Start of sleep mode!\r\n");
  4. HAL_SuspendTick();
  5. HAL_TIM_Base_Stop_IT(&htim1);
  6. HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON,PWR_SLEEPENTRY_WFE);
  7. printf("End of sleep mode!\r\n");
  8. HAL_ResumeTick();
  9. HAL_TIM_Base_Start_IT(&htim1);
  10. }

关闭Tick中断以及实时运行的串口中断,最终成功进入SLEEP模式,之所以我判断它进入睡眠模式了是因为,单片机所有按键全部失效了,串口也不再打印数据,但是已经电量的小灯还在亮着,同时ADC电压捕获、PWM输出变化依旧存在。我们由此可以得出结论:

睡眠模式下,各路时钟依旧在运行,各路IO口的配置依旧存在,引脚供电也没有变化。

所有开关失效,也就是所有的电路逻辑判断全部停止了,也就是说ARM内核停止运行了。

那么已经弄清楚原理了,现在的问题就是唤醒了,可以看到唤醒条件的事件唤醒以及中断唤醒,经过反复验证,即使将参数配置成事件唤醒,睡眠模式依旧会被中断唤醒,所以我得出结论,中断也应该是一种事件,或者说睡眠模式无论如何配置都会被中断唤醒。

内部中断会唤醒睡眠模式,所以说值得信任的只有外部触发的中断了。那么直接把按键配置成外部中断,下降沿唤醒,通时在中断回调里面点个小灯告诉我们睡眠被唤醒。

  1. void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
  2. {
  3. if(GPIO_Pin==GPIO_PIN_0)
  4. {
  5. HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_RESET);
  6. }
  7. }

在最终得出结论,外部中断唤醒确实是最优选择。唤醒稳定快捷,而且干扰因素少。

3.2、待机模式

  1. void Sys_Enter_Standby(void)
  2. {
  3. __HAL_RCC_APB2_FORCE_RESET(); //复位APB上的所有IO口
  4. __HAL_RCC_PWR_CLK_ENABLE(); //使能 PWR 时钟
  5. __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU); //清除 Wake_UP 标志
  6. HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);//设置 WKUP 用于唤醒
  7. HAL_PWR_EnterSTANDBYMode(); //进入待机模式
  8. }

可以看到,所有的IO口都要被复位,同时也要使能 PWR 时钟,这是为什么呢?

可以看到,这三步在HAL_PWR_EnterSTANDBYMode(); 被完成

所以说因为要配置 PWR 寄存器,所以必须先使能 PWR 时钟。同时复位所有IO口最大程度的达到低功耗的效果。

最后就是要设置唤醒方式了,这边尝试的是比较方便的wake_up唤醒,也就是说有个wake_up有个上升沿的变化待机模式就会被唤醒。

  1. __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);//清除 Wake_UP 标志
  2. HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1); //设置 WKUP 用于唤醒

唤醒标志函数中的PWR_WAKEUP_PIN1则代表的是唤醒引脚,我所使用的PWR_WakeUP_pin1,同时STM32F103ZET6也只有一个WAKE_UP,但是之前的STM32l则是有三个WAKE_UP,所以按键标志不能设置错误。

同时待机模式下的工作状态:

LED:熄灭状态 串口:停止工作

ADC:停止工作 TIM定时器开关:停止工作

PWM: 停止工作

由此可以得出结论,在待机模式下,所有外设停止工作(定时器停止工作),那么这也代表时钟停止运行;我用一个变量来验证代码是否重置,结果发现之前的计数变量被归零,也就是说代码相当于被复位。值得一提的是独立看门狗所专用的时钟依旧在运行,这LSI时钟独立在主时钟之外。由于待机模式会掐灭1.8V的供电,所以说可以推断出IWDG以及待机唤醒的逻辑电路不是这个1.8V供电。

同时待机模式与看门狗有着不可缓解的冲突,二者常规模式下不可共存。看门狗的复位必然会唤醒待机模式,除了RTC时钟喂狗。独立看门狗与待机模式无法同时运行。而窗口看门狗则是没有这个限制。待机模式下窗口看门狗则是关闭了。

4、ARM Hint 指令

  1. void HAL_PWR_EnterSLEEPMode(uint32_t Regulator, uint8_t SLEEPEntry)
  2. {
  3. UNUSED(Regulator);
  4. assert_param(IS_PWR_SLEEP_ENTRY(SLEEPEntry));
  5. CLEAR_BIT(SCB->SCR,((uint32_t)SCB_SCR_SLEEPDEEP_Msk));
  6. if(SLEEPEntry== PWR_SLEEPENTRY_WFI)
  7. {
  8. __WFI();
  9. }
  10. else
  11. {
  12. __SEV();
  13. __WFE();
  14. __WFE();
  15. }
  16. }

以上是进入睡眠函数的库函数的内容,可以看到除了一系列对寄存器操作外,还有一些WFI、WFE之类的字样,继续深入发现突然终止在一个宏里面了,后面经过了解才知道这是HINT指令,也就是内核指令。

一、HINT 指令

HINT可以合法地被视为 NOP指令,但它们可以具有特定于实现的效果,常见的HINT指令有:

NOP //No operation,无操作, 不保证CPU会花时间去执行

YIELD //提示当前线程正在执行可以换出的任务

WFE //Wait for Event,进入low power状态,直到等待的事件发生

WFI //Wait for interrupt,进入low power状态,直到等待的中断或与中断类似的操作发生

SEV //Send Event,发送事件,与WFE对应

SEVL //Send Event Local,发送本地事件,与WFE对应

ARM 汇编语言中包含可用于让内核进入低功耗状态(low-powerstate)的指令:WFI或WFE。ARM架构将这些指令定义为hint指令,这意味着内核在执行它们时不需要采取任何特定操作。然而,在Cortex-A 处理器系列中,这些指令的实现方式是关闭几乎所有内核部分的时钟。这意味着内核的功耗显着降低,仅消耗静态漏电流,没有动态功耗。

二,WFI指令的用途

WFI指令的主要目的就是使内核进入standby模式,直到中断或者类似中断的事件发送,才退出,内核继续工作。

standby 模式- 待机模式

在待机模式下,内核保持上电状态,但其大部分时钟停止或者进入时钟门限。这意味着内核的绝大部分都处于staticstate,唯一消耗的功率是用于寻找中断唤醒条件的泄漏电流和少量逻辑时钟。

使用 WFI(等待中断)或 WFE(等待事件)指令可以进入此模式。 ARM 建议在 WFI 或 WFE 之前使用数据同步屏障 (Data Synchronization Barrier ,DSB) 指令,以确保待处理的内存事务在更改状态之前完成。

内核进入待机状态后,会停止执行,直到检测到唤醒事件。唤醒条件取决于进入指令。对于 WFI,需要中断事件或外部调试请求来唤醒内核。对于 WFE,存在许多指定的事件,包括cluster中执行 SEV 指令的另一个内核。

WFI 指令广泛用于电池供电的系统。例如,手机可以每秒多次将内核置于待机模式,同时等待用户按下按钮才唤醒内核,从而可以节省功耗。

WFE 类似于 WFI。内核暂停执行,直到发生事件。这可以是列出的事件条件之一,也可以是cluster中另一个内核发出的事件信号。其他内核可以通过执行 SEV 指令来发出事件信号。 SEV 向所有内核发送一个事件信号。还可以对generictimer通用定时器进行编程,以触发将内核从 WFE 唤醒的周期性事件。

总而言之,WFI 指令可以提示内核在接收到中断或类似的exception之前无需执行任何操作,具体来说有两部分:

强制暂停内核的运行以及所有相关的总线活动。

暂停处理器执行指令。

三,WFI指令的唤醒事件

WFI可以使内核进入待机状态,而将内核从待机状态中唤醒,则需要中断事件或者类似中断事件的exception(debug事件):

物理的IRQ中断,但是忽略 CPSR寄存器的 I 位.

物理的FIQ中断,但是忽略 CPSR寄存器的 F 位.

物理的异步abort,但是忽略 CPSR寄存器的 A 位.

异步的调试事件(debugevent),当启用侵入式调试(invasivedebug)并允许调试事件时,比如Trace32中的break 操作,也会将内核唤醒。

包括其他实现定义的硬件机制来生成 WFI 唤醒事件.

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/520815
推荐阅读
相关标签
  

闽ICP备14008679号