![b299829ed10a9c6cf7c27f26a2348cdc.png](https://i-blog.csdnimg.cn/blog_migrate/59b28a515504b50d880c921053ee2565.jpeg)
很多单片机都有低功耗模式,STM32 也不例外。在系统或电源复位以后,微控制器处于运行状态。运行状态下的HCLK为CPU提供时钟,内核执行程序代码。当CPU不需继续运行时,可以利用多个低功耗模式来节省功耗,例如等待某个外部事件时。用户需要根据最低电源消耗,最快速启动时间和可用的唤醒源等条件,选定一个最佳的低功耗模式。
单片机内部功率是各功能部分功率的总和,低功耗模式是通过关掉部分内部功能达到省电。STM32F103单片机共有3种低功耗模式,不同模式会对系统正常工作有一定影响,需要按实际情况选择,低功耗模式只针对单片机内部功能,外接电路产生的功耗不在其内。
![2b90c0a67e641d5b0be0ca21a4632362.png](https://i-blog.csdnimg.cn/blog_migrate/b379d91b7cca005d1a81fbd63aceb51d.jpeg)
一、单片机功耗简介
![823683e9d086beda22e8f6272d7bcc53.png](https://i-blog.csdnimg.cn/blog_migrate/3e7a2be140aa6f30745324ee17bcfd17.jpeg)
单片机内部比较耗电的部分主要有内部功能、ARM内核、SRAM内存以及时钟源、分频器这四大部分组成。
其中,时钟源产生的时钟需要供给SRAM、ARM内核以及内部功能来使用。而我们的用户程序在上电之后,需要从FLASH当中调入到SRAM当中来运行,所以SRAM起到了程序运行的载体。然后程序再来控制ARM内核进行运算和处理,最终控制内部功能,包括IO端口、ADC、 IIC、SPI总线之类,最终达到我们需要的控制效果。只有这四个部分全部工作、通力配合,才能让单片机在正常模式下顺畅工作。
如果我们需要进入低功耗模式,那么就需要在这四个部分当中关闭一些功能。那么哪个部分的功能可以独立关闭?哪个部分的功能需要配合关闭呢?
首先看时钟源。时钟源是为其他三个部分提供时钟信号的。如果时钟源独立关闭,那么其他三个部分也将停止工作。所以时钟源是不能独立关闭的。
![14f1345ec0eec931e727c7cba69159bd.png](https://i-blog.csdnimg.cn/blog_migrate/ec36681f2bc0aeb99d9e411f839d94df.png)
再看SRAM内存,SRAM内存是用来存储用户程序的,程序需要在SRAM当中运行。如果独立关掉SRAM,程序将不能执行。就无法控制内核进行计算,更不能控制内部功能来达到我们的应用效果,一旦程序停止,整个系统将不能工作。所以SRAM也不能独立关闭。
再看内部功能,内部功能所面对的是直接输出应用,包括IO端口ADC、IIC、SPI,这些功能都是直接通过引脚输出到外部电路的。如果内部功能独立关闭,那么就等于切断了外部的所有联系,即使时钟源正常工作、程序正常运行、ARM内核正常计算,但是这些所有的工作都不能通过内部功能来向外输出,那么整个单片机也起不到任何作用,所以内部功能也不能单独关闭。
![b8585303d8e8f65ea8e6347641e139fb.png](https://i-blog.csdnimg.cn/blog_migrate/a6711faca248ff021623fe9e883c6e2f.jpeg)
最后是ARM内核,如果时钟源保持工作,SRAM当中的程序正常运行,内部功能也正常与外界沟通,如果只是关闭ARM内核,那么也不会对其他部分产生影响,比如IO端口正常的输出高低电平,ADC通过DMA正在独立的转换模拟信号,如果这些工作都不需要ARM内核来参与的话。那么ARM内核就可以停止工作,这样就节省了一部分的功耗。
当内部功能突然需要ARM内核来参与运算时。只要向ARM内核发送一个中断信号,ARM内核就会重新启动开始工作,来处理内部功能的相关请求。这种只关闭ARM内核的方式可以减少一部分功耗,于是我们把这种低功耗方式叫做睡眠模式。睡眠模式就是要单独关闭ARM内核,而其他三个部分保持正常工作。
接下来如果我们想进一步降低功耗,还可以关掉哪个部分呢?除了ARM内核之外,还可以关掉内部功能。
由于ARM内核最终控制的是内部功能,它作为ARM内核的配合工作也可以被关掉。也就是说,如果在某种条件下不需要内部功能来参与一些工作,这时我们就可以把ARM内核和内部功能全部关掉,如果这两个部分全部关掉,那么时钟源和分频器他们主要就是提供给ARM内核和内部功能的。如果这两个功能关掉,那么使时钟源也可以被关掉,但是由于SRAM当中存放着用户程序,为了可以让程序在唤醒的时候继续运行,我们这里还是要让SRAM保持供电来保存用户程。于是这就产生了第二种低功耗模式,也就是关闭ARM内核、关闭内部功能,同时也关闭与二者相关的时钟源和分频器,只保持SRAM当中的程序,这样就可以更进一步的降低功耗,这就是所谓的停机模式。
停机模式就是要关闭内部功能,关闭ARM内核,同时关闭是时钟源。最后一种终极低功耗模式就是将SRAM的电源也要关闭。SRAM的电源一旦关闭,内部的程序将会消失。单片机再次唤醒时程序只能从第一条从头开始执行,之前运行的数据将全部消失。这也就是所谓的待机模式。待机模式就是将四个部分的电源全部关闭。以达到最大的低功耗效果,以上介绍的就是三种低功耗模式的基本的省电原理。它的本质就是参考四个模式的协调工作状态,按照对系统的影响大小,由轻到重不断关闭相关功能。我们在使用低功耗模式的时就需要考虑到实际的程序应用对于各部分功能的依赖性从而选择适合的低功耗模式。
二、睡眠模式
在睡眠模式,只有CPU停止,所有外设处于工作状态并可在发生中断/事件时唤醒CPU。
- 在ARM内核无事可做的时候,可以进入睡眠模式。
- 电脑的CPU空闲状态就是单片机睡眠模式。
- 睡眠模式的应用不多,因只关闭ARM内核,节能有限,很少在非操作系统程序(裸机)中使用。
- 在嵌入式操作系统中,会采用睡眠模式。
优点:对系统影响最小。缺点:节能效果最差
三、停机模式
在保持SRAM和寄存器内容不丢失的情况下,停机模式可以达到最低的电能消耗。在停机模式下,停止所有内部1.8V部分的供电,PLL、HSI的RC振荡器和HSE晶体振荡器被关闭,调压器可以被置于普通模式或低功耗模式。可以通过任一配置成EXTI的信号把微控制器从停机模式中唤醒,EXTI信号可以是16个外部I/O口之一、PVD的输出、RTC闹钟或USB的唤醒信号。
- 因SRAM内容不消失,程序不复位,可在唤醒后继续运行。
- 节能效果与待机模式近似,却有着更多优势。
- 主要用于电池供电的设备上,提高电池寿命。
- 在电池供电的产品中必须使用,在外部供电的产品中没必要使用。
优点:节能效果好,程序不会复位缺点:恢复时间较长
四、待机模式
在待机模式下可以达到最低的电能消耗。内部的电压调压器被关闭,因此所有内部1.8V部分的供电被切断;PLL、HSI的RC振荡器和HSE晶体振荡器也被关闭;进入待机模式后,SRAM和寄存器的内容将消失,但后备寄存器的内容仍然保留,待机电路仍工作。从待机模式退出的条件是:NRST上的外部复位信号、IWDG复位、WKUP引脚上的一个上升边沿或RTC的闹钟到时。
- 由于SRAM内容消失,唤醒后程序必须复位,从头开始运行。
- 因为待机和停机之间的功耗差别是uA级的,几乎没有差别,所以开发者大多使用停机模式,待机模式极少使用。
- 在一些偶尔需要工作的场合,且工作量不大、不复杂的情况下,待机模式可以保证最低的功耗。
- 比如应用在室外温度测量产品上,每1小时测量一次。可用RTC闹钟唤醒,测量完再待机。
优点:最节能。 缺点:程序会复位,只有少数条件可唤醒
3种模式的功耗
单片机最小系统电路功耗,不精确测量值
- 正常模式:10mA
- 睡眠模式:2mA
- 停机模式:20uA
- 待机模式:2uA
因不同型号,不同工艺差异,不同程序下功耗不同这里只能给出大概范围。
五、RTC实时时钟简介
STM32的RTC外设(Real Time Clock),实质是一个掉电后还继续运行的定时器。从定时器的角度来说,相对于通用定时器TIM外设,它十分简单,只有很纯粹的计时和触发中断的功能;但从掉电还继续运行的角度来说,它却是STM32中唯一一个具有如此强大功能的外设。所以RTC外设的复杂之处并不在于它的定时功能,而在于它掉电还继续运行的特性。
以上所说的掉电,是指主电源VDD断开的情况,为了RTC外设掉电继续运行,必须接上锂电池给STM32的RTC、备份发卡通过VBAT引脚供电。当主电源VDD有效时,由VDD给RTC外设供电;而当VDD掉电后,由VBAT给RTC外设供电。但无论由什么电源供电,RTC中的数据都保存在属于RTC的备份域中,若主电源Vpp和VBAT都掉电,那么备份域中保存的所有数据将丢失。备份域除了RTC模块的寄存器,还有42个16位的寄存器可以在VDD掉电的情况下保存用户程序的数据,系统复位或电源复位时,这些数据也不会被复位。
从RTC的定时器特性来说,它是一个32位的计数器,只能向上计数。它使用的时钟源有三种,分别为高速外部时钟的128分频(HSE/128)、低速内部时钟LSI以及低速外部时钟LSE;使HSE分频时钟或LSI的话,在主电源VDD掉电的情况下,这两个时钟来源都会受到影响,因此没法保证RTC正常工作。因此RTC一般使用低速外部时钟LSE,在设计中,频率通常为实时时钟模块中常用的32.768KHz,这是因为32768=2^15,分频容易实现,所以它被广泛应用到RTC模块。==在主电源Vpp有效的情况下(待机),RTC还可以配置闹钟事件使STM32退出待机模式==。
![ba4a8455b292fdbd1d47225bfd8e26c7.png](https://i-blog.csdnimg.cn/blog_migrate/5d0c95cb5bb45eeac82b504a70ba920c.jpeg)
框图中浅灰色的部分都是属于备份域的,在VDD掉电时可在VBAT的驱动下继续运行。这部分仅包括RTC的分频器,计数器,和闹钟控制器。==若VDD电源有效,RTC可以触发RTC Second(秒中断)、RTCOverflow(溢出事件)和RTCAlarm(闹钟中断)==。从结构图可以分析到,其中的定时器溢出事件无法被配置为中断。若STM32原本处于待机状态,可由闹钟事件或WKUP事件(外部唤醒事件,属于EXTI模块,不属于RTC)使它退出待机模式。闹钟事件是在计数器RTCCNT的值等于闹钟寄存器RTCALR的值时触发的。
![500b7ca2ce054644250bf09521179fab.png](https://i-blog.csdnimg.cn/blog_migrate/044ab07ac9ef09bf7dbe8bf6422196fe.jpeg)
在备份域中所有寄存器都是16位的,RTC控制相关的寄存器也不例外。它的计数器RTCCNT的32位由RTCCNTL和RTCCNTH两个寄存器组成,分别保存定时计数值的低16位和高16位。在配置RTC模块的时钟时,通常把输入的32768Hz的RTCCLK进行32768分频得到实际驱动计数器的时钟TR CLK=RTCCLK/32768=1Hz,计时周期为1秒,计时器在TR_CLK的驱动下计数,即每秒计数器RTC_CNT的值加1。
六、程序讲解
为了能够更加清楚地了解利用RTC闹钟中断唤醒待机模式,假设现在利用STM3进行超声波测距,当距离大于1米时,单片机进入休眠状态,10秒钟后RTC自动唤醒STM32继续测量。在一米范围内不休眠,一但距离大于1米就开始休眠,10秒后唤醒继续测量。
rtc.c
#include "sys.h"
#include "delay.h"
#include "rtc.h"
//实时时钟配置
//初始化RTC时钟,同时检测时钟是否工作正常
void RTC_Init()
{
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP|RCC_APB1Periph_PWR,ENABLE);//使能PWR和BKP外设时钟
//默认情况下,RTC 所属的备份域禁止访问,可使用库函数 PWR_BackupAccessCmd 使能访问
PWR_BackupAccessCmd(ENABLE);//允许访问备份区域,后备区域解锁
if(PWR_GetFlagStatus(PWR_FLAG_SB)!=RESET) //如果现在处于待机模式
{
PWR_ClearFlag(PWR_FLAG_SB); //清除待机模式
RTC_ITConfig(RTC_IT_SEC, ENABLE); //打开RTC中断
RTC_WaitForSynchro(); //等待RTC寄存器同步
}
else
{
BKP_DeInit();//复位备份区域 使用此函数必须调用RCC_APB1PeriphClockCmd()函数
RCC_LSEConfig(RCC_LSE_ON);//设置外部低速晶振(LSE),外部32.768KHZ晶振开启
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) //等待稳定
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //设置RTC时钟(RTCCLK),选择LSE(晶振频率为 32.768KHz)作为RTC时钟
RCC_RTCCLKCmd(ENABLE); //使能RTC时钟
RTC_WaitForLastTask(); //等待RTC寄存器同步,因为RTC时钟是低速的,内环时钟是高速的,所以要同步
RTC_WaitForSynchro(); //写寄存器之前要确保上一次RTC的操作完成
//下面这两条语句是开启RTC秒中断的函数,每过1秒钟产生一次中断,就进入了中断服务函数
RTC_ITConfig(RTC_IT_SEC, ENABLE);//使能RTC秒中断
RTC_WaitForLastTask(); //确保上一次 RTC 的操作完成
RTC_EnterConfigMode(); //进入RTC配置模式
RTC_SetCounter(0); //初始值设定为0s 在使用本函数前必须先调用函数RTC_WaitForLastTask();等待标志位RTOFF被设置
RTC_WaitForLastTask();
RTC_SetPrescaler(32767); //设置RTC分频: 使 RTC周期为1s RTC period = RTCCLK/RTC_PR = (32.768 KHz)/(32767+1) = 1HZ 在使用本函数前必须先调用函数RTC_WaitForLastTask();等待标志位RTOFF被设置
RTC_WaitForLastTask(); //确保上一次 RTC 的操作完成
RTC_ExitConfigMode(); //退出RTC 配置模式
}
NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void RTC_IRQHandler()
{
if(RTC_GetITStatus(RTC_IT_SEC)!=RESET) //是否秒中断发生
{
printf("Time is =%d rn",RTC_GetCounter()); //输出此时的秒数
}
RTC_WaitForLastTask();
RTC_ClearITPendingBit(RTC_IT_SEC|RTC_IT_OW); //清除秒中断标志位和溢出位
}
rtc.h
#ifndef __RTC_H
#define __RTC_H
#include "sys.h"
void RTC_Init(void);
#endif
main.c
int main(void)
{
int Distance_data=0;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //NVIC优先级分组2
delay_init(); //延时函数初始化
uart_init(115200); //串口1初始化 PA9-RX PA10-TX
LED_Init(); //LED端口初始化
Beep_Init(); //BEEP端口初始化
OLED_Init() ; //OLED端口初始化
RTC_Init();
while(1)
{
Distance_data=Ultrasonic_Ranging();
if(Distance_data>=1500)
{
printf("进入休眠!n");
RTC_SetAlarm(RTC_GetCounter()+10); //闹钟在此时刻加上10秒
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
PWR_EnterSTANDBYMode(); //进入待机(STANDBY)模式
}
}
}
![b584f37532286827e4b4c39895abffc3.gif](https://i-blog.csdnimg.cn/blog_migrate/fe4c00af8c5a2458557d0e94303a4b7a.gif)