在系统或电源复位以后,微控制器处于运行状态。当CPU不需继续运行时,可以利用多种低功耗模式来节省功耗,例如等待某个外部事件时。用户需要根据最低电源消耗、最快速启动时间和可用的唤醒源等条件,选定一个最佳的低功耗模式。
STM32F10x有三种低功耗模式:
1、睡眠模式
Cortex-M3内核停止,所有外设包括Cortex™-M3核心的外设,如NVIC、系统时钟(SysTick)等仍在运行
2、停止模式
所有的时钟都已停止
3、待机模式
1.8V电源关闭(CPU核心区域)
此外,在运行模式下,可以通过以下方式中的一种降低功耗:
1、降低系统时钟。
2、关闭APB和AHB总线上未被使用的外设时钟。
通过执行WFI或WFE指令可以进入睡眠状态。根据Cortex™-M3系统控制寄存器中的SLEEPONEXIT位的值,有两种选项可用于选择睡眠模式进入机制:
1、SLEEP-NOW:如果SLEEPONEXIT位被清除,当WFI或WFE被执行时,微控制器立即进入睡眠模式。
2、SLEEP-ON_EXIT:如果SLEEPONEXIT位被置位,系统从最低优先级的中断处理程序中退出时,微控制器就立即进入睡眠模式。
SLEEP-NOW模式一览:
SLEEP-ON_EXIT模式一览:
停止模式是在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、当电压调节器处于低功耗模式下,当系统从停止模式退出时,将会有一段额外的启动延时。如果在停止模式期间保持内部调节器开启,则退出启动时间会缩短,但相应的功耗会增加。
待机模式可实现系统的最低功耗。该模式是在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)将会指示内核由待机状态退出。
睡眠模式标准库函数代码:
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();
}
低功耗模式参考代码:
#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 up\r\n");
}
EXTI_ClearITPendingBit(EXTI_Line0); //清除LINE0上的中断标志位
}
//外部中断2服务程序
void EXTI2_IRQHandler(void)
{
delay_ms(10);//消抖
if(KEY2 == 0) //按键KEY2
{
set_mcu_mode = 1; //需要进入睡眠模式
printf("key2\r\n");
}
EXTI_ClearITPendingBit(EXTI_Line2); //清除LINE2上的中断标志位
}
//外部中断3服务程序
void EXTI3_IRQHandler(void)
{
delay_ms(10);//消抖
if(KEY1 == 0) //按键KEY1
{
set_mcu_mode = 2; //需要进入停机模式
printf("key1\r\n");
}
EXTI_ClearITPendingBit(EXTI_Line3); //清除LINE3上的中断标志位
}
void EXTI4_IRQHandler(void)
{
delay_ms(10);//消抖
if(KEY0 == 0) //按键KEY0
{
set_mcu_mode = 3; //需要进入待机模式
printf("key0\r\n");
}
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 mode\r\n");
__WFI();
break;
case 2://进入停机模式
printf("Enter stop mode\r\n");
PWR_EnterSTOPMode(PWR_Regulator_ON, PWR_STOPEntry_WFI);
break;
case 3://进入待机模式
printf("Enter standby mode\r\n");
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如何进入低功耗模式就介绍到这里,如果你们有什么问题,欢迎评论区留言。
如果这篇文章能够帮到你,就…懂的。