• STM32物联网项目-低功耗模式


    低功耗模式

    电源框图

    在这里插入图片描述

    VDD供电区域一般为2V ~ 3.6V,经过电压调节器可降压到1.8V给CPU核心、存储器和内置数字外设供电,为了降低CPU的功耗,

    后备供电区域可由电池供电,输入引脚为VBAT

    STM32各种电源

    在这里插入图片描述

    STM32的低功耗模式

    STM32F10xxx有三种低功耗模式:

    1. 睡眠模式:Cortex™-M3内核停止,所有外设包括Cortex-M3核心的外设,如NVIC、系统时钟(SysTick)等仍在运行

    2. 停止模式:所有的时钟都已停止

    3. 待机模式:1.8V电源关闭

    在这里插入图片描述

    模式从上到下功耗越低,但唤醒条件越严格

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

    1. 降低系统时钟。时钟频率越慢功耗越低,例如平时使用芯片时的系统时钟频率都是72MHz,这能满足绝大部分外设的要求,但如果是点个流水灯,又想要低功耗,就没必要使用72MHz的,用8M或者更低能运行的话就用低点的,
    2. 关闭APB和AHB总线上未被使用的外设时钟

    低功耗模式下的自动唤醒(AWU)

    在这里插入图片描述

    低功耗模式下的自动唤醒(AWU)在STM32F1是没有的,在F4是有的,但F1可以通过RTC闹钟事件来唤醒

    如下面STM32F4的RTC框图,就有16位的唤醒自动重载寄存器,可以通过设定值来唤醒

    在这里插入图片描述

    实验目标

    触摸按键1被按下,则系统进入睡眠模式,点击触摸按键4退出睡眠模式;

    触摸按键2被按下,则系统进入停止模式,点击触摸按键4退出停机模式;

    触摸按键3被按下,则进入待机模式,通过复位按键退出待机模式;

    CubeMX配置

    按键外部中断的配置,LED灯作系统的指示作用,拉低WIFI模块的使能脚,不然模块会耗电

    在这里插入图片描述

    初始化串口1,用于打印信息

    在这里插入图片描述

    程序

    stm32f1xx_hal_pwr.h

    下面是进入低功耗模式的三个HAL库函数,可以直接调用

    /* 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);								//进入停机模式
    
    • 1
    • 2
    • 3
    • 4
    进入或退出睡眠模式

    在这里插入图片描述

    以WFI进入睡眠模式后,是可以通过任意一个中断来唤醒的,这里就需要注意了,平时使用的延时函数HAL_Delay()是以SysTick滴答定时器来作为时基的,并且NVIC中默认已经开启了SysTick的中断,如果直接在主函数中调用HAL_PWR_EnterSLEEPMode函数进入睡眠模式的话,SysTick滴答定时器的中断就会立马唤醒系统,达不到低功耗的效果;所以在进入睡眠模式之前,需要先关闭SysTick的中断

    同理,系统中开启的用户中断也要关闭,如定时器,外部中断等,可以留某些中断用于退出睡眠模式

    在这里插入图片描述

    在这里插入图片描述

    进入睡眠模式函数

    这里可以记住几个函数,有个印象,除了定时器其他的平时都少用,但要用的时候又难找

    停止SysTick中断函数——HAL_SuspendTick()

    停止定时器6中断——HAL_TIM_Base_Stop_IT(&htim6)

    关闭外部中断——HAL_NVIC_DisableIRQ(EXTIx_IRQn)

    恢复Systick中断——HAL_ResumeTick()

    恢复(启动)定时器6中断——HAL_TIM_Base_Start_IT(&htim6)

    恢复外部中断——HAL_NVIC_EnableIRQ(EXTIx_IRQn)

    进入睡眠模式后,通过触摸按键4,触发外部中断,可退出睡眠模式

    /*
    * @name   Enter_Sleep_Mode
    * @brief  进入睡眠模式
    * @param  None
    * @retval None
    */
    static void Enter_Sleep_Mode()
    {
        /*注意:任一中断都可以将系统从睡眠模式中唤醒*/
    
        //需要先关闭Systick和定时器6的中断,否则进入睡眠模式后立马被唤醒
        HAL_SuspendTick();              //停止Systick中断
        HAL_TIM_Base_Stop_IT(&htim6);   //停止定时器6中断
    
        //关闭外部中断0至2,只允许触摸按键4外部中断退出睡眠模式
        HAL_NVIC_DisableIRQ(EXTI0_IRQn);
        HAL_NVIC_DisableIRQ(EXTI1_IRQn);
        HAL_NVIC_DisableIRQ(EXTI2_IRQn);
    
        //以WFI进入睡眠模式,可以被任一中断唤醒
        HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON,PWR_SLEEPENTRY_WFI);
    
        //恢复中断
        HAL_ResumeTick();               //恢复Systick中断
        HAL_TIM_Base_Start_IT(&htim6);  //恢复定时器6中断
    
        //恢复外部中断0至2
        HAL_NVIC_EnableIRQ(EXTI0_IRQn);
        HAL_NVIC_EnableIRQ(EXTI1_IRQn);
        HAL_NVIC_EnableIRQ(EXTI2_IRQn);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    进入睡眠模式函数的参数说明
    
    /**
      * @brief Enters Sleep mode.
      * @note  In Sleep mode, all I/O pins keep the same state as in Run mode.
      * @param Regulator: Regulator state as no effect in SLEEP mode -  allows to support portability from legacy software
      * @param SLEEPEntry: Specifies if SLEEP mode is entered with WFI or WFE instruction.
      *           When WFI entry is used, tick interrupt have to be disabled if not desired as 
      *           the interrupt wake up source.
      *           This parameter can be one of the following values:
      *            @arg PWR_SLEEPENTRY_WFI: enter SLEEP mode with WFI instruction
      *            @arg PWR_SLEEPENTRY_WFE: enter SLEEP mode with WFE instruction
      * @retval None
      */
    void HAL_PWR_EnterSLEEPMode(uint32_t Regulator, uint8_t SLEEPEntry)
    {
      /* Check the parameters */
      /* No check on Regulator because parameter not used in SLEEP mode */
      /* Prevent unused argument(s) compilation warning */
      UNUSED(Regulator);
    
      assert_param(IS_PWR_SLEEP_ENTRY(SLEEPEntry));
      ........
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    进入睡眠模式的HAL库函数中,参数Regulator表示电压调节器,而睡眠模式下的电压调节器是始终打开的,那为什么还有这个参数呢?

    原因:可以看参数Regulator的英文说明,Regulator state as no effect in SLEEP mode - allows to support portability from legacy software,说的是电压调节器的状态在睡眠模式下是没有影响的,无论传哪个符合的参数进去,电压调节器还是会打开,定义这个参数只是为了兼容以前的代码;同时在函数内部也不对Regulator参数进行检查,该参数在睡眠模式下无效

    进入和退出停机模式

    因为停机模式只能由外部中断来退出,所以可以不停止SysTick定时器中断和定时器中断,在函数中留个按键4外部中断用来退出停机模式,其他外部中断都禁止掉

    在这里插入图片描述

    /*
    * @name   Enter_Stop_Mode
    * @brief  进入停机模式
    * @param  None
    * @retval None
    */
    static void Enter_Stop_Mode()
    {
        /*注意:外部中断可以将系统从停机模式中唤醒*/
    
        //关闭外部中断0至2,只允许触摸按键4外部中断退出停止模式
        HAL_NVIC_DisableIRQ(EXTI0_IRQn);
        HAL_NVIC_DisableIRQ(EXTI1_IRQn);
        HAL_NVIC_DisableIRQ(EXTI2_IRQn);
    
        //以WFI、电压调节器低功耗模式进入停机模式
        HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON,PWR_STOPENTRY_WFI);
    
        //退出停机模式时,HSI RC振荡器被选为系统时钟
        //重新选择系统时钟为HSE
        SystemClock_Config();
        //开启外部中断
        HAL_NVIC_EnableIRQ(EXTI0_IRQn);
        HAL_NVIC_EnableIRQ(EXTI1_IRQn);
        HAL_NVIC_EnableIRQ(EXTI2_IRQn);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    void HAL_PWR_EnterSTOPMode(uint32_t Regulator,uint8_t STOPEntry)
    
    • 1

    进入停机模式的函数中,参数Regulator(内部调压器的)有两种选择,

    一种是:PWR_MAINREGULATOR_ON(正常模式)

    另一种是:PwR_LOWPOWERREGULATOR_ON(低功耗模式)

    两种选择的区别,正常模式恢复比较快,低功耗模式功耗较低,但启动时间会长点,可根据产品来选择,如果产品要求启动时间要快,则选择正常模式,如果对启动时间没有要求,则选择低功耗模式

    注意:当一个中断或唤醒事件导致退出停止模式时,HSI RC振荡器被选为系统时钟,所以从停止模式退出后,要重新设置系统时钟,需要调用SystemClock_Config()函数

    进入和退出待机模式

    待机模式可实现系统的最低功耗。该模式是在Cortex-M3深睡眠模式时关闭电压调节器。整个 1.8V供电区域被断电。PLL、HSI和HSE振荡器也被断电。SRAM和寄存器内容丢失。只有备份的寄存器和待机电路维持供电

    直接调用库函数进入待机模式,此时芯片的IO引脚处于高阻态,并不受芯片控制,所以那些模块的电最好都先断掉

    在这里插入图片描述

    /*
    * @name   Enter_Standby_Mode
    * @brief  进入待机模式
    * @param  None
    * @retval None
    */
    static void Enter_Standby_Mode()
    {
        /*注意:系统复位键可以将系统从待机模式中唤醒*/
    
        //待机模式直接调用库函数即可
        HAL_PWR_EnterSTANDBYMode();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    System.c

    运行函数中延时1s,然后每隔10ms检测低功耗模式标志位是否被置位,是则调用函数进入对应的低功耗模式

    /*
    * @name   Run
    * @brief  系统运行
    * @param  None
    * @retval None   
    */
    static void Run()
    {
      uint8_t i;
      //延时1s,期间每隔10ms检测是否需要进入低功耗模式
      for(i=0;i<100;i++)
      {
        //进入睡眠模式
        if(LowPower.Enter_Sleep_Mode_Flag == TRUE)
        {
          LowPower.Enter_Sleep_Mode_Flag = FALSE;
          printf("系统进入睡眠模式,通过按键4唤醒\r\n");
          LowPower.Enter_Sleep_Mode();
        }
        //进入停机模式
        if(LowPower.Enter_Stop_Mode_Flag == TRUE)
        {
          LowPower.Enter_Stop_Mode_Flag = FALSE;
          printf("系统进入停机模式,通过按键4唤醒\r\n");
          LowPower.Enter_Stop_Mode();
        }
        //进入待机模式
        if(LowPower.Enter_Standby_Mode_Flag == TRUE)
        {
          LowPower.Enter_Standby_Mode_Flag = FALSE;
          printf("系统进入待机模式,通过复位按键唤醒\r\n");
          printf("待机模式,外设不受STM32控制,系统功耗可能增加!\r\n");
          printf("如WiFi模块,可以用镊子将EN管脚短接到地!\r\n");
          LowPower.Enter_Standby_Mode();
        }
        //延时
        HAL_Delay(10);
      }
      printf("系统正常运行\r\n");
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    CallBack.c

    在中断回调函数中判断触摸按键是否被按下,是则将低功耗模式标志位置位,然后主函数中就可以调用低功耗函数让系统进入低功耗模式

    
    /*
    * @name   HAL_GPIO_EXTI_Callback
    * @brief  外部中断回调函数
    * @param  GPIO_Pin:触发外部中断的引脚
    * @retval None   
    */
    void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
    {
      uint8_t i;
      if(GPIO_Pin == KEY1_Pin)
      {
        printf("检测到按键1被按下\r\n");
        LowPower.Enter_Sleep_Mode_Flag = TRUE;
        LED.LED_Fun(LED2,LED_ON);
        for(i=0;i<100;i++)
        {
          HAL_Delay(10);
          if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin) == GPIO_PIN_SET)
          {
            LowPower.Enter_Sleep_Mode_Flag = FALSE;
            printf("按键1提前释放,系统正常运行\r\n");
            break;
          }
        }
        LED.LED_Fun(LED2,LED_OFF);
      }
    
      if(GPIO_Pin == KEY2_Pin)
      {
        printf("检测到按键2被按下\r\n");
        LowPower.Enter_Stop_Mode_Flag = TRUE;
        LED.LED_Fun(LED2,LED_ON);
        for(i=0;i<100;i++)
        {
          HAL_Delay(10);
          if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin) == GPIO_PIN_SET)
          {
            LowPower.Enter_Stop_Mode_Flag = FALSE;
            printf("按键2提前释放,系统正常运行\r\n");
            break;
          }
        }
        LED.LED_Fun(LED2,LED_OFF);
      }
    
      if(GPIO_Pin == KEY3_Pin)
      {
        printf("检测到按键3被按下\r\n");
        LowPower.Enter_Standby_Mode_Flag = TRUE;
        LED.LED_Fun(LED2,LED_ON);
        for(i=0;i<100;i++)
        {
          HAL_Delay(10);
          if(HAL_GPIO_ReadPin(KEY3_GPIO_Port,KEY3_Pin) == GPIO_PIN_SET)
          {
            LowPower.Enter_Standby_Mode_Flag = FALSE;
            printf("按键3提前释放,系统正常运行\r\n");
            break;
          }
        }
        LED.LED_Fun(LED2,LED_OFF);
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    实验效果

    在这里插入图片描述

  • 相关阅读:
    python基础—面向对象
    【Linux】进程间通信——信号
    YoloV5-SPD+TensorRT:基于YoloV5-SPD的小目标检测算法部署
    PTA:数据结构实验 一 链队列
    非关系数据库
    记-Windows环境下Prometheus+alertmanager+windows_exporter+mtail监控部署提起网关日志
    Platypus:快速、廉价且强大的大模型
    函数式接口:Java 中的函数式编程利器
    Linux调度域与调度组
    【Cross-Direction and Progressive Network:交叉的挖掘信息】
  • 原文地址:https://blog.csdn.net/weixin_46251230/article/details/126749524