Bootstrap

STM32学习笔记---RTC

目录

一、什么是RTC

二、如何配置RTC

1、标准实时时钟部分(万年历部分)

1.1 时钟源分类

1.2 RTC时钟源的选择

1.3 精密校正

1.4 异步7位预分频器

1.5 粗略校正

1.6 同步15位分频

1.7 日历寄存器

1.8 RTC的初始化与配置

1.9 程序设计

2、闹钟部分

2.1 闹钟的初始化与配置

2.2 程序设计

三、具体使用RTC

需求1:利用RTC设计日历

需求2:利用RTC设计闹钟


一、什么是RTC

RTC:

        RTC能够持续记录当前的时间,包括年、月、日、时、分、秒等,自动计算闰年,能区分每个月的天数甚至可能包括星期几和时区信息

RTC框图:

二、如何配置RTC

1、标准实时时钟部分(万年历部分)

1.1 时钟源分类

1.2 RTC时钟源的选择

①LSE(32.768KHZ):外部低速时钟     分频=同步*异步=256*128        

//通常使用  外部低速时钟有独立备份电源可以让RTC独立运行

如何配置外部低速时钟源提供给RTC?

选择外部低速时钟(备份时钟)就找到RCC备份域控制寄存器相应的位

RCC备份域控制寄存器

使能外部低速振荡器       0位

等待外部低速振荡器就绪   1位

选择外部低速时钟         8:9位

LSI(32KHZ):内部低速时钟     分频=同步*异步=250*128        

选择内部低速时钟(备份时钟)就找到RCC备份域控制寄存器、RCC控制和状态寄存器应的位

RCC控制和状态寄存器:

使能内低速振荡器       0位

等待内部低速振荡器就绪 1位

RCC备份域控制寄存器

选择内部低速时钟      8:9位

1.3 精密校正

不做高精度需求是不需要此寄存器

1.4 异步7位预分频器

128分频     

1.5 粗略校正

不需要

1.6 同步15位分频

默认256分频   //1HZ    1次/s

1.7 日历寄存器

设置日期时间

获取日期时间

1.8 RTC的初始化与配置

 

总结:

①将电源控制寄存器(PWR->CR)的DBP位写1 -->解除RTC控制器和RTC备份寄存器的保护

        配置这个寄存器之前,要使能电源控制器时钟 RCC->APB1

②往RTC_WPR 中写 0xca、再写 0x53--->取消所有RTC寄存器的写保护

      往RTC_WPR寄存器中随便写一个数据就会再次激活写保护

总结: 

1、使能电源控制器(PWR)

2、使能备份域的访问

3、选择RTC时钟源

4、使能RTC时钟

设置日历寄存器的要求

总结:

要想改变日历寄存器的时间的值或者分频值,需要让日历进入初始化模式

(日历停止工作)

更改完以后,要想日历继续工作,需要退出初始化模式(自由模式)

0、取消写保护

1、进入初始化模式---> RTC->ISR 寄存器的INIT位写1

2、检测是否允许更新(改变)日历值位--->轮询RTC->ISR 寄存器的INITF位是否置1

3、分频--->同步、异步

4、设置日期和时间寄存器

5、退出初始化模式---> RTC->ISR 寄存器的INIT位写0

6、激活写保护

读取日历时间和日期

要想读时间和日期,要等待时间和日期同步(加载到影子寄存器)

如何等待时间和日期同步到影子寄存器

ISR寄存器中RSF位要先清零,然后等待置1,读时间或者日期值.

注意:在使用RTC寄存器之前需要进行取消写保护

1.9 程序设计

初始配置RTC:

初始化配置函数

{

//使能电源控制器的时钟

/*解除RTC控制器和备份域控制器寄存器的写保护*/

/*配置时钟源*/

//使能外部低速时钟

//等待外部低速时钟使能

//选择外部低速时钟作为RTC时钟

//使能RTC

/*解除RTC寄存器的写保护*/

//配置RTC相关寄存器

//CR

/*激活RTC寄存器的写保护*/

//设置初始时间

}

设置时间

设置时间和日期函数

{

//解除RTC寄存器的写保护

//进入日历初始化模式

//等待进入初始化模式完成

/*把十进制日期值转换成BCD码形式*/

//设置日期到DR寄存器

/*把十进制时间值转换成BCD码形式*/

//设置时间到TR寄存器

//退出日历初始化模式

//激活RTC寄存器的写保护

}

十进制Dec转BCD码:

BCD=(Dec/10)<<4 | (Dec%10)

获取时间

获取日期和时间函数

{

//解除RTC寄存器的写保护

将同步标志位清零

等待同步标志位置1

读日期寄存器的值

/*把BCD码形式转换成十进制日期值*/

将同步标志位清零

等待同步标志位置1

读时间寄存器的值

/*把BCD码形式转换成十进制时间值*/

//激活RTC寄存器的写保护

}

BCD码转十进制Dec:

Dec=(BCD>>4)*10+(BCD&0x0f)

2、闹钟部分

2.1 闹钟的初始化与配置

总结:

1、禁止闹钟

2、轮询RTC_ISR的ALRAWF(闹钟A)\ALRBWF(闹钟B)位是否置1

3、配置闹钟寄存器

4、使能闹钟

5、配置EXT控制器

6、使能闹钟中断

7、配置NVIC控制器

2.2 程序设计

设置RTC闹钟初始化函数

{

/*解除RTC寄存器写保护*/

/*闹钟初始化*/

//禁止闹钟运行

//等待更新闹钟

/*配置闹钟*/

//设置掩码

//获取日期BCD码

//使能闹钟

//激活RTC寄存器写保护

/*EXTI控制器配置*/

//系统配置控制器使能(APB2)

//检测边沿

//使能外部中断

/*NVIC控制器配置*/

//闹钟A中断使能

}

闹钟中断服务函数

{

        //判断EXTI17触发的中断信号

        {

                //清除EXTI中断标志位

                //清除闹钟标志位(此位不是中断标志位)

                //执行紧急事件

        }

}

不清除EXTI中断标志位则会一直执行闹钟中断服务函数;而不清除闹钟标志位,则不会进入执行闹钟中断服务函数。为什么?首先时间/日期寄存器( RTC_TR 和 RTC_DR)与闹钟寄存器 (RTC_ALRMAR) 匹配 时,RTC的闹钟标志位由0到1(上升沿跳变),而EXTI会实时监控ISR的变化,当检测到ISR由0到1(上升沿跳变)时就会触发NVIC。进入中断后清除EXTI中断标志位(以便下次再次进入),不清除闹钟标志位还是1,这时候当下一次时间匹配时,EXTI检测不到ISR由0到1(上升沿跳变)就无法再次触发NVIC。

三、具体使用RTC

需求1:利用RTC设计日历

RTC初始化函数:

/*
函数名: rtc_init
函数功能:RTC初始化
返回值:void
形参:void
函数说明:

*/
void rtc_init(RTC_t date_time)
{
	/*解除RTC和相关寄存器保护*/
	RCC->APB1ENR |= (1 << 28);//使能电源控制器时钟
	PWR->CR |= (1 << 8);//解除RTC控制器控制 
	
	/*时钟源配置*/
	RCC->BDCR |= (1 << 0);//使能外部低速振荡器 0位
	while(!(RCC->BDCR & (1 <<  1)));//等待外部低速振荡器就绪   1位
	RCC->BDCR |= (1 << 8);//选择外部低速时钟 8:9位
	RCC->BDCR |= (1 << 15);//使能RTC时钟	15位
	
	/*解除RTC寄存器的写保护*/
	RTC->WPR = 0xCA;
	RTC->WPR = 0x53;
	
	/*配置相关寄存器*/
	//CR
	RTC->CR &= ~(1 << 6);//24小时制
	RTC->CR &= ~(1 << 5);//影子寄存器
	
	/*激活RTC寄存器的写保护*/
	RTC->WPR = 0xff;
	
	/*设置初始时间*/
	if(RTC->BKP0R != 0xff)
	{
		set_date_time(date_time);
		RTC->BKP0R = 0xff;
	}
	
}

设置初始时间的相关内容:

RTC备份寄存器和备份电源

没有备份电源,有RTC备份寄存器:只能做到复位后,时间继续走;断电后重新上电后时间会重置

如何做到复位后时间继续走?

正常情况下,在不使用RTC备份寄存器时,复位后时间将会被重置;而在使用备份寄存器后,

在RTC初始化函数里首次执行设置初始时间的地方做个标志(相当于”到此一游”),如果下次复位后,运行到此标记时,则不会重新设置初始时间。说白了,就是利用这个标志绕过初始化重新设置时间。

如何做到断电后时间不会被重置?

需要备份电源(纽扣电池)向RTC供电

总结:

备份电源作用是:

芯片断电可以让RTC继续工作,同时备份寄存器不复位

RTC备份寄存器的作用:

防止芯片复位后,CUP重置初始时间(执行RTC初始化函数-->重新设定原来的时间)

设置时间和日期:

/*
函数名: set_date_time
函数功能:设置日期和时间
返回值:void
形参:void
函数说明:

*/
void set_date_time(RTC_t date_time)
{
	
	//解除RTC寄存器的写保护
	RTC->WPR = 0xCA;
	RTC->WPR = 0x53;
	
	//进入日历初始化模式
	RTC->ISR |= (1 << 7);
	//等待进入初始化模式完成
	while(!(RTC->ISR & (1 << 6)));
	//把十进制日期值转换成BCD码形式
	//设置日期到DR寄存器
	RTC->DR = in_dec_out_bcd(date_time.year-2000)<<16           |
						in_dec_out_bcd(date_time.week)<<13      |
						in_dec_out_bcd(date_time.mon)<<8        |
						in_dec_out_bcd(date_time.day);
	
	
	
	//把十进制时间值转换成BCD码形式
	//设置时间到TR寄存器
	RTC->TR = in_dec_out_bcd(date_time.hour)<<16           |
						in_dec_out_bcd(date_time.min )<<8  |
						in_dec_out_bcd(date_time.sec );
	
	
	
	//退出日历初始化模式
	RTC->ISR &= ~(1 << 7);
	
	//激活RTC寄存器的写保护
	RTC->WPR = 0xff;
}

十进制日期值转换成BCD码形式函数:

/*
函数名: in_dec_out_bcd
函数功能:十进制日期值转换成BCD码形式
返回值:void
形参:u8 dec
函数说明:

*/
u8 in_dec_out_bcd(u8 dec)
{
	return (dec/10)<<4 | (dec%10);
}

获取日期和时间函数:

/*
函数名: get_date_time
函数功能:获取日期和时间
返回值:void
形参:void
函数说明:

*/
RTC_t get_date_or_time(void)
{
	u32 d_temp;
	u32 t_temp;
	RTC_t get_date_time;
	//解除RTC寄存器的写保护
	RTC->WPR = 0xCA;
	RTC->WPR = 0x53;
	
	//将同步标志位清零
	RTC->ISR &= ~(1 << 5);
	//等待同步标志位置1
	while(!(RTC->ISR & (1 << 5)));
	//读日期寄存器的值
	d_temp = RTC->DR;
	//把BCD码形式转换成十进制日期值
	get_date_time.year = in_bcd_out_dec(d_temp>>16)+2000;
	get_date_time.week = in_bcd_out_dec((d_temp>>13) & 0x07);
	get_date_time.mon  = in_bcd_out_dec(d_temp>>8& 0x1f);
	get_date_time.day  = in_bcd_out_dec(d_temp);
	
	//将同步标志位清零
	RTC->ISR &= ~(1 << 5);
	//等待同步标志位置1
	while(!(RTC->ISR & (1 << 5)));
	//读时间寄存器的值
	t_temp = RTC->TR;
	//把BCD码形式转换成十进制时间值
	get_date_time.hour = in_bcd_out_dec(t_temp>>16);
	get_date_time.min  = in_bcd_out_dec(t_temp>>8);
	get_date_time.sec  = in_bcd_out_dec(t_temp);
	//激活RTC寄存器的写保护
	RTC->WPR = 0xff;
	
	return get_date_time;
}

 

需求2:利用RTC设计闹钟

设置RTC闹钟初始化函数:

/*
函数名: alr_init
函数功能:设置RTC闹钟初始化
返回值:void
形参:void
函数说明:

*/
RTC_t alr =  {2024,9,26,4,19,41,5};
void alr_init(RTC_t alr)
{
	//解除RTC寄存器写保护
	RTC->WPR = 0xca;
	RTC->WPR = 0x53;
	/*设置闹钟*/

	//禁止闹钟运行
	RTC->CR &= ~(1<<8);
	while(!(RTC->ISR & (1 << 0)));//等待更新闹钟A
	
	//日期、时分秒比较
    //RTC->ALRMAR |= ;//日期掩码
    //RTC->ALRMAR |= ;//小时掩码
    //RTC->ALRMAR |= ;//分钟掩码
	RTC->ALRMAR &= ~(1 << 7);//秒掩码
	RTC->ALRMAR &= ~(1 << 30);//日期个位
	RTC->ALRMAR &= ~(1 << 22);//24小时
	
	//获取日期BCD码
	RTC->ALRMAR = in_dec_out_bcd(alr.day)<<24 |
								in_dec_out_bcd(alr.hour)<<16|
								in_dec_out_bcd(alr.min)<<8  |
								in_dec_out_bcd(alr.sec);
	//设置掩码							
	RTC->ALRMAR |= (1 << 31) | (1 << 23) | (1 << 15);
	
	
	//设置闹钟
	RTC->CR |= (1 << 8);//使能闹钟A
	
	//激活RTC寄存器写保护
	RTC->WPR = 0xff;
	
	/*EXTI控制器配置*/
	//系统配置控制器使能(APB2)
	RCC->APB2ENR |= 1 << 14;
	//检测边沿       上升沿:EXTI->RTSR |= 1<<x  下降沿:EXTI->FTSR |= 1<<x
	EXTI->RTSR |= 1 << 17;
	//使能外部中断   		 EXTI->IMR |= 1<<x
	EXTI->IMR |= 1 << 17;
	
	/*NVIC控制器配置*/
	RTC->CR |= (1 << 12);//闹钟A中断使能
	u32 pri = NVIC_EncodePriority (5, 1, 1);//计算优先级编码值,设置抢占和响应的级别值
	NVIC_SetPriority(RTC_Alarm_IRQn, pri);//设置具体某个中断源的优先级
	NVIC_EnableIRQ(RTC_Alarm_IRQn);//中断信号响应通道使能
	
	
}

RTC闹钟中断服务函数:

/*
函数名:RTC_Alarm_IRQHandler
函数功能:RTC中断服务函数
返回值:void
形参:void
函数说明:

*/
void RTC_Alarm_IRQHandler(void)
{
	//判断EXTI17触发的中断信号
	if(EXTI->PR & (1 << 17))
	{
	    //清除标志位
		EXTI->PR |= 1 << 17;
	    //清除闹钟标志位
	    RTC->ISR &= ~(1 << 8);
	    //紧急事件
		printf("响铃l!\r\n");
	}
	
}

;