一、概述
实时时钟的缩写是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