基于N32G45的RTC电子钟

描述

1.RTC简介

      RTC,英文全称:Real-time clock,中文名称:实时时钟,是指可以像时钟一様输出实际时间的电子设备,一般会是集成威廉希尔官方网站 ,因此也称为时钟芯片。实时时钟芯片是日常生活中应用最为广泛的消费类电子产品之一。它为人们提供精确的实时时间,或者为电子系统提供精确的时间基准,目前实时时钟芯片大多采用精度较高的晶体振荡器作为时钟源。

RTC特性

实时时钟( RTC)是一个独立的 BCD 定时器/计数器

软件支持夏时令补偿

可编程周期性自动唤醒定时器

两个 32 位寄存器包含时、分、秒、年、月、日(几号)、星期(星期几)

独立的 32 位寄存器包含亚秒

两个编程闹钟

两个 32 位寄存器包含编程闹钟时、分、秒、年、月、日(几号)、星期(星期几)

两个独立的 32 位寄存器包含编程闹钟亚秒

数字精密校准功能

时间戳功能

在 Backup 域复位后,所有 RTC 寄存器都受到保护, 以防止可能的意外写访问

多个中断/事件唤醒源,包括闹钟 A、闹钟 B、唤醒定时器、时间戳

RCC 寄存器使能 RTC 模块且电压保持在工作范围内, RTC 在任何模式下都不会停止(包括 RUN 模式、SLEEP 模式、 STOP0 模式、 STOP2 模式和 STANDBY 模式)

RTC 提供多种唤醒源可以使 MCU 从所有的低功耗模式下唤醒( SLEEP 模式, STOP0 模式, STOP2 模式和 STANDBY 模式)

电子钟

RTC框图

电子钟

2.RTC功能特性

      RTC 包括以下功能模块:

 Alarm A 和 Alarm B 事件/中断

时间戳事件/中断

RTC 输出功能:

256 Hz 或者 1Hz 时钟输出(当 LSE 频率是 32.768 kHz)

闹钟输出(极性可配置),闹钟 A 和闹钟 B 可选

自动唤醒输出(极性可配置)

RTC 输入功能:

时间戳事件检测

通过配置输出寄存器控制 PC13:

设置 RTC_OPT.TYPE 位配置 PC13 开漏/推挽输出

3.RTC相关寄存器

      RTC在配置过程中,需要完成配置的有RTC写保护相关寄存器、RTC时钟和预分频寄存器、日历寄存器以及闹钟和校准寄存器。

RTC寄存器写保护

      PWR_CTRL.DBKP 位(见电源控制部分) 默认被清除,所以 PWR_CTRL.DBKP 必须置 1 去使能 RTC 寄存器写功能。一旦备份域复位,所有的 RTC 写保护寄存器都会写保护,所有的 RTC 写保护寄存器需要按如下步骤去解锁写保护:

将 0xCA 写入 RTC_WRP 寄存器

将 0x53 写入 RTC_WRP 寄存器

      在解锁这些寄存器后,可以通过清除 PWR_CTRL.DBKP 位激活写保护。解锁机制只检查 RTC_WRP 寄存器的写操作。在解锁过程中、解锁前、解锁后,对其他寄存器的写操作不会影响解锁结果。

RTC时钟和预分频

RTC 时钟源:

LSE 时钟

LSI 时钟

HSE/128 时钟

      为了降低功耗,将预分频器分为异步预分频器和同步预分频器。如果同时使用两个预分频器,建议异步预分频器的值尽可能大。

7 位异步预分频器由 RTC_PRE.DIVA[6:0] 位控制

15 位同步预分频器由 RTC_PRE.DIVS[14:0] 位控制

电子钟


      ck_apre 时钟用于对 RTC_SUBS 亚秒递减计数器提供时钟。当到达 0 时,用 RTC_PRE.DIVS[14:0]的值重新加载 RTC_SUBS。

RTC日历

这里有三个影子寄存器,分别是 RTC_DATE, RTC_TSH 和 RTC_SUBS。 RTC 时间和日期寄存器可以通过影子寄存器访问。也可以直接访问,以避免等待同步时间。这三个影子寄存器如下:

RTC_DATE: 设置和读取日期

RTC_TSH: 设置和读取时间

RTC_SUBS: 读取亚秒

4.RTC电子钟配置流程

      1.RTC作为实时电子钟外设模块,在默认情况下只需要初始化一次即可。为了达到该目的,我们可以借助后备域寄存器BKP。

      备份存储器位于备份域里,电源 VDD 关闭后由 VBAT 供电维持。 BKP 共有 42 个 16 位的寄存器,可用来存储并保护用户应用数据。这 84 个字节不受系统待机模式唤醒或系统复位的影响。

电子钟

      根据备份存储器复位不会清除数据特性,我们可以在第一次配置好RTC寄存器后将对应的某一个寄存器写入标志位,这样下次若检测到标志位存在,则直接启动RTC即可。

      2.选择RTC时钟源。因为RTC时钟源选择有3个:HSE/128、LSE、LSI。为了让RTC更精准,应优先选择LSE(外部低速时钟32.768KHZ),而我们当前开发板刚好有LSI。

电子钟

      在RCC_BDCRCTL的0和1位,选择LSE时钟源;RCC_BDCRCTL的第15位使能RTC时钟。

电子钟电子钟

       3.设置RTC的工作频率。设置RTC的工作频率我们可以通过RTC_PRE预分频寄存器完成。

电子钟

     我们选择的是LSE=32.768KHZ时钟源,根据时钟频率计算公式,我们可以设置RTC_PRE=0xFF7F来产生1HZ工作频率。根据官方文档,异步预分频尽量设置大些。

      4.设置电子日历。电子日历可通过时间寄存器RTC_TSH和日期寄存器RTC_DATE完成配置。

电子钟电子钟电子钟

    5.输出1HZ频率。在使用RTC日历功能时,参考官方提供示例日历功能是通过RTC校准输出引脚PC13输出,然后在开启一个外部中断器检测该引脚,从而输出电子日历。

     在使用RTC本身唤醒中断时发现无法触发,也可能是配置有问题,这个等下一次解决后再来叙述说明,本示例则按照官方示例实现。

PC13引脚模式配置

电子钟

4.1 RTC配置示例

      按照上述步骤,RTC电子日历功能配置示例如下:

 

void RTC_Init(void)
{
  //开启RTC和后备域权限
  RCC->APB1PCLKEN|=1<<27;//开启备份接口时钟
  RCC->APB1PCLKEN|=1<<28;//电源接口时钟
  /* 允许访问RTC*/
  PWR->CTRL|=1<<8;//允许写入RTC和后备区域
	if(BKP->DAT2!=0xAA)//判断是否上一次RTC初始化
	{
    printf("进入初始化rn");
    RCC->BDCTRL&=~(1<<15);//关闭RTC时钟
    
		//2.选择RTC时钟源
		RCC->BDCTRL|=1<<0;//开启32.768KHZ时钟
		while(!(RCC->BDCTRL&1<<1)){}//等待32.768KHZ时钟准备就绪	
		RCC->BDCTRL&=~(0x3<<8);//清除原来寄存器中的值
		RCC->BDCTRL|=0x1<<8;//时钟源为32.768KHZ
    RCC->BDCTRL|=1<<15;//开启RTC时钟
    /*解除RTC写保护*/
    RTC->WRP=0xCA;
    RTC->WRP=0x53; 
    while(!(RTC->INITSTS&1<<5)){} //等待日历影子寄存器同步
    
    RTC->INITSTS|=1<<7;//进入初始化模式
    while(!(RTC->INITSTS&1<<6)){}//等待初始化标志置1
    printf("进入配置模式rn");  
    /*设置分频系数,产生1HZ*/
    RTC->PRE=0;      
    RTC->PRE|=0X7F<<16;
    RTC->PRE|=0xFF;

    RTC->INITSTS&=~(1<<7);//退出初始化模式
    RTC->WRP=0xff; 
    for(int i=0;i<0x2FF;i++);//等待配置完成
    BKP->DAT2=0xAA;//RTC初始化完成标志	
    RTC_SetDate(&RTC_Time);

	}
  RTC->WRP = 0xCA;
  RTC->WRP = 0x53;  
  RTC->CTRL|=1<<19;//输出1HZ
  RTC->OPT|=1<<0;//推挽输出
  RTC->CTRL|=1<<23;//开启校准输出
  RTC->WRP = 0xFF;
  printf("初始化完成rn");
}

 

4.2 配置PA7引脚,捕获RTC电子日历

      由于RTC产生的1HZ频率是通过PC13引脚输出,所以我们配置一个硬件来实现电子日历。

 

void EXTI_Init(void)
{
  //1.GPIO口配置

  RCC->APB2PCLKEN|=1<<2;
  GPIOA->PL_CFG&=0x0FFFFFFF;
  GPIOA->PL_CFG|=0x80000000;
  //2.开AFIO时钟,选择触发源
  RCC->APB2PCLKEN|=1<<0;//开AFIO时钟
  /*外部中断7--PA7*/
  AFIO->EXTI_CFG[1]&=~(0xF<<3*4);//PA7
  EXTI->IMASK|=1<<7;//使能中断线7
  EXTI->RT_CFG|=1<<7;//检测上升沿
  N32_NVIC_SetPriority(EXTI9_5_IRQn,1,1);//设置优先级
}
void EXTI9_5_IRQHandler(void)
{
 

  else if(PAin(7))
  {
    RTC_GetDate(&RTC_Time);
  }
  EXTI->PEND|=0xf<<5;//清除标志位
}

 

4.3 RTC设置时间

      为方便后续时间校准(串口校时或者网络校时或其它方式)实现,这里单独封装一个RTC时间校准函数。

 

/*设置RTC时间和日期*/
void RTC_SetDate(RTC_TIME *RTC_Time)
{
  RTC->WRP = 0xCA;
  RTC->WRP = 0x53;
  RTC->INITSTS|=1<<7;//进入初始化模式
  while(!(RTC->INITSTS&1<<6)){}//等待初始化标志置1 
   //设置日期
   RTC->DATE=0;
  //年
  RTC->DATE=0;
  RTC->DATE|=(RTC_Time->year / 10)<<20;
  RTC->DATE|=(RTC_Time->year % 10)<<16;
  //星期1
  RTC->DATE|=(RTC_Time->week % 8)<<13;
  //月
  RTC->DATE|=(RTC_Time->mon / 10)<<12;
  RTC->DATE|=(RTC_Time->mon % 10)<<8;
  //日
  RTC->DATE|=(RTC_Time->day / 10)<<4;
  RTC->DATE|=(RTC_Time->day % 10)<<0;  
    
  /*设置时间*/
  RTC->TSH=0;
  RTC->TSH&=~(1<<22);//24小时制
  //时
  RTC->TSH|=(RTC_Time->hour / 10)<<20;
  RTC->TSH|=(RTC_Time->hour % 10)<<16; 
  //分
  RTC->TSH|=(RTC_Time->min /10)<<12; 
  RTC->TSH|=(RTC_Time->min %10)<<8;  
  //秒
  RTC->TSH|=(RTC_Time->sec /10)<<4; 
  RTC->TSH|=(RTC_Time->sec %10)<<0;
  RTC->INITSTS&=~(1<<7);//退出初始化模式
  RTC->WRP=0xff;
}

 

4.4 RTC时间读取

      为方便后续做时间显示处理,封装时间获取函数。

 

/*读取RTC时间和日期*/
void RTC_GetDate(RTC_TIME *RTC_Time)
{
  u32 date=RTC->DATE;
  u32 tsh=RTC->TSH;
  RTC_Time->year=((date>>20)&0xf)*10+ ((date>>16)&0xf);
  RTC_Time->mon=((date>>12)&0x1)*10+ ((date>>8)&0xf);
  RTC_Time->day=((date>>4)&0x3)*10+(date&0xf);
  RTC_Time->week=((date>>13)&0x7);
  //时间,注意,+-优先级 高于 &的优先级
  RTC_Time->hour=((tsh>>20)&0x3)*10+((tsh>>16)&0xf);
  RTC_Time->min=((tsh>>12)&0x7)*10+((tsh>>8)&0xf);
  RTC_Time->sec=((tsh>>4)&0x7)*10+(tsh&0xf);
 // printf("%d/%d/%d -- %d:%d:%d rn",RTC_Time->year,RTC_Time->mon,RTC_Time->day, 
                                         RTC_Time->hour,RTC_Time->min,RTC_Time->sec);
}

 

4.5 串口校时

      通过串口方式进行时间校准。串口数据格式如下:

电子钟

if(usart1_flag)
{
	usart1_rx_buff[usart1_cnt]='�';//字符串结束标志符
	printf("usart1:%srn",usart1_rx_buff);
	//*20200822102540
	if(usart1_rx_buff[0]=='*' && usart1_cnt==15)
	{
		RTC_Time.year=(usart1_rx_buff[3]-'0')*10+(usart1_rx_buff[4]-'0')*1;
		RTC_Time.mon=(usart1_rx_buff[5]-'0')*10+(usart1_rx_buff[6]-'0')*1;
		RTC_Time.day=(usart1_rx_buff[7]-'0')*10+(usart1_rx_buff[8]-'0')*1;
		RTC_Time.hour=(usart1_rx_buff[9]-'0')*10+(usart1_rx_buff[10]-'0')*1;
		RTC_Time.min=(usart1_rx_buff[11]-'0')*10+(usart1_rx_buff[12]-'0')*1;
		RTC_Time.sec=(usart1_rx_buff[13]-'0')*10+(usart1_rx_buff[14]-'0')*1;
        RTC_ChangeWeek(RTC_Time.year,RTC_Time.mon,RTC_Time.day);//星期
		RTC_SetDate(&RTC_Time);//设置时间和日期
	}
	usart1_flag=0;	
	usart1_cnt=0;			
}

 

4.6 运行效果

      将获取到的时间通过OLED屏幕显示。OLED屏幕驱动参考://www.obk20.com/d/1950506.htmltrack_id=myCenter&mod=article&share

电子钟
  审核编辑:汤梓红

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分