stm32的低功耗模式和RTC闹钟唤醒

时间:2021-02-06 14:35:18
一 、STM32待机模式简介

很多单片机都有低功耗模式,STM32也不例外。在系统或电源复位以后,微控制器处于运行状态。运行状态下的HCLK为CPU提供时钟,内核执行程序代码。当CPU不需继续运行时,可以利用多个低功耗模式来节省功耗,例如等待某个外部事件时。用户需要根据最低电源消耗,最快速启动时间和可用的唤醒源等条件,选定一个最佳的低功耗模式。

STM32的低功耗模式有3种:

1)睡眠模式(CM3内核停止,外设仍然运行)

2)停止模式(所有时钟都停止)

3)待机模式(1.8V内核电源关闭)

在运行模式下,我们也可以通过降低系统时钟关闭APB和AHB总线上未被使用的外设的时钟来降低功耗。三种低功耗模式一览表见表21.1.1所示:


stm32的低功耗模式和RTC闹钟唤醒

表21.1.1 STM32低功耗一览表

在这三种低功耗模式中,最低功耗的是待机模式,在此模式下,最低只需要2uA左右的电流。停机模式是次低功耗的,其典型的电流消耗在20uA左右。最后就是睡眠模式了。用户可以根据自己的需求来决定使用哪种低功耗模式。

现在仅对STM32的最低功耗模式-待机模式,来做介绍。待机模式可实现STM32的最低功耗。该模式是在CM3深睡眠模式时关闭电压调节器。整个1.8V供电区域被断电。PLL、HSI和HSE振荡器也被断电。SRAM和寄存器内容丢失。仅备份的寄存器和待机电路维持供电。

那么我们如何进入待机模式呢?其实很简单,只要按图21.1.1所示的步骤执行就可以了:

stm32的低功耗模式和RTC闹钟唤醒

图1 STM32进入及退出待机模式的条件

图1还列出了退出待机模式的操作,从图1可知,我们有4种方式可以退出待机模式,即当一个外部复位(NRST引脚)、IWDG复位、WKUP引脚上的上升沿或RTC闹钟事件发生时,微控制器从待机模式退出。从待机唤醒后,除了电源控制/状态寄存器(PWR_CSR),所有寄存器被复位。

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

在进入待机模式后,除了复位引脚以及被设置为防侵入或校准输出时的TAMPER引脚和被使能的唤醒引脚(WK_UP脚),其他的IO引脚都将处于高阻态。

图1已经清楚的说明了进入待机模式的通用步骤,其中涉及到2个寄存器,即电源控制寄存器(PWR_CR)和电源控制/状态寄存器(PWR_CSR)。下面我们介绍一下这两个寄存器:

电源控制寄存器(PWR_CR),该寄存器的各位描述如图2所示:

stm32的低功耗模式和RTC闹钟唤醒

图2 PWR_CR寄存器各位描述 

这里我们通过设置PWR_CR的PDDS位,使CPU进入深度睡眠时进入待机模式,同时我们通过CWUF位,清除之前的唤醒位。电源控制/状态寄存器(PWR_CSR)的各位描述如图3所示:


stm32的低功耗模式和RTC闹钟唤醒

图3 PWR_ CSR寄存器各位描述

二、这里主要说明RTC闹钟中断唤醒休眠

配置RTC前需知:

BKP:
RTC模块和时钟配置系统的寄存器是在后备区域的(即BKP),通过BKP后备区域来存储RTC配置的数据可以让在系统复位或待机模式下唤醒后 RTC里面配置的数据维持不变。

PWR:
PWR为电源的寄存器,我们需要用到的是电源控制寄存器(PWR_CR),通过使能PWR_CR的DBP位来取消后备区域BKP的写保护。

RTC :
由一组可编程计数器组成,分成两个主要模块。第一个模块是RTC的预分频
模块,它可编程产生最长为1 秒的RTC时间基准TR_CLK 。RTC的预分频模块包含了一个20位的
可编程分频器(RTC 预分频器) 。如果在RTC_CR寄存器中设置了相应的允许位,则在每个实时时钟(RTC)TR_CLK 周期中RTC产生一个中断( 秒中断) 。第二个模块是一个32位的可编程计数器,可被初始化为当前的系统时间。系统时间按TR_CLK 周期累加并与存储在RTC_ALR寄存器中的可编程时间相比较,如果RTC_CR控制寄存器中设置了相应允许位,比较匹配时将产生一个闹钟中断。

   RTC闹钟唤醒事件发生时,同时进入闹钟中断,必须在初始化时与外部中断线17关联  

   如果仅想退出待机模式,RTC闹钟事件已经足够,不必与外部中断线17关联

   退出待机模式后,接下来的流程类似于按下复位按键,程序会从头开始执行

   RTC的配置如下:

  1. NVIC_InitTypeDef NVIC_InitStructure;  
  2. EXTI_InitTypeDef EXTI_InitStructure;  

  1. //闹钟中断接到第17线外部中断  
  2.     EXTI_ClearITPendingBit(EXTI_Line17);  
  3.     EXTI_InitStructure.EXTI_Line = EXTI_Line17;  
  4.     EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;  
  5.     EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;  
  6.     EXTI_InitStructure.EXTI_LineCmd = ENABLE;  
  7.     EXTI_Init(&EXTI_InitStructure);  
  
  1. //设置RTC闹钟中断  
  2.     NVIC_InitStructure.NVIC_IRQChannel = RTCAlarm_IRQn;  
  3.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  
  4.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  
  5.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  
  6.     NVIC_Init(&NVIC_InitStructure);  

本例使用内部晶振LSI 40KHZ,如果使用外部LSE 为32.768khz   void RTC_Configuration(void)
{
  RCC_APB1PeriphClockCmd (RCC_APB1Periph_PWR | RCC_APB1Periph_BKP,ENABLE );// 通过设置寄存器RCC_APB1ENR的PWREN 和BKPEN位来打开电源和后备接口的时钟 
  PWR_BackupAccessCmd(ENABLE);//电源控制寄存器(PWR_CR) 的DBP位来使能对后备寄存器和RTC的访问
  BKP_DeInit();初始化复位BKP寄存器
  RCC_LSEConfig(RCC_LSE_OFF);//关闭LSE
  RCC_LSICmd(ENABLE);//使能LSI为40khz
  while(RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET);//设置后需要等待启动
  {
  }
  RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);////选择LSI为RTC设备 的 时钟,LSI为40khz
  RCC_RTCCLKCmd(ENABLE);;//使能RTC
  RTC_WaitForSynchro();//等待同步
  RTC_SetPrescaler(310); //设置预分频值RTC period = RTCCLK/RTC_PR = (40KHz)/(310+1) 约等于7.775ms
  RTC_WaitForLastTask();/* 1.   查询RTOFF位,直到RTOFF的值变为1 */
  RTC_ITConfig(RTC_IT_ALR, ENABLE);         //使能闹钟中断
  RTC_WaitForLastTask();
}

闹钟唤醒中断代码:

void RTCAlarm_IRQHandler(void)
{
  if(RTC_GetITStatus(RTC_IT_ALR) != RESET)
  {
    /* Clear EXTI line17 pending bit */
    EXTI_ClearITPendingBit(EXTI_Line17);  // 清EXTI_Line17挂起位 
    /* Check if the Wake-Up flag is set */
    if(PWR_GetFlagStatus(PWR_FLAG_WU) != RESET)// 检查唤醒标志是否设置 
    {
      /* Clear Wake Up flag */
      PWR_ClearFlag(PWR_FLAG_WU);
    }
    
    /* Wait until last write operation on RTC registers has finished */
    RTC_WaitForLastTask();   
    /* Clear RTC Alarm interrupt pending bit */
    RTC_ClearITPendingBit(RTC_IT_ALR);
    /* Wait until last write operation on RTC registers has finished */
    RTC_WaitForLastTask();

  下面写进去闹钟中断后执行的代码

     }

}


休眠时间长短封装函数

void Sleep(uint AlarmValue)
{
  /* Wait till RTC Second event occurs */
  RTC_ClearFlag(RTC_FLAG_SEC);
  while(RTC_GetFlagStatus(RTC_FLAG_SEC) == RESET);
  

  RTC_SetAlarm(RTC_GetCounter()+ AlarmValue);    //当前时间+预警时间
  /* Wait until last write operation on RTC registers has finished */
  RTC_WaitForLastTask();
  
  /* Request to enter STOP mode with regulator in low power mode*/
  PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI);
  
  /* Configures system clock after wake-up from STOP: enable HSE, PLL and select 
  PLL as system clock source (HSE and PLL are disabled in STOP mode) */
  SYSCLKConfig_STOP();
}

休眠时间计算

     休眠时间主要是看这个  RTC_SetAlarm(RTC_GetCounter()+ AlarmValue); RTC_SetPrescaler(value); 休眠时间AlarmValue*预分频算出来的时间。