STM32F10x进入低功耗模式



By
jonson
02 4 月 24
0
comment

1 低功耗模式简介

在系统或电源复位以后,微控制器处于运行状态。当CPU不需继续运行时,可以利用多种低功耗模式来节省功耗,例如等待某个外部事件时。用户需要根据最低电源消耗、最快速启动时间和可用的唤醒源等条件,选定一个最佳的低功耗模式。

STM32F10x有三种低功耗模式:

1、睡眠模式

Cortex-M3内核停止,所有外设包括Cortex™-M3核心的外设,如NVIC、系统时钟(SysTick)等仍在运行

2、停止模式

所有的时钟都已停止

3、待机模式

1.8V电源关闭(CPU核心区域)

低功耗模式一览:

在这里插入图片描述

STM32电源框图:
在这里插入图片描述

此外,在运行模式下,可以通过以下方式中的一种降低功耗:

1、降低系统时钟。
2、关闭APB和AHB总线上未被使用的外设时钟。

2 睡眠模式详解

通过执行WFI或WFE指令可以进入睡眠状态。根据Cortex™-M3系统控制寄存器中的SLEEPONEXIT位的值,有两种选项可用于选择睡眠模式进入机制:

1、SLEEP-NOW:如果SLEEPONEXIT位被清除,当WFI或WFE被执行时,微控制器立即进入睡眠模式。
2、SLEEP-ON_EXIT:如果SLEEPONEXIT位被置位,系统从最低优先级的中断处理程序中退出时,微控制器就立即进入睡眠模式。

SLEEP-NOW模式一览:

在这里插入图片描述
SLEEP-ON_EXIT模式一览:
在这里插入图片描述

3 停止模式详解

停止模式是在Cortex™-M3的深睡眠模式基础上结合了外设的时钟控制机制,在停止模式下电压调节器可运行在正常或低功耗模式。

此时在1.8V供电区域的的所有时钟都被停止,PLL、HSI和HSE RC振荡器的功能被禁止,SRAM和寄存器内容被保留下来。

在停止模式下,所有的I/O引脚都保持它们在运行模式时的状态。

停止模式进入和退出的方法如下:

在这里插入图片描述

进入停止模式:

在停止模式下,通过设置电源控制寄存器(PWR_CR)的LPDS位使内部调节器进入低功耗模式,能够降低更多的功耗。

需要注意:

1、如果正在进行闪存编程,直到对内存访问完成,系统才进入停止模式。
2、如果正在进行对APB的访问,直到对APB访问完成,系统才进入停止模式。
3、在进入停止模式前,如果一些外设没有被关闭,那么外设仍然消耗电流,如串口、ADC、DAC等。

退出停止模式:

通过中断或者唤醒事件可以退出停止模式。

需要注意:

1、退出停止模式后,HSI RC振荡器被默认选为系统时钟。
2、当电压调节器处于低功耗模式下,当系统从停止模式退出时,将会有一段额外的启动延时。如果在停止模式期间保持内部调节器开启,则退出启动时间会缩短,但相应的功耗会增加。

4 待机模式详解

待机模式可实现系统的最低功耗。该模式是在Cortex-M3深睡眠模式时关闭电压调节器。整个1.8V供电区域被断电。PLL、HSI和HSE振荡器也被断电。SRAM和寄存器内容丢失。只有备份的寄存器和待机电路维持供电。

待机模式进入和退出的方法如下:

在这里插入图片描述

进入待机模式:

进入待机模式需要做到以下几点:

1、设置Cortex-M3系统控制寄存器中的SLEEPDEEP位。
2、清除电源控制寄存器(PWR_CR)中的PDDS位。
3、通过设置PWR_CR中LPDS位选择电压调节器的模式。

退出待机模式:

当一个外部复位(NRST引脚)、IWDG复位、WKUP引脚上的上升沿或RTC闹钟事件的上升沿发生时,微控制器从待机模式退出。从待机唤醒后,除了电源控制/状态寄存器(PWR_CSR),所有寄存器被复位。

从待机模式唤醒后的代码执行等同于复位后的执行(采样启动模式引脚、读取复位向量等)。电源控制/状态寄存器(PWR_CSR)将会指示内核由待机状态退出。

5 示例代码

5.1 标准库函数定义

睡眠模式标准库函数代码:

static __INLINE  void __WFI()                     { __ASM ("wfi"); }
static __INLINE  void __WFE()                     { __ASM ("wfe"); }

/**
  * @brief  Selects the condition for the system to enter low power mode.
  * @param  LowPowerMode: Specifies the new mode for the system to enter low power mode.
  *   This parameter can be one of the following values:
  *     @arg NVIC_LP_SEVONPEND
  *     @arg NVIC_LP_SLEEPDEEP
  *     @arg NVIC_LP_SLEEPONEXIT
  * @param  NewState: new state of LP condition. This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState)
{
  /* Check the parameters */
  assert_param(IS_NVIC_LP(LowPowerMode));
  assert_param(IS_FUNCTIONAL_STATE(NewState));  
  
  if (NewState != DISABLE)
  {
    SCB->SCR |= LowPowerMode;
  }
  else
  {
    SCB->SCR &= (uint32_t)(~(uint32_t)LowPowerMode);
  }
}

停机模式标准库函数代码:

/**
  * @brief  Enters STOP mode.
  * @param  PWR_Regulator: specifies the regulator state in STOP mode.
  *   This parameter can be one of the following values:
  *     @arg PWR_Regulator_ON: STOP mode with regulator ON
  *     @arg PWR_Regulator_LowPower: STOP mode with regulator in low power mode
  * @param  PWR_STOPEntry: specifies if STOP mode in entered with WFI or WFE instruction.
  *   This parameter can be one of the following values:
  *     @arg PWR_STOPEntry_WFI: enter STOP mode with WFI instruction
  *     @arg PWR_STOPEntry_WFE: enter STOP mode with WFE instruction
  * @retval None
  */
void PWR_EnterSTOPMode(uint32_t PWR_Regulator, uint8_t PWR_STOPEntry)
{
  uint32_t tmpreg = 0;
  /* Check the parameters */
  assert_param(IS_PWR_REGULATOR(PWR_Regulator));
  assert_param(IS_PWR_STOP_ENTRY(PWR_STOPEntry));
  
  /* Select the regulator state in STOP mode ---------------------------------*/
  tmpreg = PWR->CR;
  /* Clear PDDS and LPDS bits */
  tmpreg &= CR_DS_MASK;
  /* Set LPDS bit according to PWR_Regulator value */
  tmpreg |= PWR_Regulator;
  /* Store the new value */
  PWR->CR = tmpreg;
  /* Set SLEEPDEEP bit of Cortex System Control Register */
  SCB->SCR |= SCB_SCR_SLEEPDEEP;
  
  /* Select STOP mode entry --------------------------------------------------*/
  if(PWR_STOPEntry == PWR_STOPEntry_WFI)
  {   
    /* Request Wait For Interrupt */
    __WFI();
  }
  else
  {
    /* Request Wait For Event */
    __WFE();
  }
  
  /* Reset SLEEPDEEP bit of Cortex System Control Register */
  SCB->SCR &= (uint32_t)~((uint32_t)SCB_SCR_SLEEPDEEP);  
}

待机模式标准库函数代码:

/**
  * @brief  Enters STANDBY mode.
  * @param  None
  * @retval None
  */
void PWR_EnterSTANDBYMode(void)
{
  /* Clear Wake-up flag */
  PWR->CR |= PWR_CR_CWUF;
  /* Select STANDBY mode */
  PWR->CR |= PWR_CR_PDDS;
  /* Set SLEEPDEEP bit of Cortex System Control Register */
  SCB->SCR |= SCB_SCR_SLEEPDEEP;
/* This option is used to ensure that store operations are completed */
#if defined ( __CC_ARM   )
  __force_stores();
#endif
  /* Request Wait For Interrupt */
  __WFI();
}

5.2 进入低功耗模式参考代码

低功耗模式参考代码:

#include "delay.h"
#include "sys.h"
#include "usart.h"

#define KEY0    GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)  //读取按键0         
#define KEY1    GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)  //读取按键1
#define KEY2    GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2)  //读取按键2 
#define WK_UP   GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)  //读取按键3(WK_UP) 

uint8_t set_mcu_mode = 0;   //设置MCU模式,0:运行状态,1:睡眠模式,2:停机模式,3:待机模式

//外部中断0服务程序
void EXTI0_IRQHandler(void)
{
	delay_ms(10);//消抖
	if(WK_UP == 1)	   //WK_UP按键
	{				 
		SystemInit();	 //MCU被唤醒,重新配置系统时钟
		set_mcu_mode = 0;
		printf("MCU wake uprn");
	}
	EXTI_ClearITPendingBit(EXTI_Line0); //清除LINE0上的中断标志位  
}

//外部中断2服务程序
void EXTI2_IRQHandler(void)
{
	delay_ms(10);//消抖
	if(KEY2 == 0)	  //按键KEY2
	{
		set_mcu_mode = 1; //需要进入睡眠模式
		printf("key2rn");
	}		 
	EXTI_ClearITPendingBit(EXTI_Line2);  //清除LINE2上的中断标志位  
}
//外部中断3服务程序
void EXTI3_IRQHandler(void)
{
	delay_ms(10);//消抖
	if(KEY1 == 0)	 //按键KEY1
	{				 
		set_mcu_mode = 2; //需要进入停机模式
		printf("key1rn");
	}		 
	EXTI_ClearITPendingBit(EXTI_Line3);  //清除LINE3上的中断标志位  
}

void EXTI4_IRQHandler(void)
{
	delay_ms(10);//消抖
	if(KEY0 == 0)	 //按键KEY0
	{
		set_mcu_mode = 3; //需要进入待机模式
		printf("key0rn");
	}		 
	EXTI_ClearITPendingBit(EXTI_Line4);  //清除LINE4上的中断标志位  
}

//外部中断初始化
void EXTIX_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
 	EXTI_InitTypeDef EXTI_InitStructure;
 	NVIC_InitTypeDef NVIC_InitStructure;

 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOE, ENABLE);//使能PORTA,PORTE时钟
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4;//KEY0-KEY2
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入
 	GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化GPIOE2,E3,E4
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0;//WK_UP KEY-->GPIOA0
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0设置成输入,默认下拉	  
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA0

  	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);	//使能复用功能时钟
    //GPIOE2 中断线以及中断初始化配置 下降沿触发
  	GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource2);
  	EXTI_InitStructure.EXTI_Line=EXTI_Line2; //KEY2
  	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;	
  	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
  	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  	EXTI_Init(&EXTI_InitStructure);	 	//根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
    //GPIOE3 中断线以及中断初始化配置 下降沿触发 
  	GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource3);
  	EXTI_InitStructure.EXTI_Line=EXTI_Line3;  //KEY1
  	EXTI_Init(&EXTI_InitStructure);	  	//根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
    //GPIOE4 中断线以及中断初始化配置 下降沿触发	
  	GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource4);
  	EXTI_InitStructure.EXTI_Line=EXTI_Line4; //KEY0
  	EXTI_Init(&EXTI_InitStructure);	  	//根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
    //GPIOA0 中断线以及中断初始化配置 上升沿触发 
 	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0); 
  	EXTI_InitStructure.EXTI_Line=EXTI_Line0; //WK_UP KEY
  	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
  	EXTI_Init(&EXTI_InitStructure);		//根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器

	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级

  	NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;				//使能按键WK_UP所在的外部中断通道
  	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;	//抢占优先级2, 
  	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03;			//子优先级3
  	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;					//使能外部中断通道
  	NVIC_Init(&NVIC_InitStructure);  	  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
    NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn;				//使能按键KEY2所在的外部中断通道
  	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;	//抢占优先级2, 
  	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;			//子优先级2
  	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;					//使能外部中断通道
  	NVIC_Init(&NVIC_InitStructure);  	  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
  	NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;				//使能按键KEY1所在的外部中断通道
  	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;	//抢占优先级2 
  	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;			//子优先级1 
  	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;					//使能外部中断通道
  	NVIC_Init(&NVIC_InitStructure);  	  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器  	  
	NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;				//使能按键KEY0所在的外部中断通道
  	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;	//抢占优先级2 
  	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00;			//子优先级0 
  	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;					//使能外部中断通道
  	NVIC_Init(&NVIC_InitStructure);  	  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
}

void LED_Init(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE);	 //使能PB,PE端口时钟

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;				 //LED0-->PB5 端口配置
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz
	GPIO_Init(GPIOB, &GPIO_InitStructure);					 //根据设定参数初始化GPIOB5
	GPIO_SetBits(GPIOB,GPIO_Pin_5);						 	 //PB5 输出高
}

int main(void)
{		
	delay_init();	    	//延时函数初始化	  
	uart_init(115200);	    //串口初始化为115200
	LED_Init();		  		//初始化与LED连接的硬件接口
	EXTIX_Init();		 	//外部中断初始化
	
	while(1)
	{	    	
		if(set_mcu_mode > 0)
		{
			switch (set_mcu_mode)
			{
			case 1://进入睡眠模式
				printf("Enter sleep modern");
				__WFI();
				break;
			case 2://进入停机模式
				printf("Enter stop modern");
				PWR_EnterSTOPMode(PWR_Regulator_ON, PWR_STOPEntry_WFI);
				break;
			case 3://进入待机模式
				printf("Enter standby modern");
				PWR_EnterSTANDBYMode();
				break;
			default:
				break;
			}
		}
		GPIO_ResetBits(GPIOB, GPIO_Pin_5);	//点亮LED0
		delay_ms(200);	  
		GPIO_SetBits(GPIOB, GPIO_Pin_5);	//关闭LED0
		delay_ms(200);	
	}
}

示例代码测试结果:

1、MCU正常运行,LED0定时闪烁。
2、按下KEY0,MCU进入待机模式,LED0熄灭,再按下KEY3(WK_UP),MCU被唤醒继续运行。
3、按下KEY1,MCU进入停机模式,LED0熄灭,再按下KEY3(WK_UP),MCU被唤醒继续运行。
4、按下KEY2,MCU进入睡眠模式,LED0熄灭,再按下KEY3(WK_UP),MCU被唤醒继续运行。

结束语

上面的测试例子只是给大家做一个参考,实际上需要根据项目的具体需求去补充很多细节,比如进入低功耗模式的触发条件,唤醒的方式,以及进入睡眠之前对外设的处理,等等,这样才能保证在实现功能需求的同时,又能尽可能的把功耗降到最低。
好了,关于STM32如何进入低功耗模式就介绍到这里,如果你们有什么问题,欢迎评论区留言。

如果这篇文章能够帮到你,就…懂的。
请添加图片描述

发表回复