8.1 RTC简介
实时时钟 (RTC) 是一个独立的 BCD 定时器/计数器。RTC 提供一个日历时钟、两个可编程闹钟中断,以及一个具有中断功能的周期性可编程唤醒标志。RTC 还包含用于管理低功耗模式的自动唤醒单元。
两个 32 位寄存器包含二进码十进数格式 (BCD) 的秒、分钟、小时(12 或 24 小时制)、星期几、日期、月份和年份。此外,还可提供二进制格式的亚秒值。系统可以自动将月份的天数补偿为 28、29(闰年)、30 和 31 天。并且还可以进行夏令时补偿。其它 32 位寄存器还包含可编程的闹钟亚秒、秒、分钟、小时、星期几和日期。还可以根据需要设置闹钟。
此外,还可以使用数字校准功能对晶振精度的偏差进行补偿。上电复位后,所有 RTC 寄存器都会受到保护,以防止可能的非正常写访问。无论器件状态如何(运行模式、低功耗模式或处于复位状态),只要电源电压保持在工作范围内,RTC 便不会停止工作。
RTC中断的wakeup引脚可以唤醒MCU,比如当MCU在低功耗状态下或者休眠之类的状态下,通过引脚的Wakeup功能可以将MCU唤醒,让MCU进入正常的工作状态。
8.2 RTC应用代码
// RTC 初始化
uint8_t RtcInit(void)
{
RTC_InitTypeDef RTC_InitStructure;
u16 retry = 0X1FFF; // 超时时间
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); // 使能PWR时钟
PWR_BackupAccessCmd(ENABLE); // 使能后备寄存器访问
if(RTC_ReadBackupRegister(RTC_BKP_DR0)!=0x5050) //是否第一次配置?
{
RCC_LSEConfig(RCC_LSE_ON); // 开启LSE
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) // 等待低速晶振就绪
{
retry++;
delay_ms(10);
}
if(retry==0) return 1; // LSE 开启失败.
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //设置RTC时钟(RTCCLK),选择LSE作为RTC时钟
RCC_RTCCLKCmd(ENABLE); //使能RTC时钟
RTC_InitStructure.RTC_AsynchPrediv = 0x7F;//RTC异步分频系数(1~0X7F)
RTC_InitStructure.RTC_SynchPrediv = 0xFF;//RTC同步分频系数(0~7FFF)
RTC_InitStructure.RTC_HourFormat = RTC_HourFormat_24;//RTC设置为,24小时格式
RTC_Init(&RTC_InitStructure);
RTC_Set_Time(8,0,0,RTC_H12_AM); //设置时间
RTC_Set_Date(24,5,1,1); //设置日期
RTC_WriteBackupRegister(RTC_BKP_DR0,0x5050); //标记已经初始化过了
}
return 0;
}
// 获取实时时间
RTC_TimeTypeDef RtcGetTime()
{
RTC_TimeTypeDef RTC_TimeStruct;
RTC_GetTime(RTC_Format_BIN,&RTC_TimeStruct);
return RTC_TimeStruct;
}
//设置闹钟时间(按星期闹铃,24小时制)
//week,hour,min,sec:周几,时,分,秒
void RtcSetAlarmA(uint8_t week,uint8_t hour,uint8_t min,uint8_t sec)
{
EXTI_InitTypeDef EXTI_InitStructure;
RTC_AlarmTypeDef RTC_AlarmTypeInitStructure;
RTC_TimeTypeDef RTC_TimeTypeInitStructure;
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_AlarmTypeInitStructure.RTC_AlarmDateWeekDay=week;//星期
RTC_AlarmTypeInitStructure.RTC_AlarmDateWeekDaySel=RTC_AlarmDateWeekDaySel_WeekDay;//按星期闹
RTC_AlarmTypeInitStructure.RTC_AlarmMask=RTC_AlarmMask_None;//精确匹配星期,时分秒
RTC_AlarmTypeInitStructure.RTC_AlarmTime=RTC_TimeTypeInitStructure;
RTC_SetAlarm(RTC_Format_BIN,RTC_Alarm_A,&RTC_AlarmTypeInitStructure);
RTC_ClearITPendingBit(RTC_IT_ALRA);//清除RTC闹钟A的标志
EXTI_ClearITPendingBit(EXTI_Line17);//清除LINE17上的中断标志位
RTC_ITConfig(RTC_IT_ALRA,ENABLE);//开启闹钟A中断
RTC_AlarmCmd(RTC_Alarm_A,ENABLE);//开启闹钟A
EXTI_InitStructure.EXTI_Line = EXTI_Line17; // LINE17
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; // 中断事件
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //上升沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;//使能LINE17
EXTI_Init(&EXTI_InitStructure);//配置
NVIC_InitStructure.NVIC_IRQChannel = RTC_Alarm_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; // 抢占优先级1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02; // 子优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能外部中断通道
NVIC_Init(&NVIC_InitStructure); //配置
}
//RTC闹钟中断服务函数
void RTC_Alarm_IRQHandler(void)
{
if(RTC_GetFlagStatus(RTC_FLAG_ALRAF)==SET) // ALARM A中断
{
RTC_ClearFlag(RTC_FLAG_ALRAF); // 清除中断标志
}
EXTI_ClearITPendingBit(EXTI_Line17); //清除中断线17的中断标志
}
//cnt:自动重装载值.减到0,产生中断.
void RTC_Set_WakeUp(u32 wksel,u16 cnt)
{
EXTI_InitTypeDef EXTI_InitStructure;
RTC_WakeUpCmd(DISABLE); // 关闭WAKE UP
RTC_WakeUpClockConfig(wksel);// 唤醒时钟选择
RTC_SetWakeUpCounter(cnt); // 设置WAKE UP自动重装载寄存器
RTC_ClearITPendingBit(RTC_IT_WUT); // 清除RTC WAKE UP的标志
EXTI_ClearITPendingBit(EXTI_Line22); // 清除LINE22上的中断标志位
RTC_ITConfig(RTC_IT_WUT,ENABLE); // 开启WAKE UP 定时器中断
RTC_WakeUpCmd( ENABLE); // 开启WAKE UP 定时器
EXTI_InitStructure.EXTI_Line = EXTI_Line22; // LINE22
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; // 中断事件
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //上升沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE; // 使能LINE22
EXTI_Init(&EXTI_InitStructure); // 配置
NVIC_InitStructure.NVIC_IRQChannel = RTC_WKUP_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; // 抢占优先级1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02; // 子优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能外部中断通道
NVIC_Init(&NVIC_InitStructure); // 配置
}
//RTC WAKE UP中断服务函数
void RTC_WKUP_IRQHandler(void)
{
if(RTC_GetFlagStatus(RTC_FLAG_WUTF)==SET)//WK_UP中断?
{
RTC_ClearFlag(RTC_FLAG_WUTF); //清除中断标志
}
EXTI_ClearITPendingBit(EXTI_Line22);//清除中断线22的中断标志
}