STM32CubeMX学习笔记(17)——电源管理(PWR)低功耗待机模式

一、低功耗模式简介

系统提供了多个低功耗模式,可在 CPU 不需要运行时(例如等待外部事件时)节省功耗。由用户根据应用选择具体的低功耗模式,以在低功耗、短启动时间和可用唤醒源之间寻求最佳平衡。

睡眠模式、停止模式及待机模式中,若备份域电源正常供电,备份域内的 RTC 都可以正常运行,备份域内的寄存器的数据会被保存,不受功耗模式影响。

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

模式名称 说明 进入方式 唤醒方式 对1.8V区域时钟的影响 对VDD区域时钟的影响 调压器
睡眠模式 内核停止,所有外设包括M3核心的外设,如NVIC、系统时钟(SysTick)等仍在运行 调用WFI命令 任意中断 内核时钟关,对其他时钟和ADC时钟无影响
睡眠模式 内核停止,所有外设包括M3核心的外设,如NVIC、系统时钟(SysTick)等仍在运行 调用WFE命令 唤醒事件 内核时钟关,对其他时钟和ADC时钟无影响
停止模式 所有的时钟都已停止 配置PWR_CR寄存器的PDDS+LPDS位+SLEEPDEEP位+WFIWFE命令 任意外部中断EXTI(在外部中断寄存器中设置) 关闭所有1.8V区域的时钟 HSI和HSE的振荡器关闭 开启或处于低功耗模式(依据电源控制寄存器的设定)
待机模式 1.8V电源关闭 配置PWR_CR寄存器的PDDS+SLEEPDEEP位+WFIWFE命令 WKUP上升沿、引脚的RTC闹钟事件、NRST引脚上的外部复位、IWDG复位 关闭所有1.8V区域的时钟 HSI和HSE的振荡器关闭

1.1 睡眠模式

在睡眠模式中,仅关闭了内核时钟,内核停止运行,但其片上外设,CM3 核心的外设全都还照常运行。有两种方式进入睡眠模式,它的进入方式决定了从睡眠唤醒的方式,分别是 WFI(wait for interrupt)WFE(wait for event),即由等待“中断”唤醒和由“事件”唤醒。

特性和说明:

  • 立即睡眠: 在执行 WFIWFE 指令时立即进入睡眠模式。
  • 退出时睡眠: 在退出优先级最低的中断服务程序后才进入睡眠模式。
  • 进入方式: 内核寄存器的 SLEEPDEEP=0 ,然后调用 WFIWFE 指令即可进入睡眠模式;SLEEPONEXIT=1 时,进入“退出时睡眠”模式。
  • 唤醒方式: 如果是使用 WFI 指令睡眠的,则可使用任意中断唤醒;如果是使用 WFE 指令睡眠的,则由事件唤醒。
  • 睡眠时: 关闭内核时钟,内核停止,而外设正常运行,在软件上表现为不再执行新的代码。这个状态会保留睡眠前的内核寄存器、内存的数据。
  • 唤醒延迟: 无延迟。
  • 唤醒后: 若由中断唤醒,先进入中断,退出中断服务程序后,接着执行 WFI 指令后的程序;若由事件唤醒,直接接着执行 WFE 后的程序。

1.2 停止模式

在停止模式中,进一步关闭了其它所有的时钟,于是所有的外设都停止了工作,但由于其 1.8V 区域的部分电源没有关闭,还保留了内核的寄存器、内存的信息,所以从停止模式唤醒,并重新开启时钟后,还可以从上次停止处继续执行代码。停止模式可以由任意一个外部中断(EXTI)唤醒,在停止模式中可以选择电压调节器为开模式或低功耗模式。

特性和说明:

  • 调压器低功耗模式: 在停止模式下调压器可工作在正常模式或低功耗模式,可进一步降低功耗。
  • 进入方式: 内核寄存器的 SLEEPDEEP=1,PWR_CR 寄存器中的 PDDS=0,然后调用 WFIWFE 指令即可进入停止模式;PWR_CR 寄存器的 LPDS=0 时,调压器工作在正常模式,LPDS=1 时工作在低功耗模式。
  • 唤醒方式: 如果是使用 WFI 指令睡眠的,可使用任意 EXTI 线的中断唤醒;如果是使用 WFE 指令睡眠的,可使用任意配置为事件模式的 EXTI 线事件唤醒。
  • 停止时: 内核停止,片上外设也停止。这个状态会保留停止前的内核寄存器、内存的数据。
  • 唤醒延迟: 基础延迟为 HSI 振荡器的启动时间,若调压器工作在低功耗模式,还需要加上调压器从低功耗切换至正常模式下的时间。
  • 唤醒后: 若由中断唤醒,先进入中断,退出中断服务程序后,接着执行 WFI 指令后的程序;若由事件唤醒,直接接着执行 WFE 后的程序。唤醒后,STM32 会使用 HSI 作为系统时钟。

1.3 待机模式

待机模式,它除了关闭所有的时钟,还把 1.8V 区域的电源也完全关闭了,也就是说,从待机模式唤醒后,由于没有之前代码的运行记录,只能对芯片复位,重新检测 boot 条件,从头开始执行程序。它有四种唤醒方式,分别是 WKUP(PA0)引脚的上升沿,RTC 闹钟事件,NRST 引脚的复位和 IWDG(独立看门狗)复位。

特性和说明:

  • 进入方式: 内核寄存器的 SLEEPDEEP=1,PWR_CR 寄存器中的 PDDS=1,PWR_CR 寄存器中的唤醒状态位 WUF=0,然后调用 WFIWFE 指令即可进入待机模式。
  • 唤醒方式: 通过 WKUP 引脚的上升沿,RTC 闹钟、唤醒、入侵、时间戳事件或 NRST 引脚外部复位及 IWDG 复位唤醒。
  • 待机时: 内核停止,片上外设也停止;内核寄存器、内存的数据会丢失;除复位引脚、RTC_AF1 引脚及 WKUP 引脚,其它 I/O 口均工作在高阻态。
  • 唤醒延迟: 芯片复位的时间。
  • 唤醒后: 相当于芯片复位,在程序表现为从头开始执行代码。

1.4 WFI与WFE命令

我们了解到进入各种低功耗模式时都需要调用 WFIWFE 命令,它们实质上都是内核指令,在库文件 core_cm3.h 中把这些指令封装成了函数。

/** brief 等待中断
  等待中断 是一个暂停执行指令
  暂停至任意中断产生后被唤醒
*/
#define __WFI        __wfi 

/** brief 等待事件
  等待事件 是一个暂停执行指令
  暂停至任意事件产生后被唤醒
*/
#define __WFE        __wfe

对于这两个指令,我们应用时一般只需要知道,调用它们都能进入低功耗模式,需要使用函数的格式“__WFI();”和“__WFE();”来调用(因为__wfi 及__wfe 是编译器内置的函数,函数内部调用了相应的汇编指令)。

其中 WFI 指令决定了它需要用中断唤醒,而 WFE 则决定了它可用事件来唤醒。

二、新建工程

1. 打开 STM32CubeMX 软件,点击“新建工程”

2. 选择 MCU 和封装

3. 配置时钟
RCC 设置,选择 HSE(外部高速时钟) 为 Crystal/Ceramic Resonator(晶振/陶瓷谐振器)
开启 LSE(外部低速时钟) 为 Crystal/Ceramic Resonator(晶振/陶瓷谐振器)


选择 Clock Configuration,配置系统时钟 SYSCLK 为 72MHz
修改 HCLK 的值为 72 后,输入回车,软件会自动修改所有配置

4. 配置调试模式
非常重要的一步,否则会造成第一次烧录程序后续无法识别调试器
SYS 设置,选择 Debug 为 Serial Wire

三、待机模式

3.1 WKUP按键唤醒

3.1.1 流程图

3.1.2 HAL库与标准库代码比较

STM32CubeMX 使用 HAL 库的代码:

int main(void)
{
    // 检测复位来源
    if(__HAL_PWR_GET_FLAG(PWR_FLAG_SB) == SET) 
    {
        __HAL_PWR_CLEAR_FLAG(PWR_FLAG_SB);
        printf("\r\n 待机唤醒复位 \r\n");
    }
    else 
    {
        printf("\r\n 非待机唤醒复位 \r\n");
    }
    ···
    while(1)
    {
        ···
        HAL_Delay(5000);
        /*清除 WU 状态位*/
        __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
        /* 使能 WKUP 引脚的唤醒功能 ,使能 PA0*/
        HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);
        /* 进入待机模式 */
        HAL_PWR_EnterSTANDBYMode();
        ···
    }
}

使用 STM32 标准库的代码:

int main(void)
{
    /* 使能电源管理单元的时钟,必须要使能时钟才能进入待机模式 */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
    // 检测复位来源
    if(PWR_GetFlagStatus(PWR_FLAG_WU) == SET) 
    {
        printf("\r\n 待机唤醒复位 \r\n");
    } 
    else 
    {
        printf("\r\n 非待机唤醒复位 \r\n");
    }
    ···
    while(1)
    {
        ···
        Delay(0xFFFF);
        /*清除 WU 状态位*/
        PWR_ClearFlag(PWR_FLAG_WU);     
        /* 使能 WKUP 引脚的唤醒功能 ,使能 PA0*/
        PWR_WakeUpPinCmd(ENABLE); 
        /* 进入待机模式 */
        PWR_EnterSTANDBYMode();
        ···
    }
}

3.1.3 添加WKUP按键

添加系统唤醒按键 PA0 ,以便当系统进入待机模式的时候可以通过按键来唤醒。


或者勾选 System Wake-Up

3.1.4 添加LED灯

添加绿灯 PB0 表示本次复位是上电或引脚复位,蓝灯 PB1 表示本次是待机唤醒的复位。
查看 STM32CubeMX学习笔记(2)——GPIO接口使用

3.1.5 添加串口打印

添加 USART1 用于打印信息。
查看 STM32CubeMX学习笔记(6)——USART串口使用

3.1.6 生成代码

输入项目名和项目路径


选择应用的 IDE 开发环境 MDK-ARM V5

每个外设生成独立的 ’.c/.h’ 文件
不勾:所有初始化代码都生成在 main.c
勾选:初始化代码生成在对应的外设文件。 如 GPIO 初始化代码生成在 gpio.c 中。

点击 GENERATE CODE 生成代码

3.1.7 修改main函数

程序中首先初始化了系统时钟、LED 灯及串口以便用于指示芯片的运行状态,由于待机模式唤醒使用 WKUP 引脚并不需要特别的引脚初始化。

使用库函数 __HAL_PWR_GET_FLAG 检测 PWR_FLAG_SB 标志位,当这个标志位为 SET 状态的时候,表示本次系统是从待机模式唤醒的复位,否则可能是上电复位。
我们利用这个区分两种复位形式,分别使用蓝色 LED 灯或绿色 LED 灯来指示。

在使用库函数 HAL_PWR_EnableWakeUpPin 发送待机命令前,要先使用库函数 __HAL_PWR_CLEAR_FLAG 清除 PWR_FLAG_WU 标志位,并且使用库函数 HAL_PWR_EnableWakeUpPin 使能 WKUP 唤醒功能,这样进入待机模式后才能使用 WKUP 唤醒。

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  printf("standby mode test\r\n");

  if(__HAL_PWR_GET_FLAG(PWR_FLAG_SB) == SET) 
  {
    __HAL_PWR_CLEAR_FLAG(PWR_FLAG_SB);
    printf("\r\n standby reset \r\n");
    HAL_GPIO_WritePin(GPIOB, LED_B_Pin, GPIO_PIN_RESET);
  }
  else 
  {
    printf("\r\n normal reset \r\n");
    HAL_GPIO_WritePin(GPIOB, LED_G_Pin, GPIO_PIN_RESET);
  }
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    HAL_Delay(5000);
    /*清除 WU 状态位*/
    __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
    /* 使能 WKUP 引脚的唤醒功能 ,使能 PA0*/
    HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);
    /* 进入待机模式 */
    HAL_PWR_EnterSTANDBYMode();
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

3.2 RTC时钟唤醒

3.2.1 添加RTC时钟

查看 STM32CubeMX学习笔记(14)——RTC实时时钟使用

3.1.2 添加LED灯

添加绿灯 PB0 表示本次复位是上电或引脚复位,蓝灯 PB1 表示本次是待机唤醒的复位。
查看 STM32CubeMX学习笔记(2)——GPIO接口使用

3.1.3 添加串口打印

添加 USART1 用于打印信息。
查看 STM32CubeMX学习笔记(6)——USART串口使用

3.2.4 使能RTC闹钟中断

3.2.5 生成代码

输入项目名和项目路径


选择应用的 IDE 开发环境 MDK-ARM V5

每个外设生成独立的 ’.c/.h’ 文件
不勾:所有初始化代码都生成在 main.c
勾选:初始化代码生成在对应的外设文件。 如 GPIO 初始化代码生成在 gpio.c 中。

点击 GENERATE CODE 生成代码

3.2.6 添加RTC闹钟中断启动函数

void RTC_AlarmStart(void)
{
    RTC_AlarmTypeDef sAlarm = {0};
    RTC_TimeTypeDef tim = {0};

    // 获取当前时间
    HAL_RTC_GetTime(&hrtc, &tim, RTC_FORMAT_BIN);

    sAlarm.AlarmTime.Hours = tim.Hours;
    sAlarm.AlarmTime.Minutes = tim.Minutes;
    sAlarm.AlarmTime.Seconds = tim.Seconds + 3;  /* 设置下次闹钟提醒时间是当前时间的3s之后 */
    sAlarm.Alarm = RTC_ALARM_A;

    // 启动闹钟中断事件
    HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BIN);
}

3.2.7 修改main函数

程序中首先初始化了系统时钟、LED 灯及串口以便用于指示芯片的运行状态,由于待机模式唤醒使用 WKUP 引脚并不需要特别的引脚初始化。

使用库函数 __HAL_PWR_GET_FLAG 检测 PWR_FLAG_SB 标志位,当这个标志位为 SET 状态的时候,表示本次系统是从待机模式唤醒的复位,否则可能是上电复位。
我们利用这个区分两种复位形式,分别使用蓝色 LED 灯或绿色 LED 灯来指示。

在使用库函数 HAL_PWR_EnableWakeUpPin 发送待机命令前,要先使用库函数 __HAL_PWR_CLEAR_FLAG 清除 PWR_FLAG_WU 标志位,并且使用库函数 HAL_PWR_EnableWakeUpPin 使能 WKUP 唤醒功能,这样进入待机模式后才能使用 WKUP 唤醒。

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  printf("standby mode test\r\n");

  if(__HAL_PWR_GET_FLAG(PWR_FLAG_SB) == SET) 
  {
    __HAL_PWR_CLEAR_FLAG(PWR_FLAG_SB);
    printf("\r\n standby reset \r\n");
    HAL_GPIO_WritePin(GPIOB, LED_B_Pin, GPIO_PIN_RESET);
  }
  else 
  {
    printf("\r\n normal reset \r\n");
    HAL_GPIO_WritePin(GPIOB, LED_G_Pin, GPIO_PIN_RESET);
  }
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    HAL_Delay(5000);
    RTC_AlarmStart();
    /*清除 WU 状态位*/
    __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
    /* 使能 WKUP 引脚的唤醒功能 ,使能 PA0*/
    HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);
    /* 进入待机模式 */
    HAL_PWR_EnterSTANDBYMode();
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

四、注意事项

用户代码要加在 USER CODE BEGIN NUSER CODE END N 之间,否则下次使用 STM32CubeMX 重新生成代码后,会被删除。

进入低功耗之前可以将引脚全部配置为浮空输入或者Anglog模式,这样最省电,如果你是用STM32CUBEMX,在这里可以看到这么一项配置就是将没有用到的引脚配置为了Anglog模式:

当系统处于睡眠模式低功耗状态时(包括后面讲解的停止模式及待机模式),使用 DAP 下载器是无法给芯片下载程序的,所以下载程序时要先把系统唤醒。或者使用如下方法:按着板子的复位按键,使系统处于复位状态,然后点击电脑端的下载按钮下载程序,这时再释放复位按键,就能正常给板子下载程序了。


• 由 Leung 写于 2021 年 3 月 11 日

• 参考:STM32CubeMX系列教程14:电源控制器(PWR)
    STM32MX电源管理低功耗模式
    STM32F1系列使用HAL库低功耗STOP和STANDBY模式唤醒(RTC时钟唤醒+外部中断唤醒示例)
    《嵌入式-STM32开发指南》第二部分 基础篇 - 第10章 低功耗(HAL库)

你可能感兴趣的:(STM32CubeMX学习笔记(17)——电源管理(PWR)低功耗待机模式)