【STM32】RTC(实时时钟)
1.RTC简介
本质:计数器
RTC中断是外部中断(EXTI)
当VDD掉电的时候,Vbat可以通过电源--->实时计时
STM32的RTC外设(Real Time Clock),实质是一个 掉电 后还继续运行的定时器。从定时器的角度来说,相对于通用定时器TIM外设,它十分简单, 只有很纯粹的计时和触发中断的功能;但从 掉电还继续运行 的角度来说,它却是STM32中唯一一个具有如此强大功能的外设。 所以RTC外设的复杂之处并不在于它的定时功能,而在于它掉电还继续运行的特性。
常用的RTC方案
1)一般都需要设计RTC外围电路(例如晶振,电源等)
2)一般都可以给RTC设置独立的电源(当内部Soc的内部电源VDD失效时,可以使用外部电源进行供电,实现掉电还能运行)
3)多数RTC的寄存器采用BCD码此处时间信息
RTC使用场景
2.STM32RTC框图介绍
一般分频后的时钟频率为1HZ
浅灰色的部分都是属于备份域的,在VDD掉电时可 在VBAT的驱动下继续运行。这部分仅包括RTC的分频器,计数器, 和闹钟控制器。
时钟源的选择
它使用的时钟源有三种,分别为高速外部时钟 的128分频(HSE/128)、低速内部时钟LSI以及低速外部时钟LSE;使HSE分频时钟或LSI的话,在主电源VDD掉电 的情况下,这两个时钟来源都会受到影响,因此没法保证RTC正常工作。因此RTC一般使用低速外部时钟LSE,在设计中, 频率通常为实时时钟模块中常用的32.768KHz,这是因为32768 = 2^15,分频容易实现,所以它被广泛应用到RTC模块。
HSE(高速外部时钟)/128---》因为是需要使用到Soc内部的电源,所以不能使用
LSI(低速内部时钟)40KHZ---》因为容易受到温度影响,使用不使用
LSE32KHZ----》低速外设接口(32.768KHZ)
1.RTC预分频器
在配置RTC模块的时钟时,通常把输入的32768Hz的RTCCLK进行32768分频得到实际驱动计数器的时钟TR_CLK = RTCCLK/32768= 1 Hz,计时周期为1秒,计时器在TR_CLK的驱动下计数,即每秒计数器RTC_CNT的值加1。
2.32位可编程计数器
1)从RTC的定时器特性来说,它是一个32位的计数器,只能向上计数。
2)在备份域中所有寄存器都是16位的, RTC控制相关的寄存器也不例外。它的计数器RTC_CNT的32位由RTC_CNTL和RTC_CNTH两个寄存器组成,分别保存定时计数值的低16位和高16位。
3.待机唤醒
有2种唤醒方式
4.RTC控制寄存器与APB1接口
3.后备寄存器和RTC寄存器特性
1)当主电源VDD有效时,由VDD给RTC外设供电; 而当VDD掉电后,由VBAT给RTC外设供电。但无论由什么电源供电,RTC中的数据都保存在 属于RTC的备份域中,若主电源VDD和VBAT都掉电,那么备份域中保存的所有数据 将丢失。
2)后备寄存器可以用于保存掉电时的数据
4.RTC相关寄存器介绍
1.APB1 外设时钟使能寄存器(RCC_APB1ENR)
使能PWR&&BKP时钟
2.电源控制寄存器(PWR_CR)
使能对后备寄存器和RTC的访问权限
3.备份域控制寄存器 (RCC_BDCR)
1.开启RTC时钟
2.开启LSE时钟
3.选择RTC计数时钟源
4.RTC控制寄存器低位(RTC_CRL)
5.RTC控制寄存器高位(RTC_CRH)
6.RTC预分频装载寄存器(RTC_PRLH/RTC_PRLL)
7.RTC计数器寄存器 (RTC_CNTH / RTC_CNTL)
将年月日时分秒---》转换为“秒”
5.RTC基本配置步骤
相关HAL库介绍
6.时间设置和读取
7.通过串口打印当前时间
1.rtc初始化
2.rtc_msp
3.rtc_set_time
将输入的时间转换形式写入寄存器
//月份数据表
u8 const table_week[12]={0,3,3,6,1,4,6,2,5,0,3,5}; //月修正数据表
//平年的月份日期表
const u8 mon_table[12]={31,28,31,30,31,30,31,31,30,31,30,31};
/*******************************************************************************
* 函 数 名 : RTC_Set
* 函数功能 : RTC设置日期时间函数(以1970年1月1日为基准,把输入的时钟转换为秒钟)
1970~2099年为合法年份
* 输 入 : syear:年 smon:月 sday:日
hour:时 min:分 sec:秒
* 输 出 : 0,成功
1,失败
*******************************************************************************/
u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec)
{
u16 t;
u32 seccount=0;
if(syear<1970||syear>2099)return 1;
for(t=1970;t<syear;t++) //把所有年份的秒钟相加
{
if(Is_Leap_Year(t))seccount+=31622400;//闰年的秒钟数
else seccount+=31536000; //平年的秒钟数
}
smon-=1;
for(t=0;t<smon;t++) //把前面月份的秒钟数相加
{
seccount+=(u32)mon_table[t]*86400;//月份秒钟数相加
if(Is_Leap_Year(syear)&&t==1)seccount+=86400;//闰年2月份增加一天的秒钟数
}
seccount+=(u32)(sday-1)*86400;//把前面日期的秒钟数相加
seccount+=(u32)hour*3600;//小时秒钟数
seccount+=(u32)min*60; //分钟秒钟数
seccount+=sec;//最后的秒钟加上去
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能PWR和BKP外设时钟
PWR_BackupAccessCmd(ENABLE); //使能RTC和后备寄存器访问
RTC_SetCounter(seccount); //设置RTC计数器的值
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
return 0;
}
4.rtc_get_time
将从寄存器获得的值进行展示
//得到当前的时间
//返回值:0,成功;其他:错误代码.
u8 RTC_Get(void)
{
static u16 daycnt=0;
u32 timecount=0;
u32 temp=0;
u16 temp1=0;
timecount=RTC_GetCounter();
temp=timecount/86400; //得到天数(秒钟数对应的)
if(daycnt!=temp)//超过一天了
{
daycnt=temp;
temp1=1970; //从1970年开始
while(temp>=365)
{
if(Is_Leap_Year(temp1))//是闰年
{
if(temp>=366)temp-=366;//闰年的秒钟数
else {temp1++;break;}
}
else temp-=365; //平年
temp1++;
}
calendar.w_year=temp1;//得到年份
temp1=0;
while(temp>=28)//超过了一个月
{
if(Is_Leap_Year(calendar.w_year)&&temp1==1)//当年是不是闰年/2月份
{
if(temp>=29)temp-=29;//闰年的秒钟数
else break;
}
else
{
if(temp>=mon_table[temp1])temp-=mon_table[temp1];//平年
else break;
}
temp1++;
}
calendar.w_month=temp1+1; //得到月份
calendar.w_date=temp+1; //得到日期
}
temp=timecount%86400; //得到秒钟数
calendar.hour=temp/3600; //小时
calendar.min=(temp%3600)/60; //分钟
calendar.sec=(temp%3600)%60; //秒钟
calendar.week=RTC_Get_Week(calendar.w_year,calendar.w_month,calendar.w_date);//获取星期
return 0;
}
//获得现在是星期几
//功能描述:输入公历日期得到星期(只允许1901-2099年)
//输入参数:公历年月日
//返回值:星期号
u8 RTC_Get_Week(u16 year,u8 month,u8 day)
{
u16 temp2;
u8 yearH,yearL;
yearH=year/100; yearL=year%100;
// 如果为21世纪,年份数加100
if (yearH>19)yearL+=100;
// 所过闰年数只算1900年之后的
temp2=yearL+yearL/4;
temp2=temp2%7;
temp2=temp2+day+table_week[month-1];
if (yearL%4==0&&month<3)temp2--;
return(temp2%7);
}
8.使用CubeMX生成代码
【精选】STM32CubeMX学习笔记(14)——RTC实时时钟使用_bcd data format-CSDN博客
1.开启RTC
RTC是在时钟部分的
2.使能时钟RCC
由于我们使用32.768MHZ(外部低速晶振),所以我们要使能低速时钟(LSE)
主时钟还是按照原来的时钟频率去设置(主时钟和RTC没有关系)
3.RTC相关的设置
勾选
Activate Clock Source
激活时钟源,勾选Activate Calendar
激活万年历
1.开启Activate Calendar
只有开启后才可以进行设置时间