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电源控制寄存器
位 [7:5]PLS 用于 设置 PVD 检测的电压阀值。
位 4 PVDE 位,用于使能或者禁止 PVD 检测,显然我们要使能 PVD 检测,该位置 1。
3.2、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;
}