STM32 RTC与待机模式

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhejfl/article/details/83271328

目录

1、背景

1.1什么是待机模式

1.2退出待机模式

1.2.1RTC唤醒退出低功耗模式(待机为例)

1.2.3 进入待机模式之前的建议操作顺序:

2、RTC初始化和配置

2.1 RTC寄存器写保护

2.2 RTC日历初始化和配置

2.3 设置RTC周期性唤醒

3、进入待机模式

4、实操

8、参考文献


1、背景

最近用到低功耗模式-待机模式和RTC唤醒,因此重新梳理RTC和睡眠模式--待机模式。

1.1什么是待机模式

待机模式: 基于CortexTM-M4F深度睡眠模式,其中调压器被禁止,因此1.2V域断电---PLL、HSI振荡器和HSE振荡器也将关闭。

备份域(RTC寄存器、RTC备份寄存器和备份SRAM)和待机电路中的寄存器外,SRAM和寄存器内容都将丢失。

从待机模式唤醒后, 除了备份域+PWR电源控制/状态寄存器(PWR_CSR)外,所有寄存器都将复位。因此程序将按照复位(启动引脚采样、复位向量已获取)后的方式重新执行。

1.2退出待机模式

检测到外部复位(NRST引脚)、IWDG复位、WKUP引脚上升沿、RTC闹钟、RTC唤醒事件、入侵事件或时间戳事件时,微处理器退出待机模式。

特别是RTC的复用功能从低功耗模式唤醒MCU,需要我特别写一章。RTC复用功能包括RTC闹钟(闹钟A和闹钟B)、RTC唤醒事件、RTC入侵事件和RTC时间戳事件。

通过使用RTC闹钟或RTC唤醒事件,无需依赖外部中断即可将系统从低功耗模式唤醒-----自动唤醒模式。

1.2.1RTC唤醒退出低功耗模式(待机为例)

基于STM32F4的RTC唤醒中断知识

要通过RTC唤醒事件从待机模式唤醒器件,必须

a》使能RTC_CR寄存器中的RTC唤醒中断

b》配置RTC已生成RTC唤醒事件。

使能RTC唤醒中断,需按照如下顺序操作:

1、将EXTI线22配置为中断模式并将其使能,然后选择上升沿有效

2、配置NVIC中的RTC_WKUP IRQ通道并将其使能

3、配置RTC以生成RTC唤醒定时器事件

STM32F4xx能够处理外部或内部事件来唤醒内核(WFE)。

仅当RTC时钟源为LSE或LSI时,才能从停机和待机模式中唤醒。

1.2.3 进入待机模式之前的建议操作顺序:

从RTC唤醒从低功耗模式唤醒器件时的属性怒

a》禁止RTC唤醒中断(RTC_CR寄存器中WUTIE位 清0);

b》将RTC唤醒(WUTF)标志清零

c》将PWR唤醒(WUF)标志清零

d》使能RTC环形中断

e》重新进入低功耗模式

2、RTC初始化和配置

2.1 RTC寄存器写保护

系统复位(区分上电复位)后,由于PWR_CR 复位后默认值为0x0000 C000, DBP位为0:禁止访问RTC寄存器、RTC备份寄存器和SRAM备份寄存器。   ,需要将DBP位  置1,才能使能对RTC寄存器的写保护。

对于上电复位,所有RTC寄存器均受写保护,向RTC_WPR(写保护寄存器)写入密钥来解开对RTC寄存器的写操作。

解开密钥: 0xCA--->0x53;  

锁上密钥:任意 如0xFF;

这部分的程序:以stm32f4

{

           u16 retry = 0x1FFF;    

            /*PWR 中关于RTC配置*/

           RCC->APB1ENR|=1<<28;    //使能电源控制器PWR接口时钟
           PWR->CR|=1<<8;               //使能后备区域写访问(RTC+SRAM)

           //if(RTC_Read_BKR(0)!=0X5050) //是否第一次配置?
           {                 

                    /*RTC 时钟源配置,由于RCC备份域控制寄存器RCC_BDCR 中关于RTC的位均在备份域中,因此 使能备份域的指令在前。*/
                     RCC->BDCR|=1<<0; //LSE 外部低速振荡器时钟开启
                    while(retry&&((RCC->BDCR&0X02)==0))//等待 LSE 准备好
                    {
                           retry--;

                          delay_ms(5);
                      }
                      if(retry==0)    return FALSE; //LSE 开启失败

                     RCC->BDCR  |= 0x01<<8;  //0b01 选择LSE振荡器时钟用作RTC时钟

                     RCC->BDCR |= 0x01<<15;    //RTC时钟使能

                     //解开(关闭)RTC寄存器的写保护

                    RTC->WPR = 0xCA;

                    RTC->WPR = 0x53;

}
 

2.2 RTC日历初始化和配置

设置时间格式和预分频器配置   设置初始时间和日期日历值。

操作顺序:
1、初始化和状态寄存器RTC_ISR中INIT位 置1,以进入初始化模式。

2、轮询RTC_ISR寄存器中INITF位。待INITF置 1时,RTC进入了初始化阶段模式。-----大约需要2个RTCCLK时钟周期;

      进入初始化模式后,日历计数器将停止工作,且其值可更新。

3、要为日历计数器生成1Hz的时钟,应首先编程RTC_PRER寄存器中的同步预分频系数,再编程异步预分频系数。

       必须要RTC_PRER寄存器执行两次单独的写访问。

4、在影子寄存器RTC_TR和RTC_DR 中加载初始时间和日期值,然后通过RTC_CR寄存器中的FMT位配置时间格式(12/24小时制)

5、通过清零INIT位退出初始化模式。 从影子寄存器 自动到实际日历计数器值。  4个RTCCLK时钟周期

当初始化系列完成之后,日历开始计数

这部分的程序:以stm32f4

{

      /*进入初始化模式,才可修改 RTC预分频器配置、RTC控制寄存器的小时格式设置*/ 

      RTC_Init_Mode();   

        { 

        RCC->PRER |=  0xFF;            //先设置同步预分频系数 255,再设置异步预分频系数127      3  

         RCC->PRER |= 0x7F<<16;     //上电复位值为0x007F 00 FF  32768 /(127+1)/(255+1) = 1 Hz

         RCC->CR  &= ~(0x01<<6);   //设置小时格式为24小时/天格式        

         RTC->ISR &= ~(0x01<<7);   //退出RTC初始化模式

        RTC->WPR = 0xFF;   //使能RTC寄存器写保护

       }

        //设置日历新值                                         4

}

进入RTC初始化模式

u8 RTC_Init_Mode(void)

{    

        retry = 0x1FFF;

         RCC->ISR |= 0x01<<7;    //置1,初始化模式,用于编程(设置、写)时间和日期寄存器(RTC_TR和RTC_DR),以及预分频寄存器(RTC_PRTR)。进入后计数器停止计数; 当INIT位被复位(清零)后,计数器从新值开始计数。 1

         while(retry&&((RCC->ISR&0x40)==0x00))   //超时或设置成功才可退出循环    2

         {

                 retry --;

                 delay_ms(5);

         }

         if(retry==0)  return FALSE;  

         return SUCCESS;

}

设置日历新值 -----即对RTC_TR(RTC时间寄存器)和RTC_DR(RTC日期寄存器)进行设置;RTC_TR和RTC_DR是日历时间和日期影子寄存器,只有在初始化模式下才能对这些寄存器执行写操作

/*********************************************************

function name: RTC_Set_Time

paras: hour 小时,min 分钟, sec 秒钟, ampm  am/pm, 0=AM/24H,  1= PM

return: 0 success, 1 fail

*********************************************************/

u8 RTC_Set_Time(u8 hour, u8 min, u8 sec, u8 ampm)

{

      u8   temp = 0;

      //使能后备域写使能后,关闭RTC寄存器写保护

      RTC->WPR = 0xCA;

      RTC->WPR = 0x53;

       if(RTC_Init_Mode()) return 1; //进入RTC初始化模式失败,则返回1

      // temp = (((u32)ampm&0x01)<<22) | (((u32)(hour/10)&0x03)<<20) | (((u32)(hour%10)&0x0F)<<16) | ()

         temp = (((u32)ampm&0x01)<<22) | ((u32)RTC_Byte2BCD(hour)<<16) | ((u32)RTC_Byte2BCD(min)<<8) | ((u32)RTC_Byte2BCD(sec));

      RTC_TR = temp;    //设置日历时间

       RTC->ISR &=  ~(0x01<<7);   //退出初始化模式,回到自由运行模式

      return 0;

}

/*********************************************************

function name: RTC_Set_Data

paras: year年,month月, date 日, week 星期(1~7,0 非法)

return: 0 success, 1 fail

*********************************************************/

u8 RTC_Set_Date(u8 year, u8 month, u8 date, u8 week)

{

       u8 temp;

       RTC->WPR = 0xCA;

       RTC->WPR = 0x53;

        if(RTC_Init_Mode()) return 1; //进入RTC初始化模式失败,则返回1

        temp = (((u32)week&0x07)<<13) | ((u32)RTC_Byte2BCD(year)<<16) | ((u32)RTC_Byte2BCD(month)<<8) |  (u32)RTC_Byte2BCD(month);  

       RTC->DR = temp;   //设置日历日期

       RTC->ISR &= ~(1<<7);   //退出初始化模式,回到自由运行模式

      return 0;

}

/*************************************************************************

function name : RTC_Byte2BCD(u8 byte)

*************************************************************************/

u8 RTC_Byte2BCD(u8 byte)

{

         u8 bcdHight = 0;

          u8 bcdLow = 0;

         bcdHight = byte/10;   //十位

          bcdLow = byte%10;   //个位

         return  ((u8)(bcdHight << 4) | bcdLow );  //十位和个位均用4个bits表示

}

//读取RTC日历时间

要想正确读取RTC日历寄存器(包括RTC_SSR、RTC_TR和RTC_DR),APB1时频必须大于或等于RTC时钟的七倍——这是处于同步机制行为的安全性。

同步机制------为了避免前后两次读取间隔小于2个RTCCLK周期(每次将日历寄存器中的值赋值到RTC_SSR、RTC_TR、RTC_DR影子寄存器是,RTC_ISR寄存器中RSF位都会置1,每两个TRCCCLK周期执行一次复制),因此必须在读取完后清零RSF位,等待RSF置1之后才可在此读取RTC_SSR、RTC_TR、RTC_DR的值。 这就有了等待RSF同步的操作

u8 RTC_Wait_Synchro(void)

{       u32 retry = 0xFFFFFF;

         RTC->WPR = 0xCA;    //关闭RTC写保护

         RTC->WPR = 0x55;

         RTC->ISR = ~(1<<5);         //清除寄存器同步标志----日历影子寄存器尚未同步

         if(retry&&(RTC->ISR&(1<<5))==0x00)

         { retry--;

        }

        if(retry==0) return 1;   同步失败

      RTC->WPR = 0xFF;  //使能RTC写保护

      return 0;

}

2.3 设置RTC周期性唤醒

设置RTC周期性唤醒 以及设置RTC闹钟中断等步骤都是十分类似。

步骤: 1、禁止唤醒定时器(即RTC_CR中的WUTE(wake up Timer Enable)清零

        2、轮询直到RTC_ISR(RTC初始化状态寄存器)中WUTWF(write flag)位 置1(这是硬件置1), 置1代表允许更新唤醒定时器配置(获得写执行能力)。

       3、配置唤醒定时器寄存器以设置唤醒自动重载值,并在RTC_CR中选择唤醒唤醒时钟(即RTC_CR的WUCKSEL[2:0]位),最后使能唤醒定时器(即RTC_CR的WUTE位置1)。唤醒定时器重新开始递减计数。

      4、设置中断使能和中断线配置,配置NVIC

       void RTC_Set_WakeUp(u8 wksel, u16 cnt)

       {

                 RTC->WPR = 0xCA;

                 RTC->WPR = 0x53;

                 RTC->CR &= ~(0x01<<10); //清零RTC_CR的WUTE位, 除能RTC 唤醒定时器

                 while(RTC->ISR & (0x04)==0x00) delay_us(10);  //等待允许更新唤醒 定时器设置  write

                 /选择RTC 唤醒定时器的时钟  RTC的 2 4 8 16 分频 或 1Hz的ck_spre

                RTC->CR &= ~(0x07);   //清零WUCKSEL[2:0]

                RTC->CR |= wksel & 0x07;  //设置时钟

                RTC->WUTR = cnt;     //设置唤醒定时器的自动重载值

                //设置中断

               RTC->ISR &= ~(0x01<<10);   //清除RTC_ISR 的唤醒定时器标志  WUTF

              RTC->CR |=0x01<<14;  //使能唤醒定时器  置1使能

              //RTC->CR |= 0x01<<10; //使能唤醒定时器 开始计数  配合低功耗一般不在配置时开启

               RTC->WPR = 0xFF;   //使能RTC写保护          

             对于RTC唤醒事件 连接的是EXTI线22.

             EXTI->PR &= ~(0x1<<22); //清除线22的挂起标志位

             EXTI->IMR |= (0x01<<22); //开放line22的中断请求

              EXTI->RTSR  |= (0x01<<22);  //line22事件上升沿触发

              MY_NVIC_Init(2,2,RTC_WKUP_IRQn,2); //抢占2,子优先级2,组2 

       }

3、进入待机模式

进入待机模式的步骤

1、将CortexTM-M4F系统控制寄存器SCB_SCR的SLEEEPDEEP位 置1-----MCU进入深度睡眠的前提

2、将电源控制寄存器(PWR_CR)中的PDDS位 零0------选择MCU进入深度睡眠时进入的是待机模式,而非停止模式

3、将电源控制/状态寄存器(PWR_CSR)中的WUF位清零-----唤醒标志位清零

4、将所选唤醒源对应的RTC中断标志清零-------唤醒源中断标志清零

5、进入待机模式命令

进入待机模式前其他外设的处理步骤

a》

b》

c》

d》

void Sys_Enter_Standby(void)

{

      

}

4、实操

任务:正常运行,按键3秒以上,进入待机模式,待机5s唤醒重新工作。

实操后修正:

8、参考文献

猜你喜欢

转载自blog.csdn.net/zhejfl/article/details/83271328