平凡

STM32学习笔记(17)-RTC实践

0
阅读(3732)

RTC实践

       BKP还没有搞完,怎么又换到了RTC上了,因为RTC和BKP有些联系,想不关联还不行呢。

以下是数据手册上有关RTC的介绍:

******介绍开始

15.1     RTC 简介 

       实时时钟是一个独立的定时器。RTC模块拥有一组连续计数的计数器,在相应软件配置下,可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。

       RTC模块和时钟配置系统(RCC_BDCR寄存器)是在后备区域,即在系统复位或从待机模式唤醒后RTC的设置和时间维持不变。

       系统复位后,禁止访问后备寄存器和RTC,防止对后备区域(BKP)的意外写操作。执行以下操作使能对后备寄存器和RTC的访问:

●  设置寄存器RCC_APB1ENR的PWREN和BKPEN位来使能电源和后备接口时钟

●  设置寄存器PWR_CR的DBP位使能对后备寄存器和RTC的访问。

******介绍结束

       下面就以库中所带的实例来玩这个RTC,正好也要解决TAMPER引脚的问题。

       这次要用到的例子是这个:

       有关配置没有什么可多说的,把上一个例子:BKP->Backup_Data中修改过的:

stm32_eval.c

stm3210e_eval.h

stm32_eval.h

三个文件复制过来,建立工程,一路顺风,非常顺利地编译、链接好,然后进入调试,结果,出问题了,见下面:

      

       进入调试界面即停在了反汇编界面。一旦执行程序,立即就停下来。这个BKPT指令是个什么东西呢?查了一下指令表,原来是个断点指令,那为什么会停止在这里呢?万能的网啊,一搜就出来了,感谢前人的努力,感谢伟大的网...。

     结论是:" 凡是用到printf的都会出现这样的问题。"

       解决方法:

打开Options for Target项,把那个Use MicroLIB前面打上勾,重新编译链接即可。

      出现问题的原因,网上的相关解释是:这是Printf库函数的问题,需要使用MicroLIB这个库来替代默认的库。原因是默认printf并非使用串口,所以要改掉。

       在main.c中有这样一段函数:

/**

  * @brief  Retargets the C library printf function to the USART.

  * @param  None

  * @retval None

  */

PUTCHAR_PROTOTYPE

{

  /* 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)

  {}

   return ch;

}

       根据这段函数的说明,就是做这个事情的。

      但是还有一些疑问:

      (1)这个例子的readme.txt中并没有说明要用MicroLIB这个库的事情。

       (2)如果不用硬件,而是用软件仿真,也是不需要使用MicroLIB库的。

       因此,有关MicroLIB的问题还不是特别明确,还需要进一步研究。是否是这段程序还用到了scanf的原因?

例子的运行

       如果是第一次运行,那么会出现如下画面:

即其中有提示RTC没有初始化,要求进行设置的提示,根据提示分别送入小时,分钟,秒的数值,即完成设置工作,程序不断地将当前时间通过串口送出。

如果在VBAT端接入后备电池,那么断电后再上电显示的信息如下:

其中提示RTC已初始化,不需要再进行初始化,直接将时间不断地从串口送出。

 

程序解读

/**

  * @brief  Configures the RTC.

  * @param  None

  * @retval None

  */

void RTC_Configuration(void)

{

  /* 开启PWR和BKP模块的时钟 */

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);

   /* 允许对BKP进行存取操作*/

  PWR_BackupAccessCmd(ENABLE);

   /* 初始化BKP区域*/

  BKP_DeInit();

   /* LSE允许,这里LSE是指外部接的32768HZ 晶振的一个振荡器,它使用PC14和PC15两条引脚。有关这部分的一些细节描述如下:

当备份区域由V DD (内部模拟开关连到V DD )供电时,下述功能可用:

●  PC14和PC15可以用于GPIO或LSE引脚

●  PC13可以作为通用I/O口、TAMPER引脚、RTC校准时钟、RTC闹钟或秒输出

当后备区域由VBAT供电时(VDD消失后模拟开关连到VBAT),可以使用下述功能:

●  PC14和PC15只能用于LSE引脚

●  PC13可以作为TAMPER引脚、RTC闹钟或秒输出

*/

  RCC_LSEConfig(RCC_LSE_ON);

  /* 等待LSE振荡器就绪 */

  while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)

  {}

   /* 选择LSE作为RTC的时钟源

       说明:RTC时钟源可以是以下三种之一:

─    HSE 时钟除以 128

─    LSE 振荡器时钟

─    LSI振荡器时钟

其中LSI是一个内部的低频RC振荡器。关于RTC的这三个时钟各有何特点,见下面的说明。

*/

  RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);

   /* RTC时钟允许*/

  RCC_RTCCLKCmd(ENABLE);

   /* 等待RTC寄存器同步

理由如下:

15.3.3  读 RTC 寄存器 

RTC核完全独立于RTC APB1接口。

软件通过APB1接口访问RTC的预分频值、计数器值和闹钟值。但是,相关的可读寄存器只在与RTC APB1时钟进行重新同步的RTC时钟的上升沿被更新。RTC标志也是如此的。

这意味着,如果APB1接口刚刚被开启之后,在第一次的内部寄存器更新之前,从APB1上读出RTC寄存器的第一个值可能被破坏了(通常读到0)。下述几种情况下能够发生这种情形:

●  发生系统复位或电源复位

●  系统刚从待机模式唤醒(参见4.3节)。

●  系统刚从停机模式唤醒(参见4.3节)。

所有以上情况中,APB1接口被禁止时(复位、无时钟或断电)RTC核仍保持运行状态。 因此,若在读取RTC寄存器曾经被禁止的RTC  APB1接口,软件首先须等待RTC_CRL寄存器中

的RSF位(寄存器同步标志)被硬件置1。

void RTC_WaitForSynchro(void)

{

  /* Clear RSF flag */

  RTC->CRL &= (uint16_t)~RTC_FLAG_RSF;

  /* Loop until RSF flag is set */

  while ((RTC->CRL & RTC_FLAG_RSF) == (uint16_t)RESET)

  {

  }

}

*/

  RTC_WaitForSynchro();

   /* 等待最后写操作完成*/

  RTC_WaitForLastTask();

   /* 允许秒中断,相应代码如下:

void RTC_ITConfig(uint16_t RTC_IT, FunctionalState NewState)

{

  /* Check the parameters */

  assert_param(IS_RTC_IT(RTC_IT)); 

  assert_param(IS_FUNCTIONAL_STATE(NewState));

    if (NewState != DISABLE)

  {

    RTC->CRH |= RTC_IT;

  }

  else

  {

    RTC->CRH &= (uint16_t)~RTC_IT;

  }

}

而调用这段程序的代码中的第一个参数是:

#define RTC_IT_SEC           ((uint16_t)0x0001)  /*!< Second interrupt */

可见它是对RTC->CRH的最低位进行操作,CRH相关内容见下面的图。

*/

  RTC_ITConfig(RTC_IT_SEC, ENABLE);

   /* 等待最后的写操作完成*/

  RTC_WaitForLastTask();

   /* 设置RTC预分频器: set RTC period to 1sec */

  RTC_SetPrescaler(32767); /* RTC period = RTCCLK/RTC_PR = (32.768 KHz)/(32767+1) */

   /* 等待最后的写操作完成*/

  RTC_WaitForLastTask();

}

    下面的图是有关RTC时钟选择的问题,其中说明了各种时钟的工作特性:

 

       下面的图是有关RTC_CRH寄存器的描述,放在这里是为了说明SECIE标志位。