STM32 学习13 低功耗模式与唤醒
- 一、介绍
- 1. STM32低功耗模式功能介绍
- 2. 常见的低功耗模式
- (1)**睡眠模式 (Sleep Mode)**:
- (2)**停止模式 (Stop Mode)**:
- (3)**待机模式 (Standby Mode)**:
- 二、睡眠模式
- 1. 进入睡眠模式
- 2. 唤醒
- 3. 代码实现
- (1)exti_utils.c
- (2)main.c
- 三、停机模式
- 四、待机模式
- 1. 进入待机模式的方法
- 2. 退出待机模式的方法
- 3. 代码实现
- (1)进入待机模式
- (2)主函数
一、介绍
1. STM32低功耗模式功能介绍
STM32微控制器提供了多种低功耗模式,以便在需要节能的应用场景中最大限度地减少功耗。默认情况下,系统复位或上电复位后,微控制器进入运行模式。在运行模式下,HCLK为CPU提供时钟,并执行程序代码。
当MCU不需要运行时,就可以利用低功耗模式来节省功耗。STM32有多种低功耗模式,用户可以根据电源消耗、启动速度、可唤醒资源来选择最佳的低功耗模式。
2. 常见的低功耗模式
STM32微控制器提供了多种低功耗模式,以便在需要节能的应用场景中最大限度地减少功耗。以下是一些常见的STM32低功耗模式及其功能介绍:
(1)睡眠模式 (Sleep Mode):
- 功能:在此模式下,CPU暂停执行,并且大多数内部设备被关闭。只有一些关键外设(例如RTC,看门狗定时器)可能仍然处于活动状态。
- 特点:功耗极低,但系统仍然可以在外部事件触发时快速唤醒。
(2)停止模式 (Stop Mode):
- 功能:在此模式下,CPU和大多数外设都被停止,只有RTC和一些外部中断仍然处于活动状态。停止模式比睡眠模式功耗更低,但唤醒时间略长。
- 特点:可实现极低功耗,适用于长时间的睡眠状态。
(3)待机模式 (Standby Mode):
- 功能:这是最低功耗的模式之一,内核1.8V电源关闭、几乎所有设备都被关闭,只有RTC和一些少量的外部唤醒源处于活动状态。
- 特点:待机模式下的功耗极低,但唤醒时间相对较长。
下表是来自《STM32F1xx中文参考手册》的几种低功耗模式的比较:
二、睡眠模式
1. 进入睡眠模式
进入睡眠模式时,Cortex™-M3内核停止,所有外设包括Cortex-M3核心的外设,如NVIC、系统时
钟(SysTick)等仍在运行。
可以通过执行WFI或WFE指令进入睡眠状态 。
进入睡眠模式有两种机制:
- SLEEP-NOW:如果系统控制寄存器SLEEPONEXIT位被清除,当WRI或WFE被执行时,MCU立即进入睡眠模式;
- SLEEP-ON-EXIT:如果SLEEPONEXIT位被设置,系统从最低优先级的中断处理程序中退出时,MCU立即进入睡眠模式。
2. 唤醒
- 如果执行WFI指令进入睡眠模式,任意一个被嵌套向量中断控制器响应的外部中断都能将系统从睡眠模式唤醒 ;
- 如果执行WFE指令进入睡眠,则一旦发生唤醒事件时,MCU将唤醒。
两种模式的进入、退出比较:
模式 | 说明 |
---|---|
SLEEP-NOW进入 | 在以下条件下执行WFI(等待中断)或 WFE(等待事件)指令: – SLEEPDEEP = 0 和 – SLEEPONEXIT = 0 参考Cortex-M3系统控制寄存器 |
SLEEP-NOW退出 | 如果执行WFI进入睡眠模式: 中断:参考中断向量表(表54) 如果执行WFE进入睡眠模式: 唤醒事件:参考唤醒事件管理(第9.2.3节) |
SLEEP-ON-EXIT进入 | 在以下条件下执行WFI指令: – SLEEPDEEP = 0和 – SLEEPONEXIT = 1 参考Cortex™-M3系统控制寄存器 |
SLEEP-ON_EXIT退出 | 中断:参考中断向量表 |
3. 代码实现
下面使用按键中断响应事件进入低功耗模式,可以使用更高级别的事件唤醒。运行效果是数码管显示0-9数字,当按下开发板的“下”时,MCU休眠,数码管不再变化。 再按下开发板的“右”时,MCU退出休眠。
(1)exti_utils.c
/**
* @brief 外部中断3中断服务函数
*/
void EXTI3_IRQHandler(void) {
if (EXTI_GetITStatus(EXTI_Line3) != RESET) {
delay_ms(10);
if (key_down_value == 0) {
__WFI();
// led_lightn(1);
EXTI_ClearITPendingBit(EXTI_Line3); // 清除中断标志位
return;
}
}
}
/**
* @brief 外部中断4中断服务函数
*/
void EXTI4_IRQHandler(void) {
if (EXTI_GetITStatus(EXTI_Line4) != RESET) {
delay_ms(20);
if (key_right_value == 0) {
//led_lightn(3);
if(EXTI_GetFlagStatus(EXTI_Line4) == SET){
printf("exit sleep\r\n");
}
EXTI_ClearITPendingBit(EXTI_Line4); // 清除中断标志位
return;
}
}
}
(2)main.c
#include "gpio_utils.h"
#include "stm32f10x.h"
#include "sys_tick_utils.h"
#include "led_utils.h"
#include "usart_utils.h"
#include "stdio.h"
#include "exti_utils.h"
// 主函数
int main(void)
{
GPIO_Configuration(); // 调用GPIO配置函数
// tick 初始化
sys_tick_init(72);
led_all_off();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
USART3_Init(9600);
printf("starting...");
// led 初始化
custom_led_init();
custom_exti_init();
int i = 0;
while (1) // 无限循环
{
delay_ms(990);
printf("i=%d", i);
led_lightn(i);
i++;
if(i>9){
i=0;
}
}
}
三、停机模式
停机模式在睡眠模式的基础上,关闭了所有1.8V区域的时钟,停止前会保存运行状态信息,恢复后可以接着停机前的程序继续运行。
停机模式本文不详细介绍。
四、待机模式
在停机模式的基础上,不再保存运行信息,系统复位从头运行程序,电源控制/状态寄存器PWR_CSR会指示内核由待机状态退出。
1. 进入待机模式的方法
- 设系统控制寄存器的 SLEEPDEEP位;
- 设置电源控制寄存器PWR_CR中的PDDS位;
- 清除电源控制/状态寄存器(PWR_CSR)中的WUF位。
配置步骤如下:
- 使能电源时钟
- 设置唤醒源
- 进入待机模式
2. 退出待机模式的方法
- WKUP引脚的上升沿;
- RTC闹钟事件的上升沿;
- NRST引脚上外部复位;
- IWDG复位。
待机模式下,大部分IO引脚处于高阻态,复位引脚、TAMPER(PC13)引脚、WKUP(PA0)引脚可用。
从实验用的开发板原理图上可以看到,WKUP引脚已经接到按键的K_UP:
下面的示例将使用KEY_UP唤醒MCU。
3. 代码实现
下面示例程序与前例类似 , LED按顺序显示0-9数字 。 当按下“下”按键时,进入 待机模式。
通过K_UP按键唤醒。与睡眠模式不同,进入待机模式后,数码管不再显示。
注意使用相关库函数,需要引用 stm32f10x_pwr.c 文件。
(1)进入待机模式
standby_utils.c
#include "standby_utils.h"
/**
* @brief 进入待机模式
*/
void Standby_Enter(void){
// ¿ªÆôʱÖÓ
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
// ÉèÖû½ÐÑÔ´,ʹÓÃWakeUpPin»½ÐÑ
PWR_WakeUpPinCmd(ENABLE);
// Çå¿Õ±êÖ¾
PWR_ClearFlag(PWR_FLAG_WU);
// ½øÈë´ý»úģʽ
PWR_EnterSTANDBYMode();
}
(2)主函数
#include "gpio_utils.h"
#include "stm32f10x.h"
#include "sys_tick_utils.h"
#include "led_utils.h"
#include "usart_utils.h"
#include "stdio.h"
#include "exti_utils.h"
// 主函数
int main(void)
{
GPIO_Configuration(); // 调用GPIO配置函数
// tick 初始化
sys_tick_init(72);
led_all_off();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
USART3_Init(9600);
printf("starting...");
// led 初始化
custom_led_init();
custom_exti_init();
int i = 0;
while (1) // 无限循环
{
delay_ms(990);
printf("i=%d", i);
led_lightn(i);
i++;
if(i>9){
i=0;
}
}
}
本系列文章代码开源地址:
https://gitee.com/xundh/stm32_arm_learn