
摘要:很多单片机都有低功耗模式,STM32也不例外。在系统或电源复位以后,微控制器处于运行状态。运行状态下的HCLK为CPU提供时钟,内核执行程序代码。当CPU不需继续运行时,可以利用多个低功耗模式来节省功耗,例如等待某个外部事件时。用户需要根据最低电源消耗,最快速启动时间和可用的唤醒源等条件,选定一个最佳的低功耗模式。 单片机内部功率是各功能部分功率的总和,低功耗模式是通过关掉部分内部功能达到省电。STM32F103单片机共有3种低功耗模式,不同模式会对系统正常工作有一定影响,需要按实际情况选择,低功耗模式只针对单片机内部功能,外接电路产生的功耗不在其内。
![]() 其中,时钟源产生的时钟需要供给SRAM、ARM内核以及内部功能来使用。而我们的用户程序在上电之后,需要从FLASH当中调入到SRAM当中来运行,所以SRAM起到了程序运行的载体。然后程序再来控制ARM内核进行运算和处理,最终控制内部功能,包括IO端口、ADC、 IIC、SPI总线之类,最终达到我们需要的控制效果。只有这四个部分全部工作、通力配合,才能让单片机在正常模式下顺畅工作。 如果我们需要进入低功耗模式,那么就需要在这四个部分当中关闭一些功能。那么哪个部分的功能可以独立关闭?哪个部分的功能需要配合关闭呢? 首先看时钟源。时钟源是为其他三个部分提供时钟信号的。如果时钟源独立关闭,那么其他三个部分也将停止工作。所以时钟源是不能独立关闭的。 ![]() 再看SRAM内存,SRAM内存是用来存储用户程序的,程序需要在SRAM当中运行。如果独立关掉SRAM,程序将不能执行。就无法控制内核进行计算,更不能控制内部功能来达到我们的应用效果,一旦程序停止,整个系统将不能工作。所以SRAM也不能独立关闭。 再看内部功能,内部功能所面对的是直接输出应用,包括IO端口ADC、IIC、SPI,这些功能都是直接通过引脚输出到外部电路的。如果内部功能独立关闭,那么就等于切断了外部的所有联系,即使时钟源正常工作、程序正常运行、ARM内核正常计算,但是这些所有的工作都不能通过内部功能来向外输出,那么整个单片机也起不到任何作用,所以内部功能也不能单独关闭。 ![]() 最后是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。
三、停机模式 在保持SRAM和寄存器内容不丢失的情况下,停机模式可以达到最低的电能消耗。在停机模式下,停止所有内部1.8V部分的供电,PLL、HSI的RC振荡器和HSE晶体振荡器被关闭,调压器可以被置于普通模式或低功耗模式。可以通过任一配置成EXTI的信号把微控制器从停机模式中唤醒,EXTI信号可以是16个外部I/O口之一、PVD的输出、RTC闹钟或USB的唤醒信号。
四、待机模式 在待机模式下可以达到最低的电能消耗。内部的电压调压器被关闭,因此所有内部1.8V部分的供电被切断;PLL、HSI的RC振荡器和HSE晶体振荡器也被关闭;进入待机模式后,SRAM和寄存器的内容将消失,但后备寄存器的内容仍然保留,待机电路仍工作。从待机模式退出的条件是:NRST上的外部复位信号、IWDG复位、WKUP引脚上的一个上升边沿或RTC的闹钟定时。
3种模式的功耗 单片机最小系统电路功耗,不精确测量值
五、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闹钟中断唤醒待机模式,假设现在利用STM3进行超声波测距,当距离大于1米时,单片机进入休眠状态,10秒钟后RTC自动唤醒STM32继续测量。在一米范围内不休眠,一但距离大于1米就开始休眠,10秒后唤醒继续测量。 rtc.c #include "sys.h"3 K( \5 S n7 @/ K& X: \8 F#include "delay.h". A# x# s" T. F #include "rtc.h" //实时时钟配置 //初始化RTC时钟,同时检测时钟是否工作正常3 F; o. ~; z, D- b: U Z void RTC_Init()0 s* H" Y! m3 u+ j6 x. ] { NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP|RCC_APB1Periph_PWR,ENABLE);//使能PWR和BKP外设时钟 //默认情况下,RTC 所属的备份域禁止访问,可使用库函数 PWR_BackupAccessCmd 使能访问% d0 `' h& Z& g! M$ \5 ^) O. O PWR_BackupAccessCmd(ENABLE);//允许访问备份区域,后备区域解锁 if(PWR_GetFlagStatus(PWR_FLAG_SB)!=RESET) //如果现在处于待机模式# K1 \% Z; d( v2 u {+ g! c, X5 l m7 t+ I$ w1 t PWR_ClearFlag(PWR_FLAG_SB); //清除待机模式* Z, z/ b- g- P. Z8 T* p* b8 { RTC_ITConfig(RTC_IT_SEC, ENABLE); //打开RTC中断 RTC_WaitForSynchro(); //等待RTC寄存器同步 } else- v6 Q# E. h$ c: G4 J2 } { 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时钟 , O6 y5 V. E& {% K ?& s5 U& X, B RTC_WaitForLastTask(); //等待RTC寄存器同步,因为RTC时钟是低速的,内环时钟是高速的,所以要同步 RTC_WaitForSynchro(); //写寄存器之前要确保上一次RTC的操作完成 & H* M- c+ n8 p' \2 {' i3 s //下面这两条语句是开启RTC秒中断的函数,每过1秒钟产生一次中断,就进入了中断服务函数' g2 Y) r* [( v* g* R A RTC_ITConfig(RTC_IT_SEC, ENABLE);//使能RTC秒中断 RTC_WaitForLastTask(); //确保上一次 RTC 的操作完成 ' s: C/ O ~0 G5 l& o9 d RTC_EnterConfigMode(); //进入RTC配置模式 RTC_SetCounter(0); //初始值设定为0s 在使用本函数前必须先调用函数RTC_WaitForLastTask();等待标志位RTOFF被设置) d1 a& X4 y* N: V 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 配置模式$ y$ G& a* A; c- } } NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn; 4 ?4 u: E+ K/ D, h1 | NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;- s! ~' m8 C& S9 `% W; C3 S NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; ; P5 T" j, C4 d& w/ O* b/ G F; H NVIC_Init(&NVIC_InitStructure); 8 T8 O/ F; |4 [! l% a }6 X: N f% N S void RTC_IRQHandler() { if(RTC_GetITStatus(RTC_IT_SEC)!=RESET) //是否秒中断发生 { $ r6 L/ h0 D E- ?, A X printf("Time is =%d \r\n",RTC_GetCounter()); //输出此时的秒数 } RTC_WaitForLastTask();& ~# G/ Q: u9 W }0 B6 z RTC_ClearITPendingBit(RTC_IT_SEC|RTC_IT_OW); //清除秒中断标志位和溢出位 } rtc.h #ifndef __RTC_H#define __RTC_H #include "sys.h". U# a% v, Z/ r; ?/ i) k ) y) M6 a, E" x! {$ H+ _4 H8 \ void RTC_Init(void);* D4 D$ }! u4 f3 p: p #endif main.c int main(void){ int Distance_data=0; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //NVIC优先级分组2 ! U: z9 d$ S% x& O delay_init(); //延时函数初始化 uart_init(115200); //串口1初始化 PA9-RX PA10-TX LED_Init(); //LED端口初始化 Beep_Init(); //BEEP端口初始化8 C, n1 }0 i* T, \8 j/ `4 i! s OLED_Init() ; //OLED端口初始化 RTC_Init();( s( b6 j. [7 F: v while(1) { . ^& s7 n! r4 v% O Distance_data=Ultrasonic_Ranging(); if(Distance_data>=1500) { printf("进入休眠!\n");8 q/ o: V. Y5 o, }! { RTC_SetAlarm(RTC_GetCounter()+10); //闹钟在此时刻加上10秒" Y. @( M- G, C+ l, s" j RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成) l: ^2 @2 \" n PWR_EnterSTANDBYMode(); //进入待机(STANDBY)模式 # Z- u4 a- i0 H }% Z! W8 w1 b( W }! U4 T1 \9 M) \/ |1 S: K' y } |