RTC时钟:一个日历

首先是了解再干什么,重点就是看懂这个原理图

1.功能

是个独立的定时器,提供日历功能,超级独立:配置的参数存到后备存储区BKP,由独立的电源供电,即使系统复位或者掉电,都不会影响RTC的配置参数,并且想要修改他的参数,需要层层关卡,就是为了当成一个合格准确又严谨的日历,他甚至还使用了更为精确的外部时钟作为自己的时钟源。

2.原理和特征

关于特征直接罗列显得人很头大,但是先搞懂原理图就能较快的掌握特征了
在这里插入图片描述总的来说是两大部分:APB1接口部分和RTC核心部分,我们需要重点理解的就是核心部分,核心部分又分成两部分,时钟的产生和中断的产生。设置了APB1接口应该是为了访问方便与保护。首先是时钟的产生即预分频模块:RTCCLK是时钟源:这就涉及到了STM32的时钟系统的复习(同一个电路,时钟频率越高功耗越大,同时抗电磁干扰能力越弱),来看看吧:

时钟模块

在这里插入图片描述1.时钟有内部时钟和外部时钟,即RC振荡器和晶振
2.由振荡器或者晶振产生锁相环PLL作为系统时钟,最高达72MHz
3.系统时钟分频得到AHB,AHB再预分频得到低速总线APB1(最高36MHz)和高速总线APB2(最高72MHz),上面挂载的模块有区别的
4.RTC时钟的选择和产生:LSE(32.768kHz)、LSI(40kHz)、HSE/128
5.还能对外输出个时钟:MCO引脚
在这里插入图片描述
STM32中有5个时钟源:
1.HSI:高速内部时钟,由RC振荡器产生,频率为8MHz
2.HSE:高速外部时钟,由外部晶振产生,范围为4~16MHz,开发板上是8
3.LSI:低速内部时钟,RC振荡器产生,40kHz独立看门口只能用LSI;LSI也可供给RTC用
4.LSE:低速外部时钟,由外部晶振产生,32.768kHz,是RTC的时钟源
5.PLL:锁相环倍频输出,其时钟输入源可选择为HSI/2,HSE或HSE/2,倍频选择2~16倍;最高为72MHz

A:对外输出时钟源,可选PLL/2,HSI,HSE,系统时钟
B: RTC时钟源,可选HSE/128,LSI,LSE
C:全速功能的USB模块,串口引擎需要一个48MHz时钟且只能从PLL输出端获取,可选择1.5分频或1分频。则使用USB模块是,PLL必须使能,并且时钟频率配置为48MHz或72MHz
D:系统时钟,可选PLL,HSI,HSE,最大为72MHz
E:指其他外设,最终来源系统时钟,经过AHB分频器分频后共其他模块使用

馋鬼步骤:

1.选择LSE作为时钟源,然后预分频,产生了TR_CLK,作为RTC的时钟。
2.接着配置了秒中断、溢出中断以及闹钟中断
在这两个过程我们能发现:
RTC时钟源有哪些呢?三种,HSE/128或者LSE或者LSI
预分频系数够大吗?影响着分频因子即最低时钟频率:这是20位的,因此分频系数最高达2的20次方
观察发现:APB1和RTC核心各自有复位系统,一个是连着系统的,一个是自带的存在BKP
三个可屏蔽中断:秒中断、溢出中断以及闹钟中断
2个分离时钟:
32位可编程计数器:计数器范围要足够大呢,能记录136年,够用了

3.又到了喜闻乐见的寄存器环节

1-时钟配置:RTCCLK / RTC_PRL = TR_CLK,配置PRL的值就能配置RTC的时钟频率,决定着最小指令周期时间;DIV是一个自减计数器,其最初的加载值是PRL,然后随着RTCCLK的指令周期一个一个减,减到0时,正好对外输出个TR_CLK,因为分频嘛就是有个PRL的倍数关系,输出TR_CLK后也刚好再把PRL加载到DIV中继续自减。DIV的每一步是根据RTCCLK的指令周期的,精度更高点,比如说,为40kHz,指令周期为1/40k = 25us,也就是DIV每减少1就是过了25us,若PRL取100,则TR_CLK的指令周期只有10025 = 2500us,显然分辨率第一点。当DIV的值为80,我们能知道过了(100-80) * 25us = 500us,提高了分辨率,如果此时还检测到TR_CLK已经过了2个指令周期,那么总走过的时间是:22500+25=5025us。

2-定时器中断:首先是一个秒中断:RTC定时器每走过1秒的时间就会触发秒中断;然后是溢出中断:当计数器RTC_CNT溢出并变回0时触发中断,RTC_CNT每过1秒才会增加1,并且是个32位的寄存器,存储范围很大的,另外是把1970年对应RTC_CNT为0,然后根据CNT当前的值就可以基于1970年这个时间远点推测当前是什么日期和时间,举个随意的值,比如此时RTC_CNT 被设置为1004556,说明距离1970年已经过了1004556秒,假设计算和后刚好是2017年5月20号13点14分25秒(这只是随口说的,并没有仔细去算,大概理解过程就行),这不就起到了日历的作用了吗。并且RTC_CNT这个最大能记录到136年后,足够用了。最后是闹钟中断:把RTC_ALR设置成某个数,当CNT=ALR时就会触发闹钟中断。
在这里插入图片描述下面是仔细介绍寄存器

在这里插入图片描述在这里插入图片描述在这里插入图片描述控制寄存器显然功能比较多:决定三个中断的开启;带有三个中断的标志位;带有RTC操作开启的标志,带有允许配置的标志,带有寄存器同步标志 —— 只有这三步都允许了才能对RTC参数进行修改即至少三道关卡;

在这里插入图片描述在这里插入图片描述
分频是常规寄存器
在这里插入图片描述CNT寄存器:就是用来计数,每过秒就增加1
在这里插入图片描述闹钟寄存器:写入个值,当CNT等于这个值就触发闹钟中断而已

4.库函数

在这里插入图片描述在这里插入图片描述在这里插入图片描述
在这里插入图片描述

5.必会步骤呢:读取参数和写入参数的一般过程:需要打开很多开关哦

在这里插入图片描述
这是读取参数:RSF这一关

在这里插入图片描述这是配置写入:RSF、CNF、RTOFF这三个标志位即三关

6.一般步骤

在这里插入图片描述
说白了RTC不过就是个定时器,不同之处在于对其参数配置时需要经过层层关卡(至少三关吧),至于日历功能,其实很简单,计数器CNT记录了多少秒,然后基于1970年对应CNT=0这个规定,进行推算,就能得到当前时间。

程序


static void RTC_NVIC_Config(void)
{	
  NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;		//RTC全局中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;	//
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;	//中断优先级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;		//使能中断
	NVIC_Init(&NVIC_InitStructure);		//使能NVIC
}

u8 RTC_Init(void)
{
	//检查是不是第一次配置RTC
	u8 temp=0;
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);	//使能时钟   
	PWR_BackupAccessCmd(ENABLE);	//使能后备寄存器的访问即允许访问  
	if (BKP_ReadBackupRegister(BKP_DR1) != 0x5050)		//读个值,0x5050是随便取得,一旦修改了0x5050就会进入RTC的配置过程呢
		{	 			 
		BKP_DeInit();	//复位备份区域,因为要重新设置了
		RCC_LSEConfig(RCC_LSE_ON);	//使用外部低速晶振LSE
		while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET&&temp<250)	//等待LSE就绪
			{
			temp++;
			delay_ms(10);
			}
		if(temp>=250)return 1;//初始化失败,LSE有问题   
		RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);		//选择LSE作为RTC时钟
		RCC_RTCCLKCmd(ENABLE);	//使能RTC时钟
		RTC_WaitForLastTask();	//等待最近一次对RTC的写操作结束
		RTC_WaitForSynchro();		//等待RTC寄存器同步
		RTC_ITConfig(RTC_IT_SEC, ENABLE);		//使能RTC秒中断
		RTC_WaitForLastTask();	//等待最近一次对RTC的写操作结束,每写一次数据都要等待完成同步
		RTC_EnterConfigMode();/// 允许配置
		RTC_SetPrescaler(32767); //设置RTC预分频
		RTC_WaitForLastTask();	//等待最近一次对RTC的写操作结束
		RTC_Set(2015,1,14,17,42,55);  //设置CNT即当前时间
		RTC_ExitConfigMode(); //退出配置模式 
		BKP_WriteBackupRegister(BKP_DR1, 0X5050);	//向某个指定的后备寄存器中写入数据
		}
	else//系统继续计时即没有进入RTC配置,配置一次后即使系统复位也不会进入RTC配置,系统复位不会导致RTC复位的
		{

		RTC_WaitForSynchro();	//等待RTC寄存器同步
		RTC_ITConfig(RTC_IT_SEC, ENABLE);	//使能秒中断
		RTC_WaitForLastTask();	//等待最近一次对RTC的写操作结束,每写一次数据都要等待完成同步
		}
	RTC_NVIC_Config();//中断分组	    				     
	RTC_Get();//更新时间
	return 0; //ok

}		 				    

//RTC中断:三个中断公用这一个中断服务函数?
void RTC_IRQHandler(void)
{		 
	if (RTC_GetITStatus(RTC_IT_SEC) != RESET)//秒中断
	{							
		RTC_Get();//更新时间 :根据CNT的值推算出当前是什么时间
 	}
	if(RTC_GetITStatus(RTC_IT_ALR)!= RESET)//闹钟中断
	{
		RTC_ClearITPendingBit(RTC_IT_ALR);		//清楚闹钟中断标志  	
	  RTC_Get();				//更新时间
  	printf("Alarm Time:%d-%d-%d %d:%d:%d\n",calendar.w_year,calendar.w_month,calendar.w_date,calendar.hour,calendar.min,calendar.sec);//把闹钟时间打印出来
		
  	} 				  								 
	RTC_ClearITPendingBit(RTC_IT_SEC|RTC_IT_OW);		//清除秒中断和闹钟中断
	RTC_WaitForLastTask();	  	    						 	   	 
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值