低功耗实验

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

电源控制(PWR)简介

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

电源系统:VDDA供电区域、VDD供电区域、1.8V 供电区域、后备供电区域。

电源监控:POR/PDR 监控器、PVD 监控器。

电源管理:低功耗模式。

电源系统

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

在电源概述框图中我们划分了 3 个区域①②③,分别是独立的 A/D 转换器供电和参考电 压、电压调节器、电池备份区域。下面分别进行简单介绍:

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

VDDA 供电区域,主要是 ADC 电源以及参考电压,STM32 的 ADC 模块配备独立的供电方 式,使用了 VDDA 引脚作为输入,使用 VSSA 引脚作为独立地连接,VREF 引脚为提供给 ADC 的 参考电压。

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

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

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

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

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

电源管理

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

1、睡眠模式 

2、停止模式 

3、待机模式 

寄存器和HAL库函数 

代码 

睡眠模式

#include "./BSP/LOW_POWER/low_power.h"

/* 低功耗模式下的按键初始化(用于唤醒睡眠模式) */
void pwr_wkup_key_init(void)
{
    GPIO_InitTypeDef gpio_init_struct = {0};
    
    __HAL_RCC_GPIOA_CLK_ENABLE();
    
    gpio_init_struct.Pin = GPIO_PIN_0;
    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(GPIOA, &gpio_init_struct);
    
    HAL_NVIC_SetPriority(EXTI0_IRQn, 2, 2);
    HAL_NVIC_EnableIRQ(EXTI0_IRQn);
}

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

void EXTI0_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
}

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if(GPIO_Pin == GPIO_PIN_0)
    {
//        __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);/* HAL_GPIO_EXTI_IRQHandler();函数清楚了中断标志位,回调函数可以不做任何事 */
    }
}
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/KEY/key.h"
#include "./BSP/LED/led.h"
#include "./BSP/LOW_POWER/low_power.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);                  /* 初始化串口 */
    
    key_init();
    led_init();
    pwr_wkup_key_init();
    
    while(1)
    {
        key = key_scan(0);
        
        if(key == KEY0_PRES)
        {
            LED1(0);/* 点亮绿灯,提示进入睡眠模式 */
            printf("进入睡眠模式!\r\n\r\n");
            
            SysTick->CTRL &=~SysTick_CTRL_ENABLE_Msk;/* 关闭Systick中断 */
            
            pwr_enter_sleep();/* 进入睡眠模式 */
            
            SysTick->CTRL &=SysTick_CTRL_ENABLE_Msk;/* 打开Systick中断 */
            
            LED1(1);/* 关闭绿灯,提示退出睡眠模式 */
            printf("退出睡眠模式!\r\n\r\n");
        }
        
        t++;
        if(t == 20)
        {
            t=0;
            LED0_TOGGLE();
        }
        delay_ms(10);
    }
}

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

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

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 指令。我们选择前者。

原来我忽略systick时钟的中断。
众所周知,在cortex-M系列的MCU中,都会有一个systick的内置时钟,该时钟在裸机中可以提供一个精确的延时,在RTOS中可以提供一个系统的心跳。问题点就出现在systick上,systick按照设定的周期循环计数,当计数溢出的时候就会进入中断函数。所以MCU进入睡眠模式就立刻被唤醒的原因就是systick中断唤醒了MCU。
解决方法就是在进入睡眠模式前先关闭systick的中断,当MCU退出睡眠模式后再打开systick的中断。
停止模式

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

 

key = key_scan(0);

        if (key == KEY0_PRES)
        {
            LED1(0);                            /* 点亮绿灯,提示进入停止模式 */
            printf("进入停止模式!\r\n\r\n");
            
            pwr_enter_stop();                   /* 进入停止模式 */

            /* 从停止模式唤醒, 需要重新设置系统时钟, 72Mhz */
            sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
            HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK_DIV8);/* 退出停止模式后,系统自动选择HSI时钟源,我们需要重新切换Systick的时钟 */
            HAL_SuspendTick();                  /* 上述操作会重新打开Systick的中断,例程中不用到,所以要把它关闭 */
            
            LED1(1);                            /* 关闭绿灯,提示退出停止模式 */
            printf("退出停止模式!\r\n\r\n");
        }

HAL_PWR_EnterSTOPMode 函数

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

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 指令。我们选择前 者。

待机模式

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();/* 进入待机模式 */
}
key = key_scan(0);

        if (key == KEY0_PRES)
        {
            printf("进入待机模式!\r\n\r\n");
            
            pwr_enter_standby();        /* 进入待机模式 */
            
            /* 从待机模式唤醒相当于系统重启(复位), 因此不会执行到这里 */
            printf("退出待机模式!\r\n\r\n");
        }

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

HAL_PWR_EnableWakeUpPin 函数

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

void HAL_PWR_EnableWakeUpPin (uint32_t WakeUpPinPolarity);

⚫ 函数描述: 用于使能唤醒引脚。

⚫ 函数形参:. 形参 1 取值范围:PWR_WAKEUP_PIN1。

HAL_PWR_EnterSTANDBYMode 函数

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

⚫ 函数描述: 用于使 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 引脚上升沿的唤醒信号,而不是普通的外部中断,唤醒待机模式。

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

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

电源监控

电源监控的部分我们主要关注 PVD 监控器,此外还需要知道上电复位(POR)/掉电复位 (PDR)。

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

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

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

可编程电压检测器(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 阀值检测波形,如图 28.1.2.2 所示。

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

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

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

PVD监控实验 

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检测 */
}

void PVD_IRQHandler(void)
{
    HAL_PWR_PVD_IRQHandler();
}

void HAL_PWR_PVDCallback(void)
{
    if (__HAL_PWR_GET_FLAG(PWR_FLAG_PVDO)) /* 电压比PLS所选电压还低 */
    {
        printf("PVD Low Voltage!\r\n\r\n");
        LED1(0);/* 点亮绿灯, 表明电压低了 */
    }
    else
    {
        printf("PVD Voltage OK!\r\n\r\n");
        LED0(0);/* 点亮红灯, 表明电压正常了 */
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值