超级版主
 
- 积分
- 5969
- 金钱
- 5969
- 注册时间
- 2019-5-8
- 在线时间
- 1674 小时
|
|
第二十七章 低功耗实验
1)实验平台:正点原子Mini STM32H562开发板
2)章节摘自【正点原子】STM32H562开发指南 V1.0
3)购买链接: https://detail.tmall.com/item.htm?id=753606659612
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/stm32/zdyz_stm32h562_mini.html
5)正点原子官方B站:https://space.bilibili.com/394620890
6)正点原子STM32开发板技术交流群:414052738
本章,我们将介绍STM32H562的电源控制(PWR),并实现低功耗模式相关功能。我们将通过四个实验来学习并实现低功耗相关功能,分别是PVD电压监控实验、睡眠模式实验、停止模式实验和待机模式实验。
本章分为如下几个小节:
27.1 电源控制(PWR)简介
27.2 PVD电压监控实验
27.3 睡眠模式实验
27.4 停止模式实验
27.5 待机模式实验
27.1 电源控制(PWR)简介
为了方便对电源系统进行管理,设计者把STM32的内核和外设等器件根据功能划分了不同的电源区域,具体请看PWR框图。
图27.1.1 PWR框图
在PWR框图中我们划分了3个区域①②③,分别是电源系统、电源监控、电源管理。下面分别进行简单介绍。
27.1.1 电源系统
①电源系统是芯片的供电端,包括:内核域(VCORE)、VDD域、备份域(VSW)、模拟域(VDDA)、SMPS电源级的电源(可在SMPS封装上使用)、10个I/O的VDDIO2电源(PD6, PD7, PG9:14, PB8, PB9)、VDDUSB(USB收发器)。
27.1.2 电源监控
②电源监控包括:POR/PDR监控器、BOR监控器、PVD监控器、AVD监控器、超出功能范围的温度监视器、超出功能范围备用域电压监测器。
电源监控的部分我们主要关注PVD监控器,此外还需要知道上电复位(POR)/掉电复位(PDR)和欠压复位(BOR)。其他部分的内容请大家查看《STM32H5xx参考手册_V1(英文版).pdf》第10.6节(401页)。
上电复位(POR)/掉电复位(PDR)
上电时,当VDD低于指定VPOR阈值时,系统无需外部复位电路便会保持复位模式。一旦VDD电源电压高于VPOR阈值,系统便会退出复位状态。掉电时,当VDD低于指定VPDR阈值时,系统就会保持复位模式。如图27.1.2.1所示,Reset为复位信号。
注意:POR与PDR的复位电压阈值是固定的,VPOR阈值(典型值)为1.67V,VPDR阈值(典型值)为1.62V。
图27.1.2.1 上电复位/掉电复位波形
欠压复位(BOR)
上电期间,欠压复位(BOR)将使系统保持复位状态,直到VDD电源电压达到指定的VBOR阈值。VBOR阈值通过系统选项字节(某些寄存器的BOR_LEV位)进行配置。默认情况下,BOR关闭。可选择以下可编程VBOR阈值:
表27.1.2.1 BOR欠压阀值等级
该表截取于《STM32H562VIT6.pdf》手册的138页。
欠压复位的描述波形图如图27.1.2.2所示:
图27.1.2.2 欠压复位波形
可编程电压检测器(PVD)
上面介绍的POR、PDR以及BOR功能都是设置电压阈值与外部供电电压VDD比较,当VDD低于设置的电压阈值时,就会直接进入复位状态,防止电压不足导致的误操作。
下面介绍可编程电压检测器(PVD),它可以实时监视VDD的电压,方法是将VDD与PWR控制寄存器1(PWR_VMCR)中的PLS[2:0]位所选的VPVD阈值进行比较。当检测到电压低于VPVD阈值时,如果使能EXTI16线中断,即使能PVD & AVD中断(可参考表16.1.2.1知道EXTI16线内部连接PVD中断事件),可以产生PVD中断,具体取决于EXTI16线配置为检测上升还是下降沿,然后在复位前,在中断服务程序中执行紧急关闭系统等任务。PVD阀值检测波形,如图27.1.2.3所示。
图27.1.2.3 PVD检测波形
PVD阀值有8个等级,有上升沿和下降沿的区别,分别就是图27.1.2.3中PVDrise电压为上升沿阀值,PVDfall为下降沿阀值,具体如表27.1.2.2所示。
表27.1.2.2 PVD阀值等级
该表截取于《STM32H562VIT6.pdf》手册的138页。
27.1.3 电源管理
③电源管理:工作模式、电压缩放控制、低功耗模式。
电源管理的部分我们主要关注低功耗模式,其它部分的内容请自行查看手册。
很多单片机都有低功耗模式,STM32也不例外。STM32H562提供了三种低功耗模式,以达到不同层次的降低功耗的目的,三种模式分别是:睡眠模式、停止模式和待机模式。这三种低功耗模式电源消耗不同、唤醒时间不同和唤醒源不同,我们要根据自身的需要选择合适的低功耗模式。
下面是低功耗模式汇总,如下表所示。
表27.1.3.1 低功耗模式汇总
下面对睡眠模式、停止模式和待机模式的进入及退出方法进行介绍。
1、睡眠模式
进入睡眠模式,CPU时钟关闭,但是其它所有的外设仍可以运行,所以任何中断或事件都可以唤醒睡眠模式。有两种方式进入睡眠模式,这两种方式进入的睡眠模式唤醒的方法不同,分别是 WFI(wait for interrupt)和 WFE(wait for event),即由等待“中断”唤醒和由“事件”唤醒。下面我们看看睡眠模式进入及退出方法:
表27.1.3.2 睡眠模式进入及退出方法
2、停止模式
进入停止模式,所有的时钟都关闭,于是所有的外设也停止了工作。但是内核域的VCORE电源是没有关闭的,所以内核的寄存器和内存信息都保留下来,等待重新开启时钟就可以从上次停止的地方继续执行程序。停止模式可以被任何一个外部中断(EXTI)或事件唤醒。在停止模式中可以选择稳压器的工作方式为主模式或者低功耗模式。下面我们看看停止模式进入及退出方法:
表27.1.3.3 停止模式进入及退出方法
3、待机模式
待机模式可实现最低功耗。该模式是在M33深睡眠模式时关闭稳压器(VCOER关闭),PLL、HIS、CSI、HSI48和HSE振荡器也被断电。除备份域(RTC寄存器、RTC备份寄存器和备份SRAM)和待机电路中的寄存器外,SRAM 和寄存器内容都将丢失。下面我们看看待机模式进入及退出方法:
表27.1.3.4 待机模式进入及退出方法
在Standby模式下,I/ o默认为浮动状态。如果设置PWR_IORETR寄存器IORETEN位,则保留I/O输出状态。除了支持备份功能和JTAG IO(PA13,PA14, PA15和PB4))之外,所有I/O都启用I/O保留模式。当进入待机模式时,对输出状态进行采样,并设置上拉或下拉电阻以维持待机模式期间的I/O输出。
如果设置了PWR_IORETR寄存器中的JTAGIORETEN位,则保留I/O输出状态。PA13、PA14、PA15和PB4开启I/O保持模式(默认不开启唤醒后的JTAG上拉/下拉模式)。
下面开始本章的四个实验的介绍。
27.2 PVD电压监控实验
本小节我们来学习PVD电压监控实验,该部分的知识点内容请回顾27.1.2电源监控。我们直接从寄存器介绍开始。
27.2.1 PWR寄存器
本实验用到PWR的部分寄存器,在《STM32H5xx参考手册_V1(英文版).pdf》手册的10.11小节(423页)可以找到PWR的寄存器描述。这里我们只介绍PVD电压监控实验用到的寄存器,还有就是我们要用到EXTI16线中断,所以还要配置EXTI相关的寄存器,具体如下:
PWR电压监视器控制寄存器 1(PWR_VMCR)
PWR控制寄存器1描述如图27.2.1.1所示:
图27.2.1.1 PWR_VMCR寄存器
位PLS [2:0]用于设置PVD检测的电压阀值,即前面我们介绍PVD的8个等级阀值选择。
位PVDE用于使能或者禁止PVD检测,显然我们要使能PVD检测,该位置1。
EXTI中断屏蔽寄存器(EXTI_IMR1)
EXTI中断屏蔽寄存器描述如图27.2.1.2所示:
图27.2.1.2 EXTI_IMR1寄存器
我们要使用到EXTI16线中断,所以IM16位要置1,即取消屏蔽EXTI16线的中断请求。
EXTI上升沿触发选择寄存器(EXTI_RTSR1)
EXTI上升沿触发选择寄存器描述如图27.2.1.3所示:
图27.2.1.3 EXTI_RTSR1寄存器
我们要使用到EXTI16线中断,所以RT16位要置1。
EXTI下降沿触发选择寄存器(EXTI_FTSR1)
EXTI下降沿触发选择寄存器描述如图27.2.1.4所示:
图27.2.1.4 EXTI_FTSR1寄存器
我们要使用到EXTI16线中断,所以FT16位要置1。
EXTI上升沿挂起寄存器(EXTI_RPR1)
EXTI挂起寄存器描述如图27.2.1.5所示:
图27.2.1.5 EXTI_RPR1寄存器
EXTI上升沿挂起寄存器EXTI_RPR1管理的是EXTI0线到EXTI16线上升沿的中断标志。发生PVD上升沿触发请求后,位RPIF16被置1,需要我们给位RPIF16写1来清零该标志位。
EXTI下降沿挂起寄存器(EXTI_FPR1)
EXTI挂起寄存器描述如图27.2.1.6所示:
图27.2.1.6 EXTI_FPR1寄存器
EXTI下降沿挂起寄存器EXTI_FPR1管理的是EXTI0线到EXTI16线下降沿的中断标志。发生PVD下降沿触发请求后,位FPIF16被置1,我们需要给位FPIF16写1来清零该标志位。
27.2.2 硬件设计
1. 例程功能
开发板供电正常的话,LCD屏会显示"PVD Voltage OK!"。当供电电压过低,则会通过PVD中断服务函数将LED1点亮;当供电电压正常后,会在PVD中断服务函数将LED1熄灭。LED0闪烁,提示程序运行。
2. 硬件资源
1)LED灯
LED0 - PB4
LED1 - PB5
2)PVD(可编程电压监测器)
3)正点原子2.8/3.5/4.3/7寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)
3. 原理图
PVD属于STM32H562的内部资源,只需要软件设置好即可正常工作。我们通过LED1和LCD来指示进入PVD中断的情况。
27.2.3 程序设计
27.2.3.1 PWR的HAL库驱动
PWR在HAL库中的驱动代码在stm32h5xx_hal_pwr.c和stm32h5xx_hal_pwr_ex.c文件(及其头文件)中。
1. 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_VMCR寄存器的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中编写,详见本例程源码。
27.2.3.2 程序流程图
图27.2.3.2.1 PVD电压监控实验程序流程图
27.2.3.3 程序解析
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。PWR源码包括两个文件:pwr.c和pwr.h。
pwr.h头文件只有函数声明,下面直接开始介绍pwr.c的程序,首先是PVD初始化函数。
- /**
- * @brief 初始化PVD电压监视器
- * [url=home.php?mod=space&uid=271674]@param[/url] 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脚上的电压
- * @retval 无
- */
- void pwr_pvd_init(uint32_t pls)
- {
- PWR_PVDTypeDef pwr_pvd = {0};
- pwr_pvd.PVDLevel = pls; /* 检测电压级别 */
- pwr_pvd.Mode = PWR_PVD_MODE_IT_RISING_FALLING;/* 使用中断线上升沿和下降沿触发 */
- HAL_PWR_ConfigPVD(&pwr_pvd);
- HAL_NVIC_SetPriority(PVD_AVD_IRQn, 3 ,3);
- HAL_NVIC_EnableIRQ(PVD_AVD_IRQn);
- HAL_PWR_EnablePVD(); /* 使能PVD检测 */
- }
复制代码 这里需要注意的是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;
- HAL_Init(); /* HAL库初始化 */
- HAL_ICACHE_Enable(); /* 使能指令缓存 */
- sys_stm32_clock_init(250, 4, 2, 2); /* 系统时钟初始化, 250Mhz */
- delay_init(250); /* 延时初始化 */
- usart_init(115200); /* 串口初始化 */
- 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++;
- }
- }
复制代码 这里我们选择PVD的检测电压阀值为2.7V,其它的代码很好理解,最后下载验证一下。
27.2.4 下载验证
下载代码后,默认LCD屏会显示"PVD Voltage OK!",当供电电压过低,则LED1会点亮,并且LCD屏会显示PVD Low Voltage!。当开发板供电正常,LED1会熄灭,LCD屏会继续显示"PVD Voltage OK!"。
27.3 睡眠模式实验
本小节我们来学习睡眠模式实验,该部分的知识点内容请回顾27.1.3电源管理。我们直接从寄存器介绍开始。
27.3.1 EXTI寄存器
本实验我们用外部中断来唤醒睡眠模式。我们用到WFI指令进入睡眠模式(参考27.1.3小节)。下面看看我们要用到的EXTI寄存器。
EXTI中断屏蔽寄存器(EXTI_IMR1)
EXTI中断屏蔽寄存器描述如图27.3.1.1所示:
图27.3.1.1 EXTI_IMR1寄存器
本实验使用到WK_UP(PA0,即EXTI0线)按键唤醒睡眠模式,所以IM0位要置1,即取消屏蔽EXTI0线的中断请求。
EXTI上升沿触发选择寄存器(EXTI_RTSR1)
EXTI上升沿触发选择寄存器描述如图27.3.1.2所示:
图27.3.1.2 EXTI_RTSR1寄存器
我们要使用到EXTI0线中断,并且是上升沿触摸,所以RT0位要置1。
EXTI上升沿挂起寄存器(EXTI_RPR1)
EXTI挂起寄存器描述如图27.3.1.3所示:
图27.3.1.3 EXTI_RPR1寄存器
EXTI上升沿挂起寄存器EXTI_RPR1管理的是EXTI0线到EXTI16线上升沿的中断标志。发生EXTI0上升沿触发请求后,位RPIF0被置1,我们需要给位RPIF0写1来清零该标志位。
27.3.2 硬件设计
1. 例程功能
1,按下按键KEY0后,LED1点亮,提示进入睡眠模式,此时LED0不再闪烁,说明已经进入睡眠模式。
2,按下按键WK_UP后,LED1熄灭,提示退出睡眠模式,此时LED0继续闪烁,说明已经退出睡眠模式。
3,LED0闪烁指示系统正在运行。
2. 硬件资源
1)LED灯
LED0 - PB4
LED1 - PB5
2)独立按键 KEY0 - PA1,WK_UP - PA0
3)电源管理(低功耗模式 - 睡眠模式)
4)正点原子2.8/3.5/4.3/7寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)
3. 原理图
PWR属于STM32H562的内部资源,只需要软件设置好即可正常工作。我们通过KEY0让CPU进入睡眠模式,再通过WK_UP 触发EXTI中断来唤醒CPU。LED0指示程序是否执行,LED1指示CPU是否进入睡眠模式。
27.3.3 程序设计
27.3.3.1 PWR的HAL库驱动
1. HAL_PWR_EnterSLEEPMode函数
进入睡眠模式函数,其声明如下:
- void HAL_PWR_EnterSLEEPMode (uint32_t Regulator, uint8_t SLEEPEntry);
复制代码 函数描述:
用于设置CPU进入睡眠模式。
函数形参:
形参1指定稳压器的状态。有两个选择,PWR_MAINREGULATOR_ON表示稳压器处于主模式,PWR_LOWPOWERREGULATOR_ON表示稳压器处于低功耗模式。通过查看HAL库源码会发现,该形参在该函数中没有实质用处。
形参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按键触发外部中断唤醒睡眠模式。
27.3.3.2 程序流程图
图27.3.3.2.1 睡眠模式实验程序流程图
27.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:中断线引脚
- * [url=home.php?mod=space&uid=60778]@note[/url] 此函数会被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函数取消屏蔽对应的外部中断线位。本实验只是验证进入和退出唤醒睡眠模式,所以HAL_GPIO_EXTI_Callback回调函数可以什么都不用做。
最后在main.c里面编写如下代码:
- int main(void)
- {
- uint8_t t = 0;
- uint8_t key = 0;
- HAL_Init(); /* HAL库初始化 */
- HAL_ICACHE_Enable(); /* 使能指令缓存 */
- sys_stm32_clock_init(250, 4, 2, 2); /* 系统时钟初始化, 250Mhz */
- delay_init(250); /* 延时初始化 */
- usart_init(115200); /* 串口初始化 */
- led_init(); /* LED初始化 */
- key_init(); /* 按键初始化 */
- lcd_init(); /* LCD初始化 */
- 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(); /* 进入睡眠模式 */
- LED1(1); /* 关闭绿灯,提示退出睡眠模式 */
- }
- if ((t % 20) == 0)
- {
- LED0_TOGGLE(); /* 每200ms,翻转一次LED0 */
- }
- delay_ms(10);
- t++;
- }
- }
复制代码 该部分程序功能是:按下KEY0后,点亮LED1、暂停滴答时钟并进入睡眠模式,然后一直等待外部中断唤醒。当按下按键WK_UP,就触发外部中断,睡眠模式就被唤醒,关闭LED1,提示推出睡眠模式,然后继续执行后面的程序。
27.3.4 下载验证
下载代码后,LED0闪烁,表明代码正在运行。按下按键KEY0后,LED1点亮,提示进入睡眠模式,此时LED0不再闪烁,说明已经进入睡眠模式。此时按下按键WK_UP后,LED1熄灭,提示退出睡眠模式,此时LED0继续闪烁,说明已经退出睡眠模式。
27.4 停止模式实验
本小节我们来学习停止模式实验,该部分的知识点内容请回顾27.1.3电源管理。我们直接从寄存器介绍开始。
27.4.1 PWR寄存器
本实验我们用到外部中断来唤醒停止模式。我们用到WFI指令进入睡眠模式(参考27.1.3小节)。下面看看我们要用到的EXTI寄存器,这里主要介绍PWR_PMCR寄存器。
PWR功耗模式控制寄存器 (PWR_PMCR)
PWR的功耗模式控制寄存器1描述如图27.4.1.1所示:
图27.4.1.1 PWR_PMCR寄存器
该寄存器主要用于配置停止模式的,大家可以参考参考手册。本实验中,该寄存器的值都采用复位后的默认值就可以了。
27.4.2 硬件设计
1. 例程功能
1,按下按键KEY0后,LED1点亮,提示进入停止模式,此时LED0不再闪烁,说明已经进入停止模式。
2,按下按键WK_UP后,LED1熄灭,提示退出停止模式,此时LED0继续闪烁,说明已经退出停止模式。
3,LED0闪烁指示系统正在运行。
2. 硬件资源
1)LED灯 LED0 - PB4,LED1 - PB5
2)独立按键 KEY0 - PA1,WK_UP - PA0
3)电源管理(低功耗模式–停止模式)
4)正点原子2.8/3.5/4.3/7寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)
3. 原理图
PWR属于STM32H562的内部资源,只需要软件设置好即可正常工作。我们通过KEY0让CPU进入停止模式,再通过WK_UP 触发EXTI中断来唤醒CPU。LED0指示程序是否执行,LED1指示CPU是否进入停止模式。
27.4.3 程序设计
27.4.3.1 PWR的HAL库驱动
1. HAL_PWR_EnterSTOPMode函数
进入停止模式函数,其声明如下:
- void HAL_PWR_EnterSTOPMode (uint32_t Regulator, uint8_t STOPEntry);
复制代码 函数描述:
用于设置CPU进入停止模式。
函数形参:
形参1指定稳压器在停止模式下的状态。有两个选择,PWR_MAINREGULATOR_ON表示稳压器处于主模式,PWR_LOWPOWERREGULATOR_ON表示稳压器处于低功耗模式。
形参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停止模式
通过按下KEY0,调用HAL_PWR_EnterSTOPMode函数进入停止模式。
3)通过按下按键触发外部中断唤醒睡眠模式
通过按下WK_UP,触发外部中断唤醒停止模式。
27.4.3.2 程序流程图
图27.4.3.2.1 停止模式实验程序流程图
27.4.3.3 程序解析
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。PWR驱动源码包括两个文件:pwr.c和pwr.h。
首先看pwr.h头文件,因为我们还是用到WK_UP(PA0)对应的外部中断线来唤醒停止模式,pwr.h头文件的WK_UP按键对应的宏定义我们也是用到的,上个实验已经讲过,这里不再赘述。下面介绍pwr.c的代码,WK_UP按键的相关函数我们还是用上个实验的。我们主要介绍进入停止模式这个函数,其定义如下:
- /**
- * @brief 进入停止模式
- * @param 无
- * @retval 无
- */
- void pwr_enter_stop(void)
- {
- HAL_SuspendTick(); /* 暂停滴答时钟,防止通过滴答时钟中断唤醒 */
- /* 当进入停止模式时,设置稳压器为低功耗模式,等待中断唤醒 */
- HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
- }
复制代码 该函数主要是调用HAL_PWR_EnterSTOPMode函数进入停止模式。
最后在main.c里面编写如下代码:
- int main(void)
- {
- uint8_t t = 0;
- uint8_t key = 0;
- HAL_Init(); /* HAL库初始化 */
- HAL_ICACHE_Enable(); /* 使能指令缓存 */
- sys_stm32_clock_init(250, 4, 2, 2); /* 系统时钟初始化, 250Mhz */
- delay_init(250); /* 延时初始化 */
- usart_init(115200); /* 串口初始化 */
- led_init(); /* LED初始化 */
- key_init(); /* 按键初始化 */
- lcd_init(); /* LCD初始化 */
- 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(); /* 进入停止模式 */
- /* 从停止模式唤醒, 需要重新设置系统时钟, 250Mhz */
- sys_stm32_clock_init(250, 4, 2, 2);
- LED1(1); /* 关闭绿灯,提示退出停止模式 */
- }
- if ((t % 20) == 0)
- {
- LED0_TOGGLE(); /* 每200ms,翻转一次LED0 */
- }
- delay_ms(10);
- t++;
- }
- }
复制代码 该部分程序,功能就是按下KEY0后,点亮LED1、暂停滴答时钟并进入停止模式,然后一直等待外部中断唤醒。当按下按键WK_UP,就触发外部中断,停止模式就被唤醒,然后继续执行后面的程序,重新设置系统时钟250MHz,关闭LED1等。
27.4.4 下载验证
下载代码后,LED0闪烁,表明代码正在运行。按下按键KEY0后,LED1点亮,提示进入停止模式,此时LED0不再闪烁,说明已经进入停止模式。按下按键WK_UP后,LED1熄灭,提示退出停止模式,此时LED0继续闪烁,说明已经退出停止模式。
27.5 待机模式实验
本小节我们来学习待机模式实验,该部分的知识点内容请回顾27.1.3电源管理。我们直接从寄存器介绍开始。
27.5.1 PWR寄存器
本实验是先对相关的电源控制寄存器配置待机模式的参数,然后通过WFI指令进入待机模式,使用特定唤醒源WKUP引脚来唤醒待机模式。
下面主要介绍本实验用到的几个寄存器。
PWR唤醒配置寄存器(PWR_WUCR)
PWR唤醒配置寄存器描述如图27.5.1.1所示:
图27.5.1.1 PWR_WUCR寄存器
本实验,我们使用EXTI0线(PA0)的上升沿触发唤醒待机模式,因此,对于PWR_WUCR寄存器,我们需要设置的位如下:
1,设置WUPEN1位为1,使能WKUP1的唤醒功能。
2,设置WUPP1位为0,选择上升沿唤醒。
3,设置WUPPUPD1[1:0]位为2,选择下拉,以保持PA0脚的低电平状态。
设置好PWR_WUCR寄存器后,就可以通过PA0引脚的上升沿唤醒待机模式了。
PWR状态寄存器(PWR_PMSR)
PWR状态寄存器描述如图27.5.1.2所示:
图27.5.1.2 PWR_PMSR寄存器
该寄存器我们只关心SBF位,系统进入待机模式后,由硬件将该位置1。
PWR状态寄存器(PWR_PMCR)
PWR状态寄存器描述如图27.5.1.3所示:
图27.5.1.3 PWR_PMSR寄存器
CSSF位,系统进入待机模式后,可以通过对该位写1,清除SBF位的标记。
LPMS位,将该位置1,允许进入待机模式。
27.5.2 硬件设计
1. 例程功能
1,按下按键KEY0后,LCD屏熄灭,LED0不再闪烁,说明已经进入待机模式。
2,按下按键WK_UP后,LCD屏点亮,LED0继续闪烁,说明已经退出待机模式。
3,LED0闪烁指示系统正在运行。
2. 硬件资源
1)LED灯
LED0 - PB4
2)独立按键
KEY0 - PA1
WK_UP - PA0
3)电源管理(低功耗模式–待机模式)
4)正点原子2.8/3.5/4.3/7寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)
3. 原理图
PWR属于STM32H562的内部资源,只需要软件设置好即可正常工作。我们通过KEY0让CPU进入待机模式,再通过WK_UP上升沿触发唤醒CPU。LED0指示程序是否执行。
27.5.3 程序设计
27.5.3.1 PWR的HAL库驱动
1. HAL_PWR_EnableWakeUpPin函数
使能唤醒引脚函数,其声明如下:
- void HAL_PWR_EnableWakeUpPin (uint32_t WakeUpPinPolarity);
复制代码 函数描述:
用于使能设置唤醒引脚,实际上该函数设置PWR唤醒配置寄存器(PWR_WUCR)。
函数形参:
形参1取值范围:PWR_WAKEUP_PIN1_HIGH~ PWR_WAKEUP_PIN8_HIGH、PWR_WAKEUP_PIN1_LOW~ PWR_WAKEUP_PIN8_LOW。
函数返回值:无
注意事项:
禁止某个唤醒引脚使用的函数如下:
- void HAL_PWR_DisableWakeUpPin (uint32_t WakeUpPinPolarity);
复制代码
2. HAL_PWR_EnterSTANDBYMode函数
进入待机模式函数,其声明如下:
- void HAL_PWR_EnterSTANDBYMode (void);
复制代码 函数描述:
用于使CPU进入待机模式。
函数形参:无
函数返回值:无
待机模式配置步骤
1)进入CPU待机模式
在进入待机模式之前我们需要做一些准备,比如:关闭RTC相关中断、清除RTC相关中断标志位等一些操作,只是防止RTC中断唤醒。这里就不细讲,详见本例程源码。
通过__HAL_PWR_CLEAR_FLAG函数清除唤醒标志位。
通过HAL_PWR_EnableWakeUpPin函数使能PA0的唤醒功能。
通过HAL_PWR_EnterSTANDBYMode函数进入待机模式。
2)通过按下WKUP引脚上升沿触发唤醒待机模式
在本实验中,通过按下KEY0按键进入待机模式,然后通过按下WK_UP按键(特定唤醒源)触发唤醒待机模式。
27.5.3.2 程序流程图
图27.4.3.2.1 待机模式实验程序流程图
27.5.3.3 程序解析
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。PWR驱动源码包括两个文件:pwr.c和pwr.h。
下面直接介绍pwr.c里面的进入待机模式函数,其定义如下:
- /**
- * @brief 进入待机模式
- * @param 无
- * @retval 无
- */
- void pwr_enter_standby(void)
- {
- __HAL_RCC_GPIOA_CLK_ENABLE();
- __HAL_GPIO_EXTI_CLEAR_IT(PWR_WKUP_GPIO_PIN); /* 清除WKUP上的中断标志位 */
- HAL_PWR_DisableWakeUpPin(PWR_WAKEUP_PIN1_HIGH); /* 禁止唤醒 */
- __HAL_RCC_BACKUPRESET_FORCE(); /* 复位备份区域 */
- HAL_PWR_EnableBkUpAccess(); /* 后备区域访问使能 */
- if (__HAL_PWR_GET_FLAG(PWR_FLAG_SBF) != RESET)
- {
- __HAL_PWR_CLEAR_FLAG(PWR_FLAG_SBF); /* 清除待机模式标志位 */
- /* 检测和清除PWR_WAKEUP_FLAG1标志位 */
- if (__HAL_PWR_GET_FLAG(PWR_WAKEUP_FLAG1) != RESET)
- {
- __HAL_PWR_CLEAR_FLAG(PWR_WAKEUP_FLAG1);
- }
- }
- __HAL_RTC_WRITEPROTECTION_DISABLE(&g_rtc_handle); /* 关闭RTC写保护 */
- /* 关闭RTC相关中断,可能在RTC实验打开了 */
- __HAL_RTC_WAKEUPTIMER_DISABLE_IT(&g_rtc_handle, RTC_IT_WUT);
- __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); /* 使能KEY_UP引脚的唤醒功能 */
- HAL_PWR_EnterSTANDBYMode(); /* 进入待机模式 */
- }
复制代码 该函数主要作用是关闭RTC相关中断,清除RTC相关中断标志位。然后使能WKUP引脚上升沿来唤醒待机模式。最后调用HAL_PWR_EnterSTANDBYMode函数进入待机模式。
最后在main.c里面编写如下代码:
- int main(void)
- {
- uint8_t t = 0;
- uint8_t key = 0;
- HAL_Init(); /* HAL库初始化 */
- HAL_ICACHE_Enable(); /* 使能指令缓存 */
- sys_stm32_clock_init(250, 4, 2, 2); /* 系统时钟初始化, 250Mhz */
- delay_init(250); /* 延时初始化 */
- usart_init(115200); /* 串口初始化 */
- led_init(); /* LED初始化 */
- key_init(); /* 按键初始化 */
- lcd_init(); /* LCD初始化 */
- 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按键进行唤醒。注意待机模式唤醒后,系统会进行复位。
27.5.4 下载验证
下载代码后,LED0闪烁,表明代码正在运行。按下按键KEY0后,TFTLCD屏熄灭,此时LED0不再闪烁,说明已经进入待机模式。按下按键WK_UP后,TFTLCD屏点亮,此时LED0继续闪烁,说明系统从待机模式中唤醒相当于复位。
|
|