目录
1.硬件原理
2.低功耗模式
3.睡眠模式实验
4.停止模式实验
5.待机模式实验
前言:STM32F10xxx系列产品都有电源管理模块,芯片功耗会影响到一个产品的续航能力;比如在一些终端传感器场合里,为了减轻后期的维护投入,要求长期工作时间较长,更需要合理的芯片功耗管理。芯片自带几种运行模式,包括正常模式、睡眠模式、停止模式、待机模式。越往后,芯片的功耗越低,但能执行功能就越少。低功耗的电源管理策略就是在芯片不需要对外界响应的时候进入低功耗模式,而当外界条件满足的时候,退出低功耗模式(唤醒),正常执行处理工作。下面对其模式之间的转换和各个模式下的芯片内部的运转情况等一探究竟。
写代码前要先了解芯片的特性及工作原理,难免会先阅读一些长长的文档。下面核心讲解一些要点。
下图是芯片的电源框架:
图左边7个电压点对应芯片的7个引脚,在芯片电路原理图能找到对应的引脚。
、:是ADC转换器的参考电压,有些芯片没有这连个引脚,内部已经把他们接到、了。
、:是ADC转换器的供电电源,独立电源供电是为了过滤和屏蔽来自印刷电路板上的毛刺干扰,提高转换的精确度。(也可以直接连到、)。
、:STM32的工作电压(VDD)为2.0~3.6V。通过内置的电压调节器提供所需的1.8V电源。
:使用电池或其他电源连接到VBAT脚上,当VDD断电时,可以保存备份寄存器的内容和维持RTC的
功能。(如果没有外部电池,好像不接也可以,但是数据手册说必须接到)。
电池备份区域:
当备份区域由VDD(内部模拟开关连到VDD)供电时,下述功能可用:
● PC14和PC15可以用于GPIO或LSE(低速外部时钟)引脚
● PC13可以作为通用I/O口、TAMPER引脚、RTC校准时钟、RTC闹钟或秒输出
当后备区域由VBAT供电时(VDD消失后模拟开关连到VBAT),可以使用下述功能:
● PC14和PC15只能用于LSE(低速外部时钟)引脚
● PC13可以作为TAMPER引脚、RTC闹钟或秒输出
电压调节器:
复位后调节器总是使能的。根据应用方式它以3种不同的模式工作。
● 运转模式:调节器以正常功耗模式提供1.8V电源(内核,内存和外设)。
● 停止模式:调节器以低功耗模式提供1.8V电源,以保存寄存器和SRAM的内容。
● 待机模式:调节器停止供电。除了备用电路和备份域外,寄存器和SRAM的内容全部丢失。
STM32F10xxx有三种低功耗模式:
● 睡眠模式(Cortex™-M3内核停止,所有外设包括Cortex-M3核心的外设,如NVIC、系统时钟(SysTick)等仍在运行)
● 停止模式(所有的时钟都已停止)
● 待机模式(1.8V电源关闭)
此外,在运行模式下,可以通过以下方式中的一种降低功耗:
● 降低系统时钟
● 关闭APB和AHB总线上未被使用的外设时钟。
注意:在睡眠模式、停止模式及待机模式中,若备份区域电源正常供电,备份区域的RTC都可以正常运行、备份区域内的寄存器及备份区域内的SRAM数据会被保存,不受功耗模式影响。
下面代码利用编译器内置函数__WFI()进入睡眠,任何中断将退出睡眠(实验使用串口中断和外部中断)。利用串口将芯片状态信息发送出来。可以先把代码复制到工程编译下载,实验现象通过串口上位机观察,如果有可调电源的话,可以对比正常工作和睡眠模式下的电流大小,正常来说睡眠模式下功率低,那么电流会比正常工作下低。下面程序并不复杂,程序进入while循环延时一段时间就会进入睡眠状态,利用上位机向串口发送数据或者将PA.0引脚接电源都可产生中断,从而退出睡眠模式。
#include "stm32f10x.h"
#include "stdio.h"
static void EXTI_Key_Config(void);
static void NVIC_Configuration(void);
static void USART1_Config(void);
static void Delay(__IO u32 nCount);
int main(void)
{
USART1_Config();
EXTI_Key_Config();
//配置中断控制器NVIC
NVIC_Configuration();
while(1)
{
printf("STM32正常运行.\r\n");
Delay(0xfffff);
//NVIC_SystemLPConfig(NVIC_LP_SLEEPONEXIT,DISABLE);//调用__WFI时立即进入睡眠
NVIC_SystemLPConfig(NVIC_LP_SLEEPONEXIT,ENABLE);//调用__WFI时,等待系统从最低优先级的中断处理程序中退出后进入睡眠
printf("STM32进入睡眠.\r\n");
__WFI();//WFI指令进入睡眠
//开始等待中断唤醒
Delay(0xfffff);
printf("已退出睡眠模式.\r\n");
}
}
static void EXTI_Key_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
//开启外设时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);
//初始化 GPIOA.0 设置为下拉输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//GPIOA.0 中断线配置
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);
//GPIOA.0 中断初始化配置
EXTI_InitStructure.EXTI_Line=EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;//上升沿时进入中断
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
//初始化外设EXTI寄存器
EXTI_Init(&EXTI_InitStructure);
}
void USART1_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
//配置串口1(USART1)时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
//配置串口1(USART1 Tx (PA.09))
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//配置串口1 USART1 Rx (PA.10)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//串口1模式(USART1 mode)配置
USART_InitStructure.USART_BaudRate = 9600;//一般设置为9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No ;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启中断
USART_Cmd(USART1, ENABLE); //使能串口
}
int fputc(int ch, FILE *f)//重写标准库的fputc函数
{
//将Printf内容发往串口
USART_SendData(USART1, (unsigned char) ch);
while( USART_GetFlagStatus(USART1,USART_FLAG_TC)!= SET);
return (ch);
}
static void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);//使能外部中断 PA.0
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_Init(&NVIC_InitStructure);//使能串口1中断
}
void Delay(__IO u32 nCount) //简单的延时函数
{
for(; nCount != 0; nCount--);
}
在stm32f10x_it.c文件加入:
#include "delay.h"
void EXTI0_IRQHandler(void)
{
Delay(0xFFFFF);//防抖
//检查指定的EXTI0线路触发请求发生与否
if(EXTI_GetITStatus(EXTI_Line0) != RESET)
{
printf("按键中断唤醒.\r\n");
}
//清除EXTI0线路挂起位
EXTI_ClearITPendingBit(EXTI_Line0);
}
void USART1_IRQHandler(void)
{
unsigned char code;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
code=USART_ReceiveData(USART1);
printf("串口中断唤醒.\r\n");
}
}
特性 | 说明 |
---|---|
立即睡眠 | 在执行__WFI或__WFE指令时立即进入睡眠模式 |
退出时睡眠 | 在退出优先级最低的中断服务程序后才进入睡眠模式 |
进入方式 | 内核寄存器SLEEPDEEP=0,然后调用WFI或者WFE指令即可进入睡眠模式 另外若内核寄存器SLEEPONEXIT=0,进入“立即睡眠”模式;SLEEPONEXIT=1,进入“退出时睡眠” |
唤醒方式 | 如果是使用WFI指令睡眠,则可使用任意中断唤醒 如果是使用WFE指令睡眠,则由事件唤醒 |
睡眠时 | 关闭内核时钟,内核停止,而外设正常运行,在软件上表现为不再执行新的代码。这个状态会保留睡眠前的内核寄存器、内存数据(RAM数据),所有的I/O引脚都保持它们在运行模式时的状态 |
唤醒延时 | 无延时 |
唤醒后 | 若有中断唤醒,先进入中断,退出中断服务程序后,接着执行WFI指令后的程序;若由事件唤醒,直接执行WFE后的程序 |
注意:在系统处于睡眠低功耗模式(包括后面介绍的停止模式、待机模式)时,不能用下载器进行程序下载,所以下载程序时要先把系统唤醒。或者使用如下方法:按着板子的复位键,点击电脑端的“下载”,然后松开复位键,就能正常给板子下载程序了。
停止模式是在Cortex™-M3的深睡眠模式基础上结合了外设的时钟控制机制,在停止模式下电压调节器可运行在正常或低功耗模式。此时在1.8V供电区域的的所有时钟都被停止,PLL、HSI和HSE RC振荡器的功能被禁止,SRAM和寄存器内容被保留下来。
停止模式的功耗比睡眠模式更低,代码操作上相对比睡眠模式复杂一点。跟睡眠模式的唤醒不同,只有EXTI线的中断才能退出停止模式,因为其他外设在停止模式时被关闭,并不能响应中断。将mani函数替换为下面代码,其他不变:
int main(void)
{
USART1_Config();
EXTI_Key_Config();
NVIC_Configuration();//配置中断控制器NVIC
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);//使能电源管理单元的时钟
while(1)
{
printf("STM32正常运行.\r\n");
Delay(0xfffff);
printf("STM32进入停止模式.\r\n");
PWR_EnterSTOPMode(PWR_Regulator_LowPower,PWR_STOPEntry_WFI);//进入停止模式,设置电压调节器为低功耗模式,等待中断唤醒
//PWR_EnterSTOPMode(PWR_Regulator_ON,PWR_STOPEntry_WFI);//进入停止模式,设置电压调节器为正常模式,等待中断唤醒
//开始等待任意EXTI线中断唤醒
SystemInit();//唤醒后需要恢复系统时钟,因为退出停止模式后,系统默认使用的HSI作为系统时钟
/*实际上在运行main函数前,会先执行SystemInit函数以配置系统时钟,这个在启动文件里可以看到。*/
//刚退出停止模式时,使用HSI作为系统时钟,回影响串口波特率
//输出不对,在重新配置时钟后才能使用串口的输出
Delay(0xfffff);
printf("已退出停止模式.\r\n");
}
}
特性 | 说明 |
---|---|
调压器低功耗模式 | 调压器可工作在正常模式或低功耗模式,可进一步降低功耗 |
进入方式 | 内核寄存器SLEEPDEEP=1,PWR_CR寄存器的PDDS=0,然后调用WFI或者WFE指令即可进入睡眠模式 PWR_CR寄存器的LPDS=0时,调压器工作在正常模式,LPDS=1时工作在低功耗模式 除了通过配置寄存器,还可以直接调用标准库函数PWR_EnterSTOPMode,如上代码 |
唤醒方式 | 如果是WFI指令进入停止模式的,可使用任意EXTI线的中断唤醒 如果是WFE指令进入停止模式的,可使用任意配置为事件模式的EXTI线事件唤醒 |
停止时 | 内核停止,外设也停止。这个状态会保留停止前的内核寄存器、内存的数据(RAM) |
唤醒方式 | 基础延时是HSI振荡器的启动时间,若调压器工作在低功耗模式,还需要加上调压器从低功耗模式切换至正常模式下的时间 |
唤醒后 | 若有中断唤醒,先进入中断,退出中断服务程序后,接着执行WFI指令后的程序;若由事件唤醒,直接执行WFE后的程序。唤醒后,STM32会使用HSI作为系统时钟 |
待机模式可实现系统的最低功耗。该模式是在Cortex-M3深睡眠模式时关闭电压调节器。整个1.8V供电区域被断电。PLL、HSI和HSE振荡器也被断电。SRAM和寄存器内容丢失。只有备份的寄存器和待机电路维持供电。也就是说从待机模式唤醒后,由于没有之前代码的运行记录,只能对芯片复位,重新检测boot条件,从头开始执行程序。它有4种唤醒方式,分别是WAUP(PA0)引脚的上升沿、RTC闹钟事件、NRST引脚的复位和IWDG(独立看门狗)复位。
我们不需要对PA.0引脚进行如上面的初始化,也不需要初始化一个外部中断,将下面两个函数替换,其余保持不变:
int main(void)
{
USART1_Config();
//EXTI_Key_Config();
//配置中断控制器NVIC
NVIC_Configuration();
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);//使能电源管理单元的时钟
if(PWR_GetFlagStatus(PWR_FLAG_WU)){
printf("\r\n待机模式复位.\r\n");
}else{
printf("\r\n非待机模式复位.\r\n");
}
while(1)
{
printf("STM32正常运行.\r\n");
Delay(0xfffff);
printf("STM32进入待机模式.\r\n");
PWR_ClearFlag(PWR_FLAG_WU);//清除唤醒标志状态
PWR_WakeUpPinCmd(ENABLE);//使能Wake引脚的唤醒功能,使能PA0
PWR_EnterSTANDBYMode();//进入待机模式
}
}
static void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
//NVIC_Init(&NVIC_InitStructure);//使能外部中断 PA.0
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_Init(&NVIC_InitStructure);//使能串口1中断
}
程序下载后,按下复位键,串口会打印非待机模式复位,接着运行while函数,延时后进入待机模式,等待唤醒,在PA0引脚给上升沿信号,串口打印已待机模式复位,代码重头开始执行。
特性 | 说明 |
---|---|
进入方式 | 内核寄存器SLEEPDEEP=1,PWR_CR寄存器中的PDDS=1,PWR_CR寄存器中的唤醒状态为WUF=0,然后调用WFI或者调用WFE指令即可进入待机模式(没区别) |
唤醒方式 | 通过WAUP引脚的上升沿,RTC闹钟、唤醒、入侵、时间戳事件或NRST引脚外部复位及IWDG复位唤醒 |
待机时 | 内核停止,片上外设也停止;内核寄存器、内存的数据会丢失;除了复位引脚、RTC_AF1引脚及WAUP引脚外,其他I/O口均工作在高阻态 |
唤醒延迟 | 芯片复位时间 |
唤醒后 | 相当于芯片复位,在程序表现为从头开始执行代码(等于重新上电) |
最后,除了利用上面三种模式实现低功耗外,还可以在运行模式下,关闭某些外设时钟或者利用分频器降低外设时钟,同样也可以减低系统功耗。电池备份区域部分这里没有细说,后面单独一篇介绍。
道虽迩,不行不至,事虽小,不为不成。——《荀子》