正点原子STM32F1系列学习笔记之低功耗(HAL库)

一、电源系统结构

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

image-20230919192009059

划分了 3 个区域①②③,分别是独立的 A/D 转换器供电和参考电压、电压调节器、电池备份区域。

① 独立的 A/D 转换器供电和参考电压(VDDA供电区域)

VDDA 供电区域,主要是 ADC 电源以及参考电压, STM32 的 ADC 模块配备独立的供电方式,使用了 VDDA 引脚作为输入,使用 VSSA引脚作为独立地连接, VREF 引脚为提供给 ADC 的参考电压。(只有引脚100脚以上的stm32芯片才会引出VREF

给模拟外设独立供电,可以提高转换精度。

② 电压调节器(VDD /1.8V 供电区域)

电压调节器是 STM32 的电源系统中最核心部分,连接 VDD 供电区域和 1.8 供电区域。 VDD供电来自于 VSS 和 VDD,给 I/O 电路以及待机电路供电,电压调节器主要为备份域以及待机电路以外的所有数字电路供电,其中包括内核、数字外设(IO、定时器等)以及RAM,调节器的输出电压约为1.8V,因此由调压器供电的区域称为 1.8V 供电区域。(电压调节器为1.8V供电区域供电,且1.8V供电区域是电源系统中最主要的部分)

电压调节器根据应用方式不同有三种不同的工作模式。在运行模式下,调节器以正常工作模式为内核、内存和外设提供 1.8V; 在停止模式下,调节器以低功耗模式提供 1.8V 电源,以保存寄存器和 SRAM 的内容。在待机模式下,调节器停止供电,除了备用电路和备份域外,寄存器和 SRAM 的内容全部丢失。

③ 电池备份区域(后备供电区域)

电池备份区域也就是后备供电区域,使用电池或者其他电源连接到 VBAT 脚上,当 VDD 断电时,可以保存备份寄存器的内容和维持 RTC 的功能。同时 VBAT 引脚也为 RTC 和 LSE 振荡器供电,这保证了当主要电源被切断时, RTC 能够继续工作。切换到 VBAT 供电由复位模块中的掉电复位功能控制。(两种供电方式:VBAT和VDD。主要电源被切断,该区域还能工作)

二、电源监控

电源监控的部分我们主要关注 PVD 监控器,此外还需要知道上电复位(POR)/掉电复位(PDR)。其他部分的内容请大家查看《STM32F10xxx 参考手册_V10(中文版) .pdf》第 4.2 节(38页)。

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

上电时,当 VDD 低于指定 VPOR 阈值时,系统无需外部复位电路便会保持复位模式。一旦VDD 电源电压高于 VPOR 阈值,系统便会退出复位状态,芯片正常工作。掉电时,当 VDD 低于指定 VPDR阈值时,系统就会保持复位模式。如图所示, RESET 为上电复位信号。

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

image-20230919225247679
可编程电压检测器(PVD)

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

下面介绍可编程电压检测器(PVD),它可以实时监视 VDD 的电压,方法是将 VDD与 PWR控制寄存器(PWR_CR)中的 PLS[2:0]位所选的 VPVD 阈值进行比较。其中 PWR_CSR 寄存器中的 PVDO 位决定了 VDD 是高于 VPVD 还是低于 VPVD,本实验中配置的是 VDD 低于 VPVD 阈值这个条件。当检测到电压低于 VPVD 阈值时,如果使能 EXTI16 线中断,即使能 PVD 中断,可以产生 PVD 中断,具体取决于 EXTI16 线配置为检测上升还是下降沿,然后在复位前,在中断服务程序中执行紧急关闭系统等任务。 PVD 阀值检测波形,如图所示

image-20230919230428460

PVD 阀值有 8 个等级,有上升沿和下降沿的区别,具体由 PWR_CSR 寄存器中的 PVDO 位决定。 PVD 阈值等级表具体如表所示

image-20230919230609158
三、低功耗模式介绍

在 STM32 的正常工作中,具有四种工作模式,运行、睡眠、停止以及待机。在上电复位后, STM32 处于运行状态时,当内核不需要继续运行,就可以选择进入后面的三种模式降低功耗。这三种低功耗模式电源消耗不同、唤醒时间不同和唤醒源不同,我们要根据自身的需要选择合适的低功耗模式。

睡眠模式

进入睡眠模式, Cortex_M3 内核停止,所有外设包括 Cortex_M3 核心的外设,如 NVIC、系统时钟(SysTick)等仍在运行,有两种进入睡眠模式的命令WFI 和 WFE。 WFI(Wait for interrupt)和 WFE(Wait for event)是内核指令, 会调用一些汇编指令,我们会使用即可,更详细的描述可以查看《CM3 权威指南》。 睡眠后唤醒的方式即由等待“中断”唤醒和“事件”唤醒。

image-20230919194618440
image-20230919200827019
停止模式

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

值得注意的是:当电压调节器处于低功耗模式下,当系统从停止模式退出时,将会有一段额外的启动延时。如果在停止模式期间保持内部调节器开启,则退出启动时间会缩短,但相应的功耗会增加。

image-20230919195111076
image-20230919200921705
待机模式

待机模式可实现最低功耗。该模式是在 CM3 深睡眠模式时关闭电压调节器,整个 1.8V 供电区域被断电。 PLL、 HSI 和 HSE 振荡器也被断电。除备份域(RTC 寄存器、 RTC 备份寄存器和备份 SRAM)和待机电路中的寄存器外, SRAM 和其他寄存器内容都将丢失。不过如果我们使能了备份区域(备份 SRAM、 RTC、 LSE),那么待机模式下的功耗,将达到 3.8uA 左右。

image-20230919195243958
image-20230919200958251

三种模式比较:

image-20230919200004502

三种模式的功耗:

同等条件下(T = 25°C,VDD = 3.3V,系统时钟72MHz)

模式主要影响唤醒时间供应电流**(典型值)**
正常模式所有外设正常工作051mA
睡眠模式CPU时钟关闭1.8us29.5mA
停止模式1.8V区域时钟关闭, 电压调节器低功耗5.4us35uA
待机模式1.8V区域时钟关闭, 电压调节器关闭50us3.8uA
四、低功耗相关寄存器
寄存器名称作用
SCB_SCR系统控制寄存器选择休眠和深度休眠模式,用于其他低功耗特性的控制
PWR_CR电源控制寄存器可以设置低功耗相关(清除标记位、模式)
PWR_CSR电源控制/状态寄存器用于查看系统当前状态(待机/唤醒标志 使能唤醒引脚)
系统控制寄存器(SCB_SCR)
image-20230919201709795

该寄存器存在于 ARM 内核中, 详细描述可查阅《Cortex-M3 权威指南》。使用睡眠模式时,需要把 SLEEPDEEP 位置 0,之后调用WFI/WFE指令便可进入睡眠模式。使用停止模式或者待机模式需要把 SLEEPDEEP 位置 1。

电源控制寄存器PWR_CR
image-20230919202657145

停止模式:PDDS清0;LPDS选调节器模式

待机模式:PDDS置1;清除唤醒位 CWUF置1

电源控制/状态寄存器(PWR_CSR)
image-20230919222227782

待机模式下 ,使用WKUP引脚唤醒并需要清除WUF标记位

WFI命令和WFE命令
image-20230919222538533

内核指令,使用函数的格式“__WFI()”和“__WFE()”来调用。__wfi和__wfe是编译器内置的函数,函数内部调用了相对应的汇编指令。更详细的描述参考《CM3权威指南》

五、PVD 电压监控实验
功能

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

注:PVD 属于 STM32F103 的内部资源,只需要软件设置好即可正常工作。

涉及的寄存器

使用电源控制寄存器PWR_CR的PLS[7:5]和PVDE位

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

EXTI 中断屏蔽寄存器(EXTI_IMR):

image-20230922185504748

我们要使用到 EXTI16 线中断,所以 MR16 位要置 1,即开放来自 EXTI16 线的中断请求

EXTI 上升沿触发选择寄存器(EXTI_RTSR):

image-20230922185616380

EXTI 下降沿触发选择寄存器(EXTI_FTSR):

image-20230922185653662

EXTI 挂起寄存器(EXTI_PR) :

image-20230922185743882

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

相关HAL 库驱动

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_CR 寄存器的 PLS 位的设置,取值范围PWR_PVDLEVEL_0 到 PWR_PVDLEVEL_7

,共八个级别。

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

PVD 电压监控配置步骤

1)配置 PVD, 使能 PVD 时钟。

调用 HAL_PWR_ConfigPVD 函数配置 PVD,包括检测电压级别、使用中断线触发方式等。

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

通过 HAL_PWR_EnablePVD 函数使能 PVD 检测。通过 HAL_NVIC_EnableIRQ 函数使能 PVD 中断。通过 HAL_NVIC_SetPriority

函数设置中断优先级。

3)编写中断服务函数。

PVD 中断服务函数为 PVD _IRQHandler,当发生中断的时候,程序就会执行中断服务函数。

HAL 库有专门的 PVD 中断处理函数,我们只需要在 PVD 中断服务函数里面调用HAL_PWR_PVD_IRQHandler() 函 数 , 然 后 逻 辑 代 码 在 PVD 中 断 服 务 回 调 函 数HAL_PWR_PVDCallback 中编写

实验源码

pwr.h


#ifndef __PWR_H
#define __PWR_H

#include "./SYSTEM/sys/sys.h"


void pwr_pvd_init(uint32_t pls);     /* PVD电压检测初始化函数 */

#endif

pwr.c

#include "./BSP/PWR/pwr.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"


/**
 * @brief       初始化PVD电压监视器
 * @param       pls: 电压等级(PWR_PVD_detection_level)
 *   @arg       PWR_PVDLEVEL_0,2.2V;
 *   @arg       PWR_PVDLEVEL_1,2.3V;
 *   @arg       PWR_PVDLEVEL_2,2.4V;
 *   @arg       PWR_PVDLEVEL_3,2.5V;
 *   @arg       PWR_PVDLEVEL_4,2.6V;
 *   @arg       PWR_PVDLEVEL_5,2.7V;
 *   @arg       PWR_PVDLEVEL_6,2.8V;
 *   @arg       PWR_PVDLEVEL_7,2.9V;
 * @retval      无
 */
void pwr_pvd_init(uint32_t pls)
{
    PWR_PVDTypeDef pwr_pvd = {0};

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

    HAL_NVIC_SetPriority(PVD_IRQn, 3 ,3);
    HAL_NVIC_EnableIRQ(PVD_IRQn);
    HAL_PWR_EnablePVD();                             /* 使能PVD检测 */
}

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

/**
 * @brief       PVD中断服务回调函数
 * @param       无
 * @retval      无
 */
void HAL_PWR_PVDCallback(void)
{
    if (__HAL_PWR_GET_FLAG(PWR_FLAG_PVDO)) /* 电压比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);                                                         /* 灭掉绿灯 */
    }
}

main.c


#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./USMART/usmart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/PWR/pwr.h"


int main(void)
{
    uint8_t t = 0;

    HAL_Init();                         /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    delay_init(72);                     /* 延时初始化 */
    usart_init(115200);                 /* 串口初始化为115200 */
    led_init();                         /* 初始化LED */
    lcd_init();                         /* 初始化LCD */
    pwr_pvd_init(PWR_PVDLEVEL_7);       /* PVD 2.9V检测 */

    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++;
    }
}
下载验证

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

正常情况下LCD显示"PVD Voltage OK!"

六、睡眠模式实验
功能

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

本实验我们用到外部中断来唤醒睡眠模式。进入睡眠模式很简单,直接调用内核指令 WFI 即可进入。用外部中断唤醒,就要在进入睡眠模式前,先对外部中断进行配置。例如,使能中断线( EXTI_IMR),使用何种触发模式(EXTI_FTSR/EXTI_RTSR)。当中断触发(即 EXTI_PR 中某位能查询到 1),跳转到中断服务函数里,最后还得手动清除该标记即(EXTI_PR 中对某位进行置 1 处理)。

涉及的寄存器

对于EXTI_IMR:本实验使用 WK_UP(PA0)唤醒,即 EXTI0 线中断,所以在外部中断服务函数要把 MR0位置 1。

对于EXTI_RTSR:我们要使用到 EXTI0 线中断,所以 TR0 位要置 1,即 EXTI0 使用的是上升沿进行触发

对于EXTI_PR:在 EXTI0 中断服务函数里面,需要清除 EXTI0 中断标记,即对 PR0 位写 1

相关HAL 库驱动

void HAL_PWR_EnterSLEEPMode (uint32_t Regulator, uint8_t SLEEPEntry); 用于设置 CPU 进入睡眠模式

形参 1 指定稳压器的状态。 有两个选择, PWR_MAINREGULATOR_ON 表示稳压器处于正常模式, PWR_LOWPOWERREGULATOR_ON 表示稳压器处于低功耗模式。对应的是 PWR_CR 寄存器的 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_PWR_EnterSLEEPMode 函数进入睡眠模式。

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

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

实验源码

pwr.h

#ifndef __PWR_H
#define __PWR_H

#include "./SYSTEM/sys/sys.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)   /* PA口时钟使能 */
  
#define PWR_WKUP_INT_IRQn               EXTI0_IRQn
#define PWR_WKUP_INT_IRQHandler         EXTI0_IRQHandler

/******************************************************************************************/

void pwr_pvd_init(uint32_t pls); /* PVD电压检测初始化函数 */
void pwr_wkup_key_init(void);    /* 唤醒按键初始化 */
void pwr_enter_sleep(void);      /* 进入睡眠模式 */

#endif

pwr.c

#include "./BSP/PWR/pwr.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"


/**
 * @brief       初始化PVD电压监视器
 * @param       pls: 电压等级(PWR_PVD_detection_level)
 *   @arg       PWR_PVDLEVEL_0,2.2V;
 *   @arg       PWR_PVDLEVEL_1,2.3V;
 *   @arg       PWR_PVDLEVEL_2,2.4V;
 *   @arg       PWR_PVDLEVEL_3,2.5V;
 *   @arg       PWR_PVDLEVEL_4,2.6V;
 *   @arg       PWR_PVDLEVEL_5,2.7V;
 *   @arg       PWR_PVDLEVEL_6,2.8V;
 *   @arg       PWR_PVDLEVEL_7,2.9V;
 * @retval      无
 */
void pwr_pvd_init(uint32_t pls)
{
    PWR_PVDTypeDef pwr_pvd = {0};

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

    HAL_NVIC_SetPriority(PVD_IRQn, 3 ,3);
    HAL_NVIC_EnableIRQ(PVD_IRQn);
    HAL_PWR_EnablePVD();                             /* 使能PVD检测 */
}

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

/**
 * @brief       PVD中断服务回调函数
 * @param       无
 * @retval      无
 */
void HAL_PWR_PVDCallback(void)
{
    if (__HAL_PWR_GET_FLAG(PWR_FLAG_PVDO)) /* 电压比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);                                                         /* 灭掉绿灯 */
    }
}

/***********************************睡眠模式实验程序*******************************************/

/**
 * @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_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);
}

/**
 * @brief       WK_UP按键 外部中断服务程序
 * @param       无
 * @retval      无
 */
void PWR_WKUP_INT_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(PWR_WKUP_GPIO_PIN);
}

/**
 * @brief       进入CPU睡眠模式
 * @param       无
 * @retval      无
 */
void pwr_enter_sleep(void)
{
    HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI); /* 执行WFI指令, 进入睡眠模式 */
}

/**
 * @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()函数已经为我们清除了中断标志位,所以我们进了回调函数可以不做任何事 */
    }
}

main.c

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./USMART/usmart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./BSP/PWR/pwr.h"

int main(void)
{
    uint8_t t = 0;
    uint8_t key = 0;

    HAL_Init();                         /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    delay_init(72);                     /* 延时初始化 */
    usart_init(115200);                 /* 串口初始化为115200 */
    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();          /* 进入睡眠模式 */

            LED1(1);                    /* 关闭绿灯,提示退出睡眠模式 */
        }

        if ((t % 20) == 0)
        {
            LED0_TOGGLE();              /* 每200ms,翻转一次LED0 */
        }

        delay_ms(10);
        t++;
    }
}
下载验证

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

七、停止模式实验
功能

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

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

涉及的寄存器

对于PWR_CR:通过 PDDS 位选择进入停止模式还是待机模式,停止模式即对 PDDS 位置 0 即可。在停止模式下,电压调节器有两种模式:开启或者低功耗,选择低功耗模式,即 LPDS 置 1

对于SCB_SCR:该寄存器存在于 ARM 内核中, 详细描述可查阅《Cortex-M3 权威指南》。在本实验中,我们需要把 SLEEPDEEP 位置 1,这样子后面调用 WFI 命令时,进入的就是停止模式了。在唤醒后,需要清除 SLEEPDEEP 位,进行置 0

相关HAL 库驱动

void HAL_PWR_EnterSTOPMode (uint32_t Regulator, uint8_t STOPEntry); 用于设置 CPU 进入停止模式

形参 1 指定稳压器在睡眠模式下的状态。 有两个选择, PWR_MAINREGULATOR_ON 表示稳压器处于正常模式, PWR_LOWPOWERREGULATOR_ON 表示稳压器处于低功耗模式。对应的是 PWR_CR 寄存器的 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 停止模式

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

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

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

实验源码

pwr.h

#ifndef __PWR_H
#define __PWR_H

#include "./SYSTEM/sys/sys.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)   /* PA口时钟使能 */
  
#define PWR_WKUP_INT_IRQn               EXTI0_IRQn
#define PWR_WKUP_INT_IRQHandler         EXTI0_IRQHandler

/******************************************************************************************/

void pwr_pvd_init(uint32_t pls); /* PVD电压检测初始化函数 */
void pwr_wkup_key_init(void);    /* 唤醒按键初始化 */
void pwr_enter_sleep(void);      /* 进入睡眠模式 */
void pwr_enter_stop(void);      /* 进入停止模式 */

#endif

pwr.c

#include "./BSP/PWR/pwr.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"


/**
 * @brief       初始化PVD电压监视器
 * @param       pls: 电压等级(PWR_PVD_detection_level)
 *   @arg       PWR_PVDLEVEL_0,2.2V;
 *   @arg       PWR_PVDLEVEL_1,2.3V;
 *   @arg       PWR_PVDLEVEL_2,2.4V;
 *   @arg       PWR_PVDLEVEL_3,2.5V;
 *   @arg       PWR_PVDLEVEL_4,2.6V;
 *   @arg       PWR_PVDLEVEL_5,2.7V;
 *   @arg       PWR_PVDLEVEL_6,2.8V;
 *   @arg       PWR_PVDLEVEL_7,2.9V;
 * @retval      无
 */
void pwr_pvd_init(uint32_t pls)
{
    PWR_PVDTypeDef pwr_pvd = {0};

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

    HAL_NVIC_SetPriority(PVD_IRQn, 3 ,3);
    HAL_NVIC_EnableIRQ(PVD_IRQn);
    HAL_PWR_EnablePVD();                             /* 使能PVD检测 */
}

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

/**
 * @brief       PVD中断服务回调函数
 * @param       无
 * @retval      无
 */
void HAL_PWR_PVDCallback(void)
{
    if (__HAL_PWR_GET_FLAG(PWR_FLAG_PVDO))   /* 电压比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);                                                        /* 灭掉绿灯 */
    }
}

/***********************************睡眠模式实验程序*******************************************/

/**
 * @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_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);
}

/**
 * @brief       WK_UP按键 外部中断服务程序
 * @param       无
 * @retval      无
 */
void PWR_WKUP_INT_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(PWR_WKUP_GPIO_PIN);
}

/**
 * @brief       进入CPU睡眠模式
 * @param       无
 * @retval      无
 */
void pwr_enter_sleep(void)
{
    HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI); /* 执行WFI指令, 进入待机模式 */
}

/**
 * @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()函数已经为我们清除了中断标志位,所以我们进了回调函数可以不做任何事 */
    }
}

/***********************************停止模式实验程序*******************************************/

/**
 * @brief       进入停止模式
 * @param       无
 * @retval      无
 */
void pwr_enter_stop(void)
{
    __HAL_RCC_PWR_CLK_ENABLE();     /* 使能电源时钟 */
    
    /* 进入停止模式,设置稳压器为低功耗模式,等待中断唤醒 */
    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
}

pwr_enter_stop函数因为涉及对电源控制寄存器的操作,所以先调用__HAL_RCC_PWR_CLK_ENABLE函数去使能 PWR 时钟,然后调用 HAL_PWR_EnterSTOPMode 函数进入停止模式,形参 1 即PWR_LOWPOWERREGU LATOR_ON 设置稳压器为低功耗模式,形参 2 则是选择 WFI 指令

main.c

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./USMART/usmart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./BSP/PWR/pwr.h"

int main(void)
{
    uint8_t t = 0;
    uint8_t key = 0;
    HAL_Init(); /* 初始化 HAL 库 */
    sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    delay_init(72); /* 延时初始化 */
    usart_init(115200); /* 串口初始化为 115200 */
    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(); /* 进入停止模式 */
            /* 从停止模式唤醒, 需要重新设置系统时钟, 72Mhz */
            sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
            delay_init(72); /* 延时初始化 */
            LED1(1); /* 关闭绿灯,提示退出停止模式 */
        }
        if ((t % 20) == 0)
        {
            LED0_TOGGLE(); /* 每 200ms,翻转一次 LED0 */
        }
        delay_ms(10);
        t++;
    }
}

该部分程序,功能就是按下 KEY0 后,点亮 LED1, 进入停止模式。然后一直等待外部中断唤醒,当按下按键 WK_UP,就触发外部中断,停止模式就被唤醒,然后继续执行后面的程序,重新设置系统时钟 72MHZ 和延时初始化,关闭 LED1 等 。注意退出停止模式时需要重新进行系统的初始化。

下载验证

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

八、待机模式实验
功能

LED0 闪烁,表明代码正在运行。按下按键 KEY0 后,进入待机模式,待机模式下大部分引脚处于高阻态,所以说这时候 LED0 会熄灭, TFTLCD 也会熄灭。按下 WK_UP 按键后,退出待机模式(相当于复位操作),程序重新执行, LED0 继续闪烁, TFTLCD 屏点亮。

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

涉及的寄存器

对于PWR_CR:这里我们通过设置 PDDS 位,使 CPU 进入深度睡眠时进入待机模式,同时,我们需要通过CWUF 位清除之前的唤醒位

对于PWR_CSR:该寄存器我们只关心 EWUP 位,设置 EWUP 为 1,即 WKUP 引脚作为待机模式的唤醒源

相关的HAL库驱动

void HAL_PWR_EnableWakeUpPin (uint32_t WakeUpPinPolarity); 用于使能唤醒引脚。

形参 1 取值范围: PWR_WAKEUP_PIN1

注:禁止某个唤醒引脚使用的函数void HAL_PWR_DisableWakeUpPin (uint32_t WakeUpPinPolarity);

void HAL_PWR_EnterSTANDBYMode (void); 进入待机模式函数

用于使 CPU 进入待机模式, 进入待机模式,首先要设置 SLEEPDEEP 位,接着我们通过

PWR_CR 设置 PDDS 位,使得 CPU 进入深度睡眠时进入待机模式,最后执行 WFI 指令开始进入待机模式,并等待 WK_UP 上升沿的到来。

配置步骤

1)进入 CPU 待机模式

在进入待机模式之前我们需要做一些准备:涉及到操作PWR寄存器的内容 ,所以首先先进行PWR 时钟的初始化 , 用__HAL_RCC_PWR_CLK_ENABLE 函数实现。

通过 HAL_PWR_EnableWakeUpPin 函数使能 WKUP 的唤醒功能。通过__HAL_PWR_CLEAR_FLAG 函数清除唤醒标记,详看源码。

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

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

本实验中,通过按下 KEY0 按键进入待机模式,然后通过按下 WK_UP 按键,使用待机模式中的 WKUP 引脚上升沿的唤醒信号,而不是普通的外部中断,唤醒待机模式。

实验源码

pwr.h

#ifndef __PWR_H
#define __PWR_H

#include "./SYSTEM/sys/sys.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)   /* PA口时钟使能 */
  
#define PWR_WKUP_INT_IRQn               EXTI0_IRQn
#define PWR_WKUP_INT_IRQHandler         EXTI0_IRQHandler

/******************************************************************************************/

void pwr_pvd_init(uint32_t pls); /* PVD电压检测初始化函数 */
void pwr_wkup_key_init(void);    /* 唤醒按键初始化 */
void pwr_enter_sleep(void);      /* 进入睡眠模式 */
void pwr_enter_stop(void);       /* 进入停止模式 */
void pwr_enter_standby(void);    /* 进入待机模式 */

#endif

pwr.c


#include "./BSP/PWR/pwr.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"


/**
 * @brief       初始化PVD电压监视器
 * @param       pls: 电压等级(PWR_PVD_detection_level)
 *   @arg       PWR_PVDLEVEL_0,2.2V;
 *   @arg       PWR_PVDLEVEL_1,2.3V;
 *   @arg       PWR_PVDLEVEL_2,2.4V;
 *   @arg       PWR_PVDLEVEL_3,2.5V;
 *   @arg       PWR_PVDLEVEL_4,2.6V;
 *   @arg       PWR_PVDLEVEL_5,2.7V;
 *   @arg       PWR_PVDLEVEL_6,2.8V;
 *   @arg       PWR_PVDLEVEL_7,2.9V;
 * @retval      无
 */
void pwr_pvd_init(uint32_t pls)
{
    PWR_PVDTypeDef pwr_pvd = {0};

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

    HAL_NVIC_SetPriority(PVD_IRQn, 3 ,3);
    HAL_NVIC_EnableIRQ(PVD_IRQn);
    HAL_PWR_EnablePVD();                             /* 使能PVD检测 */
}

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

/**
 * @brief       PVD中断服务回调函数
 * @param       无
 * @retval      无
 */
void HAL_PWR_PVDCallback(void)
{
    if (__HAL_PWR_GET_FLAG(PWR_FLAG_PVDO))   /* 电压比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);                                                        /* 灭掉绿灯 */
    }
}

/***********************************睡眠模式实验程序*******************************************/

/**
 * @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_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); 
}

/**
 * @brief       WK_UP按键 外部中断服务程序
 * @param       无
 * @retval      无
 */
void PWR_WKUP_INT_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(PWR_WKUP_GPIO_PIN);
}

/**
 * @brief       进入CPU睡眠模式
 * @param       无
 * @retval      无
 */
void pwr_enter_sleep(void)
{
    HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI); /* 执行WFI指令, 进入睡眠模式 */
}

/**
 * @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()函数已经为我们清除了中断标志位,所以我们进了回调函数可以不做任何事 */
    }
}

/***********************************停止模式实验程序*******************************************/

/**
 * @brief       进入停止模式
 * @param       无
 * @retval      无
 */
void pwr_enter_stop(void)
{
    __HAL_RCC_PWR_CLK_ENABLE();     /* 使能电源时钟 */
    
    /* 进入停止模式,设置稳压器为低功耗模式,等待中断唤醒 */
    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
}

/***********************************待机模式*******************************************/
/**
 * @brief       进入待机模式
 * @param       无
 * @retval      无
 */
void pwr_enter_standby(void)
{
    __HAL_RCC_PWR_CLK_ENABLE();               /* 使能电源时钟 */

    HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1); /* 使能KEY_UP引脚的唤醒功能 */
    __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);        /* 需要清此标记,否则将保持唤醒状态 */
    HAL_PWR_EnterSTANDBYMode();               /* 进入待机模式 */
}

main,c


#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./USMART/usmart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./BSP/PWR/pwr.h"


int main(void)
{
    uint8_t t = 0;
    uint8_t key = 0;

    HAL_Init();                         /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    delay_init(72);                     /* 延时初始化 */
    usart_init(115200);                 /* 串口初始化为115200 */
    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++;
    }
}
下载验证

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

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值