STM32F103HAL库实现低功耗(睡眠模式、停止模式和待机模式)

1. STM32电源结构

stm32的电源结构,由三个部分组成,如下图:
在这里插入图片描述

  1. 第一部分是VDDA的供电区域和参考电压:
  • 为了提高ADC的转换精度,stm32中的ADC实验一个单独的电源供电,过滤和屏蔽来自印刷板上的干扰和噪声
  • ADC的电源引脚为VDDA,中间可能会接一个RC滤波或者磁珠,然后连接到VDD
  • 以及独立的电源VSSA,如果芯片引脚中有VREF-,那么必须连接到VSSA
  • 对于100引脚和144引脚的芯片,会有VREF+和VREF-引脚;64引脚及以下的是没有的,内部和ADC的电源相连
  1. 电压调节器和VDD供电区域:
  • 电压调节器是STM32的电源系统中核心的部分,连接VDD供电区域和1.8V供电区域。
  • VDD供电区域电源来自VSS和VDD,负责IO电路以及待机电路供电。
  • 电压调节器(也叫1.8V供电区域)主要负责给备份域以及待机电路之外的所有数字电路供电,包括内核、数字外设以及RAM

电压调节器在上电之后,是自动使能的,对于的有三种工作模式:

  • 运转模式:调节器以正常功耗模式提供1.8V电源(内核、内存和外设)
  • 停止模式:调节器以低功耗模式提供1.8V电源,以确保寄存器和SRAM的内容
  • 待机模式:调节器停止供电,除了备用电路和备份域以外,寄存器和SRAM的内容全部丢失。
  1. 电池备份区域(后备供电区域)
  • 电池备份区域也就是后备供电区域,使用电池或者其他电源连接到 VBAT 脚上
  • 当 VDD 断电时,可以保存备份寄存器的内容和维持 RTC 的功能
  • 同时 VBAT 引脚也为 RTC 和 LSE 振荡器(LSE对于的引脚为PC14和PC15)供电,这保证了当主要电源被切断时,RTC 能够继续工作
  • 切换到 VBAT 供电由复位模块中的掉电复位功能控制。
  • 如果应用中没有使用外部电池,VBAT必须连接到VDD引脚上
  • 如果在应用中没有外部电池,建议VBAT在外部通过一个100nF的陶瓷电容与VDD相连,

在这里插入图片描述

2. 电源管理器

2.1 上电复位和掉电复位

STM32内部有一个完整的上电复位(POR)和掉电复位(PDR)电路,当供电电压达到2V时系统既能正常工作
在这里插入图片描述

  • 当电源的电压VDD/VDDA上升至VPOP时,经过一个滞后时间tRSTTEMPO之后,会产生复位信号,系统开始运行。
  • 当电源的电压VDD/VDDA下降至VPDR时,会立即产生复位信号,系统无法正常运行。
  • POR 与 PDR 的复位电压阈值是固定的,VPOR 阈值(典型值)为 1.92V,VPDR 阈值(典型值)为 1.88V。
  • 上电复位和掉电复位的阈值是不一样的,两者之间大概有个40mV的间距,具有迟滞性。

2.2 可编辑电压监测器(PVD)

2.1中的上电复位和掉电复位是系统自己完成的,同时用户也可利用PVD对VDD电压与电源控制寄存器(PWR_CR)中的PLS[2:0]位进行比较,来检测电源电压。

  • PVDE用来使能PVD功能
  • 电源控制/状态寄存器(PWR_CSR)中的PVDO标志用来表明VDD是高于还是低于PVD的电压阀值。
  • 该事件在内部连接到外部中断的第16线,如果该中断在外部中断寄存器中是使能的,该事件就会产生中断
  • 当VDD下降到PVD阀值以下和(或)当VDD上升到PVD阀值之上时,根据外部中断第16线的上升/下降边沿触发设置,就会产生PVD中断
  • 这一特性可用于用于执行紧急关闭任务。
    在这里插入图片描述
    下面是PWR_CR寄存器定义:
    在这里插入图片描述

3. 低功耗模式介绍

当系统或电源复位之后,微控制器处于运行状态,当CPU不需要运行的时候,我们可以利用很多低功耗模式来节省功耗,然后再等待某一个特定的外部事件,对CPU进行唤醒。STM32中提供了三种不同的低功耗模式:

  • 睡眠模式:内核停止运行,此外所有外设(包括芯片的片上外设,NVIC,定时器,ADC等)都处于运行的状态
  • 停止模式:内核停止运行,此外说有外设的时钟都已停止,再次唤醒可以继续从原来进入低功耗的位置继续运行。
  • 待机模式:再停止模式的基础上,内核核心区域的1.8V供电区域断电,下次唤醒会导致系统复位。
  • 此外,运行状态下,也可以通过降低系统时钟频率、关闭APB和AHB总线上未使用的外设时钟来降低功耗。

下面是低功耗模式一览:
在这里插入图片描述

3.1 睡眠模式

进入睡眠模式,Cortex_M3 内核停止,所有外设包括 Cortex_M3 核心的外设,如 NVIC、系统时钟(SysTick)等仍在运行,有两种进入睡眠模式的模式 WFI 和 WFE。WFI(Wait for interrupt)和 WFE(Wait for event)是内核指令,会调用一些汇编指令,根据调用内核指令的不同,睡眠后唤醒的方式对于为等待“中断”唤醒和“事件”唤醒。

在这里插入图片描述

3.2 停止模式

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

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

在这里插入图片描述

3.3 待机模式

待机模式可实现最低功耗。该模式是在 CM3 深睡眠模式时关闭电压调节器,整个 1.8V 供电区域被断电。PLL、HSI 和 HSE 振荡器也被断电。除备份域(RTC 寄存器、RTC 备份寄存器和备份 SRAM)和待机电路中的寄存器外,SRAM 和其他寄存器内容都将丢失。

在这里插入图片描述
需要注意以下几点:

  • 待机模式下,所有IO引脚都处于高阻态,除了复位引脚、被使能的唤醒引脚和被设置为防侵入或校准输出时的TAMPER引脚
  • 在待机模式下,由于内核失去了时钟,因此无法进行调试、下载等操作。

4. 低功耗相关寄存器

低功耗相关寄存器最主要的是电源控制/状态寄存器(PWR_CSR),下面是其具体定义:
在这里插入图片描述
在这里插入图片描述

5. 低功耗相关HAL库驱动

下面是一些低功耗会用到的HAL库驱动,如下表:

驱动函数关联寄存器功能描述
HAL_PWR_EnterSLEEPMode(…)SCB_SCR进入睡眠模式
HAL_PWR_EnterSTOPMode(…)PWR_CR/SCB_SCR进入停止模式
HAL_PWR_EnterSTANDBYMode(…)PWR_CR/SCB_SCR进入待机模式
HAL_PWR_EnableWakeUpPin(…)PWR_CSR使能WKUP管脚唤醒功能
__HAL_PWR_CLEAR_FLAG(…)PWR_CR清除PWR的相关标记
__HAL_RCC_PWR_CLK_ENABLE(…)RCC_APB1ENR使能电源时钟
HAL_SuspendTick()SysTick关闭sysTick计时,失能sysTick中断
HAL_ResumeTick()SysTick开启sysTick计时,使能sysTick中断

5.1 HAL_PWR_EnterSLEEPMode(…)

此函数用于HAL库进入睡眠模式,具体函数原型如下:

/**
 * @brief       HAL_PWR_EnterSLEEPMode:进入睡眠模式
 * @param       Regulator: 为了统一接口设计,无实际意义
 * @param       SLEEPEntry: 进入睡眠模式的指令,可选参数为PWR_SLEEPENTRY_WFI和PWR_SLEEPENTRY_WFE
 * @retval      无
 */
void HAL_PWR_EnterSLEEPMode(uint32_t Regulator, uint8_t SLEEPEntry);

函数定义如下:

/* 进入睡眠模式 */
void HAL_PWR_EnterSLEEPMode(uint32_t Regulator, uint8_t SLEEPEntry)
{
  /* 参数检查,Regulator实际上没有使用 */
  UNUSED(Regulator);

  /* 断言,也是为了参数的检查 */
  assert_param(IS_PWR_SLEEP_ENTRY(SLEEPEntry));

  /* 清除SLEEPDEEP位,表示立即进入睡眠模式 */
  CLEAR_BIT(SCB->SCR, ((uint32_t)SCB_SCR_SLEEPDEEP_Msk));

  /* 选择进入睡眠模式的指令*/
  if(SLEEPEntry == PWR_SLEEPENTRY_WFI)
  {
    /* 等待中断唤醒 */
    __WFI();
  }
  else
  {
    /* 等待事件唤醒 */
    __SEV();
    __WFE();
    __WFE();
  }
}

5.2 HAL_PWR_EnterSTOPMode(…)

此函数用于HAL库进入停止模式,具体函数原型如下:

/**
 * @brief       HAL_PWR_EnterSTOPMode:进入停止模式
 * @param       Regulator: 停止模式时的电源调节器状态
 * PWR_MAINREGULATOR_ON: 使用主调节器(Main Regulator)。在这种模式下,微控制器在进入停止模式时仍然可以保持一些外设的电源。
 * PWR_LOWPOWERREGULATOR_ON: 使用低功耗调节器(Low Power Regulator)。在这种模式下,电源会更低,适合需要更长时间低功耗的应用,但外设的响应可能会稍慢。
 * @param       SLEEPEntry: 进入睡眠模式的指令,可选参数为PWR_SLEEPENTRY_WFI和PWR_SLEEPENTRY_WFE
 * @retval      无
 */
void HAL_PWR_EnterSTOPMode(uint32_t Regulator, uint8_t STOPEntry);

函数定义如下:

/* 进入停止模式 */
void HAL_PWR_EnterSTOPMode(uint32_t Regulator, uint8_t STOPEntry)
{
  /* 检查参数 */
  assert_param(IS_PWR_REGULATOR(Regulator));
  assert_param(IS_PWR_STOP_ENTRY(STOPEntry));

  /* 清除电源控制寄存器(PWR_CR)中的PDDS位 */ 
  CLEAR_BIT(PWR->CR,  PWR_CR_PDDS);

  /* 通过设置PWR_CR中LPDS位选择电压调节器的模式 */
  MODIFY_REG(PWR->CR, PWR_CR_LPDS, Regulator);

  /* 设置Cortex-M3系统控制寄存器中的SLEEPDEEP位 */
  SET_BIT(SCB->SCR, ((uint32_t)SCB_SCR_SLEEPDEEP_Msk));

  /* 选择停止模式的进入指令*/
  if(STOPEntry == PWR_STOPENTRY_WFI)
  {
    /* 通过中断唤醒 */
    __WFI();
  }
  else
  {
    /*通过事件唤醒 */
    __SEV();
    PWR_OverloadWfe(); /* WFE redefine locally */
    PWR_OverloadWfe(); /* WFE redefine locally */
  }
  /*  清除Cortex-M3系统控制寄存器中的SLEEPDEEP位 */
  CLEAR_BIT(SCB->SCR, ((uint32_t)SCB_SCR_SLEEPDEEP_Msk));
}

5.3 HAL_PWR_EnterSTANDBYMode(…)

此函数用于HAL库进入待机模式,具体函数原型如下:

/**
 * @brief       HAL_PWR_EnterSTANDBYMode:进入待机模式
 * @param       无
 * @retval      无
 */
void HAL_PWR_EnterSTANDBYMode(void);

函数定义如下:

/* 进入待机模式 */
void HAL_PWR_EnterSTANDBYMode(void)
{
  /* 设置电源控制寄存器(PWR_CR)中的PDDS位 */
  SET_BIT(PWR->CR, PWR_CR_PDDS);

  /* 设置Cortex™-M3系统控制寄存器中的SLEEPDEEP位 */
  SET_BIT(SCB->SCR, ((uint32_t)SCB_SCR_SLEEPDEEP_Msk));

  /* 清除电源控制/状态寄存器(PWR_CSR)中的WUF位 */
#if defined ( __CC_ARM)
  __force_stores();
#endif
  /* 通过WFI指令进入待机模式 */
  __WFI();
}

以上就是进入低功耗模式主要涉及到的三个驱动函数,其他的函数,由于内部实现较为简单,所有这里不做介绍。下面我们开始介绍低功耗模式的配置和使用步骤:

6. 低功耗模式配置及使用步骤

6.1 CubeMX配置

我们在进入低功耗模式的时候,通常使用的是WFI指令进入,因此我们需要配置一个中断进行唤醒,并且在待机模式中,规定了可以通过WKUP引脚的上升沿触发,所以我们这里为了方便,直接使用WKUP引脚的上升沿触发,具体CubeMX配置如下:
在这里插入图片描述
在这里插入图片描述

其他部分的配置不做介绍,自行配置或者查阅往期内容即可,下面是具体使用步骤:

6.2 睡眠模式配置步骤

在这里插入图片描述

6.3 停止模式配置步骤

在这里插入图片描述
【注】:退出停止模式,HSI RC振荡器被选为系统时钟

6.4 待机模式配置步骤

在这里插入图片描述
【注】:待机模式下,所有I/O引脚处于高阻态,除了复位引脚、被使能的唤醒引脚等。同时也不能下载程序,必须退出待机模式才能下载

7. 低功耗模式操作实验

7.1. 实验内容

在STM32F103RCT6上进行实验,通过按键控制,进入低功耗模式,具体要求如下:

  • 使用按键触发方式,KEY0进入睡眠模式,KEY1进入停止模式,KEY2进入待机模式,KEY_UP唤醒
  • 通过串口打印相关信息
  • 定义一个LED闪烁,执行系统正常运行

7.2 代码实现

  1. main函数
int main(void)
{
  /* USER CODE BEGIN 1 */
uint16_t timeCount = 0;
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {

		timeCount++;
		Key_One_Scan(Key_Name_Key0,Key0_Up_Task,Key0_Down_Task);         //扫描Key0状态
		Key_One_Scan(Key_Name_Key1,Key1_Up_Task,Key1_Down_Task);         //扫描Key1状态
		Key_One_Scan(Key_Name_Key2,Key2_Up_Task,Key2_Down_Task);         //扫描Key2状态
		HAL_Delay(10);
		if(timeCount >= 20)
		{
			timeCount = 0;
			HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);
		}
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}
  1. key.c
/* USER CODE BEGIN 2 */

#include "key.h"
#include "usart.h"

void Key0_Down_Task(void)
{
	printf("进入睡眠模式\r\n");
	
	/* 暂停sysTick的计时,因为这个是CubeMX配置的工程,默认是开启了sysTick1的中断,所有这里需要停止计时,避免从睡眠模式唤醒 */
	HAL_SuspendTick();
	
	/* 进入睡眠模式,参数PWR_MAINREGULATOR_ON无实际意义,选择WFI指令进入 */
	HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);

	
	/* 下一次从睡眠模式唤醒过来,会从这里开始运行,因此开启sysTick计时 */
	HAL_ResumeTick();
	
	printf("退出睡眠模式\r\n");
	
	
}
void Key0_Up_Task(void)
{
   
}
void Key1_Down_Task(void)
{
	printf("进入停止模式\r\n");
	
	/* 暂停sysTick的计时,因为这个是CubeMX配置的工程,默认是开启了sysTick1的中断,所有这里需要停止计时,避免从睡眠模式唤醒 */
	HAL_SuspendTick();
	
	/* 进入停止模式,开启低功耗模式,使用WFI指令进入 */
	HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON,PWR_SLEEPENTRY_WFI);
	
	/* 由于停止模式会关闭时钟,所有需要重新配置时钟 */
	SystemClock_Config();
	
	/* 开启sysTick定时器 */
	HAL_ResumeTick();
	
	printf("退出停止模式\r\n");
	
}
void Key1_Up_Task(void)
{
}
void Key2_Down_Task(void)
{
     printf("进入待机模式\r\n");
	
	/* 使能电源时钟 */
	__HAL_RCC_PWR_CLK_ENABLE();
	
	/* 清除唤醒标记 */
	__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
	
	/* 使能WKUP上升沿的唤醒功能 */
	HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);
	
	/* 进入待机模式,退出后会复位,需要从头运行程序 */
	HAL_PWR_EnterSTANDBYMode();
	
	/* 退出待机模式后会复位,所以不会打印下面的内容 */
	printf("退出待机模式\r\n");
	
	
}
void Key2_Up_Task(void)
{
    
}
void WKUP_Down_Task(void)
{
  
}
void WWKUP_Up_Task(void)
{
     
}

void Key_One_Scan(uint8_t KeyName ,void(*OnKeyOneUp)(void), void(*OnKeyOneDown)(void))
{
   static uint8_t Key_Val[Key_Name_Max];    //按键值的存放位置
   static uint8_t Key_Flag[Key_Name_Max];   //KEY0~2为0时表示按下,为1表示松开,WKUP反之
    
   Key_Val[KeyName] = Key_Val[KeyName] <<1;  //每次扫描完,将上一次扫描的结果左移保存
   
    switch(KeyName)
    {
        case Key_Name_Key0:  Key_Val[KeyName] = Key_Val[KeyName] | (HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin));    //读取Key0按键值
            break;
        case Key_Name_Key1:  Key_Val[KeyName] = Key_Val[KeyName] | (HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin));   //读取Key1按键值
            break;
        case Key_Name_Key2:  Key_Val[KeyName] = Key_Val[KeyName] | (HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin));   //读取Key2按键值
            break;
//        case Key_Name_WKUP:  Key_Val[KeyName] = Key_Val[KeyName] | (HAL_GPIO_ReadPin(WKUP_GPIO_Port, WKUP_Pin));   //读取WKUP按键值
//            break; 
        default:
            break;
    }
//    if(KeyName == Key_Name_WKUP)     //WKUP的电路图与其他按键不同,所以需要特殊处理
//    {
//        //WKUP特殊情况
//        //当按键标志为1(松开)是,判断是否按下,WKUP按下时为0xff
//        if(Key_Val[KeyName] == 0xff && Key_Flag[KeyName] == 1)
//        {
//            (*OnKeyOneDown)();
//           Key_Flag[KeyName] = 0;
//        }
//        //当按键标志位为0(按下),判断按键是否松开,WKUP松开时为0x00
//        if(Key_Val[KeyName] == 0x00 && Key_Flag[KeyName] == 0)
//        {
//            (*OnKeyOneUp)();
//           Key_Flag[KeyName] = 1;
//        } 
//    }
//    else                               //Key0~2按键逻辑判断
//    {
        //Key0~2常规判断
          //当按键标志为1(松开)是,判断是否按下
	if(Key_Val[KeyName] == 0x00 && Key_Flag[KeyName] == 1)
	{
		(*OnKeyOneDown)();
	   Key_Flag[KeyName] = 0;
	}
	//当按键标志位为0(按下),判断按键是否松开
	if(Key_Val[KeyName] == 0xff && Key_Flag[KeyName] == 0)
	{
		(*OnKeyOneUp)();
	   Key_Flag[KeyName] = 1;
	}       

}

/* USER CODE END 2 */

  1. key_up的中断回调函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin == WK_UP_Pin)
	{
		/* 不需要做任何处理 */
	}
}

7.1 运行结果

在这里插入图片描述

以上就是本期的所有内容,创造不易,点个关注再走呗。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不想写代码的我

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值