五.STM32F030C8T6 MCU开发之RTC模块基础例程
文章目录
- 五.STM32F030C8T6 MCU开发之RTC模块基础例程
- 0.总体功能概述
- 1.RTC硬件介绍
- 1.1日历的功能
- 1.2 闹钟输出
- 1.3 入侵检测
- 1.4 时间戳事件检测
- 2.RTC软件配置
- 2.1.RTC 模块初始化配置
- 2.2 RTC 开始时间配置
- 2.2.1RTC 年月日 时分秒配置
- 2.3 RTC时间获取
- 3.RTC 验证例程
- 4.总结
- 4.1RTC模块只是用于时间计算可以不需要开中断。
- 4.2 rtc只初始化一次即可?
0.总体功能概述
使用STD库–en.stm32f0_stdperiph_lib_v1.6.0。
如果需要年月日时分秒的时间记录,之前开发51单片机上面外挂一个类似DS1302的时钟芯片,再加上时间芯片的外围电路。但现在MCU不再需要这么干了。因为在STM32的内部就已经集成了年月日时分秒的时钟电路–也就是实时时钟(RTC)。
本例程将RTC时间调整为当前时间,并特定格式从串口输出,每1s输出一次;
1.RTC硬件介绍
RTC (Real Time Clock):实时时钟
实时时钟是一个独立的定时器。RTC模块拥有一组连续计数的计数器,在相应软件配置下,可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。
RTC模块和时钟配置系统(RCC_BDCR寄存器)处于后备区域,即在系统复位或从待机模式唤醒后, RTC的设置和时间维持不变。
系统复位后,对后备寄存器和RTC的访问被禁止,这是为了防止对后备区域(BKP)的意外写操作。执行以下操作将使能对后备寄存器和RTC的访问:
● 设置寄存器RCC_APB1ENR的PWREN和BKPEN位,使能电源和后备接口时钟
● 设置寄存器PWR_CR的DBP位,使能对后备寄存器和RTC的访问。
实时时钟(RTC)是一个独立的定时器。
1.1日历的功能
STM32 的 RTC 模块拥有一组连续计数的计数器,在相应软件配置下,可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。
1.2 闹钟输出
当 RTC 运行的时间跟预设的闹钟时间相同的时候,相应的标志位 ALRAF(在 RTC_ISR 寄存器中)和 ALRBF 会置 1。可以作为备忘提醒功能。
如果使能了闹钟输出(由 RTC_CR 的 OSEL[0:1]位控制),则 ALRAF 和 ALRBF 会连接到闹钟输出引RTC_ALARM.
RTC_ALARM 最终连接到 RTC 的外部引脚 RTC_AF1(即 PC13),输出的极性由 RTC_CR 寄存器的 POL 位配置,可以是高电平或者低电平。
1.3 入侵检测
RTC 自带两个入侵检测引脚RTC_TAMP1 的RTC_AF1(PC13)和 RTC__TAMP2的RTC_AF2(PA0)。这两个输入既可配置为边沿检测,也可配置为带过滤的电平检测。当检测到外部事件时,可复位备份寄存器,共80个字节,可用于记录用户数据。备份寄存器不会在系统复位或电源复位时复位,也不会在器件从待机模式唤醒时复位。
1.4 时间戳事件检测
为了记录外部紧急事件触发的时间。时间戳复用功能 (RTC_TS) 可映射到 RTC_AF1 或 RTC_AF2,当发生外部的入侵事件时,即发生时间戳事件时, RTC_ISR 寄存器中的时间戳标志位 (TSF) 将置 1,日历会保存到时间戳寄存器( RTC_TSSSR、RTC_TSTR 和RTC_TSDR)中。
2.RTC软件配置
配置当前时间:时间可以采取24小时格式,也可以采用12小时格式,默认采用24小时格式。
配置时间时,还会设置日期格式,是采用BCD或者二进制。
建议采用二进制即可,可以直接填入时间数值。
第一,定义一个时间的起点,比如2000年1月1日,0点0分0秒。这里要多说一句,其实这个起点可以随便定义,0也没关系。但网络上通常的做法是1970年的1月1日,至于为什么,可以了解时间戳的概念。
第二,STM32并没有专门的记录分钟、小时、日、月、年的寄存器,换句话说,他只是记录了从时间起点(计数器的初值)到当前时间的总秒数,并且能在掉电情况下持续计数运行。要想得到年、月、日信息,是需要我们自己写函数实现的。这里总感觉STM32干得有猛,很粗鲁的样子,上电的时候直接给年、月、日的变量赋值,想要哪年哪月哪日,就直接赋值,然后在这个基础上累加时间,并不断更新,这样就确定了年、月、日的信息。
但别忘了,这是在CPU上电程序运行的情况下进行的,一旦CPU掉电,程序也不再运行,年、月、日信息就不再更新。此时在工作的,只有那个32位的秒计数器。
第三,要想再次上电后正确显示年、月、日等信息,就需要上电后重新调用自己写的这个函数,根据初始化时的年、月、日初值和计数器的当前值,输出当前时间信息。
RTC一般有单独的外部电池供电。
2.1.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();
}
2.2 RTC 开始时间配置
/* 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();
2.2.1RTC 年月日 时分秒配置
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);
}
2.3 RTC时间获取
void RTC_TimeShow(void)
{
/* Get the current Time */
RTC_GetTime(RTC_Format_BIN, &aShowTime);
RTC_GetDate(RTC_Format_BIN, &RTC_DateStructure);
}
3.RTC 验证例程
每隔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);
}
}
4.总结
4.1RTC模块只是用于时间计算可以不需要开中断。
如果需要用alarm功能 才需要开.
4.2 rtc只初始化一次即可?
每次上电后会读取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重新配置就很正常。