使用STD库–en.stm32f0_stdperiph_lib_v1.6.0。
如果需要年月日时分秒的时间记录,之前开发51单片机上面外挂一个类似DS1302的时钟芯片,再加上时间芯片的外围电路。但现在MCU不再需要这么干了。因为在STM32的内部就已经集成了年月日时分秒的时钟电路–也就是实时时钟(RTC)。
本例程将RTC时间调整为当前时间,并特定格式从串口输出,每1s输出一次;
RTC (Real Time Clock):实时时钟
实时时钟是一个独立的定时器。RTC模块拥有一组连续计数的计数器,在相应软件配置下,可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。
RTC模块和时钟配置系统(RCC_BDCR寄存器)处于后备区域,即在系统复位或从待机模式唤醒后, RTC的设置和时间维持不变。
系统复位后,对后备寄存器和RTC的访问被禁止,这是为了防止对后备区域(BKP)的意外写操作。执行以下操作将使能对后备寄存器和RTC的访问:
● 设置寄存器RCC_APB1ENR的PWREN和BKPEN位,使能电源和后备接口时钟
● 设置寄存器PWR_CR的DBP位,使能对后备寄存器和RTC的访问。
实时时钟(RTC)是一个独立的定时器。
STM32 的 RTC 模块拥有一组连续计数的计数器,在相应软件配置下,可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。
当 RTC 运行的时间跟预设的闹钟时间相同的时候,相应的标志位 ALRAF(在 RTC_ISR 寄存器中)和 ALRBF 会置 1。可以作为备忘提醒功能。
如果使能了闹钟输出(由 RTC_CR 的 OSEL[0:1]位控制),则 ALRAF 和 ALRBF 会连接到闹钟输出引RTC_ALARM.
RTC_ALARM 最终连接到 RTC 的外部引脚 RTC_AF1(即 PC13),输出的极性由 RTC_CR 寄存器的 POL 位配置,可以是高电平或者低电平。
RTC 自带两个入侵检测引脚RTC_TAMP1 的RTC_AF1(PC13)和 RTC__TAMP2的RTC_AF2(PA0)。这两个输入既可配置为边沿检测,也可配置为带过滤的电平检测。当检测到外部事件时,可复位备份寄存器,共80个字节,可用于记录用户数据。备份寄存器不会在系统复位或电源复位时复位,也不会在器件从待机模式唤醒时复位。
为了记录外部紧急事件触发的时间。时间戳复用功能 (RTC_TS) 可映射到 RTC_AF1 或 RTC_AF2,当发生外部的入侵事件时,即发生时间戳事件时, RTC_ISR 寄存器中的时间戳标志位 (TSF) 将置 1,日历会保存到时间戳寄存器( RTC_TSSSR、RTC_TSTR 和RTC_TSDR)中。
配置当前时间:时间可以采取24小时格式,也可以采用12小时格式,默认采用24小时格式。
配置时间时,还会设置日期格式,是采用BCD或者二进制。
建议采用二进制即可,可以直接填入时间数值。
第一,定义一个时间的起点,比如2000年1月1日,0点0分0秒。这里要多说一句,其实这个起点可以随便定义,0也没关系。但网络上通常的做法是1970年的1月1日,至于为什么,可以了解时间戳的概念。
第二,STM32并没有专门的记录分钟、小时、日、月、年的寄存器,换句话说,他只是记录了从时间起点(计数器的初值)到当前时间的总秒数,并且能在掉电情况下持续计数运行。要想得到年、月、日信息,是需要我们自己写函数实现的。这里总感觉STM32干得有猛,很粗鲁的样子,上电的时候直接给年、月、日的变量赋值,想要哪年哪月哪日,就直接赋值,然后在这个基础上累加时间,并不断更新,这样就确定了年、月、日的信息。
但别忘了,这是在CPU上电程序运行的情况下进行的,一旦CPU掉电,程序也不再运行,年、月、日信息就不再更新。此时在工作的,只有那个32位的秒计数器。
第三,要想再次上电后正确显示年、月、日等信息,就需要上电后重新调用自己写的这个函数,根据初始化时的年、月、日初值和计数器的当前值,输出当前时间信息。
RTC一般有单独的外部电池供电。
可以选择以下三种RTC的时钟源:
— HSE时钟除以128;
— LSE振荡器时钟;
— LSI振荡器时钟(详见6.2.8节RTC时钟)。
RTC_CLOCK_SOURCE_LSE 使用外部 32.768 时钟源
RTC_CLOCK_SOURCE_LSI 使用内部RC 32.768 时钟源
/*** @brief Configure the RTC peripheral by selecting the clock source.* @param None* @retval None*/
static void RTC_Config(void)
{/* Enable the PWR clock */RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);/* Allow access to RTC */PWR_BackupAccessCmd(ENABLE);#if defined (RTC_CLOCK_SOURCE_LSI) /* LSI used as RTC source clock*/
/* The RTC Clock may varies due to LSI frequency dispersion. */ /* Enable the LSI OSC */ RCC_LSICmd(ENABLE);/* Wait till LSI is ready */ while(RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET){}/* Select the RTC Clock Source */RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);SynchPrediv = 0x18F;AsynchPrediv = 0x63;#elif defined (RTC_CLOCK_SOURCE_LSE) /* LSE used as RTC source clock *//* Enable the LSE OSC */RCC_LSEConfig(RCC_LSE_ON);/* Wait till LSE is ready */ while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET){}/* Select the RTC Clock Source */RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);SynchPrediv = 0xFF;AsynchPrediv = 0x7F;#else#error Please select the RTC Clock source inside the main.c file
#endif /* RTC_CLOCK_SOURCE_LSI *//* Enable the RTC Clock */RCC_RTCCLKCmd(ENABLE);/* Wait for RTC APB registers synchronisation */RTC_WaitForSynchro();
}
/* Configure the RTC data register and RTC prescaler */RTC_InitStructure.RTC_AsynchPrediv = AsynchPrediv;RTC_InitStructure.RTC_SynchPrediv = SynchPrediv;RTC_InitStructure.RTC_HourFormat = RTC_HourFormat_24;printf("rtc init\r\n");/* Check on RTC init */if (RTC_Init(&RTC_InitStructure) == ERROR){}printf("rtc RTC_TimeRegulate\r\n");RTC_TimeRegulate();
static void RTC_TimeRegulate(void)
{RTC_TimeTypeDef RTC_TimeStructure;RTC_DateTypeDef RTC_DateStructure;/* Set Time hh:mm:ss */RTC_TimeStructure.RTC_H12 = RTC_H12_AM;RTC_TimeStructure.RTC_Hours = 0x08; RTC_TimeStructure.RTC_Minutes = 0x10;RTC_TimeStructure.RTC_Seconds = 0x00;RTC_SetTime(RTC_Format_BCD, &RTC_TimeStructure);/* Set Date Week/Date/Month/Year */RTC_DateStructure.RTC_WeekDay = 01;RTC_DateStructure.RTC_Date = 0x31;RTC_DateStructure.RTC_Month = 0x12;RTC_DateStructure.RTC_Year = 0x12;RTC_SetDate(RTC_Format_BCD, &RTC_DateStructure);/* Write BkUp DR0 */RTC_WriteBackupRegister(RTC_BKP_DR0, 0x32F2);
}
void RTC_TimeShow(void)
{/* Get the current Time */RTC_GetTime(RTC_Format_BIN, &aShowTime);RTC_GetDate(RTC_Format_BIN, &RTC_DateStructure);}
每隔1秒获取RTC时间,查看RTC时间实际变化
int rtc_main(void)
{unsigned int s_time = 0;unsigned int e_time = 0;RTC_Config();/* Configure the RTC data register and RTC prescaler */RTC_InitStructure.RTC_AsynchPrediv = AsynchPrediv;RTC_InitStructure.RTC_SynchPrediv = SynchPrediv;RTC_InitStructure.RTC_HourFormat = RTC_HourFormat_24;printf("rtc init\r\n");/* Check on RTC init */if (RTC_Init(&RTC_InitStructure) == ERROR){}printf("rtc RTC_TimeRegulate\r\n");RTC_TimeRegulate(); printf("rtc start\r\n");while(1){ s_time = get_curtime();while(1){e_time = get_curtime(); if(e_time>s_time ){if((e_time - s_time) >=1000){break;}}}printf("RTC_TimeShow\r\n");RTC_TimeShow();printf("RTC_Hours=%d:%d:%d\r\n",aShowTime.RTC_Hours,aShowTime.RTC_Minutes,aShowTime.RTC_Seconds);printf("y m d=%d %d %d\r\n",RTC_DateStructure.RTC_Year,RTC_DateStructure.RTC_Month,RTC_DateStructure.RTC_Date);}
}
如果需要用alarm功能 才需要开.
每次上电后会读取BKP_DR1的值,判断是否为第一次启动,如果是,则配置RTC。换句话说,出现问题时,这个判断肯定出现问题,也就是备份寄存器的值丢失,导致重复配置RTC!
备份寄存器的值丢失原因:
1》 VDD和VBAT同时掉电
2》 客户代码意外修改
3》 检测到入侵事件
;
}
}
## 4.总结### 4.1RTC模块只是用于时间计算可以不需要开中断。如果需要用alarm功能 才需要开.### 4.2 rtc只初始化一次即可?每次上电后会读取BKP_DR1的值,判断是否为第一次启动,如果是,则配置RTC。换句话说,出现问题时,这个判断肯定出现问题,也就是备份寄存器的值丢失,导致重复配置RTC!备份寄存器的值丢失原因:1》 VDD和VBAT同时掉电2》 客户代码意外修改3》 检测到入侵事件首先怀疑是VBAT引脚。要是VBAT再现异常,RTC重新配置就很正常。