RTC(Real_Time Clock)
实时时钟 (RTC) 是一个独立的 BCD 定时器/计数器。RTC 提供一个日历时钟、两个可编程闹钟中断,以及一个具有中断功能的周期性可编程唤醒标志。RTC 还包含用于管理低功耗模式的自动唤醒单元。
两个 32 位寄存器包含二进码十进数格式 (BCD) 的秒、分钟、小时(12 或 24 小时制)、星期几、日期、月份和年份。此外,还可提供二进制格式的亚秒值。
系统可以自动将月份的天数补偿为 28、29(闰年)、30 和 31 天。并且还可以进行夏令时补偿。
其它 32 位寄存器还包含可编程的闹钟亚秒、秒、分钟、小时、星期几和日期。
此外,还可以使用数字校准功能对晶振精度的偏差进行补偿。
上电复位后,所有 RTC 寄存器都会受到保护,以防止可能的非正常写访问。无论器件状态如何(运行模式、低功耗模式或处于复位状态),只要电源电压保持在工作范围内,RTC 便不会停止工作。
原理图
static uint32_t uwSynchPrediv = 0xFF;//异步分频
static uint32_t uwAsynchPrediv = 0x7F;//同步分频
/*ck_spre(1Hz) = RTCCLK(LSE) /(uwAsynchPrediv + 1)*(uwSynchPrediv + 1)*/
32.768KHz /128/256 = 1Hz
LSE-RTC_PER–RTC_PER–RTC_WUTR–WUTF唤醒标志
《STM32F4xx中文参考手册.pdf》第585页
1.将 EXTI 线 17 配置为中断模式并将其使能,然后选择上升沿有效。
2.配置 NVIC 中的 RTC_Alarm IRQ 通道并将其使能。
3.配置 RTC 以生成 RTC 闹钟(闹钟 A 或闹钟 B)。
1.将 EXTI 线 22 配置为中断模式并将其使能,然后选择上升沿有效。
2.配置 NVIC 中的 RTC_WKUP IRQ 通道并将其使能。
3.配置 RTC 以生成 RTC 唤醒定时器事件。
二进制编码的十进制数,简称BCD码。这种方式是用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码,8421分别是4位二进制的位权值。
void RCC_RTCCLKConfig(uint32_t CLKSource);//时钟源选择
void RCC_RTCCLKCmd(FunctionalState NewState)//时钟使能
ErrorStatus RTC_Init(RTC_InitTypeDef* RTC_InitStruct);
typedef struct
{
uint32_t RTC_HourFormat; //小时格式:24/12
uint32_t RTC_AsynchPrediv; //异步分频 系数
uint32_t RTC_SynchPrediv; //同步分频系数
}RTC_InitTypeDe
ErrorStatus RTC_SetTime(uint32_t RTC_Format, RTC_TimeTypeDef* RTC_TimeStruct);
void RTC_GetTime(uint32_t RTC_Format, RTC_TimeTypeDef* RTC_TimeStruct);
ErrorStatus RTC_SetDate(uint32_t RTC_Format, RTC_DateTypeDef* RTC_DateStruct);
void RTC_GetDate(uint32_t RTC_Format, RTC_DateTypeDef* RTC_DateStruct);
uint32_t RTC_GetSubSecond(void);
ErrorStatus RTC_AlarmCmd(uint32_t RTC_Alarm, FunctionalState NewState)
void RTC_SetAlarm(uint32_t RTC_Format, uint32_t RTC_Alarm,
RTC_AlarmTypeDef* RTC_AlarmStruct);
void RTC_GetAlarm(uint32_t RTC_Format, uint32_t RTC_Alarm,
RTC_AlarmTypeDef* RTC_AlarmStruct);
void RTC_AlarmSubSecondConfig(uint32_t RTC_Alarm, uint32_t RTC_AlarmSubSecondValue,
uint32_t RTC_AlarmSubSecondMask)
uint32_t RTC_GetAlarmSubSecond(uint32_t RTC_Alarm);
void RTC_ITConfig(uint32_t RTC_IT, FunctionalState NewState);
FlagStatus RTC_GetFlagStatus(uint32_t RTC_FLAG);
void RTC_ClearFlag(uint32_t RTC_FLAG);
ITStatus RTC_GetITStatus(uint32_t RTC_IT);
void RTC_ClearITPendingBit(uint32_t RTC_IT);
void RTC_WakeUpClockConfig(uint32_t RTC_WakeUpClock);
void RTC_SetWakeUpCounter(uint32_t RTC_WakeUpCounter);
uint32_t RTC_GetWakeUpCounter(void);
RTC_WakeUpCmd(DISABLE);//关闭WAKE UP
void RTC_WriteProtectionCmd(FunctionalState NewState);//取消写保护
ErrorStatus RTC_EnterInitMode(void);//进入配置模式,RTC_ISR_INITF位设置为1
void RTC_ExitInitMode(void)//退出初始化模式。
uint32_t RTC_ReadBackupRegister(uint32_t RTC_BKP_DR);
void RTC_WriteBackupRegister(uint32_t RTC_BKP_DR, uint32_t Data)
void RTC_ITConfig(uint32_t RTC_IT, FunctionalState NewState);
calendar&alarm
uint8_t G_RTCWakeUp_Status = 0; //RTC_WakeUp中断flag
uint8_t G_AlarmA_Status = 0;//AlarmA中断flag
static uint32_t uwSynchPrediv = 0xFF;//异步分频
static uint32_t uwAsynchPrediv = 0x7F;//同步分频
void RTC_init(void)
{
RTC_InitTypeDef RTC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); //使能PWR时钟
PWR_BackupAccessCmd(ENABLE); //使能后备寄存器访问
if(RTC_ReadBackupRegister(RTC_BKP_DR0)!=0x1234) //是否第一次配置?
{
RCC_LSEConfig(RCC_LSE_ON);//LSE 开启
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) //检查指定的RCC标志位设置与否,等待低速晶振就绪
{
}
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //设置RTC时钟(RTCCLK),选择LSE作为RTC时钟
RCC_RTCCLKCmd(ENABLE); //使能RTC时钟
RTC_InitStructure.RTC_AsynchPrediv = uwSynchPrediv;//RTC异步分频系数(1~0X7F)
RTC_InitStructure.RTC_SynchPrediv = uwAsynchPrediv;//RTC同步分频系数(0~7FFF)
RTC_InitStructure.RTC_HourFormat = RTC_HourFormat_24;//RTC设置为,24小时格式
RTC_Init(&RTC_InitStructure);
/* Set the date: Tuesday September 06th 2022 */
RTC_DateStructure.RTC_Year = 0x22;
RTC_DateStructure.RTC_Month = RTC_Month_September;
RTC_DateStructure.RTC_Date = 0x06;
RTC_DateStructure.RTC_WeekDay = RTC_Weekday_Tuesday;
RTC_SetDate(RTC_Format_BCD, &RTC_DateStructure);
/* Set the time to 14h 30mn 00s PM */
RTC_TimeStructure.RTC_H12 = RTC_H12_PM;
RTC_TimeStructure.RTC_Hours = 0x14;
RTC_TimeStructure.RTC_Minutes = 0x30;
RTC_TimeStructure.RTC_Seconds = 0x00;
RTC_SetTime(RTC_Format_BCD, &RTC_TimeStructure);
RTC_WriteBackupRegister(RTC_BKP_DR0,0x1234); //标记已经初始化过了
}
}
//周期性唤醒定时器设置
/*wksel: @ref RTC_Wakeup_Timer_Definitions
#define RTC_WakeUpClock_RTCCLK_Div16 ((uint32_t)0x00000000)
#define RTC_WakeUpClock_RTCCLK_Div8 ((uint32_t)0x00000001)
#define RTC_WakeUpClock_RTCCLK_Div4 ((uint32_t)0x00000002)
#define RTC_WakeUpClock_RTCCLK_Div2 ((uint32_t)0x00000003)
#define RTC_WakeUpClock_CK_SPRE_16bits ((uint32_t)0x00000004)
#define RTC_WakeUpClock_CK_SPRE_17bits ((uint32_t)0x00000006)
*/
//cnt:自动重装载值.减到0,产生中断.
void RTC_Set_WakeUp(uint32_t wksel,uint16_t cnt)
{
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_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);//配置
}
//#define RTC_AlarmDateWeekDaySel_Date ((uint32_t)0x00000000) 每天 闹
//#define RTC_AlarmDateWeekDaySel_WeekDay ((uint32_t)0x40000000) 每星期 闹
//设置闹钟时间(按星期闹铃,24小时制)
//week:星期几(1~7) @ref RTC_Alarm_Definitions
//hour,min,sec:小时,分钟,秒钟
void RTC_Set_AlarmA(uint8_t week,uint8_t hour,uint8_t min,uint8_t sec)
{
EXTI_InitTypeDef EXTI_InitStructure;
RTC_AlarmTypeDef RTC_AlarmTypeInitStructure;
RTC_TimeTypeDef RTC_TimeTypeInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
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_WKUP_IRQHandler(void)
{
if(RTC_GetITStatus(RTC_IT_WUT) == SET)
{
printf("RTC_WKUP_IRQHandler\r\n");
G_RTCWakeUp_Status = 1;
//清空标志位
RTC_ClearITPendingBit(RTC_IT_WUT);
EXTI_ClearITPendingBit(EXTI_Line22);
}
}
//RTC_AlarmA中断服务函数
void RTC_Alarm_IRQHandler(void)
{
if(RTC_GetITStatus(RTC_IT_ALRA) == SET)
{
printf("RTC_Alarm_IRQHandler\r\n");
G_AlarmA_Status = 1;
//清空标志位
RTC_ClearITPendingBit(RTC_IT_ALRA);
EXTI_ClearITPendingBit(EXTI_Line17);
}
}
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
RTC_TimeTypeDef RTC_TimeStructure;
RTC_DateTypeDef RTC_DateStructure;
USART1_init(115200);
RTC_Init();
RTC_Set_AlarmA(2,15,30,30);
while(1)
{
// if(G_RTCWakeUp_Status)//中断唤醒方式
// {
//获取时间
RTC_GetTime(RTC_Format_BCD, &RTC_TimeStructure);
printf("%02x:%02x:%02x\r\n", RTC_TimeStructure.RTC_Hours,
RTC_TimeStructure.RTC_Minutes,
RTC_TimeStructure.RTC_Seconds);
//获取日期
RTC_GetDate(RTC_Format_BCD, &RTC_DateStructure);
printf("20%02x/%02x/%02x Week:%x\r\n", RTC_DateStructure.RTC_Year,
RTC_DateStructure.RTC_Month,
RTC_DateStructure.RTC_Date,
RTC_DateStructure.RTC_WeekDay);
delay_ms(1000);
// G_RTCWakeUp_Status = 0;
// }
if(G_AlarmA_Status)
{
printf("Wake up the alarm clock is ringing\r\n");
break;
//G_AlarmA_Status = 0;
}
}
}