MM32F013x内部的RTC是一个独立的定时器单元,它拥有一组连续计数的计数器,配置相应的寄存器参数,可以实现闹钟、秒中断、毫秒中断、MCU定时唤醒、万年历等功能。
主要特征
① 可编程的预分频系数:分频系数最高为 220
② 32 位的可编程计数器,用于较长时间段的测量
③ 2 个分离的时钟:用于 APB1 接口的 PCLK1 和 RTC 时钟 (RTC 时钟的频率必须小于PCLK1 时钟频率的四分之一以上)
④ 可以选择以下三种 RTC 的时钟源
– HSE 时钟除以 128
– LSE 振荡器时钟
– LSI 振荡器时钟
⑤ 2 个独立的复位类型
– APB1 接口由系统复位
– RTC 核心 (预分频器、闹钟、计数器和分频器) 只能由后备域复位
⑥ 3 个专门的屏蔽中断
– 闹钟中断,用来产生一个软件可编程的闹钟中断
– 秒 / 毫秒中断,用来产生一个可编程的周期性中断信号 (最长可达 1 秒)
– 溢出中断,指示内部可编程计数器溢出并返回为 0 的状态
本文将重点介绍如何在MM32F013x上通过内部RTC模块实现万年历的功能。
实现功能
通过修改RTC计数器的初始值来设置系统当前的时间和日期,使能RTC秒中断功能;在RTC产生秒中断后,通过获取当前RTC的计数值,将其转换为对应的年月日信息,再通过蔡勒公式计算出星期,将最终的结果通过串口的形式输出显示。
RTC模块的电源域处在VDD数字电源域,只要MCU供电就可以使用RTC,没有独立的VBAT供电引脚,所以无法使用纽扣电池类的应用。
参考代码
01、结构体定义及全局变量
typedef struct { uint16_t year; uint8_t month; uint8_t day; uint8_t week; uint8_t hour; uint8_t minute; uint8_t second; } CALENDAR_t; const uint8_t RTC_DayOfMonth[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 }; CALENDAR_t RTC_Calendar;
02、RTC初始化配置:使用外部32.768kHz的晶振源
void RTC_Configure(void) { uint16_t BKP_Value = 0x5A5A; NVIC_InitTypeDef NVIC_InitStructure; /* Enable PWR Clock */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); /* Enable WKUP pin */ PWR_WakeUpPinCmd(ENABLE); /* Enable Access To The RTC & Backup Registers */ PWR_BackupAccessCmd(ENABLE); if(BKP_ReadBackupRegister(BKP_DR1) != BKP_Value) { BKP_DeInit(); /* Enable LSE Clock Source */ RCC_LSEConfig(RCC_LSE_ON); /* Wait LSI Clock Source Ready */ while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET); /* Config RTC Clock Source : LSE */ RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); /* Enable RTC Clock */ RCC_RTCCLKCmd(ENABLE); /* Wait For Synchronization */ RTC_WaitForSynchro(); /* Wait Until Last Write Operation On RTC REG Has Finished */ RTC_WaitForLastTask(); /* Set The RTC Prescaler Value */ RTC_SetPrescaler(32767); /* Wait Until Last Write Operation On RTC REG Has Finished */ RTC_WaitForLastTask(); /* Enable RTC Second Interrupt */ RTC_ITConfig(RTC_IT_SEC, ENABLE); /* Wait Until Last Write Operation On RTC REG Has Finished */ RTC_WaitForLastTask(); /* Exit From The RTC Configuration Mode */ RTC_ExitConfigMode(); BKP_WriteBackupRegister(BKP_DR1, BKP_Value); /* Wait Until Last Write Operation On RTC REG Has Finished */ RTC_WaitForLastTask(); /* Set initial time */ RTC_SetDateTime(2021, 1, 12, 14, 48, 0); } else { /* Wait For Synchronization */ RTC_WaitForSynchro(); /* Enable RTC Second Interrupt */ RTC_ITConfig(RTC_IT_SEC, ENABLE); /* Wait Until Last Write Operation On RTC REG Has Finished */ RTC_WaitForLastTask(); } /* Config RTC NVIC */ NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn; NVIC_InitStructure.NVIC_IRQChannelPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); }
03、RTC秒中断函数
void RTC_BKP_IRQHandler(void) { if(RTC_GetITStatus(RTC_IT_SEC) != RESET) { /* Update Date and Time */ RTC_UpdateCalendar(); /* Print current Date and Time */ RTC_PrintDateTime(); /* Clear Alarm Flag */ RTC_ClearITPendingBit(RTC_IT_SEC); /* Wait Until Last Write Operation On RTC REG Has Finished */ RTC_WaitForLastTask(); } }
04、将RTC计数值转换为日期信息
void RTC_UpdateCalendar(void) { static uint32_t PreTotalDay = 0; uint32_t TotalSecond = 0; uint32_t TotalDay = 0; uint16_t Year = 1970; uint8_t Month = 0; /* Get The RTC Counter Value */ TotalSecond = RTC_GetCounter(); TotalDay = TotalSecond / 86400; if(PreTotalDay != TotalDay) { PreTotalDay = TotalDay; while(TotalDay >= 365) { if(RTC_LeapYear(Year) == 1) { if(TotalDay >= 366) { TotalDay -= 366; } else { break; } } else { TotalDay -= 365; } Year++; } RTC_Calendar.year = Year; while(TotalDay >= 28) { if((Month == 1) && (RTC_LeapYear(RTC_Calendar.year) == 1)) { if(TotalDay >= 29) { TotalDay -= 29; } else { break; } } else { if(TotalDay >= RTC_DayOfMonth[Month]) { TotalDay -= RTC_DayOfMonth[Month]; } else { break; } } Month++; } RTC_Calendar.month = Month + 1; RTC_Calendar.day = TotalDay + 1; RTC_Calendar.week = RTC_GetWeek(RTC_Calendar.year, RTC_Calendar.month, RTC_Calendar.day); } RTC_Calendar.hour = (TotalSecond % 86400) / 3600; RTC_Calendar.minute = ((TotalSecond % 86400) % 3600) / 60; RTC_Calendar.second = ((TotalSecond % 86400) % 3600) % 60; }
05、将日期信息转换为RTC计数值
void RTC_SetDateTime(uint16_t Year, uint8_t Month, uint8_t Day, uint8_t Hour, uint8_t Min, uint8_t Sec) { uint32_t TotalSecond = 0; uint16_t y = 0; uint8_t m = 0; if((Year >= 1970) && (Year <= 2099)) { for(y = 1970; y < Year; y++) { if(RTC_LeapYear(y) == 1) { TotalSecond += 31622400; /* Total Seconds Of Leap Year */ } else { TotalSecond += 31536000; /* Total Seconds Of Normal Year */ } } for(m = 0; m < (Month - 1); m++) { TotalSecond += RTC_DayOfMonth[m] * 86400; /*Total Seconds Of Month */ if((RTC_LeapYear(Year) == 1) && (m == 1)) { TotalSecond += 86400; } } TotalSecond += (uint32_t)(Day - 1) * 86400; /* Total Seconds Of Day */ TotalSecond += (uint32_t)Hour * 3600; /* Total Seconds Of Hour */ TotalSecond += (uint32_t)Min * 60; /* Total Seconds Of Minute */ TotalSecond += Sec; /* Enable PWR Clock */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); /* Enable Access To The RTC & Backup Registers */ PWR_BackupAccessCmd(ENABLE); /* Set The RTC Counter Value */ RTC_SetCounter(TotalSecond); /* Wait Until Last Write Operation On RTC REG Has Finished */ RTC_WaitForLastTask(); RTC_UpdateCalendar(); } else { printf("\r\nError Date & Time!!!\r\n"); } }
06、RTC信息打印
void RTC_PrintDateTime(void) { printf("\r\n%04d-%02d-%02d", RTC_Calendar.year, RTC_Calendar.month, RTC_Calendar.day); switch(RTC_Calendar.week) { case 0 : printf(" SUN "); break; case 1 : printf(" MON "); break; case 2 : printf(" TUE "); break; case 3 : printf(" WED "); break; case 4 : printf(" THU "); break; case 5 : printf(" FRI "); break; case 6 : printf(" SAT "); break; default: break; } printf("%02d:%02d:%02d\r\n", RTC_Calendar.hour, RTC_Calendar.minute, RTC_Calendar.second); }
07、RTC功能函数:判断闰年、蔡勒公式计算星期
uint8_t RTC_LeapYear(uint16_t Year) { if( (((Year % 400) == 0) ) || /* Century Leap Year */ (((Year % 100) != 0) && ((Year % 4) == 0)) /* Normal Leay Year */ ) { return 1; } else { return 0; } } uint8_t RTC_GetWeek(uint16_t Year, uint8_t Month, uint8_t Day) { int w, c, y; /* Month 1 Or 2 of This Year Must Be As Last Month 13 Or 14 */ if((Month == 1) || (Month == 2)) { Month += 12; Year -= 1; } w = 0; /* Weekday */ c = Year / 100; /* Century */ y = Year % 100; /* Year */ w = y + (y / 4) + (c / 4) - (2 * c) + (26 * (Month + 1) / 10) + Day - 1; while(w < 0) w += 7; w %= 7; return w; }
运行结果
编译软件工程无误后下载代码,在串口终端工具中我们可以看到每间隔1秒钟,RTC产生一次中断,在中断中我们将当前的日期信息通过串口打印在显示终端软件上:
转自:灵动微电子