实时时钟介绍:
STM32F207的实时时钟是一个独立的BCD定时/计数器。它提供一个日历时钟,两个可编程的定时器中断,1个具有中断能力的周期性可编程唤醒标志,同时RTC还包含自动唤醒单元,用于管理低功耗模式。
两个32位的寄存器,包含了实时时钟的秒,分,时(12或24小时制),星期,日,月,年这些内容。这些内容都以BCD码存放在这两个寄存器中。
同时,RTC模块会自动判断闰年与非闰年,并对2月分的天数进行自动处理,当然也会对其他月分的天数进行自动处理,它还支持Daylight saving time compensation(这个不知道怎么译了)功能。
另外的32位寄存器包含了(可编程)闹钟的秒,分,时,星期,日期。
RTC模块还具有数字校准功能,能够补偿由于晶体振荡器精度所致的任何偏差。
上电复位后,所有与RTC相关的寄存器都自动进入保护模式,以防止意外的写操作发生。
注意:只要RTC的供电电压在正常工作范围内,则RTC永远不会停止工作,此时对于RTC来说,芯片的工作模式、低功耗模式以及芯片的复位等都不会对RTC造成影响。
RTC的主要功能介绍:
RTC单元的主要功能如下:
(1)具有秒,分,时,星期,日,月,年的日历功能;
(2)可通过软件编程的Daylight saving compensation;
(3)两个具有中断功能的可编程闹钟,每个闹钟中断都可以被日历项(指的是秒,分,时,星期等)中的任何几项的组合触发;
(4)自动唤醒单元可以产生一个周期性的标志,用于触发自动唤醒中断;
(5)参考时钟检查:一个更高精度的第二时钟源(50或60Hz)可以用于提高日历时钟的精度;
(6)可屏蔽的中断/事件:
a. 闹钟A
b. 闹钟B
c. 唤醒中断
d. 时间戳
e. 修改检测(对IO引脚状态发生变化的检测)
(7)数字校准电路(周期计数器校正),精度为50ppm;
(8)时间戳功能用于事件保存(一个事件);
(9)修改检测:1个事件修改的边沿检测(对于IO引脚,后面详述);
(10)20个备分寄存器(80字节)在修改检测事件发生时时,备分寄存器将被复位;(有待确定)
(11)RTC复用引脚输出功能(RTC_AFO)
a. AFO_CALIB:512Hz时钟输出(使用LSE频率32.768KHz)该功能与引脚RTC_AF1相连;
b. AFO_ALARM:闹钟A或闹钟B或唤醒中断(三个之中只能选定一个作为输出源),该功能与引脚RTC_AF1相连;
(12)RTC复用引脚输入功能(RTC_AFI)
a. AFI_TAMPER:修改事件检测,该功能与引脚RTC_AF1及RTC_AF2相连;
b. AFI_TIMSTAMP:时间戳事件检测,该功能与引脚RTC_AF1及RTC_AF2相连接;
RTC的时钟和预分频器:
STM32F207可以使用三种时钟作为时钟源:LSE(外总低速振荡器,一般为32768Hz的晶体)、LSI(内部低频振荡器时钟)或者HSE(外部高速振荡器,最高1MHz)。在后面的介绍中还会对其配置方法进行详细说明。
一个可编程的预分频器用于产生1Hz的时钟,用于更新日历时钟,为了降低功耗,这个预分频器又被分割成了两个预分频器:
a. 7位的异步分频器,可以通过改变RTC_PRER寄存器中的PREDIV_A位对其进行配置;
b. 13位的同步分频器,可以通过改变RTC_PRER寄存器中的PREDIV_B位对其进行配置。
注意:如果两个分频器同时使用,为了降低功耗,请尽量让异步分频器的分频因子更大一些。
将同步分频器的分频因子设为128,将异步分频器的分频因子设为256,这时候就可以使用外部的32.768Hz的振荡器时钟信息来获取一个1Hz的内部时钟信号(ck_spre),对于分频器来说,其最低的分频因子为2,最大的分频因子为2的20次方,由此,对应的输入频率最大为大约1MHz。ck_spre的计算公式如下:
F(ck_spre) = RTC_CLK/((PREDIV_S+1)*(PREDIV_A+1))
ck_spre时钟可以被用于更新日历时钟,也可以被用作16位唤醒自动重装定时器的时基信号。为了获取较短的定时周期,16位的唤醒自动重装定时器了可以使用RTC_CLK经过可编程4位异步分频器后得到的时钟作为时基信号(后面还会说述)。
实时时钟和日历
RTC的日期和时间寄存器通过与PCLK1(APB1 clock)同步的影子寄存器进行访问:
a. RTC_TR寄存器用于存储RTC的时间数据
b. RTC_DR寄存器用于存储RTC的日期数据
每两个RTCCLK周期,当前日历的数据都会被COPY到影子寄存器中,然后RTC_ISR寄存器中的RSF位会被置位。但在停止和待机模式下,以上数据COPY的过程将不会发生。在退出这些模式后,影子寄存器会在2个RTCCLK周期后被更新。
当应用程序读取日历寄存器时,实质就是访问影子寄存器中的内容。当读取RTC_TR和RTC_DR的时候,APB总线的时钟(fAPB)必须至少是RTC时钟(fRTCCLK)的7倍,以避免读取过程中出错。影子寄存器会随系统复位而被复位。
可编程闹钟
RTC模块提供的两个可编程闹钟,闹钟A和闹钟B.可编程的闹钟功能可以通过设置RTC_CR寄存器中的ALRAIE与ALRBIE位来使能。如果日历时钟的秒,分,时,星期,及日期分别与闹钟寄存器RTC_ALRMAR及RTC_ALRMBR中的对应设置相匹配,则对应的标志位ALRAF和ALRBF将置位。每个日历项可以通过RTC_ALRMAR和RTC_ALRMBR寄存器中的MAKx位独立进行选中或屏蔽。而闹钟中断功能则可以通过设置RTC_CR寄存器中的ALRAIE与ALRBE位进行使能。
闹钟A和闹钟B(如果通过RTC_CR寄存器中的OSE[0:1]位进行使能)可以通过芯片引脚进行AFO_ALARM输出。而输出的极性可以通过RTC_CR中的POL位进行配置。
注意:如果'秒'选项被选中(在RTC_ALRMAR或RTC_ALRMBR寄存器中的MSK0位置1),则RTC_PRER寄存器中的异步预分频器分频因子至少应该设置为3,以保证正确的执行。
周期和自动唤醒
周期唤醒标志由1个16位的可编程自动重装向下计数器控制。唤醒定时器可以被扩展到17位。自动重装功能可以通过RTC_CR寄存器中的WUTE位进行使能。
唤醒定时器的时钟输入可以是以下之一:
a. RTC时钟(RTCCLK)被2/4/8/16分频之后的频率。当RTCCLK选择为LSE(32.768KHz)时,允许将唤醒中断的周期配置为从122us到32s,精度为61us。
b. ck_spre(通常是1Hz的内部时钟),当ck_spre是1Hz的内部时钟时,允许以1秒的精度将唤醒中断时间配置为从1秒到36个小时,当然如此大的可编程时间范围会被分成两个部分,具体如下:
1 - 当WUCKSEL[2:1] = 1:0时,范围从1秒到18小时;
2 - 当WUCKSEL[2:1] = 1:1时,范围从18小时到36小时。在这种情况下,2的16次方被自动地加到16位计数器的当前值。初始化序列完成后,定时器开始向下计数。如果唤醒功能为使能状态,向下计数器则保持低功耗状态。当其计数到0时,RTC_ISR中的WUTF会自动置位,并且唤醒计数器会根据RTC_WUTR中的自动重装值进行自动重装。
注意:WUTF位必须由软件清除。
当通过RTC_CR2中的WUTIE位使能周期唤醒中断时,当计数器中断发生时,就可以使器件退出低功耗模式。
如果对RTC_CR寄存器中的OSEL[0:1]进行相应的配置,周期唤醒标志的变化可以影响AFO_ALARM输出。至于AFO_ALARM的输出极性则可以通过RTC_CR中的POL位进行设置。
系统复位,以及各种低功耗模式(睡眠,停止,待机)对唤醒定时器都没有任何的影响。
RTC的初始化,配置及使用请参考固件库(此处略去)
STM32F207的RTC与备分寄存器
STM32F207具有20个32位的备分寄存器,能够存储共计80字节的内容,当入侵检测(前文中提到的“改变检查”)中断发生时将会被复位,而系统复位,以及系统从其他低功耗模式被唤醒都不会对RTC和BKP造成影响。备分寄存器(BKP)与RTC有什么关系呢,我们从实际来分析一下。我们的RTC和备分寄存器都是可以在系统掉电的情况下由电池进行供电的,假如没有BKP,当系统电源断开后,或者系统复位后,或者系统从低功耗模式被唤醒后,它定要对某些模块进行初始化,而这个时候RTC由电池供电,其数据具有连续性,如果系统不能判断RTC是否该重新初始化,那系统只能对其进行初始化导致系统时间回到默认,用户或上位机软件又得对其进行修改,增加了应用的复杂性。这个时候如果加上BKP,因为它也是可以由电池供电的,掉电丢失信息,意味着如果在电池供电正常的情况下,如果系统VDD掉电,BKP的内容就会保留下来,这样的话,我们在第一次配置好RTC后就可以通过在BKP中某个寄存器内写入一个特片标志值,便可以在系统复位后用于判断,避免对RTC的重配置。
为了避免意外的写入操作,在系统复位后,对BKP和RTC的访问都被禁止,并且备分区BKP被保护起来。需要特殊的指令序列后才可以对其进行访问。
举个例子说明,假如第一次配置完RTC后我们往备分寄存器0中写入一个值:0x32F2,则每次复位后我们配置RTC就用下面一段代码即可:
if (RTC_ReadBackupRegister(RTC_BKP_DR0) != 0x32F2)
{
/* RTC configuration */
RTC_Config();
/* Configure the RTC data register and RTC prescaler */
RTC_InitStructure.RTC_AsynchPrediv = AsynchPrediv;
RTC_InitStructure.RTC_SynchPrediv = SynchPrediv;
RTC_InitStructure.RTC_HourFormat = RTC_HourFormat_24;
/* Check on RTC init */
if (RTC_Init(&RTC_InitStructure) == ERROR)
{
printf("\n\r /!\\***** RTC Prescaler Config failed ********/!\\ \n\r");
}
/* Configure the time register */
RTC_TimeRegulate();
}
当然里面有几个函数,需要你根据自已的需要进行选择性的配置和修改。