STM32的RTC

1,165 阅读28分钟

文章参考:STM32学习笔记一一RTC实时时钟STM32之RTC实时时钟【STM32】RTC实时时钟,步骤超细详解,一文看懂RTC超透彻的STM32讲解资料——RTC时钟stm32——RTC实时时钟STM32的RTC的原理与使用(附代码)

一、RTC定义

1. 定义和特征

       RTC(Real Time Clock 实时时钟):是个独立的BCD定时器/计数器。RTC 提供一个日历时钟,两个可编程闹钟中断,以及一个具有中断功能的周期性可编程唤醒标志。RTC还包含用于管理低功耗模式的自动唤醒单元。主要有如下特性: 

  • 可编程的分频器:分频因子可以高达220 
  • 用于长期测量的32位 可编程的计数器 
  • 2个独立的时钟: PCLK1 时钟(用于APB1接口)和RTC 时钟 (必须比PCLK1时钟慢至少4倍) 
  •  2个单独的复位类型: 

        APB1接口由系统复位复位

        RTC内核 (分频器,警告,计数器和除法器)通过备份区域复位来复位。  

  • 3个专用的可屏蔽中断线: 

       警告中断线,用来产生一个软件可编程的警告中断。

       秒中断线,用来产生一个周期性(周期长度可编程,高达1s)的中断信号 

       溢出中断线,用来检测内部可编程计数器什么时候翻滚到0。

2. RTC的时钟源

       STM32的RTC外设,实质是一个掉电后还继续运行的定时器。所谓掉电,是指电源Vpp断开的情况下,为了RTC外设掉电可以继续运行,必须给STM32芯片通过VBAT引脚接上锂电池。当主电源VDD有效时,由VDD给RTC外设供电。当VDD掉电后,由VBAT给RTC外设供电。无论由什么电源供电,RTC中的数据始终都保存在属于RTC的备份域中,如果主电源和VBA都掉电,那么备份域中保存的所有数据都将丢失。(备份域除了RTC模块的寄存器,还有42个16位的寄存器可以在VDD掉电的情况下保存用户程序的数序,系统复位或电源复位时,这些数据也不会被复位)。

       从RTC的定时器特性来说,它是一个32位的计数器,只能向上计数。它使用的时钟源有三种,分别为:

  • 高速外部时钟HSE**(8MHz)的128分频:HSE/128;**
  • 低速内部时钟LSI(40kHz);
  • 低速外部时钟LSE;

       使用HSE分频时钟或者LSI的时候,在主电源VDD掉电的情况下,这两个时钟来源都会受到影响,因此没法保证RTC正常工作。所以RTC一般都时钟低速外部时钟LSE,频率为实时时钟模块中常用的32.768KHz,因为32768 = 2^15,分频容易实现,所以被广泛应用到RTC模块。(在主电源VDD有效的情况下(待机),RTC还可以配置闹钟事件使STM32退出待机模式)。

3. RTC的工作原理

       RTC由2个主要部分组成,第一部分(APB1接口)用来和APB1总线相连。此单元还包含一组16位寄存器,可通过APB1总线对其进行读写操作。APB1接口由APB1总线时钟驱动,用来与APB1总线接口。第二部分(RTC核心)由一组可编程计数器组成,分成两个主要模块。第一模块是RTC的预分频模块,它可编程产生最长为1秒的RTC时间基准TR_CLK。RTC的预分频模块包含了一个20位的可编程分频器(RTC_PRL)。如果在RTC_CR寄存器中设置了相应的允许位,则在每个TR_CLK周期中RTC产生一个中断(秒中断)。第二个模块是一个32位的可编程计数器,可被初始化为当前的系统时间,一个32位的时钟计数器,按秒钟计算,可以记录4,294,967,296秒,约合136年左右,作为一般应用,这已经是足够了的。

       RTC 还有一个闹钟寄存器 RTC_ALR,用于产生闹钟。系统时间按 TR_CLK 周期累加并与存储在 RTC_ALR 寄存器中的可编程时间相比较,如果 RTC_CR 控制寄存器中设置了相应允许位,比较匹配时将产生一个闹钟中断。RTC 内核完全独立于 RTC APB1 接口,而软件是通过 APB1 接口访问 RTC 的预分频值、计数器值和闹钟值的。但是相关可读寄存器只在 RTC APB1 时钟进行重新同步的 RTC 时钟的上升沿被更新, RTC 标志也是如此。这就意味着,如果 APB1 接口刚刚被开启之后,在第一次的内部寄存器更新之前,从 APB1 上读取的 RTC 寄存器值可能被破坏了(通常读到 0)。因此,若在读取 RTC 寄存器曾经被禁止的 RTC APB1 接口,软件首先必须等待 RTC_CRL 寄存器的 RSF位(寄存器同步标志位, bit3)被硬件置 1。

       图中浅灰色的部分都是属于备份域的,在VDD掉电时可在VBAT的驱动下继续运行。这部分仅包括RTC的分频器,计数器和闹钟控制器。若VDD电源有效,RTC可以触发RTC_Second(秒中断)、RTC_Overflow(溢出事件)和RTC_Alarm(闹钟中断)。闹钟事件是在计数器RTC_CNT的值等于闹钟寄存器RTC_ALR的值时触发的。

       因为RTC的寄存器是属于备份域,所以它的所有寄存器都是16位的。它的计数RTC_CNT的32位由RTC_CNTLRTC_CNTH两个寄存器组成,分别保存计数值的低16位和高16位。在配置RTC模块的时钟时,把输入的32768Hz的RTCCLK进行32768分频得到实际驱动计数器的时钟TR_CLK = RTCCLK/37768 = 1Hz,计时周期为1秒,计时器在TR_CLK的驱动下计数,即每秒计数器RTC_CNT的值加1(常用)。

       由于备份域的存在,使得RTC核具有了完全独立于APB1接口的特性,也因此对RTC寄存器的访问要遵守一定的规则。
系统复位后,禁止访问后备寄存器和RCT,防止对后卫区域(BKP)的意外写操作。(执行以下操作使能对后备寄存器好RTC的访问):

  • 设置RCC_APB1ENR寄存器的PWREN和BKPEN位来使能电源和后备接口时钟.
  • 设置PWR_CR寄存器的DBP位使能对后备寄存器和RTC的访问.

      设置为可访问后,在第一次通过APB1接口访问RTC时,必须等待APB1与RTC外设同步,确保被读取出来的RTC寄存器值是正确的,如果在同步之后,一直没有关闭APB1的RTC外设接口,就不需要再次同步了。
如果内核要对RTC寄存器进行任何的写操作,在内核发出写指令后,RTC模块在3个RTCCLK时钟之后,才开始正式的写RTC寄存器操作。我们知道RTCCLK的频率比内核主频低得多,所以必须要检查RTC关闭操作标志位RTOFF当这个标志被置1时,写操作才正式完成。

二、RTC的寄存器

typedef struct
{
  __IO uint16_t CRH;
  uint16_t  RESERVED0;
  __IO uint16_t CRL;
  uint16_t  RESERVED1;
  __IO uint16_t PRLH;
  uint16_t  RESERVED2;
  __IO uint16_t PRLL;
  uint16_t  RESERVED3;
  __IO uint16_t DIVH;
  uint16_t  RESERVED4;
  __IO uint16_t DIVL;
  uint16_t  RESERVED5;
  __IO uint16_t CNTH;
  uint16_t  RESERVED6;
  __IO uint16_t CNTL;
  uint16_t  RESERVED7;
  __IO uint16_t ALRH;
  uint16_t  RESERVED8;
  __IO uint16_t ALRL;
  uint16_t  RESERVED9;
} RTC_TypeDef;

2.1 RTC 的控制寄存器——RTC_CRH 寄存器

    该寄存器用来控制中断的。

2.2 RTC 的控制寄存器——RTC_CRL 寄存器

       RTC 用到的是该寄存器的 0、 3~5 这几个位,第 0 位是秒钟标志位,我们在进入闹钟中断的时候,通过判断这位来决定是不是发生了秒钟中断,然后必须通过软件将该位清零(写 0)。第 3 位为寄存器同步标志位,我们在修改控制寄存器 RTC_CRH``/CRL 之前,必须先判断该位,是否已经同步了,如果没有则等待同步,在没同步的情况下修改RTC_CRH/CRL 的值是不行的。第 4 位为配置标位,在软件修改 RTC_CNT/RTC_ALR/RTC_PRL 的值的时候,必须先软件置位该位,以允许进入配置模式。第 5 位为 RTC 操作位,该位由硬件操作,软件只读。通过该位可以判断上次对 RTC 寄存器的操作是否完成,如果没有,我们必须等待上一次操作结束才能开始下一次操作。

       RTC模块的各个寄存器的访问规则为:首先确认RTOFF位为1,然后,置CNF位为1进入配置模式,接着,配置各个RTC寄存器(包括RTC_CRH),之后,清零CNF位退出配置模式,最后,等待RTOFF位为1。 

2.3 RTC 预分频装载寄存器——RTC_PRLH 寄存器和RTC_PRLL 寄存器

       RTC_PRLHRTC_PRLL是两个16位的寄存器,RTC_PRLH的高14位保留,RTC_PRLH的第[3:0]位(作为PRL[19:16])与RTC_PRLL的第[15:0]位(作为PRL[15:0])组合成PRL[19:0],TR_CLK = RTCCLK / (PRL[19:0]+1)。

       这两个寄存器用来配置 RTC 时钟的分频数的,比如我们使用外部32.768K 的晶振作为时钟的输入频率,那么我们要设置这两个寄存器的值为32767,以得到一秒钟的计数频率。

2.4  RTC 预分频器余数寄存器——RTC_DIVH 寄存器和RTC_DIVL 寄存器

      这两个寄存器的作用就是用来获得比秒钟更为准确的时钟,比如可以得到 0.1 秒,或者 0.01 秒等。该寄存器的值自减的,用于保存还需要多少时钟周期获得一个秒信号。在一次秒钟更新后,由硬件重新装载。其减计数到0后,RTC_PRLHRTC_PRLL中的预装值将自动装入RTC_DIVHRTC_DIVL中。

2.5 RTC 计数器寄存器——RTC_CNTH寄存器和RTC_CNTL寄存器

       RTC_CNT寄存器由 2 个 16 位的寄存器组成 RTC_CNTHRTC_CNTL,总共 32 位,用来记录秒钟值(一般情况下)。在修改这个寄存器的时候要先进入配置模式。

2.6 RTC 闹钟寄存器——RTC_ALRH寄存器和 RTC_ALRL寄存器

      该寄存器也是由 2 个 16 位的寄存器组成 RTC_ALRH 和 RTC_ALRL。总共也是 32 位,用来标记闹钟产生的时间(以秒为单位),如果 RTC_CNT 的值与 RTC_ALR的值相等,并使能了中断的话,会产生一个闹钟中断。该寄存器的修改也要进入配置模式才能进行。

附加:2.7 备份区域控制寄存器RCC_BDCR

       因为我们使用到备份寄存器来存储RTC的相关信息(我们这里主要用来标记时钟是否已经经过了配置),这里顺便介绍以下STM32的备份寄存器。      

       备份寄存器是42个16位的寄存器(大容量产品才有,ALIENTEK Mini STM32开发板使用的是STM32F103RBT6,属于小容量产品,只有10个16位的寄存器),可用来存储 84 个字节的用户应用程序数据。它们处在备份域里,当 VDD 电源被切断,它们仍然由 VBAT 维持供电。即使系统在待机模式下被唤醒,或系统复位或电源复位时,它们也不会被复位。

       此外,BKP控制寄存器用来管理侵入检测和RTC校准功能。

复位后,对备份寄存器和 RTC 的访问被禁止,并且备份域被保护以防止可能存在的意外的
写操作。执行以下操作可以使能对备份寄存器和 RTC 的访问:

1)通过设置寄存器 **RCC_APB1ENR** **PWREN** **BKPEN** 位来打开电源和后备接口的时钟;

2)电源控制寄存器 (**PWR_CR**) 的 **DBP** 位来使能对后备寄存器和 RTC 的访问。

      一般用 BKP 来存储 RTC 的校验值或者记录一些重要的数据,相当于一个 EEPROM,不过这个 EEPROM 并不是真正的 EEPROM,而是需要电池来维持它的数据。

       RTC 的时钟源选择及使能设置都是通过这个备份区域控制寄存器RCC_BDCR来实现的,所以我们在 RTC 操作之前先要通过这个寄存器选择 RTC 的时钟源,然后才能开始其他的操作。

三、RTC的初始化配置

       "RTC"是Real Time Clock 的简称,意为实时时钟,修改计数器的值可以重新设置系统当前的时间和日期。RTC模块之所以具有实时时钟功能,是因为它内部维持了一个独立的定时器,通过配置,可以让它准确地每秒钟中断一次。但实际上,RTC就只是一个定时器而已,掉电之后所有信息都会丢失,因此我们需要找一个地方来存储这些信息,于是就找到了备份寄存器。其在掉电后仍然可以通过纽扣电池供电,所以能时刻保存这些数据。

1. 配置RTC前须知:

  BKP:

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

PWR:

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

2. 初始化配置RTC

(1) 使能电源时钟和备份区域时钟

       要访问RTC和备份区域就必须先使能电源时钟和备份区域时钟。这个通过RCC_APB1ENR寄存器来设置。

/* Enable PWR and BKP clocks */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);

(2) 取消备份区写保护

      要向备份区域写入数据,就要先取消备份区域写保护(写保护在每次硬复位之后被使能,因为后备寄存器中放的是重要数据,默认是不允许往里面写入值的),否则是无法向备份区域写入数据的。我们需要用到向备份区域写入一个字节,来标志时钟已经配置过了,这样避免每次复位之后重新配置时钟。

/* Allow access to BKP Domain */
PWR_BackupAccessCmd(ENABLE); //使能 RTC 和后备寄存器访问

(3) 复位备份区域,开启外部低速振荡器。

      在取消备份区域写保护之后,我们可以先对这个区域复位,以清除前面的设置,当然这个操作不要每次都执行,因为备份区域的复位将导致之前存在的数据丢失,所以要不要复位,要看情况而定。然后我们使能外部低速振荡器LSE,注意这里一般要先判断RCC_BDCRLSERDY位来确定外部低速振荡器已经就绪了才开始下面的操作。

/* Reset Backup Domain */
BKP_DeInit();//复位备份区域

/* Enable LSE */
RCC_LSEConfig(RCC_LSE_ON);//开启外部低速振荡器

/* Wait till LSE is ready */
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)//判断外部低速振荡器是否已经就绪
{}

(4) 选择 RTC 时钟,并使能

      将通过RCC_BDCRRTCSEL来选择外部LSE作为RTC的时钟。然后通过RTCEN位使能RTC时钟。RCC_RTCCLKSource包括RCC_RTCCLKSource_LSE、RCC_RTCCLKSource_LSI 和 RCC_RTCCLKSource_HSE_Div128这三种选择。

/* Select LSE as RTC Clock Source */
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //选择 LSE 作为 RTC 时钟

/* Enable RTC Clock */
RCC_RTCCLKCmd(ENABLE); //使能 RTC 时钟

(5) 设置 RTC 的分频,以及配置 RTC 时钟

      在开启了 RTC 时钟之后,我们要做的是设置 RTC 时钟的分频数,通过 RTC_PRLHRTC_PRLL 来设置,然后等待 RTC 寄存器操作完成,并同步之后,设置秒钟中断。然后设置 RTC 的允许配置位(RTC_CRHCNF 位),设置时间(其实就是设置RTC_CNTHRTC_CNTL两个寄存器)。

  /* Wait for RTC registers synchronization */
  RTC_WaitForSynchro();//等待RTC寄存器与APB1同步

  /* Wait until last write operation on RTC registers has finished */
  RTC_WaitForLastTask();//等待对RTC的写操作完成

  /* Enable the RTC Second */
  RTC_ITConfig(RTC_IT_SEC, ENABLE);//使能RTC秒中断

  /* Wait until last write operation on RTC registers has finished */
  RTC_WaitForLastTask();//等待对RTC的写操作完成

  /* Set RTC prescaler: set RTC period to 1sec */
  RTC_SetPrescaler(32767); /* RTC period = RTCCLK/RTC_PR = (32.768 KHz)/(32767+1) */
//设置RT 时钟分频: 使RTC定时周期为1秒 

  /* Wait until last write operation on RTC registers has finished */
  RTC_WaitForLastTask();//等待对RTC的写操作完成 

(6) 编写中断服务函数

       在这个函数中并没有任何对RTC_CNT的操作,如果VDD掉电,RTC是无法触发秒中断的,所以想利用秒中断的方案实现实时时钟是不现实的,秒中断最适合用在类似本例程的触发显示的时间更新场合,而不是用于计数。

    TimeDisplay是一个标志位,只有等于1时才让串口发送时间数据,即让串口一秒发一次时间值。

void RTC_IRQHandler(void)
{
  if (RTC_GetITStatus(RTC_IT_SEC) != RESET)
  {
    /* Clear the RTC Second interrupt */
    //清除秒中断标志
    RTC_ClearITPendingBit(RTC_IT_SEC);

    /* Toggle LED1 */
    STM_EVAL_LEDToggle(LED1);

    /* Enable time update */
    //把标志位置 1
    TimeDisplay = 1;

    /* Wait until last write operation on RTC registers has finished */
    //等待RTC写操作完成
    RTC_WaitForLastTask();
    /* Reset RTC Counter when Time is 23:59:59 */
   // 当时间走到23:59:59时,RTC计数器中的值清零,0x15180=Dec(23*3600+59*60+59+1)
    if (RTC_GetCounter() == 0x00015180)
    {
      RTC_SetCounter(0x0);
      /* Wait until last write operation on RTC registers has finished */
      RTC_WaitForLastTask();
    }
  }
}

四、RTC的应用

1. main函数

main函数流程:

(1)配置串口(代码和之前的例程一样);
(2)配置RTC秒中断优先级,这里设置主优先级为1,次优先级为0(只用到一个RTC,中断随便写都可以)。(代码和之前的中断例程相似,只不过中断通道不一样,这里使用的中断通道是RTC_IRQn);
(3)查看RTC外设是否在本次VDD上电前被配置过,如果没有被配置过,则需要输入当前时间,重新初始化RTC和配置时间;
(4)配置好RTC后,根据秒中断设置的标志位,每隔1秒向终端更新一次。

int main(void)
{
//系统时钟初始化设置  
SystemInit();

//配置串口
USART2_Config();

//配置RTC秒中断优先级  
NVIC_Configuration();

//RTC检测及配置
RTC_CheckAndConfig();

//刷新时间  
Time_Show();
}

2. 串口配置(见STM32的UART串口通信协议)

    配置串口。

 void USART2_Configuration(void)
 { 
  USART_InitTypeDef USART_InitStructure;

  USART_InitStructure.USART_BaudRate = 115200;
  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;

  //STM_EVAL_COMInit(COM1, &USART_InitStructure);
  
  GPIO_InitTypeDef GPIO_InitStructure;

  /* Enable GPIO clock */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD| RCC_APB2Periph_AFIO, ENABLE);

    /* Enable the USART2 Pins Software Remapping */
    GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);


  /* Configure USART Tx as alternate function push-pull */
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOD, &GPIO_InitStructure);

  /* Configure USART Rx as input floating */
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
  GPIO_Init(GPIOD, &GPIO_InitStructure);

  /* USART configuration */
  USART_Init(USART2, USART_InitStruct);
    
  /* Enable USART */
  USART_Cmd(USART2, ENABLE);
  }

3. 配置RTC秒中断优先级

      配置RTC秒中断优先级,这里设置主优先级为1,次优先级为0,这里使用的中断通道是RTC_IRQn。

void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;

  /* Configure one bit for preemption priority */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

  /* Enable the RTC Interrupt */
  NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}

4. 输出时间到终端Time_Show():

       时间的初始值设置好之后,让时间正常的走起来,同时在超级终端上显示当前时间。

       TimeDisplay是RTC秒中断标志,RTC的秒中断被触发后,进入中断服务函数,把这个变量 TimeDisplay置1。这个函数是死循环检查这个标志,变为1时,调用Time_Display()显示最新时间,实现每隔1秒向终端更新一次时间,更新完后再把 TimeDisplay置0,等待下次秒中断。

void Time_Show(void)
{
  printf("\n\r");

  /* Infinite loop */
  while (1)
  {
    /* If 1s has paased */
    if (TimeDisplay == 1)
    {
      /* Display current time */
      Time_Display(RTC_GetCounter());
      TimeDisplay = 0;
    }
  }
}

4.1 显示时间Time_Display():

void Time_Display(uint32_t TimeVar)
{
  uint32_t THH = 0, TMM = 0, TSS = 0;
//将计数器中的值换成时分秒分别赋值给THH,TMM,TSS
  /* Compute  hours */
  THH = TimeVar / 3600;
  /* Compute minutes */
  TMM = (TimeVar % 3600) / 60;
  /* Compute seconds */
  TSS = (TimeVar % 3600) % 60;
//将时间传到超级终端上显示
  printf("Time: %0.2d:%0.2d:%0.2d\r", THH, TMM, TSS);
}

5. RTC检测及配置

       查看RTC外设是否在本次VDD上电前被配置过(先读取 BKP_DR1 的值,然后判断是否是 0XA5A5 来决定是不是要配置,需要配置的话,我们在备份区域 BKP_DR1 中写入 0XA5A5 代表我们已经初始化过时钟了),如果没有被配置过,则需要输入当前时间,重新初始化RTC和配置时间。

       对于RTC的使用,首先我们要判断是否是第一次使用RTC,如果是第一次使用,那么肯定要设置时间的初始值以及对RTC进行相应的配置,如果不是第一次使用,那么我们就无需再设置时间的初始值以及对RTC进行相应的配置,只需让RTC计数器继续技术就可以了。那么如何才能判断RTC是否为第一次使用呢?STM32中有一个后备寄存器,寄存器中的值不会因为掉电而改变,那么我们第一次使用RTC时,往后备寄存器中写入一个值,下次再使用时,只要判断后备寄存器中的值是否为我第一次使用RTC写入的值,如果相等,说明我之前已经用过了RTC了,现在无需对RTC进行配置了,因为第一次都配置好了(RTC和后备寄存器一样,RTC寄存器中设置的值不会因为掉电而改变),但是要注意的是RTC的允许中断这一位再每次复位后会回到默认值,所以每次复位后我们都要再次设置允许RTC中断。

void RTC_CheckAndConfig(void)
{
//检查备份寄存器BKP_DR1,内容不为0xA5A5,则需要重新配置时间并且询问用户调整时间
if (BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5)  {
    /* Backup data register value is not correct or not yet programmed (when
       the first time the program is executed) */

    printf("\rRTC not yet configured....");
    printf("\r\nconnect UART to configure");

    /* RTC Configuration */
    //RTC初始化配置
    RTC_Configuration();

    printf("\r\nRTC configured....");

    /* Adjust time by values entred by the user on the hyperterminal */
    Time_Adjust();

    //再往备份寄存器BKP_DR1写入0xA5A5
    BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);
  }
  //启动无需设置新时钟
  else
  {
    /* Check if the Power On Reset flag is set */
    //检查是否掉电重启
    if (RCC_GetFlagStatus(RCC_FLAG_PORRST) != RESET)
    {
      printf("\rPower On Reset occurred....");
    }
    /* Check if the Pin Reset flag is set */
    //检查是否Reset复位
    else if (RCC_GetFlagStatus(RCC_FLAG_PINRST) != RESET)
    {
      printf("\rExternal Reset occurred....");
    }

    printf("\rNo need to configure RTC....");
    /* Wait for RTC registers synchronization */
   //等待寄存器同步
    RTC_WaitForSynchro();

    /* Enable the RTC Second */
    //允许RTC秒中断
    RTC_ITConfig(RTC_IT_SEC, ENABLE);
    /* Wait until last write operation on RTC registers has finished */
    //等待上次RTC寄存器写操作完成
    RTC_WaitForLastTask();
  }
//定义了时钟输出宏,则配置校正时钟输出到 PC13(tamper_RTC),用于RTC时钟频率的校准或调整时间补偿#ifdef RTCClockOutput_Enable
  /* Enable PWR and BKP clocks */
  //使能PWR和BKP的时钟
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);

  /* Allow access to BKP Domain */
  //允许访问BKP备份域
  PWR_BackupAccessCmd(ENABLE);

  /* Disable the Tamper Pin */
  BKP_TamperPinCmd(DISABLE); /* To output RTCCLK/64 on Tamper pin, the tamper
                                 functionality must be disabled */

  /* Enable RTC Clock Output on Tamper Pin */
  //输出64分频时钟
  BKP_RTCOutputConfig(BKP_RTCOutputSource_CalibClock);
#endif

  /* Clear reset flags */
  RCC_ClearFlag();
}

5.1  第一个if语句:RTC未被配置或者配置已经失效的情况

       第一个if语句if (BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5){}:调用BKP_ReadBackupRegister()读取RTC备份域寄存器里面的值,判断备份寄存器里面的值是否正确,根据后面代码,如果配置成功,会向备份域寄存器写入数值0xA5A5。(这个数值在VDD掉电后仍然会保存,如果VBAT也掉电,那么备份域,RTC所有寄存器将被复位,这时这个寄存器的值就不会等于0xA5A5了,RTC的计数器的值也是无效的。简单的说,就是写入的这个数值用作标志RTC是否从未被配置或配置是否已经失效,然后写入任何数值到任何一个备份域寄存器,只要检查的时候与写入值匹配就行了)。

       第一个if语句显示 RTC未被配置或者配置已经失效的情况:
1)如果RTC从未被配置或者配置已经失效(备份域寄存器写入值不等于0xA5A5)这两种情况其中一种为真的话,则调用RTC_Configuration()来初始化RTC,配置RTC外设的控制参数,时钟分频等,并往电脑的超级终端打印出相应的调试信息;
2)初始化好RTC之后,调用函数 Time_Adjust() 让用户键入(通过超级终端输入)时间值;
3)输入时间值后,Time_Adjust() 函数把用户输入的北京时间转化为UNIX时间戳,并把这个UNIX时间戳写入到RTC外设的计数寄存器RTC_CNT。接着RTC外设在这个时间戳的基础上,每秒对RTC_CNT加1,RTC时钟就运行起来了,并且在VDD掉电还运行,以后需要知道时间就直接读取RTC的计时值,就可以计算出时间了;
4)设置好时间后,调用BKP_WriteBackupRegister()把0xA5A5这个值写入备份域寄存器,作为配置成功的标志。

5.1.1 RTC 初始化配置RTC_Configuration()

void RTC_Configuration(void)
{
  /* Enable PWR and BKP clocks */
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);

  /* Allow access to BKP Domain */
  PWR_BackupAccessCmd(ENABLE);

  /* Reset Backup Domain */
  BKP_DeInit();

  /* Enable LSE */
  RCC_LSEConfig(RCC_LSE_ON);
  /* Wait till LSE is ready */
  while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)
  {}

  /* Select LSE as RTC Clock Source */
  RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);

  /* Enable RTC Clock */
  RCC_RTCCLKCmd(ENABLE);

  /* Wait for RTC registers synchronization */
  RTC_WaitForSynchro();

  /* Wait until last write operation on RTC registers has finished */
  RTC_WaitForLastTask();

  /* Enable the RTC Second */
  RTC_ITConfig(RTC_IT_SEC, ENABLE);

  /* Wait until last write operation on RTC registers has finished */
  RTC_WaitForLastTask();

  /* Set RTC prescaler: set RTC period to 1sec */
  RTC_SetPrescaler(32767); /* RTC period = RTCCLK/RTC_PR = (32.768 KHz)/(32767+1) */

  /* Wait until last write operation on RTC registers has finished */
  RTC_WaitForLastTask();
}

5.1.2 时间调节Time_Adjust():

       使用Time_Regulate()从终端获取设置的初始时间值,调用库函数RTC_SetCounter()把初始时间值戳写入到计数器RTC_CNT,RTC就正式运行了。

void Time_Adjust(void)
{
  /* Wait until last write operation on RTC registers has finished */
  // 等待前面可能的 RTC 写操作完成
  RTC_WaitForLastTask();
  /* Change the current time */
  //将设置的初始时间值装入RTC计数器,RTC开始运行时,计数器里面的值会在初始值的基础上自动一秒加一次
  RTC_SetCounter(Time_Regulate());
  /* Wait until last write operation on RTC registers has finished */
  //等待 RTC 写操作完成
  RTC_WaitForLastTask();
}

5.1.3 获取时间Time_Regulate():

       从终端获取用户输入的时间,并将值储存在RTC计数寄存器中。要留意的是,从终端输入的是ASCII码,而不是实际数值(在USART_Scanf里面做处理)。

uint32_t Time_Regulate(void)
{
  uint32_t Tmp_HH = 0xFF, Tmp_MM = 0xFF, Tmp_SS = 0xFF;

  printf("\r\n===Time Settings==");
  printf("\r\n  Please Set Hours");

  while (Tmp_HH == 0xFF)
  {
   //将串口接收的数据给Tmp_HH,23便是传递给USART_Scanf中的形参Value的    Tmp_HH = USART_Scanf(23);
  }
  printf(":  %d", Tmp_HH);
  printf("\r\n  Please Set Minutes");
  while (Tmp_MM == 0xFF)
  {
    Tmp_MM = USART_Scanf(59);
  }
  printf(":  %d", Tmp_MM);
  printf("\r\n  Please Set Seconds");
  while (Tmp_SS == 0xFF)
  {
    Tmp_SS = USART_Scanf(59);
  }
  printf(":  %d", Tmp_SS);

  /* Return the value to store in RTC counter register */
  //将时分秒转换成秒放入RTC计数器中
  return((Tmp_HH*3600 + Tmp_MM*60 + Tmp_SS));
}

5.1.4 ASCII 码转换为数字的USART_Scanf()

串口从超级终端中接收设置的时间初始值,转换为数字。

uint8_t USART_Scanf(uint32_t value)
{
  uint32_t index = 0;
  uint32_t tmp[2] = {0, 0};

  while (index < 2)
  {
    /* Loop until RXNE = 1 */
    //等待数据接收完成
    while (USART_GetFlagStatus(USART2, USART_FLAG_RXNE) == RESET)
    {}
   //将数据给数组tmp,从串口终端里面输进去的数是ASCII码值
    tmp[index++] = (USART_ReceiveData(USART2));
   //数字0到9的ASCII码为0x30至0x39,判断接收到的数据是否在0-9之间
    if ((tmp[index - 1] < 0x30) || (tmp[index - 1] > 0x39))
    {
      printf("\n\rPlease enter valid number between 0 and 9");
      index--;
    }
  }
  /* Calculate the Corresponding value */
  //计算输入字符的 ASCII 码转换为数字,将接收到的两个数据组成一个两位数
  index = (tmp[1] - 0x30) + ((tmp[0] - 0x30) * 10);
  /* Checks */
//判断组成后的值是否有效,比如组成的小时不能超过23,分秒不能超过59,Value即23或59
  if (index > value)
  {
    printf("\n\rPlease enter valid number between 0 and %d", value);
    return 0xFF;
  }
  return index;
}

5.2  第一个else语句:确认RTC曾经被配置过的情况

       1)调用RCC_GetFlagStatus检测是上电复位还是按键复位,根据不同的复位情况在超级终端中打印出不同的调试信息(两种复位都不需要重新设置RTC里面的时间值);

       2)调用RTC_WaitForSynchro等待APB1接口与RTC外设同步,上电后第一次通过APB1接口访问RTC时必须要等待同步;

       3)同步完成后调用RTC_ITConfig()使能RTC外设的秒中断(使能RTC的秒中断是一个对RTC外设寄存器的写操作);
4)进行写操作以后,必须调用RTC_WaitForLastTask()来等待,确保写操作完成;

5.3  时钟输出宏定义

       在下面有一个条件编译选项询问是否需要output RTCCLK/64 on Tamper pin,这是RTC的时钟输出配置,在rtc的头文件定义 RTCClockOutput_Enable这个宏,PC13引脚会输出RTCCLK的64分频时钟,主要是用于RTC时钟频率的校准或调整时间补偿。

五、工程建立

1.  新建工程添加的文件夹和子文件

硬件:STM32F107VC( Cortex-M3)

软件:Keil μVision4,JLINK

2. 完整的main程序

/**
  ******************************************************************************
  * @file    RTC/Calendar/main.c 
  * @brief   Main program body
  */

/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include "stm3210c_eval_lcd.h"
#include "stm32_eval.h"
#include "fonts.h"
#include <stdio.h>

/** @addtogroup STM32F10x_StdPeriph_Examples
  * @{
  */

/** @addtogroup RTC_Calendar
  * @{
  */

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define RTCClockOutput_Enable  /* RTC Clock/64 is output on tamper pin(PC.13) */

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
__IO uint32_t TimeDisplay = 0;
  USART_InitTypeDef USART_InitStructure;
extern uint16_t Xaddr;
extern uint8_t Yaddr;
extern const uint16_t ASCII_Table[];
/* Private function prototypes -----------------------------------------------*/
void RTC_Configuration(void);
void NVIC_Configuration(void);
uint32_t Time_Regulate(void);
void Time_Adjust(void);
void Time_Show(void);
void Time_Display(uint32_t TimeVar);
uint8_t USART_Scanf(uint32_t value);

#ifdef __GNUC__
/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
   set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */

/* Private functions ---------------------------------------------------------*/

/**
  * @brief  Main program.
  * @param  None
  * @retval None
  */


int main(void)
{
  /* Setup the microcontroller system. Initialize the Embedded Flash Interface,
     initialize the PLL and update the SystemFrequency variable. */

  SystemInit();

  /* Initialize the LCD */
  STM3210C_LCD_Init();
  /* Clear the LCD */ 
  LCD_Clear(White);
  /* Set the LCD Text Color */
  LCD_SetTextColor(Black);

  /* Initialize LED1 mounted on STM3210X-EVAL board */       
  STM_EVAL_LEDInit(LED1);

  /* USARTx configured as follow:
        - BaudRate = 115200 baud  
        - Word Length = 8 Bits
        - One Stop Bit
        - No parity
        - Hardware flow control disabled (RTS and CTS signals)
        - Receive and transmit enabled
  */
  USART_InitStructure.USART_BaudRate = 115200;
  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;

  STM_EVAL_COMInit(COM1, &USART_InitStructure);

  printf("   STM3210C-EVAL    \n\r");
  printf("   RTC Caslendar    \n\r");

  /* NVIC configuration */
  NVIC_Configuration();

  if (BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5)
  {
    /* Backup data register value is not correct or not yet programmed (when
       the first time the program is executed) */

    printf("\rRTC not yet configured....");
    printf("\r\nconnect UART to configure");
	    /* RTC Configuration */
    RTC_Configuration();

    printf("\r\nRTC configured....");

    /* Adjust time by values entred by the user on the hyperterminal */
    Time_Adjust();

    BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);
  }
  else
  {
    /* Check if the Power On Reset flag is set */
    if (RCC_GetFlagStatus(RCC_FLAG_PORRST) != RESET)
    {
      printf("\rPower On Reset occurred....");
    }
    /* Check if the Pin Reset flag is set */
    else if (RCC_GetFlagStatus(RCC_FLAG_PINRST) != RESET)
    {
      printf("\rExternal Reset occurred....");
    }

    printf("\rNo need to configure RTC....");
    /* Wait for RTC registers synchronization */
    RTC_WaitForSynchro();

    /* Enable the RTC Second */
    RTC_ITConfig(RTC_IT_SEC, ENABLE);
    /* Wait until last write operation on RTC registers has finished */
    RTC_WaitForLastTask();
  }

#ifdef RTCClockOutput_Enable
  /* Enable PWR and BKP clocks */
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);

  /* Allow access to BKP Domain */
  PWR_BackupAccessCmd(ENABLE);

  /* Disable the Tamper Pin */
  BKP_TamperPinCmd(DISABLE); /* To output RTCCLK/64 on Tamper pin, the tamper
                                 functionality must be disabled */

  /* Enable RTC Clock Output on Tamper Pin */
  BKP_RTCOutputConfig(BKP_RTCOutputSource_CalibClock);
#endif

  /* Clear reset flags */
  RCC_ClearFlag();

  /* Display time in infinite loop */
  Time_Show();
}


/**
  * @brief  Configures the nested vectored interrupt controller.
  * @param  None
  * @retval None
  */
void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;

  /* Configure one bit for preemption priority */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

  /* Enable the RTC Interrupt */
  NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}

/**
  * @brief  Configures the RTC.
  * @param  None
  * @retval None
  */
void RTC_Configuration(void)
{
  /* Enable PWR and BKP clocks */
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);

  /* Allow access to BKP Domain */
  PWR_BackupAccessCmd(ENABLE);

  /* Reset Backup Domain */
  BKP_DeInit();

  /* Enable LSE */
  RCC_LSEConfig(RCC_LSE_ON);
  /* Wait till LSE is ready */
  while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)
  {}

  /* Select LSE as RTC Clock Source */
  RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);

  /* Enable RTC Clock */
  RCC_RTCCLKCmd(ENABLE);

  /* Wait for RTC registers synchronization */
  RTC_WaitForSynchro();

  /* Wait until last write operation on RTC registers has finished */
  RTC_WaitForLastTask();

  /* Enable the RTC Second */
  RTC_ITConfig(RTC_IT_SEC, ENABLE);

  /* Wait until last write operation on RTC registers has finished */
  RTC_WaitForLastTask();

  /* Set RTC prescaler: set RTC period to 1sec */
  RTC_SetPrescaler(32767); /* RTC period = RTCCLK/RTC_PR = (32.768 KHz)/(32767+1) */

  /* Wait until last write operation on RTC registers has finished */
  RTC_WaitForLastTask();
}

/**
  * @brief  Returns the time entered by user, using Hyperterminal.
  * @param  None
  * @retval Current time RTC counter value
  */
uint32_t Time_Regulate(void)
{
  uint32_t Tmp_HH = 0xFF, Tmp_MM = 0xFF, Tmp_SS = 0xFF;

  printf("\r\n===Time Settings==");
  printf("\r\n  Please Set Hours");

  while (Tmp_HH == 0xFF)
  {
    Tmp_HH = USART_Scanf(23);
  }
  printf(":  %d", Tmp_HH);
  printf("\r\n  Please Set Minutes");
  while (Tmp_MM == 0xFF)
  {
    Tmp_MM = USART_Scanf(59);
  }
  printf(":  %d", Tmp_MM);
  printf("\r\n  Please Set Seconds");
  while (Tmp_SS == 0xFF)
  {
    Tmp_SS = USART_Scanf(59);
  }
  printf(":  %d", Tmp_SS);

  /* Return the value to store in RTC counter register */
  return((Tmp_HH*3600 + Tmp_MM*60 + Tmp_SS));
}

/**
  * @brief  Adjusts time.
  * @param  None
  * @retval None
  */
void Time_Adjust(void)
{
  /* Wait until last write operation on RTC registers has finished */
  RTC_WaitForLastTask();
  /* Change the current time */
  RTC_SetCounter(Time_Regulate());
  /* Wait until last write operation on RTC registers has finished */
  RTC_WaitForLastTask();
}

/**
  * @brief  Displays the current time.
  * @param  TimeVar: RTC counter value.
  * @retval None
  */
void Time_Display(uint32_t TimeVar)
{
  uint32_t THH = 0, TMM = 0, TSS = 0;

  /* Compute  hours */
  THH = TimeVar / 3600;
  /* Compute minutes */
  TMM = (TimeVar % 3600) / 60;
  /* Compute seconds */
  TSS = (TimeVar % 3600) % 60;

  printf("Time: %0.2d:%0.2d:%0.2d\r", THH, TMM, TSS);
}

/**
  * @brief  Shows the current time (HH:MM:SS) on the Hyperterminal.
  * @param  None
  * @retval None
  */   
void Time_Show(void)
{
  printf("\n\r");

  /* Infinite loop */
  while (1)
  {
    /* If 1s has paased */
    if (TimeDisplay == 1)
    {
      /* Display current time */
      Time_Display(RTC_GetCounter());
      TimeDisplay = 0;
    }
  }
}


/**
  * @brief  Retargets the C library printf function to the USART.
  * @param  None
  * @retval None
  */
PUTCHAR_PROTOTYPE
{
   uint8_t Ascii;
  /* Place your implementation of fputc here */
  /* e.g. write a character to the USART */
  USART_SendData(EVAL_COM1, (uint8_t) ch);

  /* Loop until the end of transmission */
  while (USART_GetFlagStatus(EVAL_COM1, USART_FLAG_TC) == RESET)
  {}
  Ascii = ch-32;
  switch(ch)
	{case('\n'):
    	Xaddr=0;
	    Yaddr+=24;
	  	if(Yaddr>216)
   		   {Yaddr=Line0;
		   LCD_Clear(White);
		   }
		break;
	case('\r'):
		Xaddr=0;
		break;
	case('\b'):
		if(Xaddr==0)
   		   {Xaddr=304;
             if(Yaddr>0)
                Yaddr-=24;		   
			}
         Xaddr-=16;
        break;
	default:
		if(Xaddr==320)
    		{Xaddr=0;
	 		Yaddr+=24;
	 	    }
		if(Yaddr>216)
     		{Yaddr=Line0;
			LCD_Clear(White);
			}
		LCD_DrawChar(Yaddr, Xaddr+15, &ASCII_Table[Ascii * 24]);
     	Xaddr+=16;
		break;
    }

  LCD_SetCursor(Xaddr,Yaddr);
  return ch;
}

/**
  * @brief  Gets numeric values from the hyperterminal.
  * @param  None
  * @retval None
  */
uint8_t USART_Scanf(uint32_t value)
{
  uint32_t index = 0;
  uint32_t tmp[2] = {0, 0};

  while (index < 2)
  {
    /* Loop until RXNE = 1 */
    while (USART_GetFlagStatus(EVAL_COM1, USART_FLAG_RXNE) == RESET)
    {}
    tmp[index++] = (USART_ReceiveData(EVAL_COM1));
    if ((tmp[index - 1] < 0x30) || (tmp[index - 1] > 0x39))
    {
      printf("\n\rPlease enter valid number between 0 and 9");
      index--;
    }
  }
  /* Calculate the Corresponding value */
  index = (tmp[1] - 0x30) + ((tmp[0] - 0x30) * 10);
  /* Checks */
  if (index > value)
  {
    printf("\n\rPlease enter valid number between 0 and %d", value);
    return 0xFF;
  }
  return index;
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *   where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t* file, uint32_t line)
{
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

  /* Infinite loop */
  while (1)
  {}
}
#endif

/**
  * @}
  */

/**
  * @}
  */

/******************* (C) COPYRIGHT 2009 STMicroelectronics *****END OF FILE****/

3. 完整的stm32f10x_it.c

/**
  ******************************************************************************
  * @file    RTC/Calendar/stm32f10x_it.c 
  * @brief   Main Interrupt Service Routines.
  *         This file provides template for all exceptions handler and
  *         peripherals interrupt service routine.
  */

/* Includes ------------------------------------------------------------------*/
#include "stm32f10x_it.h"
#include "stm32_eval.h"

/** @addtogroup STM32F10x_StdPeriph_Examples
  * @{
  */

/** @addtogroup RTC_Calendar
  * @{
  */ 

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
extern __IO uint32_t TimeDisplay;

/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/

/******************************************************************************/
/*            Cortex-M3 Processor Exceptions Handlers                         */
/******************************************************************************/

/**
  * @brief  This function handles NMI exception.
  * @param  None
  * @retval None
  */
void NMI_Handler(void)
{
}

/**
  * @brief  This function handles Hard Fault exception.
  * @param  None
  * @retval None
  */
void HardFault_Handler(void)
{
  /* Go to infinite loop when Hard Fault exception occurs */
  while (1)
  {}
}

/**
  * @brief  This function handles Memory Manage exception.
  * @param  None
  * @retval None
  */
void MemManage_Handler(void)
{
  /* Go to infinite loop when Memory Manage exception occurs */
  while (1)
  {}
}

/**
  * @brief  This function handles Bus Fault exception.
  * @param  None
  * @retval None
  */
void BusFault_Handler(void)
{
  /* Go to infinite loop when Bus Fault exception occurs */
  while (1)
  {}
}

/**
  * @brief  This function handles Usage Fault exception.
  * @param  None
  * @retval None
  */
void UsageFault_Handler(void)
{
  /* Go to infinite loop when Usage Fault exception occurs */
  while (1)
  {}
}

/**
  * @brief  This function handles Debug Monitor exception.
  * @param  None
  * @retval None
  */
void DebugMon_Handler(void)
{
}

/**
  * @brief  This function handles SVCall exception.
  * @param  None
  * @retval None
  */
void SVC_Handler(void)
{
}

/**
  * @brief  This function handles PendSV_Handler exception.
  * @param  None
  * @retval None
  */
void PendSV_Handler(void)
{
}

/**
  * @brief  This function handles SysTick Handler.
  * @param  None
  * @retval None
  */
void SysTick_Handler(void)
{
}

/******************************************************************************/
/*            STM32F10x Peripherals Interrupt Handlers                        */
/******************************************************************************/

/**
  * @brief  This function handles RTC global interrupt request.
  * @param  None
  * @retval None
  */
void RTC_IRQHandler(void)
{
  if (RTC_GetITStatus(RTC_IT_SEC) != RESET)
  {
    /* Clear the RTC Second interrupt */
    RTC_ClearITPendingBit(RTC_IT_SEC);

    /* Toggle LED1 */
    STM_EVAL_LEDToggle(LED1);

    /* Enable time update */
    TimeDisplay = 1;

    /* Wait until last write operation on RTC registers has finished */
    RTC_WaitForLastTask();
    /* Reset RTC Counter when Time is 23:59:59 */
    if (RTC_GetCounter() == 0x00015180)
    {
      RTC_SetCounter(0x0);
      /* Wait until last write operation on RTC registers has finished */
      RTC_WaitForLastTask();
    }
  }
}

/******************************************************************************/
/*                 STM32F10x Peripherals Interrupt Handlers                   */
/*  Add here the Interrupt Handler for the used peripheral(s) (PPP), for the  */
/*  available peripheral interrupt handler's name please refer to the startup */
/*  file (startup_stm32f10x_xx.s).                                            */
/******************************************************************************/

/**
  * @brief  This function handles PPP interrupt request.
  * @param  None
  * @retval None
  */
/*void PPP_IRQHandler(void)
{
}*/

/**
  * @}
  */ 

/**
  * @}
  */ 

/******************* (C) COPYRIGHT 2009 STMicroelectronics *****END OF FILE****/