RTC介绍
STM32F1:
RTC模块拥有一组连续计数的计数器,在相应软件配置下,可提供时钟日历的功能。
即在F1系列,RTC的日历部分只有一个32位的寄存器
该寄存器直接存放 时间戳 的值,即:秒数值
想要获取其他时间,想要软件来实现。
STM32F4:
RTC日历部分包含两个32位的寄存器,分为日期寄存器和时间寄存器
可直接输出时分秒,星期、月、日、年
让我们在软件编程时大大降低了难度。
以下是基于F4的RTC内容。
RTC说明
年月日时分秒,自动计算闰年,能区分每个月的天数
理解要点:
Unix时间戳 从1970年开始每1s计1个数
BKP备份域 相当于存储器有20个u32的寄存器,断电复位
BCD码 二进制十进数
BCD:
4位二进制表示1位十进制中的0~9
十进制 BCD码
19 ---------- 0x19
备份域:
RTC有20个u32的备份域寄存器RTC_BKPxR 即有80个字节空间
备份域的作用:防止程序重新设置RTC日历时间
RTC运行:
RTC控制器及其备份域,只要有供电就可以一直运行
供电是指有电源,而CPU复位不算掉电源
当有主供电电路,RTC电源由主电路提供,节省备用电源(纽扣电池)
当主供电电路断电,RTC在有备用电源的情况下,能够继续运行。
RTC框图
RTC时钟源
RTC有3个时钟源选择 HSE LSE LSI
主要选择LSE低速外部时钟
HSE:为总线提供时钟,而且频率太高
LSI:时钟频率不稳定
LSE:外接32.768KHz,专门设计为RTC提供时钟
RTC内部有两个分频器:
一个异步分频器 默认128分频
一个同步分频器 默认256分频
刚好将LSE的32.768KHz 分频 为 1Hz
即 1s 计 1个数
RTC实时时钟
1.使能RTC控制器:
- 将电源控制寄存器(PWR->CR)的DBP位写1
//解除RTC控制器和备份寄存器的保护
//配置这个寄存器之前,要使能电源控制器时钟 RCC->APB1 28位
- 时钟源配置选择LSE低速外部时钟
2.RTC时钟源选择:
RCC 备份域控制寄存器RCC->BDCR
使能LSE时钟,并等待时钟就绪
RTCSEL[1:0]直接赋值选择LSE
不能先清零再配置此位,最后使能RTC时钟
3.解除RTC寄存器写保护:
- 往RTC_WPR寄存器 中写 0xca 再写 0x53
//取消RTC所有寄存器的写保护
//往RTC_WPR寄存器中随便写一个数据就会再次激活写保护 0xff
4.配置RTC:
要想改变日历寄存器的时间的值或者分频值
需要让日历进入初始化模式(日历停止工作)
更改完以后,要想日历继续工作,需要退出初始化模式(自由模式)
5.进入初始化模式:
RTC->ISR 寄存器的INIT位写1
等待是否允许更新(改变)日历值位 INITF
设置日期和时间寄存器 TD&DR
退出初始化模式 RTC->ISR 寄存器的INIT位写0
读取日历:
获取 DR和TR的值
需要用 两次 同步影子寄存器 来读取
因为通过实验测试,一次读两个数据寄存器 数值有误
同步步骤:先将同步标志位清零,确保为最新鲜的数据
等待同步标志置1,读取数据寄存器
十进制转BCD码 return ((dec / 10) << 4) | (dec % 10);
BCD码转十进制 return (bcd >> 4) * 10 + (bcd & 0x0f);
RTC闹钟
因此需要配置EXTI控制器的17号中断线
使能SYSCFG时钟
打开EXTI 17号线中断请求使能
EXTI 17 选择上升沿检测
同样,配置NVIC控制器管理
中断源:RTC_Alarm_IRQn
中断服务函数:RTC_Alarm_IRQHandler
清除中断标志位是 EXTI->PR & (1<<17)
同时也要清除RTC->ISR & (1<<8)闹钟匹配位
因为EXTI就是监控 该闹钟匹配位 的 边沿跳变
RTC实时时钟初始化代码
/***************************************
*函数名 :rtc_init
*函数功能 :RTC初始化配置函数
*函数参数 :RTC_t time
*函数返回值 :无
*函数描述 :
****************************************/
void rtc_init(RTC_t time)
{
/*解除RTC控制器和相关寄存器保护*/
//电源控制器时钟使能
RCC->APB1ENR |= (1<<28);
//PWR->CR 的DBP位写1解除RTC控制器控制
PWR->CR |= (1<<8);
/*RTC时钟源设置*/
//开启LSE时钟
RCC->BDCR |= (1<<0);
//等待LSE时钟就绪
while(!(RCC->BDCR & (1<<1)));
//选择LSE作为RTC时钟源
RCC->BDCR |= (1<<8);
//使能RTC时钟
RCC->BDCR |= (1<<15);
//解除RTC寄存器写保护
RTC->WPR = 0xca;
RTC->WPR = 0x53;
/*RTC相关寄存器配置*/
//选择24小时/天格式
RTC->CR &= ~(1<<6);
//日历值取自影子寄存器
RTC->CR &= ~(1<<5);
//激活RTC寄存器写保护
RTC->WPR = 0xff;
/*设置初始时间*/
if(RTC->BKP0R != 0xff)
{
rtc_set_time(time);
RTC->BKP0R = 0xff;
}
}
RTC闹钟初始化函数 及 中断服务函数
/***************************************
*函数名 :clockA_init
*函数功能 :闹钟A初始化函数
*函数参数 :RTC_t time
*函数返回值 :无
*函数描述 :
****************************************/
void clock_init(RTC_t time)
{
u32 a_temp;
//解除RTC寄存器写保护
RTC->WPR = 0xca;
RTC->WPR = 0x53;
//禁止闹钟
RTC->CR &= ~(3<<8);
//闹钟A中断使能
RTC->CR |= (1<<12);
//等待允许更新闹钟
while(!(RTC->ISR & (1<<0)) && !(RTC->ISR & (1<<1)));
//获取日期BCD码
a_temp = (in_dec_out_bcd(time.day)<<24) |
(in_dec_out_bcd(time.hour)<<16) |
(in_dec_out_bcd(time.min)<<8) |
(in_dec_out_bcd(time.sec));
//设置闹钟A寄存器
RTC->ALRMAR = a_temp;
/*EXTI控制器配置*/
//打开SYSCFG时钟
RCC->APB2ENR |= 1<<14;
//打开中断请求使能
EXTI->IMR |= (1<<17);
//上升沿检测
EXTI->RTSR |= (1<<17);
/*NVIC*控制器配置*/
//优先级分组 ----在主函数
//计算优先级编码值
u32 pri= NVIC_EncodePriority (5,2,2);
//设置具体中断源
NVIC_SetPriority(RTC_Alarm_IRQn, pri);
//使能NVIC响应通道
NVIC_EnableIRQ(RTC_Alarm_IRQn);
//使能闹钟A
RTC->CR |= (1<<8);
//激活RTC寄存器写保护
RTC->WPR = 0xff;
}
/**********************************************
*函数名 :RTC_Alarm_IRQHandler
*函数功能 :RTC闹钟中断服务函数
*函数参数 :无
*函数返回值 :无
*函数描述 :
***********************************************/
void RTC_Alarm_IRQHandler(void)
{
///判断是闹钟A信号
if(EXTI->PR & (1<<17))
{
//清除标志位
EXTI->PR |= (1<<17);
RTC->ISR &= ~(1<<8);
//执行紧急事件
LED1_ON;
printf("延时后\r\n");
LED2_ON;
LED1_OFF;
}
}