ITPub博客

首页 > 嵌入式/内核开发 > 嵌入式/内核开发 > 《STM32H7R7开发指南 V1.1 》第三十一章 低功耗实验

《STM32H7R7开发指南 V1.1 》第三十一章 低功耗实验

原创 嵌入式/内核开发 作者:正点原子 时间:2025-08-29 10:10:49 0 删除 编辑

第三十一章 低功耗实验


       本章,我们将介绍STM32H7R7的电源控制(PWR),并实现低功耗模式相关功能。我们将通过四个实验来学习并实现低功耗相关功能,分别是PVD电压监控实验、睡眠模式实验、停止模式实验和待机模式实验。

       本章分为如下几个小节:

       31.1 电源控制(PWR)简介

       31.2 PVD电压监控实验

       31.3 睡眠模式实验

       31.4 停止模式实验

       31.5 待机模式实验

        31.1 电源控制(PWR)简介

       电源控制部分(PWR)概述了不同电源域的电源架构以及电源配置控制器。PWR的内容比较多,我们把它们的主要特性概括为以下3点:

       电源系统:USB稳压器、内核域(VCORE)、VDD域、备份域、模拟域(VDDA)。

       电源监控:POR/PDR监控器、BOR监控器、PVD监控器、AVD监控器、VBAT阈值、温度阈值。

       电源管理:VBAT电池充电、工作模式、电压调节控制、低功耗模式。

       下面将分别对这3个特性进行简单介绍。


       31.1.1 电源系统 

       为了方便对电源系统进行管理,设计者把STM32的内核和外设等器件根据功能划分了不同的电源区域,具体如图31.1.1.1所示。


图31.1.1.1 电源概述框图


       在电源概述框图中我们划分了5个区域①②③④⑤,分别是内核域、VDD域、备份域、USB稳压器械和模拟域。下面分别进行简单的介绍:


       ① 内核域(VCORE)

       V DDLDO是稳压器供电的外部电源。

       V SS 是所有电源和模拟稳压器的公共地。

       V CAP数字内核域电源,该电源独立于所有其它电源:

       1)当稳压器使能时,V CORE由内部稳压器提供。

       2)当稳压器禁止时,V CORE由外部电源通过V CAP引脚提供。

       V CORE内核域电源可通过稳压器或者外部电源(V CAP)供电。V CORE为除备份域和待机电路以外的所有数字电路供电。

       当发生系统复位时,稳压器使能并为V CORE供电。稳压器的输出电压为1.2V(M4/M7),如果是M3,则是1.8V。稳压器提供三种不同的工作模式:主 (MR) 模式、低功耗模式 (LP) 或关闭模式。这些模式将根据系统工作模式(运行、停止和待机)进行使用,详细如下:

       1、运行模式:稳压器工作在主模式,并为V CORE域(内核、存储器和数字外设)提供全功率。稳压器输出电源可通过软件调节为不同电压级别(VOS1、VOS2 和 VOS3),这些级别通过PWR D3域控制寄存器(PWR_D3CR) 中的VOS位配置。

       2、停止模式:V CORE域的所有时钟都被关闭,相应的外设都停止了工作,但稳压器还会为VCORE供电以保存内核寄存器和内部存储器(SRAM)的内容。稳压器模式通过PWR控制寄存器1 (PWR_CR1) 中的SVOS和LPDS位选择。如果选择了SVOS3电压调节,则可以选择主模式或 低功耗模式;如果选择SVOS4和SVOS5调节,则只能选择低功耗模式。由于SVOS4和SVOS5 调节的电压级别低,因此可进一步降低停止模式的功耗。

       3、待机模式:

       稳压器关闭且VCORE域掉电。除待机电路和备份域外,内核寄存器和内部存储器(SRAM)的内容都将丢失。


       ② V DD域

       V DD为I/O和系统模拟模块(如复位、电源管理和时钟)供电的外部电源。

       V BAT是后备电源(来源于开发板上3V的纽扣电池),当VDD不存在时(开发板断电),VBAT为备份域供电。


       ③备份域

       备份域的电源自来VSW,VSW来自VDD域的VDD或者VBAT。在备份域中,包含LSI、LSE、RTC、唤醒逻辑、备份寄存器、复位以及备份SRAM等器件。


       ④USB稳压器械

       V DD50USB为USB稳压器供电的外部电源。

       V DD33USB为USB接口供电的USB稳压器供电输出。

       1)当USB稳压器使能时,V DD33USB由内部USB稳压器提供。

       2)当USB稳压器禁止时,V DD33USB由独立的外部电源输入提供。


       ⑤模拟域

       V DDA为ADC、DAC、OPAMP、比较器和电压参考缓冲器供电的外部模拟电源。该电源独立于所有其它电源。

       V SSA是独立的模拟和参考电压地。

       V REF+ 是ADC和DAC的外部参考电压。

       1)当电压参考缓冲器使能时,V REF+ 和V REF- 由内部电压参考缓冲器提供。

       2)当电压参考缓冲器禁止时,V REF+ 由独立的外部参考电源提供。


       31.1.2 电源监控 

       电源监控的部分我们主要关注POR/PDR监控器、BOR监控器、PVD监控器、AVD监控器、超出功能范围的温度监视器、超出功能范围备用域电压监测器。其他部分的内容请大家查看《STM32H7Rx参考手册_V6(英文版).pdf》第6.5节(261页)。


       l 上电复位(POR)/掉电复位(PDR)

       上电时,当V DD低于指定V POR阈值时,系统无需外部复位电路便会保持复位模式。一旦V DD电源电压高于V POR阈值,系统便会退出复位状态。掉电时,当V DD低于指定V PDR阈值时,系统就会保持复位模式。如图31.1.2.1所示,R eset为上电复位信号。

       注意:POR与PDR的复位电压阈值是固定的,V POR阈值(典型值)为1.67V,V PDR阈值(典型值)为1.62V。


图31.1.2.1 上电复位/掉电复位波形


       l 欠压复位(BOR)

       上电期间,欠压复位(BOR)将使系统保持复位状态,直到V DD电源电压达到指定的V BOR阈值。V BOR阈值通过系统选项字节(某些寄存器的BOR_LEV位)进行配置。默认情况下,BOR关闭。可选择以下可编程V BOR阈值:


表31.1.2.1 BOR欠压阀值等级


       该表截取于《STM32H7R7L8H6H.pdf》手册的159页。

       欠压复位的描述波形图如图31.1.2.2所示,pwr_bor_rst为欠压复位信号。


图31.1.2.2 欠压复位波形


       l 可编程电压检测器(PVD)

       上面介绍的POR、PDR以及BOR功能都是设置电压阈值与外部供电电压V DD比较,当V DD低于设置的电压阈值时,就会直接进入复位状态,防止电压不足导致的误操作。

       下面介绍可编程电压检测器(PVD),它可以实时监视V DD的电压,方法是将V DD与PWR控制寄存器1(PWR_CR1)中的PLS[2:0]位所选的V PVD阈值进行比较。当检测到电压低于V PVD阈值时,如果使能EXTI16线中断,即使能PVD & AVD中断(可参考外部中断章节的表15.1.2.1知道EXTI16线内部连接PVD中断事件),可以产生PVD中断,具体取决于EXTI16线配置为检测上升还是下降沿,然后在复位前,在中断服务程序中执行紧急关闭系统等任务。PVD阀值检测波形,如图31.1.2.3所示。


图31.1.2.3 PVD检测波形


       PVD阀值有8个等级,有上升沿和下降沿的区别,分别就是图31.1.2.3中PVDrise电压为上升沿阀值,PVDfall为下降沿阀值。PVD阈值等级具体如表31.1.2.2所示。


表31.1.2.2 PVD阀值等级


       该表截取于《STM32H7R7L8H6H.pdf》手册的159页。


       31.1.3 电源管理 

       电源管理的部分我们主要关注低功耗模式,其它部分的内容请自行查看手册。

       很多单片机都有低功耗模式,STM32也不例外。STM32H7R7提供了三种低功耗模式,以达到不同层次的降低功耗的目的,三种模式分别是:睡眠模式、停止模式和待机模式。这三种低功耗模式电源消耗不同、唤醒时间不同和唤醒源不同,我们要根据自身的需要选择合适的低功耗模式。

       下面是低功耗模式汇总介绍,如下表所示。


表31.1.3.1 低功耗模式汇总


       下面对睡眠模式、停止模式和待机模式的进入及退出方法进行介绍。


       1、睡眠模式

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


表31.1.3.2 睡眠模式进入及退出方法


       2、停止模式

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

       停止模式可以被任何一个外部中断(EXTI)或事件唤醒。在停止模式中可以选择稳压器的工作方式为主模式或者低功耗模式。下面我们看看停止模式进入及退出方法:


表31.1.3.3 停止模式进入及退出方法


       3、待机模式

       待机模式可实现最低功耗。该模式是在CM7深睡眠模式时关闭稳压器(V COER关闭),PLL、HSI、CSI、HSI48和HSE振荡器也被断电。除备份域(RTC寄存器、RTC备份寄存器和备份SRAM)和待机电路中的寄存器外,SRAM 和寄存器内容都将丢失。不过如果我们使能了备份区域(备份SRAM、RTC、LSE),那么待机模式下的功耗,将达到6uA左右。下面我们看看待机模式进入及退出方法:


表31.1.3.4 待机模式进入及退出方法


       从待机模式唤醒后的代码执行等同于复位后的执行(采样启动模式引脚,读取复位向量等)。 在进入待机模式后,除了复位引脚、RTC_TAMP1/2/3引脚,如果针对入侵、时间戳、RTC 闹钟输出或 RTC 时钟校准输出进行了配置)和WK_UP(如果使能了)等引脚外,其他所有IO引脚都将处于高阻态。

       下面开始本章的四个实验的介绍。


        31.2 PVD电压监控实验

       本小节我们来学习PVD电压监控实验,该部分的知识点内容请回顾31.1.2电源监控。我们直接从寄存器介绍开始。


       31.2.1 PWR寄存器 

       本实验用到PWR的部分寄存器,在《STM32H7Rx参考手册_V6(英文版).pdf》手册的6.8小节(317页)可以找到PWR的寄存器描述。这里我们只介绍PVD电压监控实验我们用到的PWR的控制寄存器1(PWR_CR1),还有就是我们要用到EXTI16线中断,所以还要配置EXTI相关的寄存器,具体如下:


       l PWR控制寄存器 1(PWR_CR1)

       PWR控制寄存器1描述如图31.2.1.1所示:


图31.2.1.1 PWR_CR1寄存器(部分)


       位[7:5] PLS用于设置PVD检测的电压阀值,即前面我们介绍PVD的8个等级阀值选择。位4 PVDE位,用于使能或者禁止PVD检测,显然我们要使能PVD检测,该位置1。


       l EXTI中断屏蔽寄存器(EXTI_IMR1)

       EXTI中断屏蔽寄存器描述如图31.2.1.2所示:


图31.2.1.2 EXTI_IMR1寄存器


       我们要使用到EXTI16线中断,所以MR16位要置1,即取消屏蔽EXTI16线的中断请求。


       l EXTI上升沿触发选择寄存器(EXTI_RTSR1)

       EXTI上升沿触发选择寄存器描述如图31.2.1.3所示:


图31.2.1.3 EXTI_RTSR寄存器


       我们要使用到EXTI16线中断,所以TR16位要置1。


       l EXTI下降沿触发选择寄存器(EXTI_FTSR1)

       EXTI下降沿触发选择寄存器描述如图31.2.1.4所示:


图31.2.1.4 EXTI_FTSR1寄存器


       我们要使用到EXTI16线中断,所以TR16位要置1。


       l EXTI挂起寄存器(EXTI_PR1)

       EXTI挂起寄存器描述如图31.2.1.5所示:


图31.2.1.5 EXTI_PR1寄存器


       EXTI挂起寄存器EXTI_PR1管理的是EXTI0线到EXTI21线的中断标志位。在PVD中断服务函数里面,我们记得要对PR16位写1,来清除EXTI16线的中断标志。


       31.2.2 硬件设计


       1. 例程功能

       开发板供电正常的话,LCD屏会显示"PVD Voltage OK!"。当供电电压过低,则会通过PVD中断服务函数将LED1点亮;当供电电压正常后,会在PVD中断服务函数将LED1熄灭。LED0闪烁,提示程序运行。


       2. 硬件资源


       1)LED灯

              LED0:LED0 – PD14

              LED1:   LED1 – PC0


       2)PVD(可编程电压监测器)


       3)正点原子2.8/3.5/4.3/7/10寸TFTLCD模块(包括MCU屏和RGB屏,都支持)


       3. 原理图

       PVD属于STM32H7R7的内部资源,只需要软件设置好即可正常工作。我们通过LED1和LCD来指示进入PVD中断的情况。


       31.2.3 程序设计 


       31.2.3.1 PWR的HAL库驱动

       PWR在HAL库中的驱动代码在stm32h7rsxx_hal_pwr.c文件(及其头文件)中。


       1. HAL_PWR_ConfigPVD函数

       PVD的初始化函数,其声明如下:

void HAL_PWR_ConfigPVD (PWR_PVDTypeDef *sConfigPVD);


       l 函数描述:

       用于初始化PWR。


       l 函数形参:

       形参1是PWR_PVDTypeDef结构体类型指针变量,其定义如下:

typedef struct
{
  uint32_t PVDLevel;  /* 指定PVD检测级别 */
  uint32_t Mode;       /* 指定PVD的EXTI检测模式 */
}PWR_PVDTypeDef;


       1)PVDLevel:指向PVD检测级别,对应PWR_CR1寄存器的PLS位的设置,取值范围PWR_PVDLEVEL_1到PWR_PVDLEVEL_7。


       2)Mode:指定PVD的EXTI边沿检测模式。


       l 函数返回值:

       无


       PVD电压监控配置步骤


       1)配置PVD

       调用HAL_PWR_ConfigPVD函数配置PVD,包括检测电压级别、使用中断线的什么边沿缘触发等。


       2)使能PVD检测,配置PVD/AVD中断优先级,开启PVD/AVD中断

       通过HAL_PWR_EnablePVD函数使能PVD检测。

       通过HAL_NVIC_EnableIRQ函数使能PVD/AVD中断。

       通过HAL_NVIC_SetPriority函数设置中断优先级。


       3)编写中断服务函数

       PVD/AVD中断服务函数为:PVD_AVD_IRQHandler,当发生中断的时候,程序就会执行中断服务函数。HAL库有专门的PVD/AVD中断处理函数,我们只需要在PVD/AVD中断服务函数里面调用HAL_PWR_PVD_IRQHandler函数,然后逻辑代码在PVD/AVD中断服务回调函数HAL_PWR_PVDCallback中编写,详见本例程源码。


       31.2.3.2 程序解析

       这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。PWR源码包括两个文件:pwr.c和pwr.h。

       pwr.h头文件只有函数声明,下面直接开始介绍pwr.c的程序,首先是PVD初始化函数。

/**
 * @brief   初始化PVD
 * @param   pl: 电压等级
 * @retval  无
 */
void pwr_pvd_init(uint32_t pl)
{
    PWR_PVDTypeDef pwr_pvd_struct = {0};
    
    /* 配置并使能PVD */
    pwr_pvd_struct.PVDLevel = pl;
    pwr_pvd_struct.Mode = PWR_PVD_MODE_IT_RISING_FALLING;
    HAL_PWR_ConfigPVD(&pwr_pvd_struct);
    HAL_PWR_EnablePVD();
    
    HAL_NVIC_SetPriority(PVD_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(PVD_IRQn);
}

       这里需要注意的就是PVD中断线选择的是上升沿和下降沿双边沿触发,其他的内容前面已经讲过。

       下面介绍的是PVD/AVD中断服务函数及其回调函数,函数定义如下:

/**
 * @brief       PVD中断服务函数
 * @param       无
 * @retval      无
 */
void PVD_IRQHandler(void)
{
    HAL_PWR_PVD_IRQHandler();
}
 
/**
 * @brief       PVD/AVD中断服务回调函数
 * @param       无
 * @retval      无
 */
void HAL_PWR_PVDCallback(void)
{
    if (__HAL_PWR_GET_FLAG(PWR_FLAG_PVDO) != RESET)   /* 电压比PLS所选电压还低 */
    {
        lcd_show_string(30, 130, 200, 16, 16, "PVD Low Voltage!", RED); 
 /* LCD显示电压低 */
        LED1(0);    /* 点亮绿灯, 表明电压低了 */
    }
    else
    {
        lcd_show_string(30, 130, 200, 16, 16, "PVD Voltage OK! ", BLUE);
             /* LCD显示电压正常 */
        LED1(1);     /* 灭掉绿灯 */
    }
}

       HAL_PWR_PVDCallback回调函数中首先是判断VDD电压是否比PLS所选电压还低,是的话,就在LCD显示PVD Low Voltage!并且点亮LED1,否则,在LCD显示PVD Voltage OK!并且关闭LED1。

       在main函数里面编写如下代码:

int main(void)
{
uint8_t t = 0;
 
    sys_cache_enable();                      /* 打开L1-Cache */
    HAL_Init();                              /* 初始化HAL库 */
    sys_stm32_clock_init(300, 6, 2);         /* 配置时钟,600MHz */
    delay_init(600);                         /* 初始化延时 */
    usart_init(115200);                      /* 初始化USART */
led_init();                              /* 初始化LED */
mpu_memory_protection();                 /* 保护相关存储区域 */
    sdram_init();                            /* 初始化SDRAM */
    lcd_init();                              /* 初始化LCD */
    pwr_pvd_init(PWR_PVDLEVEL_5);            /* 初始化PVD */
 
    lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
    lcd_show_string(30, 70, 200, 16, 16, "PVD TEST", RED);
    lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
                /* 默认LCD显示电压正常 */
    lcd_show_string(30, 110, 200, 16, 16, "PVD Voltage OK", BLUE);
 
    while (1)
    {
        if ((++t == 20) 
        {
t = 0;
            LED0_TOGGLE();      /* 每200ms,翻转一次LED0 */
        }
 
        delay_ms(10);
    }
}

       这里我们选择PVD的检测电压阀值为2.7V,其他的代码很好理解,最后下载验证一下。


       31.2.4 下载验证

       下载代码后,默认LCD屏会显示"PVD Voltage OK!",当供电电压过低,则LED1会点亮,并且LCD屏会显示PVD Low Voltage!。当开发板供电正常,LED1会熄灭,LCD屏会继续显示"PVD Voltage OK!"。


        31.3 睡眠模式实验

       本小节我们来学习睡眠模式实验,该部分的知识点内容请回顾31.1.3电源管理。我们直接从寄存器介绍开始。


       31.3.1 EXTI寄存器 

       本实验我们用到外部中断来唤醒睡眠模式。我们用到WFI指令进入睡眠模式,这个后面会讲,进入睡眠模式后,使用外部中断唤醒。进入外部中断后,EXTI_IMR1寄存器的值会自动清零,我们需要把对应的外部中断线位置1,取消屏蔽,相当于其他中断的中断标志位进入中断后硬件自动置1,需要手动清零。


       l EXTI中断屏蔽寄存器(EXTI_IMR1)

       EXTI中断屏蔽寄存器描述如图31.3.1.1所示:


图31.3.1.1 EXTI_IMR1寄存器


       实验中我们使用WK_UP(PC13)唤醒,即EXTI13线中断,所以在外部中断服务函数要把MR13位要置1。


       31.3.2 硬件设计


       1. 例程功能

       LED0闪烁,表明代码正在运行。按下按键KEY0后,LED1点亮,提示进入睡眠模式,此时LED0不再闪烁,说明已经进入睡眠模式。按下按键WK_UP后,LED1熄灭,提示退出睡眠模式,此时LED0继续闪烁,说明已经退出睡眠模式。


       2. 硬件资源


       1) LED灯

              LED0: LED0 – PD14

              LED1: LED1 – PC0


       2) 独立按键 

              KEY0 – PE9

              WK_UP – PC13


       3)电源管理(低功耗模式 - 睡眠模式)


       4)正点原子2.8/3.5/4.3/7/10寸TFTLCD模块(包括MCU屏和RGB屏,都支持)


       3. 原理图

       PWR属于STM32H7R7的内部资源,只需要软件设置好即可正常工作。我们通过KEY0让CPU进入睡眠模式,再通过WK_UP 触发EXTI中断来唤醒CPU。LED0指示程序是否执行,LED1指示CPU是否进入睡眠模式。


       31.3.3 程序设计 


       31.3.3.1 PWR的HAL库驱动


       1. HAL_PWR_EnterSLEEPMode函数

       进入睡眠模式函数,其声明如下:

void HAL_PWR_EnterSLEEPMode (uint32_t Regulator, uint8_t SLEEPEntry);


       l 函数描述:

       用于设置CPU进入睡眠模式。


       l 函数形参:

       形参1指定稳压器的状态。有两个选择,PWR_MAINREGULATOR_ON表示稳压器处于主模式,PWR_LOWPOWERREGULATOR_ON表示稳压器处于低功耗模式。对应的是PWR_CR寄存器的LPDS位的设置。

       形参2指定进入睡眠模式的方式。有两个选择,PWR_SLEEPENTRY _WFI表示使用WFI指令,PWR_SLEEPENTRY_WFE表示使用WFE指令。我们选择前者,不了解这两种指令的区别,请问度娘。


       l 函数返回值:

       无


       睡眠模式配置步骤


       1)配置唤醒睡眠模式的方式

       这里我们用外部中断的方式唤醒睡眠模式,所以这里需要配置一个外部中断功能,我们用WK_UP按键作为中断触发源,接下来就是配置PC13(连接按键WK_UP)。

       通过__HAL_RCC_GPIOC_CLK_ENABLE函数使能GPIOC的时钟。

       通过HAL_GPIO_Init函数配置PC13为上升沿触发检测的外部中断模式,开启下拉电阻等。

       通过HAL_NVIC_EnableIRQ函数使能EXTI13中断。

       通过HAL_NVIC_SetPriority函数设置中断优先级。

       编写EXTI13_IRQHandle中断函数,在中断服务函数中调用HAL_GPIO_EXTI_IRQHandler。

       最后编写HAL_GPIO_EXTI_Callback回调函数。由于前面已经介绍过外部中断的配置步骤,这里就介绍到这里,详见本例程源码。


       2)进入CPU睡眠模式

       通过HAL_SuspendTick函数暂停滴答时钟,防止通过滴答时钟中断唤醒。

       通过HAL_PWR_EnterSLEEPMode函数进入睡眠模式。


       3)通过按下按键触发外部中断唤醒睡眠模式

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


       31.3.3.2 程序解析

       这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。PWR驱动源码包括两个文件:pwr.c和pwr.h。

       首先看pwr.h头文件的几个宏定义:

/* 引脚定义 */
#define PWR_WKUP_INT_GPIO_PORT          GPIOC
#define PWR_WKUP_INT_GPIO_PIN           GPIO_PIN_13
#define PWR_WKUP_INT_GPIO_CLK_ENABLE()  do{__HAL_RCC_GPIOC_CLK_ENABLE();}while (0)
#define PWR_WKUP_INT_IRQn               EXTI13_IRQn
#define PWR_WKUP_INT_IRQHandler         EXTI13_IRQHandler

       这些定义是WK_UP按键的相关宏定义,以及其对应的外部中断线13的相关定义。

       pwr.h头文件就介绍这部分的程序,下面是pwr.c文件,先看低功耗模式下的按键初始化函数,其定义如下:

/**
 * @brief   进入睡眠模式
 * @param   无
 * @retval  无
 */
void pwr_enter_sleep(void)
{
    GPIO_InitTypeDef gpio_init_struct = {0};
    
    PWR_WKUP_INT_GPIO_CLK_ENABLE();
    
    HAL_NVIC_SetPriority(PWR_WKUP_INT_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(PWR_WKUP_INT_IRQn);
    
    /* 配置唤醒引脚 */
    gpio_init_struct.Pin = PWR_WKUP_INT_GPIO_PIN;
    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_INT_GPIO_PORT, &gpio_init_struct);
    
    /* 挂起HAL库时基定时器 */
    HAL_SuspendTick();
    
    /* 进入睡眠模式 */
    HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);
}

       该函数初始化WK_UP按键(PC13),并设置上升沿触发的外部中断线13,最后设置中断优先级并使能外部中断线13。

       下面介绍的是WK_UP按键外部中断服务函数及其回调函数,函数定义如下:

/**
 * @brief       WK_UP按键 外部中断服务程序
 * @param       无
 * @retval     无
 */
void PWR_WKUP_INT_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(PWR_WKUP_INT_GPIO_PIN);
}
 
/**
 * @brief       外部中断回调函数
 * @param       无
 * @retval      无
 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if (GPIO_Pin == PWR_WKUP_INT_GPIO_PIN)
{
/* 重新开启SysTick中断 */
SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk;
    }
}

       在WK_UP按键外部中断服务函数中我们调用HAL库的HAL_GPIO_EXTI_IRQHandler函数来处理外部中断。该函数会调用__HAL_GPIO_EXTI_CLEAR_IT函数取消屏蔽对应的外部中断线位,这里是EXTI_IMR寄存器相应位,还有其他寄存器控制其他外部中断线。我们只是唤醒睡眠模式而已,不需要其他的逻辑程序,所以HAL_GPIO_EXTI_Callback回调函数可以什么都不用做,甚至也可以不重新定义这个回调函数(屏蔽该回调函数也可以)。

       最后在main.c里面编写如下代码:

int main(void)
{
uint8_t t = 0;
uint8_t key;
 
    sys_mpu_config();                   /* 配置MPU */
    sys_cache_enable();                 /* 使能Cache */
    HAL_Init();                         /* 初始化HAL库 */
    sys_stm32_clock_init(300, 6, 2);    /* 配置时钟,600MHz */
    delay_init(600);                    /* 初始化延时 */
    usart_init(115200);                 /* 初始化串口 */
    led_init();                         /* 初始化LED */
    key_init();                         /* 初始化按键 */
    hyperram_init();                    /* 初始化HyperRAM */
    lcd_init();                         /* 初始化LCD */
 
    lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
    lcd_show_string(30, 70, 200, 16, 16, "SLEEP TEST", RED);
lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
 
    lcd_show_string(30, 110, 200, 16, 16, "KEY0:Enter SLEEP MODE", RED);
    lcd_show_string(30, 130, 200, 16, 16, "KEY_UP:Exit SLEEP MODE", RED);
 
    while (1)
    {
        key = key_scan(0);
 
        if (key == KEY0_PRES)
        {
            LED1(0);            /* 点亮绿灯,提示进入停止模式 */ 
            pwr_enter_sleep();  /* 进入睡眠模式 */
            LED1(1);            /* 关闭绿灯,提示退出停止模式 */
        }
 
        if (++t == 20)
        {
t = 0;
            LED0_TOGGLE();      /* 每200ms,翻转一次LED0 */
        }
 
        delay_ms(10);
    }
}

       该部分程序,功能就是按下KEY0后,点亮LED1、暂停滴答时钟并进入睡眠模式。然后一直等待外部中断唤醒,当按下按键WK_UP,就触发外部中断,睡眠模式就被唤醒,然后继续执行后面的程序,恢复滴答时钟,关闭LED1等。


       31.3.4 下载验证

       下载代码后,LED0闪烁,表明代码正在运行。按下按键KEY0后,LED1点亮,提示进入睡眠模式,此时LED0不再闪烁,说明已经进入睡眠模式。按下按键WK_UP后,LED1熄灭,提示退出睡眠模式,此时LED0继续闪烁,说明已经退出睡眠模式。


        31.4 停止模式实验

       本小节我们来学习停止模式实验,该部分的知识点内容请回顾31.1.3电源管理。我们直接从寄存器介绍开始。


       31.4.1 PWR寄存器 

       本实验我们用到外部中断来唤醒停止模式。我们用到WFI指令进入停止模式,这个后面会讲,进入停止模式后,使用外部中断唤醒。外部中断部分内容参照睡眠模式即可,都是共用同样的配置。

       下面主要介绍PWR_CR1寄存器相关位。


       l PWR控制寄存器 (PWR_CR1)

       PWR的控制寄存器描述如图31.4.1.1所示:


图31.4.1.1 PWR_CR1寄存器


       进入停止模式,我们设置稳压器为低功耗模式,等待中断唤醒。


       31.4.2 硬件设计


       1. 例程功能

       LED0闪烁,表明代码正在运行。按下按键KEY0后,LED1点亮,提示进入停止模式,此时LED0不再闪烁,说明已经进入停止模式。按下按键WK_UP后,LED1熄灭,提示退出停止模式,此时LED0继续闪烁,说明已经退出停止模式。


       2. 硬件资源


       1)LED灯

              LED0: LED0 – PD14

              LED1: LED1 – PC0


       2)独立按键  

               KEY0 – PE9

               WK_UP – PC13


       3)电源管理(低功耗模式 – 停止模式)


       4)正点原子2.8/3.5/4.3/7/10寸TFTLCD模块(包括MCU屏和RGB屏,都支持)


       3. 原理图

       PWR属于STM32H7R7的内部资源,只需要软件设置好即可正常工作。我们通过KEY0让CPU进入停止模式,再通过WK_UP 触发EXTI中断来唤醒CPU。LED0指示程序是否执行,LED1指示CPU是否进入停止模式。


       31.4.3 程序设计 


       31.4.3.1 PWR的HAL库驱动


       1. HAL_PWR_EnterSTOPMode函数

       进入停止模式函数,其声明如下:

void HAL_PWR_EnterSTOPMode (uint32_t Regulator, uint8_t STOPEntry);


       l 函数描述:

       用于设置CPU进入停止模式。


       l 函数形参:

       形参1 STM32H7RS系列不使用该参数,设置为0即可。

       形参2指定用WFI还是WFE指令进入停止模式。有三个选择,PWR_STOPENTRY_WFI表示使用WFI指令,PWR_STOPENTRY_WFE表示使用WFE指令,PWR_STOPENTRY_WFE_NO_EVT_CLEAR表示使用WFE指令不清除未处理事件。我们选择第一个,不了解这三种指令的区别,请问度娘。


       l 函数返回值:

       无


       停止模式配置步骤


       1)配置唤醒停止模式的方式

       这里我们用外部中断的方式唤醒停止模式,所以这里需要配置一个外部中断功能,我们用WK_UP按键作为中断触发源,接下来就是配置PC13(连接按键WK_UP)。

       通过__HAL_RCC_GPIOC_CLK_ENABLE函数使能GPIOC的时钟。

       通过HAL_GPIO_Init函数配置PC13为上升沿触发检测的外部中断模式,开启下拉电阻等。

       通过HAL_NVIC_EnableIRQ函数使能EXTI13中断。

       通过HAL_NVIC_SetPriority函数设置中断优先级。

       编写EXTI13_IRQHandle中断函数,在中断服务函数中调用HAL_GPIO_EXTI_IRQHandler。

       最后编写HAL_GPIO_EXTI_Callback回调函数。由于前面已经介绍过外部中断的配置步骤,这里就介绍到这里,详见本例程源码。


       2)进入CPU停止模式

       通过sys_stm32_clock_init函数降频。

       通过HAL_PWR_EnterSTOPMode函数进入停止模式。


       3)通过按下按键触发外部中断唤醒睡眠模式

       在本实验中,通过按下KEY0按键进入停止模式,然后通过按下WK_UP按键触发外部中断唤醒停止模式。


       31.4.3.2 程序解析

       这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。PWR驱动源码包括两个文件:pwr.c和pwr.h。

       首先看pwr.h头文件,因为我们还是用到WK_UP对应的外部中断线来唤醒停止模式的CPU,pwr.h头文件的WK_UP按键对应的宏定义我们也是用到的,上个实验已经讲过,这里不再赘述。下面是pwr.c文件,WK_UP按键的相关函数我们还是用上个实验的,我们主要介绍进入停止模式函数,其定义如下:

/**
 * @brief   进入停止模式
 * @param   无
 * @retval  无
 */
void pwr_enter_stop(void)
{
    GPIO_InitTypeDef gpio_init_struct = {0};
    
    PWR_WKUP_INT_GPIO_CLK_ENABLE();
    
    HAL_NVIC_SetPriority(PWR_WKUP_INT_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(PWR_WKUP_INT_IRQn);
    
    /* 配置唤醒引脚 */
    gpio_init_struct.Pin = PWR_WKUP_INT_GPIO_PIN;
    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_INT_GPIO_PORT, &gpio_init_struct);
    
    /* 挂起HAL库时基定时器 */
    HAL_SuspendTick();
    
    /* 进入停止模式 */
    HAL_PWR_EnterSTOPMode(0, PWR_STOPENTRY_WFI);
}

       该函数首先是初始化唤醒按键引脚,然后调用HAL_SuspendTick函数设置暂停滴答时钟,防止通过滴答时钟中断唤醒。最后调用HAL_PWR_EnterSTOPMode函数进入停止模式,第1个参数不用于该产品系列,作为参数保存只是为了保持与低功率族的兼容性,形参2则是选择WFI指令。 

       最后在main.c里面编写如下代码:

int main(void)
{
uint8_t t = 0;
uint8_t key;
 
    sys_mpu_config();                   /* 配置MPU */
    sys_cache_enable();                 /* 使能Cache */
    HAL_Init();                         /* 初始化HAL库 */
    sys_stm32_clock_init(300, 6, 2);    /* 配置时钟,600MHz */
    delay_init(600);                    /* 初始化延时 */
    usart_init(115200);                 /* 初始化串口 */
    led_init();                         /* 初始化LED */
    key_init();                         /* 初始化按键 */
    hyperram_init();                    /* 初始化HyperRAM */
    lcd_init();                         /* 初始化LCD */
 
    lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
    lcd_show_string(30, 70, 200, 16, 16, "STOP TEST", RED);
    lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
    
lcd_show_string(30, 110, 200, 16, 16, "KEY0:Enter STOP MODE", RED);
    lcd_show_string(30, 130, 200, 16, 16, "KEY_UP:Exit STOP MODE", RED);
 
    while (1)
    {
        key = key_scan(0);
 
        if (key == KEY0_PRES)
        {
            /* 按下KEY0按键,开启LED1后进入停止模式 */
            LED1(0);
            pwr_enter_stop();
            sys_stm32_clock_init(240, 5, 2);
            LED1(1);
        }
 
        if (++t == 20)
        {
t = 0;
            LED0_TOGGLE();      /* 每200ms,翻转一次LED0 */
        }
 
        delay_ms(10);
    }
}

       该部分程序,功能就是按下KEY0后,点亮LED1、暂停滴答时钟并进入停止模式。然后一直等待外部中断唤醒,当按下按键WK_UP,就触发外部中断,停止模式就被唤醒,然后继续执行后面的程序,关闭LED1等。


       31.4.4 下载验证

       下载代码后,LED0闪烁,表明代码正在运行。按下按键KEY0后,LED1点亮,提示进入停止模式,此时LED0不再闪烁,说明已经进入停止模式。按下按键WK_UP后,LED1熄灭,提示退出停止模式,此时LED0继续闪烁,说明已经退出停止模式。


        31.5 待机模式实验

       本小节我们来学习待机模式实验,该部分的知识点内容请回顾31.1.3电源管理。我们直接从寄存器介绍开始。


       31.5.1 PWR寄存器 

       本实验是先对相关的电源控制寄存器配置待机模式的参数,然后通过WFI指令进入待机模式,使用特定唤醒源WKUP引脚来唤醒待机模式。

       下面主要介绍本实验用到的几个寄存器。


       l PWR唤醒使能和极性寄存器(PWR_WKUPEPR)

       PWR唤醒使能和极性寄存器描述如图31.5.1.1所示:


图31.5.1.1 PWR_WKUPEPR寄存器


       本章,我们使用PC13的上升沿来唤醒MCU,PC13对应的WKUP源为:WKUP1,因此,对于PWR_WKUPEPR寄存器,我们需要设置的位如下:

       1,设置WKUPEN1位为1,使能WKUP1的唤醒功能。

       2,设置WKUPPP1位为0,选择上升沿唤醒。

       3,设置WKUPPUPD1[1:0]位为2,选择使用下拉电阻,以保持WKUP1脚的低电平状态。

       根据这三个步骤,设置好PWR_WKUPEPR寄存器,就可以设置PC13引脚的上升沿唤醒MCU了。


       l PWR唤醒清除寄存器(PWR_WKUPCR)

       PWR唤醒清除寄存器描述如图31.5.1.2所示:


图31.5.1.2 PWR_WKUPCR寄存器


       该寄存器我们只关心WKUPC1位,设置WKUPC1为1,可以清除PC13的唤醒标志位。


       l PWR CPU控制寄存器(PWR_CSR3)

       PWR CPU控制寄存器描述如图31.5.1.3所示:


图31.5.1.3 PWR_CSR3寄存器


       该寄存器我们只需要关心CSSF位,CSSF用于设置是否清空待机和停止模式的标志位。


       31.5.2 硬件设计


       1. 例程功能

       LED0闪烁,表明代码正在运行。按下按键KEY0后,TFTLCD屏熄灭,提示进入待机模式,此时LED0不再闪烁,说明已经进入待机模式。按下按键WK_UP后,TFTLCD屏点亮,提示退出待机模式(相当于复位操作),程序重新执行,LED0继续闪烁。


       2. 硬件资源


       1)LED灯

              LED0: LED0 – PD14


       2)独立按键  

              KEY0 – PE9

              WK_UP – PC13


       3)电源管理(低功耗模式 – 待机模式)


       4)正点原子2.8/3.5/4.3/7/10寸TFTLCD模块(包括MCU屏和RGB屏,都支持)


       3. 原理图

       PWR属于STM32H7R7的内部资源,只需要软件设置好即可正常工作。我们通过KEY0让CPU进入待机模式,再通过WK_UP上升沿触发唤醒CPU。LED0指示程序是否执行。


       31.5.3 程序设计 


       31.5.3.1 PWR的HAL库驱动


       1. HAL_PWR_EnableWakeUpPin函数

       使能唤醒引脚函数,其声明如下:

void HAL_PWR_EnableWakeUpPin (uint32_t WakeUpPinPolarity);


       l 函数描述:

       用于使能唤醒引脚。


       l 函数形参:

       形参1取值范围:PWR_WAKEUP_PIN1_HIGH~ PWR_WAKEUP_PIN6_HIGH(等同于PWR_WAKEUP_PIN1~PWR_WAKEUP_PIN6)、PWR_WAKEUP_PIN1_LOW~ PWR_WAKEUP_PIN6_LOW。


       l 函数返回值:无


       l 注意事项:

       禁止某个唤醒引脚使用的函数如下:

void HAL_PWR_DisableWakeUpPin (uint32_t WakeUpPinPolarity);


       2. HAL_PWR_EnterSTANDBYMode函数

       进入待机模式函数,其声明如下:

void HAL_PWR_EnterSTANDBYMode (void);


       l 函数描述:

       用于使CPU进入待机模式,进入待机模式,首先要设置SLEEPDEEP位,接着我们通过PWR_CR设置PDDS位,使得CPU进入深度睡眠时进入待机模式,最后执行WFI指令开始进入待机模式,并等待WK_UP唤醒的到来。


       l 函数形参:无


       l 函数返回值:无


       待机模式配置步骤


       1)进入CPU待机模式

       在进入待机模式之前我们需要做一些准备,比如:关闭RTC相关中断、清除RTC相关中断标志位等一些操作,只是防止RTC中断唤醒。这里就不细讲,详见本例程源码。

       通过__HAL_PWR_CLEAR_FLAG函数清除唤醒标志位。

       通过HAL_PWR_EnableWakeUpPin函数使能PA0的唤醒功能。

       通过HAL_PWR_EnterSTANDBYMode函数进入待机模式。


       2)通过按下WKUP引脚上升沿触发唤醒待机模式

       在本实验中,通过按下KEY0按键进入待机模式,然后通过按下WK_UP按键(特定唤醒源)触发唤醒待机模式。


       31.5.3.2 程序解析

       这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。PWR驱动源码包括两个文件:pwr.c和pwr.h。

       pwr.h头文件上的宏定义我们是没有用到的,这里我们使用的是特定唤醒源,与外部中断无关。下面是pwr.c文件,我们主要介绍进入待机模式函数,其定义如下:

/**
 * @brief       进入待机模式
 * @param       无
 * @retval      无
 */
void pwr_enter_standby(void)
{
HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN3);      /* 配置唤醒引脚 */
    __HAL_PWR_CLEAR_WAKEUPFLAG(PWR_FLAG_WKUP3);    /* 清除Wake_UP标志 */
    HAL_PWR_EnterSTANDBYMode();                    /* 进入待机模式 */
}

       该函数首先是配置WKUP引脚来唤醒待机模式。然后是清除WKUP的标志,最后调用HAL_PWR_EnterSTANDBYMode函数进入待机模式。

       最后在main.c里面编写如下代码:

int main(void)
{
uint8_t t = 0;
uint8_t key;
 
    sys_mpu_config();                   /* 配置MPU */
    sys_cache_enable();                 /* 使能Cache */
    HAL_Init();                         /* 初始化HAL库 */
    sys_stm32_clock_init(300, 6, 2);    /* 配置时钟,600MHz */
    delay_init(600);                    /* 初始化延时 */
    usart_init(115200);                 /* 初始化串口 */
    led_init();                         /* 初始化LED */
    key_init();                         /* 初始化按键 */
    hyperram_init();                    /* 初始化HyperRAM */
    lcd_init();                         /* 初始化LCD */
  
    lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
    lcd_show_string(30, 70, 200, 16, 16, "STANDBY TEST", RED);
lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
 
    lcd_show_string(30, 110, 200, 16, 16, "KEY0:Enter STANDBY MODE", RED);
    lcd_show_string(30, 130, 200, 16, 16, "KEY_UP:Exit STANDBY MODE", RED);
    
    while (1)
    {
        key = key_scan(0);
        if (key == KEY0_PRES)
        {
            pwr_enter_standby();                   /* 进入待机模式 */
            /* 从待机模式唤醒相当于系统重启(复位), 因此不会执行到这里 */
        }
 
        if (++t == 20) 
        {
t = 0;
            LED0_TOGGLE();         
        }
 
        delay_ms(10);
    }
}

       该部分程序,经过一系列初始化后,判断到KEY0按下就调用pwr_enter_standby函数进入待机模式,然后等待按下WK_UP按键进行唤醒。注意待机模式唤醒后,系统会进行复位。


       31 . 5.4   下载验证

       下载代码后,LED0闪烁,表明代码正在运行。按下按键KEY0后,TFTLCD屏熄灭,此时LED0不再闪烁,说明已经进入待机模式。按下按键WK_UP后,TFTLCD屏点亮,此时LED0继续闪烁,说明系统从待机模式中唤醒相当于复位。


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70040860/viewspace-3094493/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2024-08-30

  • 博文量
    588
  • 访问量
    764723