• 【正点原子STM32连载】 第二十九章 低功耗实验 摘自【正点原子】MiniPro STM32H750 开发指南_V1.1


    1)实验平台:正点原子MiniPro H750开发板
    2)平台购买地址:https://detail.tmall.com/item.htm?id=677017430560
    3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-336836-1-1.html
    4)对正点原子STM32感兴趣的同学可以加群讨论:879133275

    第二十九章 低功耗实验

    本章,我们将介绍STM32H750的电源控制(PWR),并实现低功耗模式相关功能。我们将通过四个实验来学习并实现低功耗相关功能,分别是PVD电压监控实验、睡眠模式实验、停止模式实验和待机模式实验。
    本章分为如下几个小节:
    29.1 电源控制(PWR)简介
    29.2 PVD电压监控实验
    29.3 睡眠模式实验
    29.4 停止模式实验
    29.5 待机模式实验

    29.1 电源控制(PWR)简介

    电源控制部分(PWR)概述了不同电源域的电源架构以及电源配置控制器。PWR的内容比较多,我们把它们的主要特性概括为以下3点:
    电源系统:USB稳压器、内核域(VCORE)、VDD域、备份域、模拟域(VDDA)。
    电源监控:POR/PDR监控器、BOR监控器、PVD监控器、AVD监控器、VBAT阈值、温度阈值。
    电源管理:VBAT电池充电、工作模式、电压调节控制、低功耗模式。
    下面将分别对这3个特性进行简单介绍。
    29.1.1 电源系统
    为了方便对电源系统进行管理,设计者把STM32的内核和外设等器件根据功能划分了不同的电源区域,具体如图29.1.1.1所示。
    第二十九章 低功耗实验

    本章,我们将介绍STM32H750的电源控制(PWR),并实现低功耗模式相关功能。我们将通过四个实验来学习并实现低功耗相关功能,分别是PVD电压监控实验、睡眠模式实验、停止模式实验和待机模式实验。
    本章分为如下几个小节:
    29.1 电源控制(PWR)简介
    29.2 PVD电压监控实验
    29.3 睡眠模式实验
    29.4 停止模式实验
    29.5 待机模式实验

    29.1 电源控制(PWR)简介
    电源控制部分(PWR)概述了不同电源域的电源架构以及电源配置控制器。PWR的内容比较多,我们把它们的主要特性概括为以下3点:
    电源系统:USB稳压器、内核域(VCORE)、VDD域、备份域、模拟域(VDDA)。
    电源监控:POR/PDR监控器、BOR监控器、PVD监控器、AVD监控器、VBAT阈值、温度阈值。
    电源管理:VBAT电池充电、工作模式、电压调节控制、低功耗模式。
    下面将分别对这3个特性进行简单介绍。
    29.1.1 电源系统
    为了方便对电源系统进行管理,设计者把STM32的内核和外设等器件根据功能划分了不同的电源区域,具体如图29.1.1.1所示。

    图29.1.1.1 电源概述框图
    在电源概述框图中我们划分了5个区域①②③④⑤,分别是USB稳压器域、内核域、VDD域、备份域和模拟域。下面分别进行简单介绍:
    ①USB稳压器域
    VSS 是所有电源和模拟稳压器的公共地。
    VDD50USB为USB稳压器供电的外部电源。
    VDD33USB为USB接口供电的USB稳压器供电输出。
    1)当USB稳压器使能时,VDD33USB由内部USB稳压器提供。
    2)当USB稳压器禁止时,VDD33USB由独立的外部电源输入提供。
    ② 内核域(VCORE)
    VDDLDO是稳压器供电的外部电源。
    VCAP数字内核域电源,该电源独立于所有其它电源:
    1)当稳压器使能时,VCORE由内部稳压器提供。
    2)当稳压器禁止时,VCORE由外部电源通过VCAP引脚提供。
    VCORE内核域电源可通过稳压器或者外部电源(VCAP)供电。VCORE为除备份域和待机电路以外的所有数字电路供电。VCORE域分为3个部分:
    1、D1域(CPU (Cortex-M7)、外设、RAM和Flash)。
    2、D2域(外设、RAM)。
    3、D3域(系统逻辑、EXTI、低功耗外设、RAM和IO逻辑)。
    当发生系统复位时,稳压器使能并为VCORE供电。稳压器的输出电压为1.2V(M4/M7),如果是M3,则是1.8V。稳压器提供三种不同的工作模式:主 (MR) 模式、低功耗模式 (LP) 或关闭模式。这些模式将根据系统工作模式(运行、停止和待机)进行使用,详细如下:
    1、运行模式:稳压器工作在主模式,并为VCORE域(内核、存储器和数字外设)提供全功率。稳压器输出电源可通过软件调节为不同电压级别(VOS1、VOS2 和 VOS3),这些级别通过PWR D3域控制寄存器(PWR_D3CR) 中的VOS位配置。
    2、停止模式:VCORE域的所有时钟都被关闭,相应的外设都停止了工作,但稳压器还会为VCORE供电以保存内核寄存器和内部存储器(SRAM)的内容。稳压器模式通过PWR控制寄存器1 (PWR_CR1) 中的SVOS和LPDS位选择。如果选择了SVOS3电压调节,则可以选择主模式或 低功耗模式;如果选择SVOS4和SVOS5调节,则只能选择低功耗模式。由于SVOS4和SVOS5 调节的电压级别低,因此可进一步降低停止模式的功耗。
    3、待机模式:
    稳压器关闭且VCORE域掉电。除待机电路和备份域外,内核寄存器和内部存储器(SRAM)的内容都将丢失。
    ③ VDD域
    VDD为I/O和系统模拟模块(如复位、电源管理和时钟)供电的外部电源。
    VBAT是后备电源(来源于开发板上3V的纽扣电池),当VDD不存在时(开发板断电),VBAT为备份域供电。
    ④备份域
    备份域的电源自来VSW,VSW来自VDD域的VDD或者VBAT。在备份域中,包含LSI、LSE、
    RTC、唤醒逻辑、备份寄存器、复位以及备份SRAM等器件。
    ⑤模拟域
    VDDA为ADC、DAC、OPAMP、比较器和电压参考缓冲器供电的外部模拟电源。该电源独立于所有其它电源。
    VSSA是独立的模拟和参考电压地。
    VREF+ 是ADC和DAC的外部参考电压。
    1)当电压参考缓冲器使能时,VREF+ 和VREF- 由内部电压参考缓冲器提供。
    2)当电压参考缓冲器禁止时,VREF+ 由独立的外部参考电源提供。
    29.1.2 电源监控
    电源监控的部分我们主要关注PVD监控器,实验17-1就是根据该监控器进行的,此外还需要知道上电复位(POR)/掉电复位(PDR)和欠压复位(BOR)。其他部分的内容请大家查看《STM32H7xx参考手册_V7(英文版).pdf》第6.5节(266页)。
    上电复位(POR)/掉电复位(PDR)
    上电时,当VDD低于指定VPOR阈值时,系统无需外部复位电路便会保持复位模式。一旦VDD电源电压高于VPOR阈值,系统便会退出复位状态。掉电时,当VDD低于指定VPDR阈值时,系统就会保持复位模式。如图29.1.2.1所示,pwr_por_rst为上电复位信号。
    注意:POR与PDR的复位电压阈值是固定的,VPOR阈值(典型值)为1.72V,VPDR阈值(典型值)为1.68V。

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

    表29.1.2.1 BOR欠压阀值等级
    该表截取于《STM32H750VBT6.pdf》手册的207页。
    欠压复位的描述波形图如图29.1.2.2所示,pwr_bor_rst为欠压复位信号。

    图29.1.2.2 欠压复位波形
    可编程电压检测器(PVD)
    上面介绍的POR、PDR以及BOR功能都是设置电压阈值与外部供电电压VDD比较,当VDD低于设置的电压阈值时,就会直接进入复位状态,防止电压不足导致的误操作。
    下面介绍可编程电压检测器(PVD),它可以实时监视VDD的电压,方法是将VDD与PWR控制寄存器1(PWR_CR1)中的PLS[2:0]位所选的VPVD阈值进行比较。当检测到电压低于VPVD阈值时,如果使能EXTI16线中断,即使能PVD & AVD中断(可参考表16.1.2.1知道EXTI16线内部连接PVD中断事件),可以产生PVD中断,具体取决于EXTI16线配置为检测上升还是下降沿,然后在复位前,在中断服务程序中执行紧急关闭系统等任务。PVD阀值检测波形,如图29.1.2.1所示。

    图29.1.2.3 PVD检测波形
    PVD阀值有8个等级,有上升沿和下降沿的区别,分别就是图29.1.2.3中PVDrise电压为上升沿阀值,PVDfall为下降沿阀值,具体如表29.1.2.2所示。

    表29.1.2.2 PVD阀值等级
    该表截取于《STM32H750VBT6.pdf》手册的207页。
    表29.1.2.2中只有7个PVD阀值等级,最后一个就是PVD_IN引脚上的外部电压级别(与内部VREFINT值相比较)。
    29.1.3 电源管理
    电源管理的部分我们主要关注低功耗模式,其他部分的内容请自行查看手册。
    很多单片机都有低功耗模式,STM32也不例外。STM32H750提供了6种低功耗模式,以达到不同层次的降低功耗的目的,这六种模式如下:
    1、CSleep(CPU时钟停止,所有的外设仍可以运行);
    2、CStop(CPU时钟停止,大多数CPU子系统时钟停止,所以大部分外设也停止工作);
    3、DStop(域总线矩阵时钟停止,D1、D2域进入停止模式)
    4、停止(系统时钟停止,D3域进入停止模式,D1和D2域进入停止或者待机模式)
    5、DStandby(域掉电,D1/D2/D3进入待机模式)
    6、待机(系统掉电,达到最低功耗)
    在这六种低功耗模式中,最低功耗的是待机模式,在此模式下,最低只需要2uA左右的电流。停止模式是次低功耗的,其典型的电流消耗在170uA左右。最后就是睡眠模式了。用户可以根据最低电源消耗,最快速启动时间和可用的唤醒源等条件,选定一个最佳的低功耗模式。
    下面是低功耗模式汇总介绍,如下表所示。

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

    进入模式 WFI(等待中断)或WFE(等待事件),条件为:
    1、CM7系统控制寄存器中的SLEEPDEEP位置0
    2、没有中断(针对WFI)和事件(针对WFE)挂起
    从ISR返回,条件为:
    1、CM7系统控制寄存器中的SLEEPDEEP位置0,及SLEEPONEXIT位置1
    2、没有中断和事件挂起

    退出模式 如果使用WFI或从ISR返回进入:在NVIC中使能的任何中断都可以
    如果使用WFE进入且SEVONPEND = 0:任何事件都可以
    如果使用WFE进入且SEVONPEND = 1:任何中断(即使在NVIC中禁止时)
    唤醒延迟 无延时
    表29.1.3.2 睡眠模式进入及退出方法
    2、停止模式
    进入停止模式,所有的时钟都关闭,于是所有的外设也停止了工作。但是内核域的VCORE电源是没有关闭的,所以内核的寄存器和内存信息都保留下来,等待重新开启时钟就可以从上次停止的地方继续执行程序。停止模式可以被任何一个外部中断(EXTI)或事件唤醒。在停止模式中可以选择稳压器的工作方式为主模式或者低功耗模式。下面我们看看停止模式进入及退出方法:
    停止模式 说明

    进入模式 WFI(等待中断)或WFE(等待事件),条件为:
    1、CM7系统控制寄存器中的SLEEPDEEP位置1
    2、没有中断(针对WFI)和事件(针对WFE)挂起
    3、所有CPU EXTI唤醒源清除
    从ISR返回,条件为:
    1、CM7系统控制寄存器中的SLEEPDEEP位置1,及SLEEPONEXIT位置1
    2、没有中断和事件挂起
    3、所有CPU EXTI唤醒源清除
    退出模式 如果使用WFI或从ISR返回进入:在NVIC中使能的任意EXTI线中断
    如果使用WFE进入且SEVONPEND = 0:EXTI事件
    如果使用WFE进入且SEVONPEND = 1:EXTI中断(即使在NVIC中禁止)
    唤醒延迟 1、为达到VOS3(默认电压)而需要的稳压器稳定时间。
    2、系统时钟重启延时(HSI/CSI 重启延时)
    3、EXTI和RCC唤醒同步
    表29.1.3.3 停止模式进入及退出方法
    3、待机模式
    待机模式可实现最低功耗。该模式是在CM7深睡眠模式时关闭稳压器(VCOER关闭),PLL、HIS、CSI、HSI48和HSE振荡器也被断电。除备份域(RTC寄存器、RTC备份寄存器和备份SRAM)和待机电路中的寄存器外,SRAM 和寄存器内容都将丢失。不过如果我们使能了备份区域(备份SRAM、RTC、LSE),那么待机模式下的功耗,将达到6uA左右。下面我们看看待机模式进入及退出方法:
    待机模式 说明

    进入模式 WFI(等待中断)或WFE(等待事件),条件为:
    1、CM7系统控制寄存器中的SLEEPDEEP位置1
    2、没有中断(针对WFI)和事件(针对WFE)挂起
    3、所有WKUPF位清零
    4、RUN_D3位清
    5、PDDS_D1、PDDS_D2、PDDS_D3全设置为1
    从ISR返回,条件为:
    1、CM7系统控制寄存器中的SLEEPDEEP和SLEEPONEXIT位置1
    2、没有中断挂起
    3、所有WKUPF位清零
    4、RUN_D3位清零
    5、PDDS_D1、PDDS_D2、PDDS_D3全设置为1
    退出模式 WKUP脚的上升沿或下降、RTC闹钟(闹钟A或闹钟B)、RTC唤醒事件、入侵事件、时间戳事件、硬件复位、IWDG复位
    唤醒延迟 系统复位阶段
    表29.1.3.4 待机模式进入及退出方法
    从待机模式唤醒后的代码执行等同于复位后的执行(采样启动模式引脚,读取复位向量等)。 在进入待机模式后,除了复位引脚、RTC_TAMP1/2/3引脚(PC13/PI8/PC1)(如果针对入侵、时间戳、RTC 闹钟输出或 RTC 时钟校准输出进行了配置)和WK_UP(PA0/PA2/PC1/PC13/PI8/ PI11)(如果使能了)等引脚外,其他所有IO引脚都将处于高阻态。
    下面开始本章的四个实验的介绍。
    29.2 PVD电压监控实验
    本小节我们来学习PVD电压监控实验,该部分的知识点内容请回顾29.1.2电源监控。我们直接从寄存器介绍开始。
    29.2.1 PWR寄存器
    本实验用到PWR的部分寄存器,在《STM32H7xx参考手册_V3(中文版).pdf》手册的6.8小节(247页)可以找到PWR的寄存器描述。这里我们只介绍PVD电压监控实验我们用到的PWR的控制寄存器1(PWR_CR1),还有就是我们要用到EXTI16线中断,所以还要配置EXTI相关的寄存器,具体如下:
    PWR控制寄存器 1(PWR_CR1)
    PWR控制寄存器1描述如图29.2.1.1所示:

    图29.2.1.1 PWR_CR1寄存器(部分)
    位[7:5] PLS用于设置PVD检测的电压阀值,即前面我们介绍PVD的8个等级阀值选择。
    位4 PVDE位,用于使能或者禁止PVD检测,显然我们要使能PVD检测,该位置1。
    EXTI中断屏蔽寄存器(EXTI_CPUIMR1)
    EXTI中断屏蔽寄存器描述如图29.2.1.2所示:

    图29.2.1.2 EXTI_CPUIMR1寄存器
    我们要使用到EXTI16线中断,所以MR16位要置1,即取消屏蔽EXTI16线的中断请求。
    EXTI上升沿触发选择寄存器(EXTI_RTSR1)
    EXTI上升沿触发选择寄存器描述如图29.2.1.3所示:

    图29.2.1.3 EXTI_RTSR1寄存器
    我们要使用到EXTI16线中断,所以TR16位要置1。
    EXTI下降沿触发选择寄存器(EXTI_FTSR1)
    EXTI下降沿触发选择寄存器描述如图29.2.1.4所示:

    图29.2.1.4 EXTI_FTSR1寄存器
    我们要使用到EXTI16线中断,所以TR16位要置1。
    EXTI挂起寄存器(EXTI_CPUPR1)
    EXTI挂起寄存器描述如图29.2.1.5所示:

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

    1. 例程功能
      开发板供电正常的话,LCD屏会显示"PVD Voltage OK!"。当供电电压过低,则会通过PVD中断服务函数将LED1点亮;当供电电压正常后,会在PVD中断服务函数将LED1熄灭。LED0闪烁,提示程序运行。
    2. 硬件资源
      1)RGB灯
      BLUE :LED2 - PE5
      GREEN : LED1 - PE6
      2)PVD(可编程电压监测器)
      3)正点原子2.8/3.5/4.3/7/10寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)
    3. 原理图
      PVD属于STM32H750的内部资源,只需要软件设置好即可正常工作。我们通过LED1和LCD来指示进入PVD中断的情况。
      29.2.3 程序设计
      29.2.3.1 PWR的HAL库驱动
      PWR在HAL库中的驱动代码在stm32h7xx_hal_pwr.c文件(及其头文件)中。
    4. HAL_PWR_ConfigPVD函数
      PVD的初始化函数,其声明如下:
      void HAL_PWR_ConfigPVD (PWR_PVDTypeDef sConfigPVD);
      函数描述:
      用于初始化PWR。
      函数形参:
      形参1是PWR_PVDTypeDef结构体类型指针变量,其定义如下:
      typedef struct
      {
      uint32_t PVDLevel; /
      指定PVD检测级别 /
      uint32_t Mode; /
      指定PVD的EXTI检测模式 */
      }PWR_PVDTypeDef;
      1)PVDLevel:指向PVD检测级别,对应PWR_CR1寄存器的PLS位的设置,取值范围PWR_PVDLEVEL_0到PWR_PVDLEVEL_7,共八个级别。
      2)Mode:指定PVD的EXTI边沿检测模式。
      函数返回值:

      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中编写,详见本例程源码。
      29.2.3.2 程序流程图

    图29.2.3.2.1 PVD电压监控实验程序流程图
    29.2.3.3 程序解析
    这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。PWR源码包括两个文件:pwr.c和pwr.h。该章节有四个实验,每一个实验的代码都是在上一个实验后面追加。
    pwr.h头文件只有函数声明,下面直接开始介绍pwr.c的程序,首先是PVD初始化函数。
    /**

    • @brief 初始化PVD电压监视器

    • @param pls: 电压等级

    • @arg PWR_PVDLEVEL_0,1.95V; PWR_PVDLEVEL_1,2.1V

    • @arg PWR_PVDLEVEL_2,2.25V; PWR_PVDLEVEL_3,2.4V;

    • @arg PWR_PVDLEVEL_4,2.55V; PWR_PVDLEVEL_5,2.7V;

    • @arg PWR_PVDLEVEL_6,2.85V; PWR_PVDLEVEL_7,使用PVD_IN脚上的电压(与
      Vrefint比较)

    • @retval 无
      */
      void pwr_pvd_init(uint32_t pls)
      {
      PWR_PVDTypeDef pvd_handle = {0};

      HAL_PWR_EnablePVD(); /* 使能PVD时钟 */

    pvd_handle.PVDLevel = pls; /* 检测电压级别 /
    /
    使用中断线的上升沿和下降沿双边缘触发 */
    pvd_handle.Mode = PWR_PVD_MODE_IT_RISING_FALLING;
    HAL_PWR_ConfigPVD(&pvd_handle);

    HAL_NVIC_SetPriority(PVD_AVD_IRQn, 3 ,3);
    HAL_NVIC_EnableIRQ(PVD_AVD_IRQn);
    HAL_PWR_EnablePVD();                                /* 使能PVD检测 */
    
    • 1
    • 2
    • 3

    }
    这里需要注意的就是PVD中断线选择的是上升沿和下降沿双边沿触发,其他的内容前面已经讲过。
    下面介绍的是PVD/AVD中断服务函数及其回调函数,函数定义如下:
    /**

    • @brief PVD/AVD中断服务函数
    • @param 无
    • @retval 无
      */
      void PVD_AVD_IRQHandler(void)
      {
      HAL_PWR_PVD_IRQHandler();
      }

    /**

    • @brief PVD/AVD中断服务回调函数

    • @param 无

    • @retval 无
      /
      void HAL_PWR_PVDCallback(void)
      {
      if (__HAL_PWR_GET_FLAG(PWR_FLAG_PVDO)) /
      电压比PLS所选电压还低 /
      {
      /
      LCD显示电压低 /
      lcd_show_string(30, 130, 200, 16, 16, “PVD Low Voltage!”, RED);
      LED1(0); /
      点亮绿灯, 表明电压低了 /
      }
      else
      {
      /
      LCD显示电压正常 /
      lcd_show_string(30, 130, 200, 16, 16, "PVD Voltage OK! ", BLUE);
      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(240, 2, 2, 4); /
      设置时钟, 480Mhz /
      delay_init(480); /
      延时初始化 /
      usart_init(115200); /
      串口初始化为115200 /
      mpu_memory_protection(); /
      保护相关存储区域 /
      led_init(); /
      初始化LED /
      lcd_init(); /
      初始化LCD /
      pwr_pvd_init(PWR_PVDLEVEL_5); /
      PVD 2.7V检测 */

      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) == 0)
      {
      LED0_TOGGLE(); /* 每200ms,翻转一次LED0 */
      }

       delay_ms(10);
       t++;
      
      • 1
      • 2

      }
      }
      这里我们选择PVD的检测电压阀值为2.7V,其他的代码很好理解,最后下载验证一下。
      29.2.4 下载验证
      下载代码后,默认LCD屏会显示"PVD Voltage OK!“,当供电电压过低,则LED1会点亮,并且LCD屏会显示PVD Low Voltage!。当开发板供电正常,LED1会熄灭,LCD屏会继续显示"PVD Voltage OK!”。

    29.3 睡眠模式实验
    本小节我们来学习睡眠模式实验,该部分的知识点内容请回顾29.1.23电源管理。我们直接从寄存器介绍开始。
    29.3.1 EXTI寄存器
    本实验我们用到外部中断来唤醒睡眠模式。我们用到WFI指令进入睡眠模式,这个后面会讲,进入睡眠模式后,使用外部中断唤醒。进入外部中断后,EXTI_CPUIMR1寄存器的值会自动清零,我们需要对对应的外部中断线位置1,取消屏蔽,相当于其他中断的中断标志位进入中断后硬件自动置1,需要手动清零。
    EXTI中断屏蔽寄存器(EXTI_CPUIMR1)
    EXTI中断屏蔽寄存器描述如图29.3.1.1所示:

    图29.3.1.1 EXTI_CPUIMR1寄存器
    实验中我们使用WK_UP(PA0)唤醒,即EXTI0线中断,所以在外部中断服务函数要把MR0位要置1。
    29.3.2 硬件设计

    1. 例程功能
      LED0闪烁,表明代码正在运行。按下按键KEY0后,LED1点亮,提示进入睡眠模式,此时LED0不再闪烁,说明已经进入睡眠模式。按下按键WK_UP后,LED1熄灭,提示退出睡眠模式,此时LED0继续闪烁,说明已经退出睡眠模式。
    2. 硬件资源
      1)RGB灯
      RED : LED0 - PB4
      GREEN : LED1 - PE6
      2)独立按键 KEY0 - PA1,WK_UP - PA0
      3)电源管理(低功耗模式 - 睡眠模式)
      4)正点原子2.8/3.5/4.3/7/10寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)
    3. 原理图
      PWR属于STM32H750的内部资源,只需要软件设置好即可正常工作。我们通过KEY0让CPU进入睡眠模式,再通过WK_UP 触发EXTI中断来唤醒CPU。LED0指示程序是否执行,LED1指示CPU是否进入睡眠模式。
      29.3.3 程序设计
      29.3.3.1 PWR的HAL库驱动
    4. HAL_PWR_EnterSLEEPMode函数
      进入睡眠模式函数,其声明如下:
      void HAL_PWR_EnterSLEEPMode (uint32_t Regulator, uint8_t SLEEPEntry);
      函数描述:
      用于设置CPU进入睡眠模式。
      函数形参:
      形参1指定稳压器的状态。有两个选择,PWR_MAINREGULATOR_ON表示稳压器处于主模式,PWR_LOWPOWERREGULATOR_ON表示稳压器处于低功耗模式。对应的是PWR_CR1寄存器的LPDS位的设置(该形参在该函数中没有实质用处)。
      形参2指定进入睡眠模式的方式。有两个选择,PWR_SLEEPENTRY _WFI表示使用WFI指令,PWR_SLEEPENTRY_WFE表示使用WFE指令。我们选择前者,不了解这两种指令的区别,请问度娘。
      函数返回值:

      睡眠模式配置步骤
      1)配置唤醒睡眠模式的方式
      这里我们用外部中断的方式唤醒睡眠模式,所以这里需要配置一个外部中断功能,我们用WK_UP按键作为中断触发源,接下来就是配置PA0(连接按键WK_UP)。
      通过__HAL_RCC_GPIOA_CLK_ENABLE函数使能GPIOA的时钟。
      通过HAL_GPIO_Init函数配置PA0为上升沿触发检测的外部中断模式,开启下拉电阻等。
      通过HAL_NVIC_EnableIRQ函数使能EXTI0中断。
      通过HAL_NVIC_SetPriority函数设置中断优先级。
      编写EXTI0_IRQHandle中断函数,在中断服务函数中调用HAL_GPIO_EXTI_IRQHandler。
      最后编写HAL_GPIO_EXTI_Callback回调函数。由于前面已经介绍过外部中断的配置步骤,这里就介绍到这里,详见本例程源码。
      2)进入CPU睡眠模式
      通过HAL_SuspendTick函数暂停滴答时钟,防止通过滴答时钟中断唤醒。
      通过HAL_PWR_EnterSLEEPMode函数进入睡眠模式。
      3)通过按下按键触发外部中断唤醒睡眠模式
      在本实验中,通过按下KEY0按键进入睡眠模式,然后通过按下WK_UP按键触发外部中断唤醒睡眠模式。
      29.3.3.2 程序流程图

    图29.3.3.2.1 睡眠模式实验程序流程图
    29.3.3.3 程序解析
    这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。PWR驱动源码包括两个文件:pwr.c和pwr.h。
    首先看pwr.h头文件的几个宏定义:
    /* PWR WKUP 按键 引脚和中断 定义

    • 我们通过WK_UP按键唤醒 MCU, 因此必须定义这个按键及其对应的中断服务函数
      */
      #define PWR_WKUP_GPIO_PORT GPIOA
      #define PWR_WKUP_GPIO_PIN GPIO_PIN_0
      #define PWR_WKUP_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)

    #define PWR_WKUP_INT_IRQn EXTI0_IRQn
    #define PWR_WKUP_INT_IRQHandler EXTI0_IRQHandler
    这些定义是WK_UP按键的相关宏定义,以及其对应的外部中断线0的相关定义。
    pwr.h头文件就介绍这部分的程序,下面是pwr.c文件,先看低功耗模式下的按键初始化函数,其定义如下:
    /**

    • @brief 低功耗模式下的按键初始化(用于唤醒睡眠模式/停止模式)

    • @param 无

    • @retval 无
      */
      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_VERY_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);
      }
      该函数初始化WK_UP按键(PA0),并设置上升沿触发的外部中断线0,最后设置中断优先级并使能外部中断线0。
      下面介绍的是进入CPU睡眠模式函数,其定义如下:
      /
      *

    • @brief 进入CPU睡眠模式

    • @param 无

    • @retval 无
      /
      void pwr_enter_sleep(void)
      {
      HAL_SuspendTick(); /
      暂停滴答时钟,防止通过滴答时钟中断唤醒 /
      /
      进入睡眠模式 /
      HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);
      }
      首先是调用HAL_SuspendTick函数,暂停滴答时钟,防止睡眠模式被滴答时钟中断唤醒。然后调用HAL_PWR_EnterSLEEPMode函数使用WFI指令进入睡眠模式。
      下面介绍的是WK_UP按键外部中断服务函数及其回调函数,函数定义如下:
      /
      *

    • @brief WK_UP按键 外部中断服务程序

    • @param 无

    • @retval 无
      */
      void PWR_WKUP_INT_IRQHandler(void)
      {
      HAL_GPIO_EXTI_IRQHandler(PWR_WKUP_GPIO_PIN);
      }

    /**

    • @brief 外部中断回调函数

    • @param GPIO_Pin:中断线引脚

    • @note 此函数会被PWR_WKUP_INT_IRQHandler()调用

    • @retval 无
      /
      void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
      {
      if (GPIO_Pin == PWR_WKUP_GPIO_PIN)
      {
      /
      HAL_GPIO_EXTI_IRQHandler()函数已经为我们清除了中断标志位,
      所以我们进了回调函数可以不做任何事 */
      }
      }
      在WK_UP按键外部中断服务函数中我们调用HAL库的HAL_GPIO_EXTI_IRQHandler函数来处理外部中断。该函数会调用__HAL_GPIO_EXTI_CLEAR_IT函数取消屏蔽对应的外部中断线位,这里是EXTI_CPUIMR1寄存器相应位,还有其他寄存器控制其他外部中断线。我们只是唤醒睡眠模式而已,不需要其他的逻辑程序,所以HAL_GPIO_EXTI_Callback回调函数可以什么都不用做,甚至也可以不重新定义这个回调函数(屏蔽该回调函数也可以)。
      最后在main.c里面编写如下代码:
      int main(void)
      {
      uint8_t t = 0;
      uint8_t key = 0;

      sys_cache_enable(); /* 打开L1-Cache /
      HAL_Init(); /
      初始化HAL库 /
      sys_stm32_clock_init(240, 2, 2, 4); /
      设置时钟, 480Mhz /
      delay_init(480); /
      延时初始化 /
      usart_init(115200); /
      串口初始化为115200 /
      mpu_memory_protection(); /
      保护相关存储区域 /
      led_init(); /
      初始化LED /
      lcd_init(); /
      初始化LCD /
      key_init(); /
      初始化按键 /
      pwr_wkup_key_init(); /
      唤醒按键初始化 */

      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();  	/* 进入睡眠模式 */            
           HAL_ResumeTick();   	/* 恢复滴答时钟 */
           LED1(1);              	/* 关闭绿灯,提示退出睡眠模式 */
       }
      
       if ((t % 20) == 0)
       {
           LED0_TOGGLE();      /* 每200ms,翻转一次LED0 */
       }
      
       delay_ms(10);
       t++;
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15

      }
      }
      该部分程序,功能就是按下KEY0后,点亮LED1、暂停滴答时钟并进入睡眠模式。然后一直等待外部中断唤醒,当按下按键WK_UP,就触发外部中断,睡眠模式就被唤醒,然后继续执行后面的程序,恢复滴答时钟,关闭LED1等。
      29.3.4 下载验证
      下载代码后,LED0闪烁,表明代码正在运行。按下按键KEY0后,LED1点亮,提示进入睡眠模式,此时LED0不再闪烁,说明已经进入睡眠模式。按下按键WK_UP后,LED1熄灭,提示退出睡眠模式,此时LED0继续闪烁,说明已经退出睡眠模式。

    29.4 停止模式实验
    本小节我们来学习停止模式实验,该部分的知识点内容请回顾29.1.23电源管理。我们直接从寄存器介绍开始。
    29.4.1 PWR寄存器
    本实验我们用到外部中断来唤醒停止模式。我们用到WFI指令进入停止模式,这个后面会讲,进入停止模式后,使用外部中断唤醒。外部中断部分内容参照睡眠模式即可,都是共用同样的配置。
    下面主要介绍PWR_CR1和PWR_CPUCR寄存器相关位。
    PWR控制寄存器 1(PWR_CR1)
    PWR的控制寄存器1描述如图29.4.1.1所示:

    图29.4.1.1 PWR_CR1寄存器
    进入停止模式,我们设置稳压器为低功耗模式,等待中断唤醒,所以位LPDS置1。
    PWR CPU控制寄存器(PWR_CPUCR)
    PWR CPU控制寄存器描述如图29.4.1.2所示:

    图29.4.1.2 PWR_CPUCR寄存器
    位PDDS_D1~ PDDS_D3都设置为0, 保持D1/D2/D3进入深度睡眠后,进入停止模式。在设置该寄存器前,我们要使能系统控制寄存器SCR的位2(SLEEPDEEP),该位置1,该寄存器在M7内核手册。然后在停止模式后,关闭SLEEPDEEP位。
    29.4.2 硬件设计

    1. 例程功能
      LED0闪烁,表明代码正在运行。按下按键KEY0后,LED1点亮,提示进入停止模式,此时LED0不再闪烁,说明已经进入停止模式。按下按键WK_UP后,LED1熄灭,提示退出停止模式,此时LED0继续闪烁,说明已经退出停止模式。
    2. 硬件资源
      1)RGB灯
      RED : LED0 - PB4
      GREEN : LED1 - PE6
      2)独立按键
      KEY0 - PA1
      WK_UP - PA0
      3)电源管理(低功耗模式 – 停止模式)
      4)正点原子2.8/3.5/4.3/7/10寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)
    3. 原理图
      PWR属于STM32H750的内部资源,只需要软件设置好即可正常工作。我们通过KEY0让CPU进入停止模式,再通过WK_UP 触发EXTI中断来唤醒CPU。LED0指示程序是否执行,LED1指示CPU是否进入停止模式。
      29.4.3 程序设计
      29.4.3.1 PWR的HAL库驱动
    4. HAL_PWR_EnterSTOPMode函数
      进入停止模式函数,其声明如下:
      void HAL_PWR_EnterSTOPMode (uint32_t Regulator, uint8_t STOPEntry);
      函数描述:
      用于设置CPU进入停止模式。
      函数形参:
      形参1指定稳压器在停止模式下的状态。有两个选择,PWR_MAINREGULATOR_ON表示稳压器处于主模式,PWR_LOWPOWERREGULATOR_ON表示稳压器处于低功耗模式。对应的是PWR_CR1寄存器的LPDS位的设置。
      形参2指定用WFI还是WFE指令进入停止模式。有两个选择,PWR_STOPENTRY_WFI表示使用WFI指令,PWR_STOPENTRY_WFE表示使用WFE指令。我们选择前者,不了解这两种指令的区别,请问度娘。
      函数返回值:

      停止模式配置步骤
      1)配置唤醒停止模式的方式
      这里我们用外部中断的方式唤醒停止模式,所以这里需要配置一个外部中断功能,我们用WK_UP按键作为中断触发源,接下来就是配置PA0(连接按键WK_UP)。
      通过__HAL_RCC_GPIOA_CLK_ENABLE函数使能GPIOA的时钟。
      通过HAL_GPIO_Init函数配置PA0为上升沿触发检测的外部中断模式,开启下拉电阻等。
      通过HAL_NVIC_EnableIRQ函数使能EXTI0中断。
      通过HAL_NVIC_SetPriority函数设置中断优先级。
      编写EXTI0_IRQHandle中断函数,在中断服务函数中调用HAL_GPIO_EXTI_IRQHandler。
      最后编写HAL_GPIO_EXTI_Callback回调函数。由于前面已经介绍过外部中断的配置步骤,这里就介绍到这里,详见本例程源码。
      2)进入CPU停止模式
      通过sys_stm32_clock_init函数降频。降低性能前,应先减小系统频率,再更改电压调节。
      通过HAL_PWR_EnterSTOPMode函数进入停止模式。
      3)通过按下按键触发外部中断唤醒睡眠模式
      在本实验中,通过按下KEY0按键进入停止模式,然后通过按下WK_UP按键触发外部中断唤醒停止模式。
      29.4.3.2 程序流程图

    图29.4.3.2.1 停止模式实验程序流程图
    29.4.3.3 程序解析
    这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。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)
      {
      sys_stm32_clock_init(200, 2, 2, 4); /
      设置时钟,400Mhz,降频 /
      /
      当SVOS3进入停止模式时,设置稳压器为低功耗模式,等待中断唤醒 */
      HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
      }
      该函数首先是调用sys_stm32_clock_init函数重新设置系统时钟频率,降频到400MHZ。然后调用HAL_PWR_EnterSTOPMode函数进入停止模式,形参1选择PWR_LOWPOWERREGU LATOR_ON表示设置稳压器为低功耗模式,形参2则是选择WFI指令。这里我们不再需要像睡眠模式实验一样要暂停滴答时钟,因为滴答时钟中断无法唤醒停止模式,只有外部中断可以唤醒。
      最后在main.c里面编写如下代码:
      int main(void)
      {
      uint8_t t = 0;
      uint8_t key = 0;

      sys_cache_enable(); /* 打开L1-Cache /
      HAL_Init(); /
      初始化HAL库 /
      sys_stm32_clock_init(240, 2, 2, 4); /
      设置时钟, 480Mhz /
      delay_init(480); /
      延时初始化 /
      usart_init(115200); /
      串口初始化为115200 /
      mpu_memory_protection(); /
      保护相关存储区域 /
      led_init(); /
      初始化LED /
      lcd_init(); /
      初始化LCD /
      key_init(); /
      初始化按键 /
      pwr_wkup_key_init(); /
      唤醒按键初始化 */

      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)
       {
           LED1(0);    /* 点亮绿灯,提示进入停止模式 */ 
           pwr_enter_stop(); /* 进入停止模式 */
           /* 从停止模式唤醒, 需要重新设置系统时钟, 480Mhz */
           sys_stm32_clock_init(240, 2, 2, 4);
           LED1(1);            /* 关闭绿灯,提示退出停止模式 */
       }
      
       if ((t % 20) == 0)
       {
           LED0_TOGGLE();      /* 每200ms,翻转一次LED0 */
       }
      
       delay_ms(10);
       t++;
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16

      }
      }
      该部分程序,功能就是按下KEY0后,点亮LED1、暂停滴答时钟并进入停止模式。然后一直等待外部中断唤醒,当按下按键WK_UP,就触发外部中断,停止模式就被唤醒,然后继续执行后面的程序,重新设置系统时钟480MHZ,关闭LED1等。
      29.4.4 下载验证
      下载代码后,LED0闪烁,表明代码正在运行。按下按键KEY0后,LED1点亮,提示进入停止模式,此时LED0不再闪烁,说明已经进入停止模式。按下按键WK_UP后,LED1熄灭,提示退出停止模式,此时LED0继续闪烁,说明已经退出停止模式。

    29.5 待机模式实验
    本小节我们来学习待机模式实验,该部分的知识点内容请回顾29.1.23电源管理。我们直接从寄存器介绍开始。
    29.5.1 PWR寄存器
    本实验是先对相关的电源控制寄存器配置待机模式的参数,然后通过WFI指令进入待机模式,使用特定唤醒源WKUP引脚来唤醒待机模式。
    下面主要介绍本实验用到的几个寄存器。
    PWR唤醒使能和极性寄存器(PWR_WKUPEPR)
    PWR唤醒使能和极性寄存器描述如图29.5.1.1所示:

    图29.5.1.1 PWR_WKUPEPR寄存器
    本章,我们使用PA0的上升沿来唤醒MCU,PA0对应的WKUP源为:WKUP1,因此,对于PWR_WKUPEPR寄存器,我们需要设置的位如下:
    1,设置WKUPEN1位为1,使能WKUP1的唤醒功能。
    2,设置WKUPPP1位为0,选择上升沿唤醒。
    3,设置WKUPPUPD1[1:0]位为2,选择使用下拉电阻,以保持WKUP1脚的低电平状态。
    根据这三个步骤,设置好PWR_WKUPEPR寄存器,就可以设置PA0引脚的上升沿唤醒MCU了。
    PWR唤醒清除寄存器(PWR_WKUPCR)
    PWR唤醒清除寄存器描述如图29.5.1.2所示:

    图29.5.1.2 PWR_WKUPCR寄存器
    该寄存器我们只关心WKUPC1位,设置WKUPC1为1,可以清除PA0的唤醒标志位。
    PWR CPU控制寄存器(PWR_CPUCR)
    PWR CPU控制寄存器描述如图29.5.1.3所示:

    图29.5.1.3 PWR_CPUCR寄存器
    该寄存器我们只需要关心最低3个位,分别用于设置在D1域、D2域和D3域进入深度睡眠模式时,允许待机模式,已达到最低功耗的目的。所以这三个位都要设置为1。
    系统控制寄存器(SCB_SCR)
    系统控制寄存器描述如图29.5.1.4所示:

    图29.5.1.4 SCB_SCR寄存器
    该寄存器我们只关心:SLEEPDEEP位,要进入待机模式,我们必须设置该位为1。
    29.5.2 硬件设计

    1. 例程功能
      LED0闪烁,表明代码正在运行。按下按键KEY0后,进入待机模式。待机模式下大部分引脚处于高阻态,所以说这时候LED0会熄灭,TFTLCD屏熄灭。按下按键WK_UP后,退出待机模式(相当于复位操作),程序重新执行,LED0继续闪烁,TFTLCD屏点亮。
    2. 硬件资源
      1)RGB灯
      RED : LED0 - PB4
      2)独立按键
      KEY0 - PA1
      WK_UP - PA0
      3)电源管理(低功耗模式 – 待机模式)
      4)正点原子2.8/3.5/4.3/7/10寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)
    3. 原理图
      PWR属于STM32H750的内部资源,只需要软件设置好即可正常工作。我们通过KEY0让CPU进入待机模式,再通过WK_UP上升沿触发唤醒CPU。LED0指示程序是否执行。
      29.5.3 程序设计
      29.5.3.1 PWR的HAL库驱动
    4. HAL_PWR_EnableWakeUpPin函数
      使能唤醒引脚函数,其声明如下:
      void HAL_PWR_EnableWakeUpPin (uint32_t WakeUpPinPolarity);
      函数描述:
      用于使能唤醒引脚,实际上该函数设置PWR唤醒使能和极性寄存器(PWR_WKUPEPR)的位[5:0]和位[13:8]。
      函数形参:
      形参1取值范围:PWR_WAKEUP_PIN1_HIGH~ PWR_WAKEUP_PIN6_HIGH(等同于PWR_WAKEUP_PIN1PWR_WAKEUP_PIN6)、PWR_WAKEUP_PIN1_LOW PWR_WAKEUP_PIN6_LOW。
      函数返回值:无
      注意事项:
      禁止某个唤醒引脚使用的函数如下:
      void HAL_PWR_DisableWakeUpPin (uint32_t WakeUpPinPolarity);
    5. HAL_PWR_EnterSTANDBYMode函数
      进入待机模式函数,其声明如下:
      void HAL_PWR_EnterSTANDBYMode (void);
      函数描述:
      用于使CPU进入待机模式,进入待机模式,首先要设置SLEEPDEEP位,接着我们通过PWR_CR设置PDDS位,使得CPU进入深度睡眠时进入待机模式,最后执行WFI指令开始进入待机模式,并等待WK_UP唤醒的到来。
      函数形参:无
      函数返回值:无
      待机模式配置步骤
      1)进入CPU待机模式
      在进入待机模式之前我们需要做一些准备,比如:关闭RTC相关中断、清除RTC相关中断标志位等一些操作,只是防止RTC中断唤醒。这里就不细讲,详见本例程源码。
      通过__HAL_PWR_CLEAR_FLAG函数清除唤醒标志位。
      通过HAL_PWR_EnableWakeUpPin函数使能PA0的唤醒功能。
      通过HAL_PWR_EnterSTANDBYMode函数进入待机模式。
      2)通过按下WKUP引脚上升沿触发唤醒待机模式
      在本实验中,通过按下KEY0按键进入待机模式,然后通过按下WK_UP按键(特定唤醒源)触发唤醒待机模式。
      29.5.3.2 程序流程图

    图29.4.3.2.1 待机模式实验程序流程图
    29.5.3.3 程序解析
    这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。PWR驱动源码包括两个文件:pwr.c和pwr.h。
    pwr.h头文件上的宏定义我们是没有用到的,这里我们使用的是特定唤醒源,与外部中断无关。下面是pwr.c文件,我们主要介绍进入待机模式函数,其定义如下:
    /**

    • @brief 进入待机模式

    • @param 无

    • @retval 无
      /
      void pwr_enter_standby(void)
      {
      __HAL_GPIO_EXTI_CLEAR_IT(PWR_WKUP_GPIO_PIN); /
      清除WKUP上的中断标志位 /
      HAL_PWR_DisableWakeUpPin(PWR_WAKEUP_PIN1_HIGH); /
      禁止唤醒 */

      __HAL_RCC_BACKUPRESET_FORCE(); /* 复位备份区域 /
      HAL_PWR_EnableBkUpAccess(); /
      后备区域访问使能 */

      __HAL_PWR_CLEAR_FLAG(PWR_FLAG_SB);
      __HAL_RTC_WRITEPROTECTION_DISABLE(&g_rtc_handle); /* 关闭RTC写保护 */

      __HAL_RTC_WAKEUPTIMER_DISABLE_IT(&g_rtc_handle, RTC_IT_WUT);/关RTC相关中断/
      __HAL_RTC_TIMESTAMP_DISABLE_IT(&g_rtc_handle, RTC_IT_TS);
      __HAL_RTC_ALARM_DISABLE_IT(&g_rtc_handle, RTC_IT_ALRA | RTC_IT_ALRB);
      /* 清除RTC相关中断标志位 */
      __HAL_RTC_ALARM_CLEAR_FLAG(&g_rtc_handle, RTC_FLAG_ALRAF | RTC_FLAG_ALRBF);
      __HAL_RTC_TIMESTAMP_CLEAR_FLAG(&g_rtc_handle, RTC_FLAG_TSF);
      __HAL_RTC_WAKEUPTIMER_CLEAR_FLAG(&g_rtc_handle, RTC_FLAG_WUTF);

      __HAL_RCC_BACKUPRESET_RELEASE(); /* 备份区域复位结束 /
      __HAL_RTC_WRITEPROTECTION_ENABLE(&g_rtc_handle); /
      使能RTC写保护 */

      HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1_HIGH); /* 使能WK_UP唤醒功能 /
      HAL_PWR_EnterSTANDBYMode(); /
      进入待机模式 /
      }
      该函数首先是关闭RTC相关中断,清除RTC相关中断标志位。然后使能WKUP引脚上升沿来唤醒待机模式。最后调用HAL_PWR_EnterSTANDBYMode函数进入待机模式。
      最后在main.c里面编写如下代码:
      int main(void)
      {
      uint8_t t = 0;
      uint8_t key = 0;
      sys_cache_enable(); /
      打开L1-Cache /
      HAL_Init(); /
      初始化HAL库 /
      sys_stm32_clock_init(240, 2, 2, 4); /
      设置时钟, 480Mhz /
      delay_init(480); /
      延时初始化 /
      usart_init(115200); /
      串口初始化为115200 /
      mpu_memory_protection(); /
      保护相关存储区域 /
      led_init(); /
      初始化LED /
      lcd_init(); /
      初始化LCD /
      key_init(); /
      初始化按键 /
      pwr_wkup_key_init(); /
      唤醒按键初始化 */
      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) == 0)
      {
      LED0_TOGGLE(); /
      每200ms,翻转一次LED0 */
      }
      delay_ms(10);
      t++;
      }
      }
      该部分程序,经过一系列初始化后,判断到KEY0按下就调用pwr_enter_standby函数进入待机模式,然后等待按下WK_UP按键进行唤醒。注意待机模式唤醒后,系统会进行复位。
      29.5.4 下载验证
      下载代码后,LED0闪烁,表明代码正在运行。按下按键KEY0后,TFTLCD屏熄灭,此时LED0不再闪烁,说明已经进入待机模式。按下按键WK_UP后,TFTLCD屏点亮,此时LED0继续闪烁,说明系统从待机模式中唤醒相当于复位。

    图29.1.1.1 电源概述框图
    在电源概述框图中我们划分了5个区域①②③④⑤,分别是USB稳压器域、内核域、VDD域、备份域和模拟域。下面分别进行简单介绍:
    ①USB稳压器域
    VSS 是所有电源和模拟稳压器的公共地。
    VDD50USB为USB稳压器供电的外部电源。
    VDD33USB为USB接口供电的USB稳压器供电输出。
    1)当USB稳压器使能时,VDD33USB由内部USB稳压器提供。
    2)当USB稳压器禁止时,VDD33USB由独立的外部电源输入提供。
    ② 内核域(VCORE)
    VDDLDO是稳压器供电的外部电源。
    VCAP数字内核域电源,该电源独立于所有其它电源:
    1)当稳压器使能时,VCORE由内部稳压器提供。
    2)当稳压器禁止时,VCORE由外部电源通过VCAP引脚提供。
    VCORE内核域电源可通过稳压器或者外部电源(VCAP)供电。VCORE为除备份域和待机电路以外的所有数字电路供电。VCORE域分为3个部分:
    1、D1域(CPU (Cortex-M7)、外设、RAM和Flash)。
    2、D2域(外设、RAM)。
    3、D3域(系统逻辑、EXTI、低功耗外设、RAM和IO逻辑)。
    当发生系统复位时,稳压器使能并为VCORE供电。稳压器的输出电压为1.2V(M4/M7),如果是M3,则是1.8V。稳压器提供三种不同的工作模式:主 (MR) 模式、低功耗模式 (LP) 或关闭模式。这些模式将根据系统工作模式(运行、停止和待机)进行使用,详细如下:
    1、运行模式:稳压器工作在主模式,并为VCORE域(内核、存储器和数字外设)提供全功率。稳压器输出电源可通过软件调节为不同电压级别(VOS1、VOS2 和 VOS3),这些级别通过PWR D3域控制寄存器(PWR_D3CR) 中的VOS位配置。
    2、停止模式:VCORE域的所有时钟都被关闭,相应的外设都停止了工作,但稳压器还会为VCORE供电以保存内核寄存器和内部存储器(SRAM)的内容。稳压器模式通过PWR控制寄存器1 (PWR_CR1) 中的SVOS和LPDS位选择。如果选择了SVOS3电压调节,则可以选择主模式或 低功耗模式;如果选择SVOS4和SVOS5调节,则只能选择低功耗模式。由于SVOS4和SVOS5 调节的电压级别低,因此可进一步降低停止模式的功耗。
    3、待机模式:
    稳压器关闭且VCORE域掉电。除待机电路和备份域外,内核寄存器和内部存储器(SRAM)的内容都将丢失。
    ③ VDD域
    VDD为I/O和系统模拟模块(如复位、电源管理和时钟)供电的外部电源。
    VBAT是后备电源(来源于开发板上3V的纽扣电池),当VDD不存在时(开发板断电),VBAT为备份域供电。
    ④备份域
    备份域的电源自来VSW,VSW来自VDD域的VDD或者VBAT。在备份域中,包含LSI、LSE、
    RTC、唤醒逻辑、备份寄存器、复位以及备份SRAM等器件。
    ⑤模拟域
    VDDA为ADC、DAC、OPAMP、比较器和电压参考缓冲器供电的外部模拟电源。该电源独立于所有其它电源。
    VSSA是独立的模拟和参考电压地。
    VREF+ 是ADC和DAC的外部参考电压。
    1)当电压参考缓冲器使能时,VREF+ 和VREF- 由内部电压参考缓冲器提供。
    2)当电压参考缓冲器禁止时,VREF+ 由独立的外部参考电源提供。
    29.1.2 电源监控
    电源监控的部分我们主要关注PVD监控器,实验17-1就是根据该监控器进行的,此外还需要知道上电复位(POR)/掉电复位(PDR)和欠压复位(BOR)。其他部分的内容请大家查看《STM32H7xx参考手册_V7(英文版).pdf》第6.5节(266页)。
    上电复位(POR)/掉电复位(PDR)
    上电时,当VDD低于指定VPOR阈值时,系统无需外部复位电路便会保持复位模式。一旦VDD电源电压高于VPOR阈值,系统便会退出复位状态。掉电时,当VDD低于指定VPDR阈值时,系统就会保持复位模式。如图29.1.2.1所示,pwr_por_rst为上电复位信号。
    注意:POR与PDR的复位电压阈值是固定的,VPOR阈值(典型值)为1.72V,VPDR阈值(典型值)为1.68V。
    在这里插入图片描述

    图29.1.2.1 上电复位/掉电复位波形
    欠压复位(BOR)
    上电期间,欠压复位(BOR)将使系统保持复位状态,直到VDD电源电压达到指定的VBOR阈值。VBOR阈值通过系统选项字节(某些寄存器的BOR_LEV位)进行配置。默认情况下,BOR关闭。可选择以下可编程VBOR阈值:
    在这里插入图片描述

    表29.1.2.1 BOR欠压阀值等级
    该表截取于《STM32H750VBT6.pdf》手册的207页。
    欠压复位的描述波形图如图29.1.2.2所示,pwr_bor_rst为欠压复位信号。
    在这里插入图片描述

    图29.1.2.2 欠压复位波形
    可编程电压检测器(PVD)
    上面介绍的POR、PDR以及BOR功能都是设置电压阈值与外部供电电压VDD比较,当VDD低于设置的电压阈值时,就会直接进入复位状态,防止电压不足导致的误操作。
    下面介绍可编程电压检测器(PVD),它可以实时监视VDD的电压,方法是将VDD与PWR控制寄存器1(PWR_CR1)中的PLS[2:0]位所选的VPVD阈值进行比较。当检测到电压低于VPVD阈值时,如果使能EXTI16线中断,即使能PVD & AVD中断(可参考表16.1.2.1知道EXTI16线内部连接PVD中断事件),可以产生PVD中断,具体取决于EXTI16线配置为检测上升还是下降沿,然后在复位前,在中断服务程序中执行紧急关闭系统等任务。PVD阀值检测波形,如图29.1.2.1所示。
    在这里插入图片描述

    图29.1.2.3 PVD检测波形
    PVD阀值有8个等级,有上升沿和下降沿的区别,分别就是图29.1.2.3中PVDrise电压为上升沿阀值,PVDfall为下降沿阀值,具体如表29.1.2.2所示。
    在这里插入图片描述

    表29.1.2.2 PVD阀值等级
    该表截取于《STM32H750VBT6.pdf》手册的207页。
    表29.1.2.2中只有7个PVD阀值等级,最后一个就是PVD_IN引脚上的外部电压级别(与内部VREFINT值相比较)。
    29.1.3 电源管理
    电源管理的部分我们主要关注低功耗模式,其他部分的内容请自行查看手册。
    很多单片机都有低功耗模式,STM32也不例外。STM32H750提供了6种低功耗模式,以达到不同层次的降低功耗的目的,这六种模式如下:
    1、CSleep(CPU时钟停止,所有的外设仍可以运行);
    2、CStop(CPU时钟停止,大多数CPU子系统时钟停止,所以大部分外设也停止工作);
    3、DStop(域总线矩阵时钟停止,D1、D2域进入停止模式)
    4、停止(系统时钟停止,D3域进入停止模式,D1和D2域进入停止或者待机模式)
    5、DStandby(域掉电,D1/D2/D3进入待机模式)
    6、待机(系统掉电,达到最低功耗)
    在这六种低功耗模式中,最低功耗的是待机模式,在此模式下,最低只需要2uA左右的电流。停止模式是次低功耗的,其典型的电流消耗在170uA左右。最后就是睡眠模式了。用户可以根据最低电源消耗,最快速启动时间和可用的唤醒源等条件,选定一个最佳的低功耗模式。
    下面是低功耗模式汇总介绍,如下表所示。
    在这里插入图片描述

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

    进入模式 WFI(等待中断)或WFE(等待事件),条件为:
    1、CM7系统控制寄存器中的SLEEPDEEP位置0
    2、没有中断(针对WFI)和事件(针对WFE)挂起
    从ISR返回,条件为:
    1、CM7系统控制寄存器中的SLEEPDEEP位置0,及SLEEPONEXIT位置1
    2、没有中断和事件挂起
    在这里插入图片描述

    表29.1.3.2 睡眠模式进入及退出方法
    2、停止模式
    进入停止模式,所有的时钟都关闭,于是所有的外设也停止了工作。但是内核域的VCORE电源是没有关闭的,所以内核的寄存器和内存信息都保留下来,等待重新开启时钟就可以从上次停止的地方继续执行程序。停止模式可以被任何一个外部中断(EXTI)或事件唤醒。在停止模式中可以选择稳压器的工作方式为主模式或者低功耗模式。下面我们看看停止模式进入及退出方法:
    停止模式 说明

    进入模式 WFI(等待中断)或WFE(等待事件),条件为:
    1、CM7系统控制寄存器中的SLEEPDEEP位置1
    2、没有中断(针对WFI)和事件(针对WFE)挂起
    3、所有CPU EXTI唤醒源清除
    从ISR返回,条件为:
    1、CM7系统控制寄存器中的SLEEPDEEP位置1,及SLEEPONEXIT位置1
    2、没有中断和事件挂起
    3、所有CPU EXTI唤醒源清除
    在这里插入图片描述

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

    表29.1.3.4 待机模式进入及退出方法
    从待机模式唤醒后的代码执行等同于复位后的执行(采样启动模式引脚,读取复位向量等)。 在进入待机模式后,除了复位引脚、RTC_TAMP1/2/3引脚(PC13/PI8/PC1)(如果针对入侵、时间戳、RTC 闹钟输出或 RTC 时钟校准输出进行了配置)和WK_UP(PA0/PA2/PC1/PC13/PI8/ PI11)(如果使能了)等引脚外,其他所有IO引脚都将处于高阻态。
    下面开始本章的四个实验的介绍。
    29.2 PVD电压监控实验
    本小节我们来学习PVD电压监控实验,该部分的知识点内容请回顾29.1.2电源监控。我们直接从寄存器介绍开始。
    29.2.1 PWR寄存器
    本实验用到PWR的部分寄存器,在《STM32H7xx参考手册_V3(中文版).pdf》手册的6.8小节(247页)可以找到PWR的寄存器描述。这里我们只介绍PVD电压监控实验我们用到的PWR的控制寄存器1(PWR_CR1),还有就是我们要用到EXTI16线中断,所以还要配置EXTI相关的寄存器,具体如下:
    PWR控制寄存器 1(PWR_CR1)
    PWR控制寄存器1描述如图29.2.1.1所示:
    在这里插入图片描述

    图29.2.1.1 PWR_CR1寄存器(部分)
    位[7:5] PLS用于设置PVD检测的电压阀值,即前面我们介绍PVD的8个等级阀值选择。
    位4 PVDE位,用于使能或者禁止PVD检测,显然我们要使能PVD检测,该位置1。
    EXTI中断屏蔽寄存器(EXTI_CPUIMR1)
    EXTI中断屏蔽寄存器描述如图29.2.1.2所示:
    在这里插入图片描述

    图29.2.1.2 EXTI_CPUIMR1寄存器
    我们要使用到EXTI16线中断,所以MR16位要置1,即取消屏蔽EXTI16线的中断请求。
    EXTI上升沿触发选择寄存器(EXTI_RTSR1)
    EXTI上升沿触发选择寄存器描述如图29.2.1.3所示:
    在这里插入图片描述

    图29.2.1.3 EXTI_RTSR1寄存器
    我们要使用到EXTI16线中断,所以TR16位要置1。
    EXTI下降沿触发选择寄存器(EXTI_FTSR1)
    EXTI下降沿触发选择寄存器描述如图29.2.1.4所示:
    在这里插入图片描述

    图29.2.1.4 EXTI_FTSR1寄存器
    我们要使用到EXTI16线中断,所以TR16位要置1。
    EXTI挂起寄存器(EXTI_CPUPR1)
    EXTI挂起寄存器描述如图29.2.1.5所示:
    在这里插入图片描述

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

    1. 例程功能
      开发板供电正常的话,LCD屏会显示"PVD Voltage OK!"。当供电电压过低,则会通过PVD中断服务函数将LED1点亮;当供电电压正常后,会在PVD中断服务函数将LED1熄灭。LED0闪烁,提示程序运行。
    2. 硬件资源
      1)RGB灯
      BLUE :LED2 - PE5
      GREEN : LED1 - PE6
      2)PVD(可编程电压监测器)
      3)正点原子2.8/3.5/4.3/7/10寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)
    3. 原理图
      PVD属于STM32H750的内部资源,只需要软件设置好即可正常工作。我们通过LED1和LCD来指示进入PVD中断的情况。
      29.2.3 程序设计
      29.2.3.1 PWR的HAL库驱动
      PWR在HAL库中的驱动代码在stm32h7xx_hal_pwr.c文件(及其头文件)中。
    4. HAL_PWR_ConfigPVD函数
      PVD的初始化函数,其声明如下:
      void HAL_PWR_ConfigPVD (PWR_PVDTypeDef sConfigPVD);
      函数描述:
      用于初始化PWR。
      函数形参:
      形参1是PWR_PVDTypeDef结构体类型指针变量,其定义如下:
      typedef struct
      {
      uint32_t PVDLevel; /
      指定PVD检测级别 /
      uint32_t Mode; /
      指定PVD的EXTI检测模式 */
      }PWR_PVDTypeDef;
      1)PVDLevel:指向PVD检测级别,对应PWR_CR1寄存器的PLS位的设置,取值范围PWR_PVDLEVEL_0到PWR_PVDLEVEL_7,共八个级别。
      2)Mode:指定PVD的EXTI边沿检测模式。
      函数返回值:

      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中编写,详见本例程源码。
      29.2.3.2 程序流程图
      在这里插入图片描述

    图29.2.3.2.1 PVD电压监控实验程序流程图
    29.2.3.3 程序解析
    这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。PWR源码包括两个文件:pwr.c和pwr.h。该章节有四个实验,每一个实验的代码都是在上一个实验后面追加。
    pwr.h头文件只有函数声明,下面直接开始介绍pwr.c的程序,首先是PVD初始化函数。

    /**
     * @brief     	初始化PVD电压监视器
     * @param      	pls: 电压等级
     *   @arg       	PWR_PVDLEVEL_0,1.95V;  PWR_PVDLEVEL_1,2.1V
     *   @arg       	PWR_PVDLEVEL_2,2.25V;  PWR_PVDLEVEL_3,2.4V;
     *   @arg       	PWR_PVDLEVEL_4,2.55V;  PWR_PVDLEVEL_5,2.7V;
     *   @arg       	PWR_PVDLEVEL_6,2.85V;  PWR_PVDLEVEL_7,使用PVD_IN脚上的电压(与
    Vrefint比较)
     * @retval      	无
     */
    void pwr_pvd_init(uint32_t pls)
    {
        PWR_PVDTypeDef pvd_handle = {0};
    
        HAL_PWR_EnablePVD();                                /* 使能PVD时钟 */
    
    pvd_handle.PVDLevel = pls;                         /* 检测电压级别 */
    /* 使用中断线的上升沿和下降沿双边缘触发 */
        pvd_handle.Mode = PWR_PVD_MODE_IT_RISING_FALLING;   
        HAL_PWR_ConfigPVD(&pvd_handle);
    
        HAL_NVIC_SetPriority(PVD_AVD_IRQn, 3 ,3);
        HAL_NVIC_EnableIRQ(PVD_AVD_IRQn);
        HAL_PWR_EnablePVD();                                /* 使能PVD检测 */
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    这里需要注意的就是PVD中断线选择的是上升沿和下降沿双边沿触发,其他的内容前面已经讲过。
    下面介绍的是PVD/AVD中断服务函数及其回调函数,函数定义如下:

    /**
     * @brief       PVD/AVD中断服务函数
     * @param       无
     * @retval      无
     */
    void PVD_AVD_IRQHandler(void)
    {
        HAL_PWR_PVD_IRQHandler();
    }
    
    /**
     * @brief       PVD/AVD中断服务回调函数
     * @param       无
     * @retval      无
     */
    void HAL_PWR_PVDCallback(void)
    {
        if (__HAL_PWR_GET_FLAG(PWR_FLAG_PVDO))   /* 电压比PLS所选电压还低 */
    {
    /* LCD显示电压低 */
            lcd_show_string(30, 130, 200, 16, 16, "PVD Low Voltage!", RED); 
            LED1(0);    /* 点亮绿灯, 表明电压低了 */
        }
        else
    {
    /* LCD显示电压正常 */
            lcd_show_string(30, 130, 200, 16, 16, "PVD Voltage OK! ", BLUE);
            LED1(1);    /* 灭掉绿灯 */
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    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(240, 2, 2, 4); 	/* 设置时钟, 480Mhz */
        delay_init(480);                    		/* 延时初始化 */
        usart_init(115200);                 		/* 串口初始化为115200 */
        mpu_memory_protection();              	/* 保护相关存储区域 */
        led_init();                         		/* 初始化LED */
        lcd_init();                        			/* 初始化LCD */
        pwr_pvd_init(PWR_PVDLEVEL_5);        	/* PVD 2.7V检测 */
    
        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) == 0)
            {
                LED0_TOGGLE();  /* 每200ms,翻转一次LED0 */
            }
    
            delay_ms(10);
            t++;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    这里我们选择PVD的检测电压阀值为2.7V,其他的代码很好理解,最后下载验证一下。
    29.2.4 下载验证
    下载代码后,默认LCD屏会显示"PVD Voltage OK!“,当供电电压过低,则LED1会点亮,并且LCD屏会显示PVD Low Voltage!。当开发板供电正常,LED1会熄灭,LCD屏会继续显示"PVD Voltage OK!”。

    29.3 睡眠模式实验
    本小节我们来学习睡眠模式实验,该部分的知识点内容请回顾29.1.23电源管理。我们直接从寄存器介绍开始。
    29.3.1 EXTI寄存器
    本实验我们用到外部中断来唤醒睡眠模式。我们用到WFI指令进入睡眠模式,这个后面会讲,进入睡眠模式后,使用外部中断唤醒。进入外部中断后,EXTI_CPUIMR1寄存器的值会自动清零,我们需要对对应的外部中断线位置1,取消屏蔽,相当于其他中断的中断标志位进入中断后硬件自动置1,需要手动清零。
    EXTI中断屏蔽寄存器(EXTI_CPUIMR1)
    EXTI中断屏蔽寄存器描述如图29.3.1.1所示:
    在这里插入图片描述

    图29.3.1.1 EXTI_CPUIMR1寄存器
    实验中我们使用WK_UP(PA0)唤醒,即EXTI0线中断,所以在外部中断服务函数要把MR0位要置1。
    29.3.2 硬件设计

    1. 例程功能
      LED0闪烁,表明代码正在运行。按下按键KEY0后,LED1点亮,提示进入睡眠模式,此时LED0不再闪烁,说明已经进入睡眠模式。按下按键WK_UP后,LED1熄灭,提示退出睡眠模式,此时LED0继续闪烁,说明已经退出睡眠模式。
    2. 硬件资源
      1)RGB灯
      RED : LED0 - PB4
      GREEN : LED1 - PE6
      2)独立按键 KEY0 - PA1,WK_UP - PA0
      3)电源管理(低功耗模式 - 睡眠模式)
      4)正点原子2.8/3.5/4.3/7/10寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)
    3. 原理图
      PWR属于STM32H750的内部资源,只需要软件设置好即可正常工作。我们通过KEY0让CPU进入睡眠模式,再通过WK_UP 触发EXTI中断来唤醒CPU。LED0指示程序是否执行,LED1指示CPU是否进入睡眠模式。
      29.3.3 程序设计
      29.3.3.1 PWR的HAL库驱动
    4. HAL_PWR_EnterSLEEPMode函数
      进入睡眠模式函数,其声明如下:
      void HAL_PWR_EnterSLEEPMode (uint32_t Regulator, uint8_t SLEEPEntry);
      函数描述:
      用于设置CPU进入睡眠模式。
      函数形参:
      形参1指定稳压器的状态。有两个选择,PWR_MAINREGULATOR_ON表示稳压器处于主模式,PWR_LOWPOWERREGULATOR_ON表示稳压器处于低功耗模式。对应的是PWR_CR1寄存器的LPDS位的设置(该形参在该函数中没有实质用处)。
      形参2指定进入睡眠模式的方式。有两个选择,PWR_SLEEPENTRY _WFI表示使用WFI指令,PWR_SLEEPENTRY_WFE表示使用WFE指令。我们选择前者,不了解这两种指令的区别,请问度娘。
      函数返回值:

      睡眠模式配置步骤
      1)配置唤醒睡眠模式的方式
      这里我们用外部中断的方式唤醒睡眠模式,所以这里需要配置一个外部中断功能,我们用WK_UP按键作为中断触发源,接下来就是配置PA0(连接按键WK_UP)。
      通过__HAL_RCC_GPIOA_CLK_ENABLE函数使能GPIOA的时钟。
      通过HAL_GPIO_Init函数配置PA0为上升沿触发检测的外部中断模式,开启下拉电阻等。
      通过HAL_NVIC_EnableIRQ函数使能EXTI0中断。
      通过HAL_NVIC_SetPriority函数设置中断优先级。
      编写EXTI0_IRQHandle中断函数,在中断服务函数中调用HAL_GPIO_EXTI_IRQHandler。
      最后编写HAL_GPIO_EXTI_Callback回调函数。由于前面已经介绍过外部中断的配置步骤,这里就介绍到这里,详见本例程源码。
      2)进入CPU睡眠模式
      通过HAL_SuspendTick函数暂停滴答时钟,防止通过滴答时钟中断唤醒。
      通过HAL_PWR_EnterSLEEPMode函数进入睡眠模式。
      3)通过按下按键触发外部中断唤醒睡眠模式
      在本实验中,通过按下KEY0按键进入睡眠模式,然后通过按下WK_UP按键触发外部中断唤醒睡眠模式。
      29.3.3.2 程序流程图
      在这里插入图片描述

    图29.3.3.2.1 睡眠模式实验程序流程图
    29.3.3.3 程序解析
    这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。PWR驱动源码包括两个文件:pwr.c和pwr.h。
    首先看pwr.h头文件的几个宏定义:
    /* PWR WKUP 按键 引脚和中断 定义

    • 我们通过WK_UP按键唤醒 MCU, 因此必须定义这个按键及其对应的中断服务函数
     */
    #define PWR_WKUP_GPIO_PORT        	GPIOA
    #define PWR_WKUP_GPIO_PIN      		GPIO_PIN_0
    #define PWR_WKUP_GPIO_CLK_ENABLE()	do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)
      
    #define PWR_WKUP_INT_IRQn           	EXTI0_IRQn
    #define PWR_WKUP_INT_IRQHandler 	EXTI0_IRQHandler
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    这些定义是WK_UP按键的相关宏定义,以及其对应的外部中断线0的相关定义。
    pwr.h头文件就介绍这部分的程序,下面是pwr.c文件,先看低功耗模式下的按键初始化函数,其定义如下:

    /**
     * @brief     	低功耗模式下的按键初始化(用于唤醒睡眠模式/停止模式)
     * @param      	无
     * @retval    	无
     */
    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_VERY_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); 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    该函数初始化WK_UP按键(PA0),并设置上升沿触发的外部中断线0,最后设置中断优先级并使能外部中断线0。
    下面介绍的是进入CPU睡眠模式函数,其定义如下:

    /**
     * @brief     	进入CPU睡眠模式
     * @param     	无
     * @retval     	无
     */
    void pwr_enter_sleep(void)
    {
        HAL_SuspendTick();  /* 暂停滴答时钟,防止通过滴答时钟中断唤醒 */
    /* 进入睡眠模式 */
        HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    首先是调用HAL_SuspendTick函数,暂停滴答时钟,防止睡眠模式被滴答时钟中断唤醒。然后调用HAL_PWR_EnterSLEEPMode函数使用WFI指令进入睡眠模式。
    下面介绍的是WK_UP按键外部中断服务函数及其回调函数,函数定义如下:

    /**
     * @brief      	WK_UP按键 外部中断服务程序
     * @param      	无
     * @retval    	无
     */
    void PWR_WKUP_INT_IRQHandler(void)
    {
        HAL_GPIO_EXTI_IRQHandler(PWR_WKUP_GPIO_PIN);
    }
    
    /**
     * @brief      	外部中断回调函数
     * @param      	GPIO_Pin:中断线引脚
     * @note       	此函数会被PWR_WKUP_INT_IRQHandler()调用
     * @retval     	无
     */
    void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
    {
        if (GPIO_Pin == PWR_WKUP_GPIO_PIN)
        {
            /* HAL_GPIO_EXTI_IRQHandler()函数已经为我们清除了中断标志位,
    所以我们进了回调函数可以不做任何事 */
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

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

    int main(void)
    {
        uint8_t t = 0;
    uint8_t key = 0;
    
        sys_cache_enable();                 		/* 打开L1-Cache */
        HAL_Init();                         		/* 初始化HAL库 */
        sys_stm32_clock_init(240, 2, 2, 4); 	/* 设置时钟, 480Mhz */
        delay_init(480);                    		/* 延时初始化 */
        usart_init(115200);                 		/* 串口初始化为115200 */
        mpu_memory_protection();              	/* 保护相关存储区域 */
        led_init();                         		/* 初始化LED */
        lcd_init();                         		/* 初始化LCD */
        key_init();                        			/* 初始化按键 */
        pwr_wkup_key_init();                		/* 唤醒按键初始化 */
    
        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();  	/* 进入睡眠模式 */            
                HAL_ResumeTick();   	/* 恢复滴答时钟 */
                LED1(1);              	/* 关闭绿灯,提示退出睡眠模式 */
            }
    
            if ((t % 20) == 0)
            {
                LED0_TOGGLE();      /* 每200ms,翻转一次LED0 */
            }
    
            delay_ms(10);
            t++;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    该部分程序,功能就是按下KEY0后,点亮LED1、暂停滴答时钟并进入睡眠模式。然后一直等待外部中断唤醒,当按下按键WK_UP,就触发外部中断,睡眠模式就被唤醒,然后继续执行后面的程序,恢复滴答时钟,关闭LED1等。
    29.3.4 下载验证
    下载代码后,LED0闪烁,表明代码正在运行。按下按键KEY0后,LED1点亮,提示进入睡眠模式,此时LED0不再闪烁,说明已经进入睡眠模式。按下按键WK_UP后,LED1熄灭,提示退出睡眠模式,此时LED0继续闪烁,说明已经退出睡眠模式。

    29.4 停止模式实验
    本小节我们来学习停止模式实验,该部分的知识点内容请回顾29.1.23电源管理。我们直接从寄存器介绍开始。
    29.4.1 PWR寄存器
    本实验我们用到外部中断来唤醒停止模式。我们用到WFI指令进入停止模式,这个后面会讲,进入停止模式后,使用外部中断唤醒。外部中断部分内容参照睡眠模式即可,都是共用同样的配置。
    下面主要介绍PWR_CR1和PWR_CPUCR寄存器相关位。
    PWR控制寄存器 1(PWR_CR1)
    PWR的控制寄存器1描述如图29.4.1.1所示:
    在这里插入图片描述

    图29.4.1.1 PWR_CR1寄存器
    进入停止模式,我们设置稳压器为低功耗模式,等待中断唤醒,所以位LPDS置1。
    PWR CPU控制寄存器(PWR_CPUCR)
    PWR CPU控制寄存器描述如图29.4.1.2所示:
    在这里插入图片描述

    图29.4.1.2 PWR_CPUCR寄存器
    位PDDS_D1~ PDDS_D3都设置为0, 保持D1/D2/D3进入深度睡眠后,进入停止模式。在设置该寄存器前,我们要使能系统控制寄存器SCR的位2(SLEEPDEEP),该位置1,该寄存器在M7内核手册。然后在停止模式后,关闭SLEEPDEEP位。
    29.4.2 硬件设计

    1. 例程功能
      LED0闪烁,表明代码正在运行。按下按键KEY0后,LED1点亮,提示进入停止模式,此时LED0不再闪烁,说明已经进入停止模式。按下按键WK_UP后,LED1熄灭,提示退出停止模式,此时LED0继续闪烁,说明已经退出停止模式。
    2. 硬件资源
      1)RGB灯
      RED : LED0 - PB4
      GREEN : LED1 - PE6
      2)独立按键
      KEY0 - PA1
      WK_UP - PA0
      3)电源管理(低功耗模式 – 停止模式)
      4)正点原子2.8/3.5/4.3/7/10寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)
    3. 原理图
      PWR属于STM32H750的内部资源,只需要软件设置好即可正常工作。我们通过KEY0让CPU进入停止模式,再通过WK_UP 触发EXTI中断来唤醒CPU。LED0指示程序是否执行,LED1指示CPU是否进入停止模式。
      29.4.3 程序设计
      29.4.3.1 PWR的HAL库驱动
    4. HAL_PWR_EnterSTOPMode函数
      进入停止模式函数,其声明如下:
      void HAL_PWR_EnterSTOPMode (uint32_t Regulator, uint8_t STOPEntry);
      函数描述:
      用于设置CPU进入停止模式。
      函数形参:
      形参1指定稳压器在停止模式下的状态。有两个选择,PWR_MAINREGULATOR_ON表示稳压器处于主模式,PWR_LOWPOWERREGULATOR_ON表示稳压器处于低功耗模式。对应的是PWR_CR1寄存器的LPDS位的设置。
      形参2指定用WFI还是WFE指令进入停止模式。有两个选择,PWR_STOPENTRY_WFI表示使用WFI指令,PWR_STOPENTRY_WFE表示使用WFE指令。我们选择前者,不了解这两种指令的区别,请问度娘。
      函数返回值:

      停止模式配置步骤
      1)配置唤醒停止模式的方式
      这里我们用外部中断的方式唤醒停止模式,所以这里需要配置一个外部中断功能,我们用WK_UP按键作为中断触发源,接下来就是配置PA0(连接按键WK_UP)。
      通过__HAL_RCC_GPIOA_CLK_ENABLE函数使能GPIOA的时钟。
      通过HAL_GPIO_Init函数配置PA0为上升沿触发检测的外部中断模式,开启下拉电阻等。
      通过HAL_NVIC_EnableIRQ函数使能EXTI0中断。
      通过HAL_NVIC_SetPriority函数设置中断优先级。
      编写EXTI0_IRQHandle中断函数,在中断服务函数中调用HAL_GPIO_EXTI_IRQHandler。
      最后编写HAL_GPIO_EXTI_Callback回调函数。由于前面已经介绍过外部中断的配置步骤,这里就介绍到这里,详见本例程源码。
      2)进入CPU停止模式
      通过sys_stm32_clock_init函数降频。降低性能前,应先减小系统频率,再更改电压调节。
      通过HAL_PWR_EnterSTOPMode函数进入停止模式。
      3)通过按下按键触发外部中断唤醒睡眠模式
      在本实验中,通过按下KEY0按键进入停止模式,然后通过按下WK_UP按键触发外部中断唤醒停止模式。
      29.4.3.2 程序流程图
      在这里插入图片描述

    图29.4.3.2.1 停止模式实验程序流程图
    29.4.3.3 程序解析
    这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。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)
    {
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    sys_stm32_clock_init(200, 2, 2, 4);	/* 设置时钟,400Mhz,降频 */
    /* 当SVOS3进入停止模式时,设置稳压器为低功耗模式,等待中断唤醒 */
    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
    
    • 1
    • 2
    • 3

    }
    该函数首先是调用sys_stm32_clock_init函数重新设置系统时钟频率,降频到400MHZ。然后调用HAL_PWR_EnterSTOPMode函数进入停止模式,形参1选择PWR_LOWPOWERREGU LATOR_ON表示设置稳压器为低功耗模式,形参2则是选择WFI指令。这里我们不再需要像睡眠模式实验一样要暂停滴答时钟,因为滴答时钟中断无法唤醒停止模式,只有外部中断可以唤醒。
    最后在main.c里面编写如下代码:

    int main(void)
    {
        uint8_t t = 0;
    uint8_t key = 0;
    
        sys_cache_enable();                 		/* 打开L1-Cache */
        HAL_Init();                         		/* 初始化HAL库 */
        sys_stm32_clock_init(240, 2, 2, 4); 	/* 设置时钟, 480Mhz */
        delay_init(480);                    		/* 延时初始化 */
        usart_init(115200);                 		/* 串口初始化为115200 */
        mpu_memory_protection();              	/* 保护相关存储区域 */
        led_init();                         		/* 初始化LED */
        lcd_init();                         		/* 初始化LCD */
        key_init();                        			/* 初始化按键 */
        pwr_wkup_key_init();                		/* 唤醒按键初始化 */
    
        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)
            {
                LED1(0);    /* 点亮绿灯,提示进入停止模式 */ 
                pwr_enter_stop(); /* 进入停止模式 */
                /* 从停止模式唤醒, 需要重新设置系统时钟, 480Mhz */
                sys_stm32_clock_init(240, 2, 2, 4);
                LED1(1);            /* 关闭绿灯,提示退出停止模式 */
            }
    
            if ((t % 20) == 0)
            {
                LED0_TOGGLE();      /* 每200ms,翻转一次LED0 */
            }
    
            delay_ms(10);
            t++;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    该部分程序,功能就是按下KEY0后,点亮LED1、暂停滴答时钟并进入停止模式。然后一直等待外部中断唤醒,当按下按键WK_UP,就触发外部中断,停止模式就被唤醒,然后继续执行后面的程序,重新设置系统时钟480MHZ,关闭LED1等。
    29.4.4 下载验证
    下载代码后,LED0闪烁,表明代码正在运行。按下按键KEY0后,LED1点亮,提示进入停止模式,此时LED0不再闪烁,说明已经进入停止模式。按下按键WK_UP后,LED1熄灭,提示退出停止模式,此时LED0继续闪烁,说明已经退出停止模式。

    29.5 待机模式实验
    本小节我们来学习待机模式实验,该部分的知识点内容请回顾29.1.23电源管理。我们直接从寄存器介绍开始。
    29.5.1 PWR寄存器
    本实验是先对相关的电源控制寄存器配置待机模式的参数,然后通过WFI指令进入待机模式,使用特定唤醒源WKUP引脚来唤醒待机模式。
    下面主要介绍本实验用到的几个寄存器。
    PWR唤醒使能和极性寄存器(PWR_WKUPEPR)
    PWR唤醒使能和极性寄存器描述如图29.5.1.1所示:
    在这里插入图片描述

    图29.5.1.1 PWR_WKUPEPR寄存器
    本章,我们使用PA0的上升沿来唤醒MCU,PA0对应的WKUP源为:WKUP1,因此,对于PWR_WKUPEPR寄存器,我们需要设置的位如下:
    1,设置WKUPEN1位为1,使能WKUP1的唤醒功能。
    2,设置WKUPPP1位为0,选择上升沿唤醒。
    3,设置WKUPPUPD1[1:0]位为2,选择使用下拉电阻,以保持WKUP1脚的低电平状态。
    根据这三个步骤,设置好PWR_WKUPEPR寄存器,就可以设置PA0引脚的上升沿唤醒MCU了。
    PWR唤醒清除寄存器(PWR_WKUPCR)
    PWR唤醒清除寄存器描述如图29.5.1.2所示:
    在这里插入图片描述

    图29.5.1.2 PWR_WKUPCR寄存器
    该寄存器我们只关心WKUPC1位,设置WKUPC1为1,可以清除PA0的唤醒标志位。
    PWR CPU控制寄存器(PWR_CPUCR)
    PWR CPU控制寄存器描述如图29.5.1.3所示:
    在这里插入图片描述

    图29.5.1.3 PWR_CPUCR寄存器
    该寄存器我们只需要关心最低3个位,分别用于设置在D1域、D2域和D3域进入深度睡眠模式时,允许待机模式,已达到最低功耗的目的。所以这三个位都要设置为1。
    系统控制寄存器(SCB_SCR)
    系统控制寄存器描述如图29.5.1.4所示:
    在这里插入图片描述

    图29.5.1.4 SCB_SCR寄存器
    该寄存器我们只关心:SLEEPDEEP位,要进入待机模式,我们必须设置该位为1。
    29.5.2 硬件设计

    1. 例程功能
      LED0闪烁,表明代码正在运行。按下按键KEY0后,进入待机模式。待机模式下大部分引脚处于高阻态,所以说这时候LED0会熄灭,TFTLCD屏熄灭。按下按键WK_UP后,退出待机模式(相当于复位操作),程序重新执行,LED0继续闪烁,TFTLCD屏点亮。
    2. 硬件资源
      1)RGB灯
      RED : LED0 - PB4
      2)独立按键
      KEY0 - PA1
      WK_UP - PA0
      3)电源管理(低功耗模式 – 待机模式)
      4)正点原子2.8/3.5/4.3/7/10寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)
    3. 原理图
      PWR属于STM32H750的内部资源,只需要软件设置好即可正常工作。我们通过KEY0让CPU进入待机模式,再通过WK_UP上升沿触发唤醒CPU。LED0指示程序是否执行。
      29.5.3 程序设计
      29.5.3.1 PWR的HAL库驱动
    4. HAL_PWR_EnableWakeUpPin函数
      使能唤醒引脚函数,其声明如下:
      void HAL_PWR_EnableWakeUpPin (uint32_t WakeUpPinPolarity);
      函数描述:
      用于使能唤醒引脚,实际上该函数设置PWR唤醒使能和极性寄存器(PWR_WKUPEPR)的位[5:0]和位[13:8]。
      函数形参:
      形参1取值范围:PWR_WAKEUP_PIN1_HIGH~ PWR_WAKEUP_PIN6_HIGH(等同于PWR_WAKEUP_PIN1PWR_WAKEUP_PIN6)、PWR_WAKEUP_PIN1_LOW PWR_WAKEUP_PIN6_LOW。
      函数返回值:无
      注意事项:
      禁止某个唤醒引脚使用的函数如下:
      void HAL_PWR_DisableWakeUpPin (uint32_t WakeUpPinPolarity);
    5. HAL_PWR_EnterSTANDBYMode函数
      进入待机模式函数,其声明如下:
      void HAL_PWR_EnterSTANDBYMode (void);
      函数描述:
      用于使CPU进入待机模式,进入待机模式,首先要设置SLEEPDEEP位,接着我们通过PWR_CR设置PDDS位,使得CPU进入深度睡眠时进入待机模式,最后执行WFI指令开始进入待机模式,并等待WK_UP唤醒的到来。
      函数形参:无
      函数返回值:无
      待机模式配置步骤
      1)进入CPU待机模式
      在进入待机模式之前我们需要做一些准备,比如:关闭RTC相关中断、清除RTC相关中断标志位等一些操作,只是防止RTC中断唤醒。这里就不细讲,详见本例程源码。
      通过__HAL_PWR_CLEAR_FLAG函数清除唤醒标志位。
      通过HAL_PWR_EnableWakeUpPin函数使能PA0的唤醒功能。
      通过HAL_PWR_EnterSTANDBYMode函数进入待机模式。
      2)通过按下WKUP引脚上升沿触发唤醒待机模式
      在本实验中,通过按下KEY0按键进入待机模式,然后通过按下WK_UP按键(特定唤醒源)触发唤醒待机模式。
      29.5.3.2 程序流程图
      在这里插入图片描述

    图29.4.3.2.1 待机模式实验程序流程图
    29.5.3.3 程序解析
    这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。PWR驱动源码包括两个文件:pwr.c和pwr.h。
    pwr.h头文件上的宏定义我们是没有用到的,这里我们使用的是特定唤醒源,与外部中断无关。下面是pwr.c文件,我们主要介绍进入待机模式函数,其定义如下:

    /**
     * @brief      	进入待机模式
     * @param      	无
     * @retval     	无
     */
    void pwr_enter_standby(void)
    {
        __HAL_GPIO_EXTI_CLEAR_IT(PWR_WKUP_GPIO_PIN); 	/* 清除WKUP上的中断标志位 */
        HAL_PWR_DisableWakeUpPin(PWR_WAKEUP_PIN1_HIGH);	/* 禁止唤醒 */
    
        __HAL_RCC_BACKUPRESET_FORCE();     	/* 复位备份区域 */
        HAL_PWR_EnableBkUpAccess();       	/* 后备区域访问使能 */
    
        __HAL_PWR_CLEAR_FLAG(PWR_FLAG_SB);
        __HAL_RTC_WRITEPROTECTION_DISABLE(&g_rtc_handle);	/* 关闭RTC写保护 */
    
        __HAL_RTC_WAKEUPTIMER_DISABLE_IT(&g_rtc_handle, RTC_IT_WUT);/*关RTC相关中断*/
        __HAL_RTC_TIMESTAMP_DISABLE_IT(&g_rtc_handle, RTC_IT_TS);
        __HAL_RTC_ALARM_DISABLE_IT(&g_rtc_handle, RTC_IT_ALRA | RTC_IT_ALRB);
        /* 清除RTC相关中断标志位 */
        __HAL_RTC_ALARM_CLEAR_FLAG(&g_rtc_handle, RTC_FLAG_ALRAF | RTC_FLAG_ALRBF);
        __HAL_RTC_TIMESTAMP_CLEAR_FLAG(&g_rtc_handle, RTC_FLAG_TSF); 
        __HAL_RTC_WAKEUPTIMER_CLEAR_FLAG(&g_rtc_handle, RTC_FLAG_WUTF);
    
        __HAL_RCC_BACKUPRESET_RELEASE();                   	/* 备份区域复位结束 */
        __HAL_RTC_WRITEPROTECTION_ENABLE(&g_rtc_handle);	/* 使能RTC写保护 */
    
        HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1_HIGH); 		/* 使能WK_UP唤醒功能 */
        HAL_PWR_EnterSTANDBYMode();                          	/* 进入待机模式 */
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    该函数首先是关闭RTC相关中断,清除RTC相关中断标志位。然后使能WKUP引脚上升沿来唤醒待机模式。最后调用HAL_PWR_EnterSTANDBYMode函数进入待机模式。
    最后在main.c里面编写如下代码:

    int main(void)
    {
        uint8_t t = 0;
        uint8_t key = 0;
        sys_cache_enable();                 		/* 打开L1-Cache */
        HAL_Init();                         		/* 初始化HAL库 */
        sys_stm32_clock_init(240, 2, 2, 4); 	/* 设置时钟, 480Mhz */
        delay_init(480);                    		/* 延时初始化 */
        usart_init(115200);                 		/* 串口初始化为115200 */
        mpu_memory_protection();              	/* 保护相关存储区域 */
        led_init();                         		/* 初始化LED */
        lcd_init();                         		/* 初始化LCD */
        key_init();                        			/* 初始化按键 */
        pwr_wkup_key_init();                		/* 唤醒按键初始化 */
        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) == 0)
            {
                LED0_TOGGLE();         /* 每200ms,翻转一次LED0 */
            }
            delay_ms(10);
            t++;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    该部分程序,经过一系列初始化后,判断到KEY0按下就调用pwr_enter_standby函数进入待机模式,然后等待按下WK_UP按键进行唤醒。注意待机模式唤醒后,系统会进行复位。
    29.5.4 下载验证
    下载代码后,LED0闪烁,表明代码正在运行。按下按键KEY0后,TFTLCD屏熄灭,此时LED0不再闪烁,说明已经进入待机模式。按下按键WK_UP后,TFTLCD屏点亮,此时LED0继续闪烁,说明系统从待机模式中唤醒相当于复位。

  • 相关阅读:
    【数据结构】HashMap 和 HashSet
    使用Java合并PDF文档
    音视频开发常见问题(四):视频花屏和绿屏
    数据脱敏的场景与价值【总结】
    针对服务器实例安全组(Security Groups)的配置
    基于python的django框架选题推荐生鲜超市供应平台
    基于JavaWeb的汽车在线租赁管理系统
    基于微信理发预约小程序系统设计与实现 开题报告
    2023艾灸展/中国山东·济南国际艾灸仪器设备与艾制品展览会
    MindSpore是一种适用于端边云场景的新型开源深度学习训练/推理框架
  • 原文地址:https://blog.csdn.net/weixin_55796564/article/details/126769983