STM32低功耗实验学习日记
STM32低功耗实验学习日记
写于2024/9/25晚
文章目录
- STM32低功耗实验学习日记
- 1. 简介
- 2. STM32F1电源系统
- 2.1 电源系统
- 2.2 电源管理
- 2.2.1 睡眠模式
- 2.2.2 停止模式
- 2.2.3 待机模式
- 2.3 相关寄存器介绍
- 2.3.1 系统控制寄存器(SCB_SCR)
- 2.3.2 电源控制寄存器(PWR_CR)
- 2.3.3 电源控制/状态寄存器(PWR_CSR)
- 2.3.4 WFI与WFE指令
- 3. 进入低功耗模式的使用步骤
- 4. 代码解析
1. 简介
我们将介绍 STM32F103 的电源控制(PWR),并实现低功耗模式相关功能。我们将通过四个实验来学习并实现低功耗相关功能,分别是 PVD 电压监控实验、睡眠模式实验、停止模式实验和待机模式实验
2. STM32F1电源系统
电源控制部分(PWR)概述了不同电源域的电源架构以及电源配置控制器。PWR 的内容比较多,我们把它们的主要特性概括为以下 3 点:
-
电源系统:VDDA供电区域、VDD供电区域、1.8V 供电区域、后备供电区域。
-
电源监控:POR/PDR 监控器、PVD 监控器。
-
电源管理:低功耗模式。
下面将分别对这 3 个特性进行简单介绍。
2.1 电源系统
在电源概述框图中我们划分了 3 个区域①②③,分别是独立的 A/D 转换器供电和参考电压、电压调节器、电池备份区域。下面分别进行简单介绍:
① 独立的 A/D转换器供电和参考电压(VDDA供电区域)
VDDA 供电区域,主要是 ADC 电源以及参考电压,STM32 的 ADC 模块配备独立的供电方式,使用了 VDDA 引脚作为输入,使用 VSSA 引脚作为独立地连接,VREF 引脚为提供给 ADC 的参考电压。
② 电压调节器(VDD /1.8V供电区域)
电压调节器是 STM32 的电源系统中最核心部分,连接 VDD供电区域和 1.8 V供电区域。VDD供电来自于 VSS 和 VDD,给 I/O 电路以及待机电路供电,电压调节器主要为备份域以及待机电路以外的所有数字电路供电,其中包括内核、数字外设以及RAM,调节器的输出电压约为1.8V,因此由调压器供电的区域称为 1.8V 供电区域。电压调节器根据应用方式不同有三种不同的工作模式。在运行模式下,调节器以正常工作模式为内核、内存和外设提供 1.8V;在停止模式下,调节器以低功耗模式提供 1.8V 电源,以保存寄存器和 SRAM 的内容。在待机模式下,调节器停止供电,除了备用电路和备份域外,寄存器和 SRAM 的内容全部丢失。
③ 电池备份区域(后备供电区域)
电池备份区域也就是后备供电区域,使用电池或者其他电源连接到 VBAT脚上,当 VDD断电时,可以保存备份寄存器的内容和维持 RTC 的功能。同时 VBAT 引脚也为 RTC 和 LSE 振荡器供电,这保证了当主要电源被切断时,RTC 能够继续工作。切换到 VBAT 供电由复位模块中的掉电复位功能控制。
2.2 电源管理
电源管理的部分我们要关注低功耗模式,在 STM32 的正常工作中,具有四种工作模式,运行、睡眠、停止以及待机。在上电复位后,STM32 处于运行状态时,当内核不需要继续运行,就可以选择进入后面的三种模式降低功耗。
这三种低功耗模式电源消耗不同、唤醒时间不同和唤醒源不同,我们要根据自身的需要选择合适的低功耗模式。下面是低功耗模式汇总介绍,如下表所示。
2.2.1 睡眠模式
进入睡眠模式,Cortex_M3 内核停止,所有外设包括 Cortex_M3 核心的外设,如 NVIC、系统时钟(SysTick)等仍在运行,有两种进入睡眠模式的模式 WFI 和 WFE。WFI(Wait for interrupt等待中断)和 WFE(Wait for event等待事件)是内核指令,会调用一些汇编指令,会使用即可。睡眠后唤醒的方式即由等待“中断”唤醒和“事件”唤醒。
2.2.2 停止模式
进入停止模式,所有的时钟都关闭,所有的外设也就停止了工作。但是 VDD电源是没有关闭的,所以内核的寄存器和内存信息都保留下来,等待重新开启时钟就可以从上次停止的地方继续执行程序。
值得注意的是:当电压调节器处于低功耗模式下,当系统从停止模式退出时,将会有一段额外的启动延时。如果在停止模式期间保持内部调节器开启,则退出启动时间会缩短,但相应的功耗会增加。
2.2.3 待机模式
待机模式可实现最低功耗。该模式是在 CM3 深睡眠模式时关闭电压调节器,整个 1.8V 供电区域被断电。PLL、HSI 和 HSE 振荡器也被断电。除备份域(RTC 寄存器、RTC 备份寄存器和备份 SRAM)和待机电路中的寄存器外,SRAM 和其他寄存器内容都将丢失。不过如果我们使能了备份区域(备份 SRAM、RTC、LSE),那么待机模式下的功耗,将达到 3.8uA 左右。
2.3 相关寄存器介绍
2.3.1 系统控制寄存器(SCB_SCR)
2.3.2 电源控制寄存器(PWR_CR)
2.3.3 电源控制/状态寄存器(PWR_CSR)
2.3.4 WFI与WFE指令
内核指令,使用函数的格式__WFI()
和__WFE()
来调用。__wfi
和__wfe
是编译器内置的函数,函数内部调用了相对应的汇编指令。
驱动函数 | 关联寄存器 | 功能描述 |
---|---|---|
HAL_PWR_EnterSLEEPMode(…) | SCB_SCR | 进入睡眠模式 |
HAL_PWR_EnterSTOPMode(…) | PWR_CR/SCB_SCR | 进入停止模式 |
HAL_PWR_EnterSTANDBYMode(…) | PWR_CR/SCB_SCR | 进入待机模式 |
HAL_PWR_EnableWakeUpPin(…) | PWR_CSR | 使能WKUP管脚唤醒功能 |
__HAL_PWR_CLEAR_FLAG(…) | PWR_CR | 清除PWR的相关标记 |
__HAL_RCC_PWR_CLK_ENABLE(…) | RCC_APB1ENR | 使能电源时钟 |
3. 进入低功耗模式的使用步骤
在退出停止模式后,需要重新设置时钟、重新选择滴答时钟源、失能systick中断
待机模式配置步骤
- 初始化WKUP为中断触发源
- 使能电源时钟:__HAL_RCC_PWR_CLK_ENABLE
- 使能WKUP的唤醒功能:HAL_PWR_EnableWakeUpPin
- 清除唤醒标记WUF:__HAL_PWR_CLEAR_FLAG
- 进入待机模式:HAL_PWR_EnterSTANDBYMode
4. 代码解析
驱动函数 | 关联寄存器 | 功能描述 |
---|---|---|
HAL_PWR_EnterSLEEPMode(…) | SCB_SCR | 进入睡眠模式 |
HAL_PWR_EnterSTOPMode(…) | PWR_CR/SCB_SCR | 进入停止模式 |
HAL_PWR_EnterSTANDBYMode(…) | PWR_CR/SCB_SCR | 进入待机模式 |
HAL_PWR_EnableWakeUpPin(…) | PWR_CSR | 使能WKUP管脚唤醒功能 |
__HAL_PWR_CLEAR_FLAG(…) | PWR_CR | 清除PWR的相关标记 |
__HAL_RCC_PWR_CLK_ENABLE(…) | RCC_APB1ENR | 使能电源时钟 |
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/BEEP/beep.h"
#include "./BSP/KEY/key.h"
#include "./BSP/PWR/pwr.h"
void pwr_wkup_key_init(void)
{
GPIO_InitTypeDef gpio_init_struct;
PWR_WKUP_GPIO_CLK_ENABLE(); /* WKUP引脚时钟使能 */
gpio_init_struct.Pin = PWR_WKUP_GPIO_PIN; /* WKUP引脚 */
gpio_init_struct.Mode = GPIO_MODE_IT_RISING; /* 中断,上升沿 */
gpio_init_struct.Pull = GPIO_PULLDOWN; /* 下拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(PWR_WKUP_GPIO_PORT, &gpio_init_struct); /* WKUP引脚初始化 */
HAL_NVIC_SetPriority(PWR_WKUP_INT_IRQn, 2, 2); /* 抢占优先级2,子优先级2 */
HAL_NVIC_EnableIRQ(PWR_WKUP_INT_IRQn);
}
void PWR_WKUP_INT_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(PWR_WKUP_GPIO_PIN);
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == PWR_WKUP_GPIO_PIN)
{
/* HAL_GPIO_EXTI_IRQHandler()函数已经为我们清除了中断标志位,所以我们进了回调函数可以不做任何事 */
}
}
int main(void)
{
uint8_t key;
uint8_t t = 0;
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
usart_init(115200); /* 串口初始化 */
led_init(); /* 初始化LED */
key_init(); /* 初始化按键 */
pwr_wkup_key_init(); /* WKUP引脚初始化 */
pwr_pvd_init(); /* PVD配置 */
printf("Enter to LowPower Test \r\n");
while(1)
{
key = key_scan(0); /* 得到键值 */
if (key)
{
switch (key)
{
/* 进入待机模式 */
case KEY2_PRES:
/* 使能电源时钟 */
__HAL_RCC_PWR_CLK_ENABLE();
/* 使能WKUP上升沿的唤醒功能 */
HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);
/* 清除唤醒标记 */
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
printf("Enter STANDBY Mode \r\n");
HAL_PWR_EnterSTANDBYMode();
printf("Exit STANDBY Mode \r\n");
break;
/* 进入停止模式 */
case KEY1_PRES:
LED1(0); /* 点亮绿灯,提示进入停止模式 */
printf("Enter STOP Mode \r\n");
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_SLEEPENTRY_WFI);
sys_stm32_clock_init(RCC_PLL_MUL9); /* 重新设置时钟, 72Mhz */
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK_DIV8);
HAL_SuspendTick();
printf("Exit STOP Mode \r\n");
LED1(1);
break;
/* 进入睡眠模式 */
case KEY0_PRES:
printf("Enter SLEEP Mode \r\n");
HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);
printf("Exit SLEEP Mode \r\n");
break;
}
}
if ((t % 20) == 0)
{
LED0_TOGGLE(); /* 每200ms,翻转一次LED0 */
}
delay_ms(10);
t++;
}
}