50. 低功耗模式

一、STM32电源系统结构

电源概述框图

  在电源概述框图中我们划分了 3 个区域,分别是独立的 A/D 转换器供电和参考电压、电压调节器、电池备份区域。

①、电池备份区域(后备供电区域)

  电池备份区域也就是后备供电区域,使用电池或者其他电源连接到 VBAT 脚上,当 VDD断电时,可以保存备份寄存器的内容和维持 RTC 的功能。同时 VBAT 引脚也为 RTC、SRAM 和 LSE振荡器供电,这保证了当主要电源被切断时,RTC 能够继续工作。切换到 VBAT 供电由复位模块中的掉电复位功能控制。

②、电压调节器(VDD /1.2V 供电区域)

  嵌入式线性调压器为备份域和待机电路以外的所有数字电路供电。调压器输出电压约为 1.2V。此调压器需要将两个外部电容连接到专用引脚 VCAP_1 和 VCAP_2,所有封装都配有这两个引脚。为激活或停用调压器,必须将特定引脚连接到 VSS 或 VDD。具体引脚与封装有关。通过软件激活时,调压器在复位后始终处于使能状态。

  根据应用模式的不同,可采用三种不同的模式工作。在 运转模式 下,调节器以正常工号模式为内核、内存和外设提供 1.2V;在 停止模式 下,调节器以低功耗模式提供 1.2V 电源,以保存寄存器和 SRAM 的内容。在 待机模式 下,调节器停止供电,除了备用电路和备份域外,寄存器和 SRAM 的内容全部丢失。

③、独立的 A/D 转换器供电和参考电压(VDDA 供电区域)

  为了提高转换精度,ADC 配有独立电源,可以单独滤波并屏蔽 PCB 上的噪声。ADC 电源电压从单独的 VDDA 引脚接入,VSSA 提供了独立的电源接地连接。为了确保测量低电压时具有更高的精度,用户可以再 VREF 上连接到单独的 ADC 外部参考电压输入,VREF 电压介于1.8V 到 VDDA 之间。

二、低功耗模式介绍

  STM32具有 运行睡眠停止待机 四种工作模式。上电后默认是在 运行模式,当内核不需要继续运行时,可以选择后面三种低功耗模式。这三种低功耗模式电源消耗不同、唤醒时间不同和唤醒源不同,我们要根据自身的需要选择合适的低功耗模式。

低功耗模式汇总

2.1、睡眠模式

睡眠模式

  进入睡眠模式,CPU 时钟关闭,但是 其它所有的外设仍可以运行,所以 任何中断或事件都可以唤醒睡眠模式。有两种方式进入睡眠模式,这两种方式进入的睡眠模式唤醒的方法不同,分别是 WFI(wait for interrupt)和 WFE(wait for event),即由等待 “中断” 唤醒和由 “事件” 唤醒。

睡眠模式进入及退出方法

2.2、停止模式

停止模式

  进入停止模式,所有的时钟都关闭所有的外设也就停止了工作。但是 VDD 电源是没有关闭的,所以 内核的寄存器和内存信息都保留下来,等待重新开启时钟就可以从上次停止的地方继续执行程序。

停止模式进入及退出方法

当电压调节器处于低功耗模式下,当系统从停止模式退出时,将会有一段额外的启动延时。如果在停止模式期间保持内部调节器开启,则退出启动时间会缩短,但相应的功耗会增加。

2.3、待机模式

待机模式

  待机模式可实现最低功耗。该模式是 在 CM4 深睡眠模式时关闭电压调节器整个 1.8V 供电区域被断电PLL、HSI 和 HSE 振荡器也被断电除备份域(RTC 寄存器、RTC 备份寄存器和备份 SRAM)和待机电路中的寄存器外,SRAM 和其他寄存器内容都将丢失。不过如果我们使能了备份区域(备份 SRAM、RTC、LSE),那么待机模式下的功耗,将达到 6uA 左右。

待机模式进入及退出方法

待机模式下,除了复位引脚、被使能的唤醒引脚,其它 I/0 引脚处于高阻态,在此模式下不能下载程序。

三、电源控制寄存器

3.1、PWR电源控制寄存器

PWR电源控制寄存器

  位 [7:5]PLS 用于 设置 PVD 检测的电压阀值

  位 4 PVDE 位,用于使能或者禁止 PVD 检测,显然我们要使能 PVD 检测,该位置 1。

3.2、PWR电源控制状态寄存器

PWR电源控制状态寄存器

四、低功耗模式配置步骤

4.1、睡眠模式配置步骤

4.1.1、设置中断触发源

  这里我们用外部中断的方式唤醒睡眠模式,所以这里需要配置一个外部中断功能,我们用 WK_UP 按键作为中断触发源。进入外部中断后,EXTI_IMR 寄存器的值会自动清零,我们需要对对应的外部中断线位置 1,取消屏蔽,相当于其它中断的中断标志位进入中断后硬件自动置 1,需要手动清零。

4.1.2、进入睡眠模式

  HAL_PWR_EnterSLEEPMode() 是 STM32 HAL 库中的一个函数,用于将微控制器置于 SLEEP 模式,这是一种低功耗模式,其中 CPU 时钟被停止,但 RAM 和大多数外设保持运行。这种模式在需要快速响应中断或保持数据完整性时非常有用,因为它提供了比 STOP 或 STANDBY 模式更快的唤醒时间。

void HAL_PWR_EnterSLEEPMode(uint32_t Regulator, uint8_t SLEEPEntry);

  形参 Regulator 用来 指定稳压器的状态。有两个选择,PWR_MAINREGULATOR_ON 表示 稳压器处于正常模式PWR_LOWPOWERREGULATOR_ON 表示 稳压器处于低功耗模式。对应的是 PWR_CR 寄存器的 LPDS 位的设置(该形参在该函数中没有实质用处)。

#define PWR_MAINREGULATOR_ON                        0x00000000U
#define PWR_LOWPOWERREGULATOR_ON                    PWR_CR_LPDS

  形参 SLEEPEntry 用来 指定进入睡眠模式的方式。有两个选择,PWR_SLEEPENTRY_WFI 表示 使用 WFI 指令PWR_SLEEPENTRY_WFE 表示 使用 WFE 指令

#define PWR_SLEEPENTRY_WFI              ((uint8_t)0x01)
#define PWR_SLEEPENTRY_WFE              ((uint8_t)0x02)

4.1.3、等待中断唤醒

  通过按下 KEY0 按键进入睡眠模式,然后通过按下 WK_UP 按键触发外部中断唤醒睡眠模式。

4.2、停止模式配置步骤

4.2.1、设置中断触发源

  这里我们用外部中断的方式唤醒停止模式,所以这里需要配置一个外部中断功能,我们用 WK_UP 按键作为中断触发源。进入外部中断后,EXTI_IMR 寄存器的值会自动清零,我们需要对对应的外部中断线位置 1,取消屏蔽,相当于其它中断的中断标志位进入中断后硬件自动置 1,需要手动清零。

4.2.2、进入停止模式

  HAL_PWR_EnterSTOPMode() 是 STM32 HAL 库中的一个函数,用于将微控制器进入 STOP 模式,这是一种低功耗模式,其中 CPU 和大部分外设的时钟被停止,但 RAM 和一些关键外设(如 RTC)保持工作状态。这使得微控制器可以在低功耗状态下等待外部事件或中断唤醒。

void HAL_PWR_EnterSTOPMode(uint32_t Regulator, uint8_t STOPEntry);

  形参 Regulator 用来 指定稳压器的状态。有两个选择,PWR_MAINREGULATOR_ON 表示 稳压器处于正常模式PWR_LOWPOWERREGULATOR_ON 表示 稳压器处于低功耗模式。对应的是 PWR_CR 寄存器的 LPDS 位的设置(该形参在该函数中没有实质用处)。

  形参 SLEEPEntry 用来 指定进入睡眠模式的方式。有两个选择,PWR_SLEEPENTRY_WFI 表示 使用 WFI 指令PWR_SLEEPENTRY_WFE 表示 使用 WFE 指令

4.2.3、等待中断唤醒

  通过按下 KEY1 按键进入睡眠模式,然后通过按下 WK_UP 按键触发外部中断唤醒睡眠模式。

4.2.4、重新设置系统时钟

  退出停止模式后,HSI RC 振荡器被选为系统时钟,此时,我们需要重新设置系统时钟。

4.3、待机模式配置步骤

4.3.1、使能电源时钟

#define __HAL_RCC_PWR_CLK_ENABLE()     do { \
                                        __IO uint32_t tmpreg = 0x00U; \
                                        SET_BIT(RCC->APB1ENR, RCC_APB1ENR_PWREN);\
                                        /* Delay after an RCC peripheral clock enabling */ \
                                        tmpreg = READ_BIT(RCC->APB1ENR, RCC_APB1ENR_PWREN);\
                                        UNUSED(tmpreg); \
                                          } while(0U)

4.3.2、设置中断触发源

  这里我们用外部中断的方式唤醒待机模式,所以这里需要配置一个外部中断功能,我们用 WK_UP 按键作为中断触发源。进入外部中断后,EXTI_IMR 寄存器的值会自动清零,我们需要对对应的外部中断线位置 1,取消屏蔽,相当于其它中断的中断标志位进入中断后硬件自动置 1,需要手动清零。

4.3.3、使能中断触发源的唤醒功能

  HAL_PWR_EnableWakeUpPin() 函数在 STM32 HAL 库中用于配置指定的 GPIO 引脚作为唤醒源,以便在低功耗模式下(如 STOP 或 STANDBY 模式)能够通过该引脚的变化唤醒微控制器。

void HAL_PWR_EnableWakeUpPin(uint32_t WakeUpPinx);

  参数 WakeupPinx 实际上是指定的 GPIO 引脚编号。这个参数是一个枚举类型,表示要配置为唤醒源的 GPIO 引脚。具体的枚举值依赖于你使用的 STM32 系列和型号。

4.3.4、清除唤醒标记WUF

  当 STM32 处于停止模式时,它可以被配置的唤醒源唤醒,比如外部中断、RTC 闹钟、I/O 端口变化等。一旦发生唤醒事件,相应的唤醒标记(如 PWR_CR 寄存器中的 WUF 标记)会被设置。如果不清除这个标记,即使唤醒事件已经处理完毕,下次进入停止模式时,该标记仍可能被检测到,导致不必要的再次唤醒。

#define __HAL_PWR_CLEAR_FLAG(__FLAG__) (PWR->CR |=  (__FLAG__) << 2U)

  这个宏定义是在 STM32 HAL 库中用于清除 PWR 控制寄存器(PWR_CR)中特定标志位的。PWR_CR 寄存器包含了多个控制和状态位,用于管理微控制器的电源管理和低功耗模式。这里,我们需要清除唤醒标记,它的宏定义如下:

#define PWR_FLAG_WU                     PWR_CSR_WUF

4.3.5、进入待机模式

 &esmp;HAL_PWR_EnterSTANDBYMode() 是 STM32 HAL 库中的一个函数,用于将微控制器置于 STANDBY 模式,这是 STM32 支持的一种深度低功耗模式。在 STANDBY 模式下,除了备份域(Backup Domain)和一些特定的唤醒源之外,几乎所有的电源都被切断,从而达到极低的功耗状态。

void HAL_PWR_EnterSTANDBYMode(void)

五、程序源码

5.1、设置中断触发源

  EXTI0 初始化函数:

/**
 * @brief EXTI0初始化函数
 * 
 */
void EXTI0_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    __HAL_RCC_GPIOA_CLK_ENABLE();                                               // 使能GPIOE的时钟

    GPIO_InitStruct.Pin = GPIO_PIN_0;                                           // GPIO引脚
    GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;                                 // 上降沿触发
    GPIO_InitStruct.Pull = GPIO_PULLDOWN;                                       // 使用下拉
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);                                     // GPIO初始化

    HAL_NVIC_SetPriority(EXTI0_IRQn, 4, 0);                                     // 设置中断优先级
    HAL_NVIC_EnableIRQ(EXTI0_IRQn);                                             // 使能中断
}

  EXTI0 中断服务函数:

/**
 * @brief EXTI0中断服务函数
 * 
 */
void EXTI0_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);                                       // 调用HAL库的EXTI公共中断处理函数
}

  EXTI 中断回调函数:

/**
 * @brief 重写HAL库的EXTI回调函数
 * 
 * @param GPIO_Pin EXTI的引脚
 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if (GPIO_Pin == GPIO_PIN_0)
    {
        // HAL库的EXIT公共中断处理函数已经清除了中断标志位
    }
}

5.2、main()函数

int main(void)
{
    HAL_Init();
    System_Clock_Init(8, 336, 2, 7);
    Delay_Init(168);
    HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);

    EXTI0_Init();
    UART_Init(&g_usart1_handle, USART1, 115200);
    LED_Init();
    Key_Init();

    while (1)
    {
        switch (Key_Scan(0))
        {
        case KEY1_PRESS:
            printf("进入睡眠模式!\r\n");
            HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9, GPIO_PIN_RESET);
            HAL_SuspendTick();                                                  // 暂停滴答时钟,防止通过滴答时钟中断唤醒

            HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);   // 进入睡眠模式

            HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9, GPIO_PIN_SET);
            printf("退出睡眠模式!\r\n");
            break;

        case KEY2_PRESS:
            printf("进入停止模式!\r\n");
            HAL_GPIO_WritePin(GPIOF, GPIO_PIN_10, GPIO_PIN_RESET);

            HAL_PWR_EnterSTOPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);    // 进入停止模式
  
            System_Clock_Init(8, 336, 2, 7);                                    // 重新设置系统时钟,退出停止模式后,默认选中HSI作为系统时钟
            HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK_DIV8);           // 重新设置滴答定时器时钟

            HAL_GPIO_WritePin(GPIOF, GPIO_PIN_10, GPIO_PIN_SET);
            printf("退出停止模式!\r\n");
            break;

        case KEY3_PRESS:
            printf("进入待机模式!\r\n");
            HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9, GPIO_PIN_RESET);

            __HAL_RCC_PWR_CLK_ENABLE();                                         // 使能电源时钟
            HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);                           // 使能WKUP上升沿唤醒功能
            __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);                                  // 清除唤醒标志

            HAL_PWR_EnterSTANDBYMode();                                         // 进入待机模式

            // 退出待机模式,程序从头执行,下面的打码不会执行
            HAL_GPIO_WritePin(GPIOF, GPIO_PIN_10, GPIO_PIN_RESET);
            printf("退出待机模式!\r\n");
            break;

        default:
            break;
        }
    }
  
    return 0;
}
posted @ 2024-01-27 23:38  星光映梦  阅读(135)  评论(0编辑  收藏  举报