一、概述
实时时钟的缩写是RTC,RTC是集成电路,通常称为时钟芯片。RTC通常情况下需要外接32.768kHz晶体,匹配电容、备份电源等元件。
作用:经常用来做时间的计算和显示来源。使用定时器,通过定时一秒钟也可以达到这个时间的计算和显示,定时器程序复位会回复到默认的数值。
RTC的优点是:只要优点,时间就会一直的走下去,就算程序复位,时间也不会重置,有纽扣电池,就算单片机没电,时间也会一直计算下去,就算没有纽扣电池,拔掉单片机所有的输入电源,RTC才会停止。
二、 特性
包含亚秒、秒、分钟、小时( 12/24 小时制)、星期几、日期、月份和年份的日历。软件可编程的夏令时补偿。
两个具有中断功能的可编程闹钟。可通过任意日历字段的组合驱动闹钟。
● 自动唤醒单元,可周期性地生成标志以触发自动唤醒中断。
● 参考时钟检测:可使用更加精确的第二时钟源(50 Hz 或 60 Hz)来提高日历的精确度。
● 利用亚秒级移位特性与外部时钟实现精确同步。
● 可屏蔽中断/事件:
— 闹钟 A
— 闹钟 B
— 唤醒中断
— 时间戳
— 入侵检测
● 数字校准电路(周期性计数器调整)
— 精度为 5 ppm
— 精度为 0.95 ppm,在数秒钟的校准窗口中获得
● 用于事件保存的时间戳功能( 1 个事件)
● 入侵检测:
— 2 个带可配置过滤器和内部上拉的入侵事件
● 20 个备份寄存器( 80 字节)。发生入侵检测时间时,可以复位备份寄存器。
三、RTC的时钟
LSE:外部低速时钟
初始化配置
RTC寄存器是一个32位的寄出去你,除了当BYPSHAD=0时对日历影子寄存器执行的读访问之外,APB接口会在访问RTC寄存器时引入2个等待周期。
日历配置
系统复位后,应用可读取 RTC_ISR 寄存器中的 INITS 标志,以检查日历是否已初始化。如
果该标志为 0,表明日历尚未初始化,因为年份字段设置为其上电复位时的默认值 (0x00)。
要在初始化之后读取日历,必须首先用软件检查 RTC_ISR 寄存器的 RSF 标志是否置 1。
复位RTC
同步RTC
初始化平移操作前,用户必须检查确认 SS[15] = 0,以确保不会发生上溢。对 RTC_SHIFTR 寄存器执行写操作以启动平移操作时,硬件会将 SHPF 标志置 1 以指示平 移操作挂起。完成平移操作时,硬件会将将该位清零。该同步功能与参考时钟检测功能不兼容:当 REFCKON=1 时,固件不能对 RTC_SHIFTR 执 行写操作。
RTC初始化步骤
1、选择时钟源
在时钟树开启相应的时钟,顺序如下:
这一步的代码
void rtc_init()
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);//开电源
PWR_BackupAccessCmd(ENABLE ); //使能后备寄存器访问
}
打开LSE后,需要进行精密校准,需要精密校准RTC的LSE时钟。
使能RTC时钟,RCC文件里面专门为RTC提供时钟函数 ;
2、使能PWR时钟
使能PWR的电源接口时钟,
想要激活RTC的写保护,那么必须显示使能PWR电源控制寄存器,因为这个控制器控制这RTC的写保护。
接下来,就要进行对备份区的使能操作,
3、使能后备区寄存器访问
后备区和RTC内存是一起,所以后备区也需要使能,作用:第一次初始化完后,写入一个值,用于记录已经初始化过RTC的时间了,系统重启或者复位,我们需要读取这个备份区的数据,判断是否需要重新设置初始化时间。
4、初始化RTC结构体
在设置时间格式、预分频、初始化时间和日期,需要先进入初始化模式,时间和日期的值才可以更新。
rtc.c
void rtc_init()
{
RTC_InitTypeDef RTC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);//开电源
PWR_BackupAccessCmd(ENABLE ); //使能后备寄存器访问
if(RTC_ReadBackupRegister(RTC_BKP_DR0)!=0X11)
{
RCC_LSEConfig(RCC_LSE_ON);//LSE开启
while(!RCC_GetFlagStatus(RCC_FLAG_LSERDY));//等待lse稳定
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);//设置LSE为RTC时钟
RCC_RTCCLKCmd(ENABLE);
rtc_SetTimeData(11,22,54,22,4,26,2);
RTC_WriteBackupRegister(RTC_BKP_DR0,0X11);
}
RTC_Set_AlarmA(2,11,38,20);
RTC_Set_AlarmB(2,16,21,0);
}
5、设置时间
RTC_TimeTypeInitStructure.RTC_Hours=hour;
RTC_TimeTypeInitStructure.RTC_Minutes=min;
RTC_TimeTypeInitStructure.RTC_Seconds=sec;
RTC_TimeTypeInitStructure.RTC_H12=RTC_H12_AM;
6、时钟日期
现在相应的功能文件stm32f407x.rtc.h找到相应的时间和日期结构体函数;
设置日期
RTC_DateTypeInitStructure.RTC_Date=date;
RTC_DateTypeInitStructure.RTC_Month=month;
RTC_DateTypeInitStructure.RTC_WeekDay=week;
RTC_DateTypeInitStructure.RTC_Year=year;
7、备份区读写,用于判断是否初始化过时间
void rtc_init()
{
RTC_InitTypeDef RTC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);//开电源
PWR_BackupAccessCmd(ENABLE ); //使能后备寄存器访问
if(RTC_ReadBackupRegister(RTC_BKP_DR0)!=0X11)
{
RCC_LSEConfig(RCC_LSE_ON);//LSE开启
while(!RCC_GetFlagStatus(RCC_FLAG_LSERDY));//等待lse稳定
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);//设置LSE为RTC时钟
RCC_RTCCLKCmd(ENABLE);
rtc_SetTimeData(11,22,54,22,4,26,2);
RTC_WriteBackupRegister(RTC_BKP_DR0,0X11);
}
RTC_Set_AlarmA(2,11,38,20);
RTC_Set_AlarmB(2,16,21,0);
}
完整代码
rtc.c
#include "rtc.h"
#include "beep.h"
#include "lcd.h"
#include "stdio.h"
u32 rct_AlarmA_flag;
RTC_TimeTypeDef RTC_TimeTypeInitStructure;
RTC_DateTypeDef RTC_DateTypeInitStructure;
RTC_AlarmTypeDef RTC_AlarmStruct;
//RTC时间设置函数
//hour,min,sec:时,分,秒设定值
//ampm:@RTC_AM_PM_Definitions:RTC_H12_AM/RTC_H12_PM
//返回值:SUCEE(1),成功;ERROR(0),进入初始化模式失败
void rtc_SetTimeData(u8 hour,u8 min,u8 sec,u8 year,
u8 month,u8 date,u8 week)
{
RTC_TimeTypeInitStructure.RTC_Hours=hour;
RTC_TimeTypeInitStructure.RTC_Minutes=min;
RTC_TimeTypeInitStructure.RTC_Seconds=sec;
RTC_TimeTypeInitStructure.RTC_H12=RTC_H12_AM;
RTC_SetTime(RTC_Format_BIN,&RTC_TimeTypeInitStructure);
RTC_DateTypeInitStructure.RTC_Date=date;
RTC_DateTypeInitStructure.RTC_Month=month;
RTC_DateTypeInitStructure.RTC_WeekDay=week;
RTC_DateTypeInitStructure.RTC_Year=year;
RTC_SetDate(RTC_Format_BIN,&RTC_DateTypeInitStructure);
}
void RTC_GetTimeDate(void)
{
static u8 temp=0;
RTC_GetTime(RTC_Format_BIN,&RTC_TimeTypeInitStructure);
RTC_GetDate(RTC_Format_BIN,&RTC_DateTypeInitStructure);
if(temp !=RTC_TimeTypeInitStructure.RTC_Seconds)
{
temp=RTC_TimeTypeInitStructure.RTC_Seconds;
printf("%d:%d:%d\r\n",RTC_TimeTypeInitStructure.RTC_Hours,RTC_TimeTypeInitStructure.RTC_Minutes,RTC_TimeTypeInitStructure.RTC_Seconds);
}
}
void RTC_Set_AlarmA(u8 week,u8 hour,u8 min,u8 sec)
{
NVIC_InitTypeDef NVIC_InitStruct;
EXTI_InitTypeDef EXTI_InitStruct;
RTC_AlarmCmd(RTC_Alarm_A, DISABLE);//关闭闹钟A
RTC_TimeTypeInitStructure.RTC_Hours=hour;
RTC_TimeTypeInitStructure.RTC_Minutes=min;
RTC_TimeTypeInitStructure.RTC_Seconds=sec;
RTC_TimeTypeInitStructure.RTC_H12=RTC_H12_AM;
RTC_AlarmStruct.RTC_AlarmDateWeekDay=week;
RTC_AlarmStruct.RTC_AlarmDateWeekDaySel=RTC_AlarmDateWeekDaySel_WeekDay;
RTC_AlarmStruct.RTC_AlarmMask=RTC_AlarmMask_None;
RTC_AlarmStruct.RTC_AlarmTime=RTC_TimeTypeInitStructure;
RTC_SetAlarm(RTC_Format_BIN, RTC_Alarm_A,&RTC_AlarmStruct);
EXTI_InitStruct.EXTI_Line = EXTI_Line17;//中断线号0
EXTI_InitStruct.EXTI_LineCmd = ENABLE; //中断线使能
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;//中断模式
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;//上升沿触发
EXTI_Init(&EXTI_InitStruct);
NVIC_InitStruct.NVIC_IRQChannel = RTC_Alarm_IRQn ;//中断源
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;//使能中断
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;//抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;//响应优先级
NVIC_Init(&NVIC_InitStruct);
RTC_ITConfig( RTC_IT_ALRA, ENABLE);//S使能闹钟A中断
RTC_AlarmCmd(RTC_Alarm_A, ENABLE);//开启闹钟A
}
void RTC_Alarm_IRQHandler (void)
{
if(RTC_GetFlagStatus(RTC_FLAG_ALRAF))
{
rct_AlarmA_flag=1;
RTC_ClearITPendingBit(RTC_IT_ALRA);
EXTI_ClearITPendingBit( EXTI_Line17);
}
else if(RTC_GetFlagStatus(RTC_FLAG_ALRBF ))
{
RTC_ClearITPendingBit(RTC_IT_ALRB);
EXTI_ClearITPendingBit( EXTI_Line17);
}
}
void RTC_Set_AlarmB(u8 week,u8 hour,u8 min,u8 sec)
{
NVIC_InitTypeDef NVIC_InitStruct;
EXTI_InitTypeDef EXTI_InitStruct;
RTC_AlarmCmd(RTC_Alarm_B, DISABLE);//关闭闹钟B
RTC_TimeTypeInitStructure.RTC_Hours=hour;
RTC_TimeTypeInitStructure.RTC_Minutes=min;
RTC_TimeTypeInitStructure.RTC_Seconds=sec;
RTC_TimeTypeInitStructure.RTC_H12=RTC_H12_AM;
RTC_AlarmStruct.RTC_AlarmDateWeekDay=week;
RTC_AlarmStruct.RTC_AlarmDateWeekDaySel=RTC_AlarmDateWeekDaySel_WeekDay;
RTC_AlarmStruct.RTC_AlarmMask=RTC_AlarmMask_None;
RTC_AlarmStruct.RTC_AlarmTime=RTC_TimeTypeInitStructure;
RTC_SetAlarm(RTC_Format_BIN, RTC_Alarm_B,&RTC_AlarmStruct);
EXTI_InitStruct.EXTI_Line = EXTI_Line17;//中断线号0
EXTI_InitStruct.EXTI_LineCmd = ENABLE; //中断线使能
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;//中断模式
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;//上升沿触发
EXTI_Init(&EXTI_InitStruct);
NVIC_InitStruct.NVIC_IRQChannel = RTC_Alarm_IRQn ;//中断源
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;//使能中断
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;//抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;//响应优先级
NVIC_Init(&NVIC_InitStruct);
RTC_ITConfig( RTC_IT_ALRB, ENABLE);//S使能闹钟A中断
RTC_AlarmCmd(RTC_Alarm_B, ENABLE);//开启闹钟A
}
void Rtc_Lcd_Display(void)
{
char hour[10];
char min[10];
char sec[10];
RTC_GetTimeDate();
sprintf(hour,"%.2d",RTC_TimeTypeInitStructure.RTC_Hours);
sprintf(min,"%.2d",RTC_TimeTypeInitStructure.RTC_Minutes);
sprintf(sec,"%.2d",RTC_TimeTypeInitStructure.RTC_Seconds);
LCD_Show_Str_ChineseorChar(50,70,(u8 *)hour,BLACK,WHITE);
LCD_Show_Str_ChineseorChar(80,70,(u8 *)min,BLACK,WHITE);
LCD_Show_Str_ChineseorChar(110,70,(u8 *)sec,BLACK,WHITE);
}
void rtc_init()
{
RTC_InitTypeDef RTC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);//开电源
PWR_BackupAccessCmd(ENABLE ); //使能后备寄存器访问
if(RTC_ReadBackupRegister(RTC_BKP_DR0)!=0X11)
{
RCC_LSEConfig(RCC_LSE_ON);//LSE开启
while(!RCC_GetFlagStatus(RCC_FLAG_LSERDY));//等待lse稳定
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);//设置LSE为RTC时钟
RCC_RTCCLKCmd(ENABLE);
rtc_SetTimeData(11,22,54,22,4,26,2);
RTC_WriteBackupRegister(RTC_BKP_DR0,0X11);
}
RTC_Set_AlarmA(2,11,38,20);
RTC_Set_AlarmB(2,16,21,0);
}
rtc.h
#ifndef __RTC_DECLS
#define __RTC_DECLS
#include "stm32f4xx.h"
#include "io_bit.h"
extern u32 rct_AlarmA_flag;
void beep_init(void);
void rtc_init(void);
void RTC_GetTimeDate(void);
void Rtc_Lcd_Display(void);
#endif