RTC是什么–简介
RTC,英文全称:Real-time clock,中文名称:实时时钟,是指可以像时钟一様输出实际时间的电子设备,一般会是集成电路,因此也称为时钟芯片。实时时钟芯片是日常生活中应用最为广泛的消费类电子产品之一。它为人们提供精确的实时时间,或者为电子系统提供精确的时间基准,目前实时时钟芯片大多采用精度较高的晶体振荡器作为时钟源。
对于STM32F的RTC实时时钟提供了一个日历时钟,两个可编程闹钟中断和一个具有中断功能的可编程唤醒标志。由于RTC的时钟配置是在后备区域,因此在后备区域供电正常的情况下,即使是系统复位或者是从待机模式唤醒之后时间依然维持不变。下边就以RTC模块的框图为引线,对RTC的相关功能和操作做相关介绍。
二、框图:
首先是时钟的选择,一般选择LSE作为时钟来源,频率32768Hz,随后经过一个7位的异步预分频(默认值为127+1)和一个15位的同步预分频(255+1),得到1Hz的时钟频率,对于日历的配置,寄存器RTC_TR用来配置时间(时分秒),寄存器RTC_DR用来配置日期(年月日和星期),由于寄存器RTC_SSR由第一次分频得到的频率来驱动,则寄存器RTC_SSR可以存放比秒还要小的单位压秒(该寄存器不用来设置,可以读取)。闹钟A和闹钟B设定时间,来和当前时间比较,如果相等则会触发相应的中断。下边是自动唤醒功能,首先是预分频器的分频,得到相应时钟,然后选择相应的时钟驱动自动重载寄存器(RTC_WUTR),自动重载寄存器的值依次递减至0,做相应的中断操作。
时钟选择
从RTC的定时器特性来说,它是一个32位的计数器,只能向上计数.他使用的时钟源有三种,分别为:
1,高速外部时钟的128分频:HSE/128;
2,低速内部时钟LSI;
3,低速外部时钟LSE;
使用HSE分频时钟或者LSI的时候,在主电源VDD掉电的情况下,这两个时钟来源都会受到影响,因此没法保证RTC正常工作.所以RTC一般都时钟低速外部时钟LSE,频率为实时时钟模块中常用的32.768KHz,因为32768 = 2^15,分频容易实现,所以被广泛应用到RTC模块.(在主电源VDD有效的情况下(待机),RTC还可以配置闹钟事件使STM32退出待机模式).
RTC工作过程:
中断配置
由于时钟选择的是外部时钟32.768KHz的故其时间较为准确,可以提供准确的时间兵器由于有电池的供电故在单片机掉电后依然可以及时当主电源 VDD 断电时,可通过 VBAT 电压为实时时钟 (RTC)、RTC 备份寄存器和备份SRAM (BKP SRAM) 供电
代码原理
void rtc_init(void)
{
/* Enable the PWR clock ,使能电源时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
/* Allow access to RTC ,允许访问RTC*/
PWR_BackupAccessCmd(ENABLE);
#if 0
/* 使能LSE*/
RCC_LSEConfig(RCC_LSE_ON);
/* 检查该LSE是否有效*/
while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);
/* 选择LSE作为RTC的硬件时钟源*/
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
#else //若LSE无法工作,可用内部LSI
/* 使能LSI*/
RCC_LSICmd(ENABLE);
/* 检查该LSI是否有效*/
while(RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET);
/* 选择LSI作为RTC的硬件时钟源*/
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
#endif
/* ck_spre(1Hz) = RTCCLK(LSE) /(uwAsynchPrediv + 1)*(uwSynchPrediv + 1)
*/
/* Enable the RTC Clock ,使能RTC时钟*/
RCC_RTCCLKCmd(ENABLE);
/* Wait for RTC APB registers synchronisation ,等待RTC相关寄存器就绪*/
RTC_WaitForSynchro();
#if 0 //LSE
/* Configure the RTC data register and RTC prescaler,配置RTC数据寄存器与
RTC的分频值 */
RTC_InitStructure.RTC_AsynchPrediv = 0x7F; //异步分频系数
RTC_InitStructure.RTC_SynchPrediv = 0xFF; //同步分频系数
RTC_InitStructure.RTC_HourFormat = RTC_HourFormat_24; //24小时格式
RTC_Init(&RTC_InitStructure);
#else //LSI
/* Configure the RTC data register and RTC prescaler,配置RTC数据寄存器与
RTC的分频值 */
RTC_InitStructure.RTC_AsynchPrediv = 0x7F; //异步分频系数
RTC_InitStructure.RTC_SynchPrediv = 0xF9; //同步分频系数
RTC_InitStructure.RTC_HourFormat = RTC_HourFormat_24; //24小时格式
RTC_Init(&RTC_InitStructure);
#endif
/* Set the date: Wednesday 2017/11/29 */
RTC_DateStructure.RTC_Year = 0x17;
RTC_DateStructure.RTC_Month = RTC_Month_November;
RTC_DateStructure.RTC_Date = 0x29;
RTC_DateStructure.RTC_WeekDay = RTC_Weekday_Wednesday;
RTC_SetDate(RTC_Format_BCD, &RTC_DateStructure);
/* Set the time to 14h 56mn 00s PM */
RTC_TimeStructure.RTC_H12 = RTC_H12_PM;
RTC_TimeStructure.RTC_Hours = 0x14;
RTC_TimeStructure.RTC_Minutes = 0x56;
RTC_TimeStructure.RTC_Seconds = 0x00;
RTC_SetTime(RTC_Format_BCD, &RTC_TimeStructure);
//关闭唤醒功能
RTC_WakeUpCmd(DISABLE);
//为唤醒功能选择RTC配置好的时钟源
RTC_WakeUpClockConfig(RTC_WakeUpClock_CK_SPRE_16bits);
//设置唤醒计数值为自动重载,写入值默认是0
RTC_SetWakeUpCounter(0);
//清除RTC唤醒中断标志
RTC_ClearITPendingBit(RTC_IT_WUT);
//使能RTC唤醒中断
RTC_ITConfig(RTC_IT_WUT, ENABLE);
//使能唤醒功能
RTC_WakeUpCmd(ENABLE);
/* Configure EXTI Line22,配置外部中断控制线22 */
EXTI_InitStructure.EXTI_Line = EXTI_Line22; //当前使用外部中断控制线22
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //中断模式
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //上升沿触发中断
EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能外部中断控制线22
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = RTC_WKUP_IRQn; //允许RTC唤醒中断触发
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x03; //抢占优先
为0x3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03; //响应优先级为0x3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能
NVIC_Init(&NVIC_InitStructure);
rtc的唤醒中断服务函数
void RTC_WKUP_IRQHandler(void)
{
if(RTC_GetITStatus(RTC_IT_WUT) != RESET)
{
printf("RTC_WKUP_IRQHandler\r\n");
RTC_ClearITPendingBit(RTC_IT_WUT);
EXTI_ClearITPendingBit(EXTI_Line22);
}
}
- 获取时间与日期
RTC_GetTime(RTC_Format_BCD,&RTC_TimeStructure);
printf("%02x:%02x:%02x\r\n",RTC_TimeStructure.RTC_Hours,RTC_TimeStructure.
TC_Minutes,RTC_TimeStructure.RTC_Seconds);
//获取日期
RTC_GetDate(RTC_Format_BCD,&RTC_DateStructure);
printf("20%02x/%02x/%02xWeek:%x\r\n",RTC_DateStructure.RTC_Year,RTC_DateS
tructure.RTC_Month,RTC_DateStructure.RTC_Date,RTC_DateStructure.RTC_WeekDay);
- 改写时间与日期
/* Set the date: Wednesday 2017/11/29 */
RTC_DateStructure.RTC_Year = 0x17;
RTC_DateStructure.RTC_Month = RTC_Month_November;
RTC_DateStructure.RTC_Date = 0x29;
RTC_DateStructure.RTC_WeekDay = RTC_Weekday_Wednesday;
RTC_SetDate(RTC_Format_BCD, &RTC_DateStructure);
/* Set the time to 14h 56mn 00s PM */
RTC_TimeStructure.RTC_H12 = RTC_H12_PM;
RTC_TimeStructure.RTC_Hours = 0x14;
RTC_TimeStructure.RTC_Minutes = 0x56;
RTC_TimeStructure.RTC_Seconds = 0x00;
RTC_SetTime(RTC_Format_BCD, &RTC_TimeStructure);
注意:在设置时间与读取时间的值都是咦BCD码模式输出的即 12 -》bcd码12知识我们看的发不动
二进制编码的十进制数,简称BCD码(Binary Coded Decimal) 。这种方法是用4位二进制码
的组合代表十进制数的0,1,2,3,4,5,6 ,7,8,9 十个数符。4位二进制数码有16种组合,
原则上可任选其中的10种作为代码,分别代表十进制中的0,1,2,3,4,5,6,7,8,9 这十个
数符。最常用的BCD码称为8421BCD码,8.4.2.1 分别是4位二进数的位取值。 下图为十进制数和
8421BCD编码的对应关系表:
RTC家具有闹钟功能
闹钟代码
1.闹钟初始化
void rtc_alarm_init(void)
{
/* 允许RTC的A闹钟触发中断 */
RTC_ITConfig(RTC_IT_ALRA, ENABLE);
/* 清空标志位 */
RTC_ClearFlag(RTC_FLAG_ALRAF);
/*使能外部中断控制线17的中断*/
EXTI_ClearITPendingBit(EXTI_Line17);
EXTI_InitStructure.EXTI_Line = EXTI_Line17;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
/*使能闹钟的中断 */
NVIC_InitStructure.NVIC_IRQChannel = RTC_Alarm_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
设定闹钟的时间`
void rtc_alarm_set(RTC_AlarmTypeDef RTC_AlarmStructure)
{
/* 关闭闹钟,若不关闭,配置闹钟触发的中断有BUG,无论怎么配置,只要到00秒,则触
发中断*/
RTC_AlarmCmd(RTC_Alarm_A, DISABLE);
/* 配置RTC的A闹钟,注:RTC的闹钟有两个,分别为闹钟A与闹钟B */
RTC_SetAlarm(RTC_Format_BCD, RTC_Alarm_A, &RTC_AlarmStructure);
/* 让RTC的闹钟A工作*/
RTC_AlarmCmd(RTC_Alarm_A, ENABLE);
}
RTC_AlarmTypeDef RTC_AlarmStructure;
int main(void)
{
#if 1
//闹钟每天生效
RTC_AlarmStructure.RTC_AlarmMask = RTC_AlarmMask_DateWeekDay;
rtc_alarm_set(RTC_AlarmStructure);
#endif
#if 0
//闹钟指定20号生效
RTC_AlarmStructure.RTC_AlarmDateWeekDay = 0x20; //20号
RTC_AlarmStructure.RTC_AlarmDateWeekDaySel = RTC_AlarmDateWeekDaySel_Da
te; //指定哪一天生效
RTC_AlarmStructure.RTC_AlarmMask = RTC_AlarmMask_None; //不屏蔽哪一天和星
期的配置
rtc_alarm_set(RTC_AlarmStructure);
#endif
#if 0
//闹钟指定星期三
RTC_AlarmStructure.RTC_AlarmDateWeekDay = RTC_Weekday_Wednesday; //星期
三
RTC_AlarmStructure.RTC_AlarmDateWeekDaySel = RTC_AlarmDateWeekDaySel_We
ekDay;//指定星期几生效
RTC_AlarmStructure.RTC_AlarmMask = RTC_AlarmMask_None; //不屏蔽哪一天和星
期的配置
rtc_alarm_set(RTC_AlarmStructure);
#endif
}
- 闹钟相关参数详细分析
1) RTC_AlarmTime:闹钟时间设置,配置的是 RTC时间初始化结构体,主要配置小时的制
式,有 12小时或者是 24 小时,配套具体的时、分、秒。
2) RTC_AlarmMask:闹钟掩码字段选择,即选择闹钟时间哪些字段无效,取值可为:
RTC_AlarmMask_None(全部有效)
RTC_AlarmMask_DateWeekDay(日期或者星期无效)
RTC_AlarmMask_Hours(小时无效)
RTC_AlarmMask_Minutes(分钟无效)
RTC_AlarmMask_Seconds(秒钟无效
RTC_AlarmMask_All(全部无效)。
比如我们选择 RTC_AlarmMask_DateWeekDay,那么就是当 RTC 的时间的小时等于闹钟
时间小时字段时,每天的这个小时都会产生闹钟中断。
3) RTC_AlarmDateWeekDaySel : 闹 钟 日 期 或 者 星 期 选 择 , 可 选 择
RTC_AlarmDateWeekDaySel_WeekDay 或者 RTC_AlarmDateWeekDaySel_Date。要想这个配置
有效,则 RTC_AlarmMask 不能配置为 RTC_AlarmMask_DateWeekDay,否则会被 MASK掉。
4) RTC_AlarmDateWeekDay:具体的日期或者星期几,当 RTC_AlarmDateWeekDaySel 设置
成 RTC_AlarmDateWeekDaySel_WeekDay时,取值为 17,对应星期一星期日,当设置成
RTC_AlarmMask_DateWeekDay时,取值为 1~31。