STM32学习笔记12— RTC实验
12.1 概述
实时时钟Real TimeClock(简称RTC),实时时钟芯片是日常生活中应用最为广泛的消费类电子产品之一。它为人们提供精确的实时时间,或者为电子系统提供精确的时间基准,目前实时时钟芯片大多采用精度较高的晶体振荡器作为时钟源。有些时钟芯片为了在主电源掉电时,还可以工作,需要外加电池供电。
现在的ARM体系处理器基本都会内置RTC模块,STM32也不例外。STM32内部RTC结构如下图所示。
RTC主要有两个部分组成,第一部分的APB1接口用来和APB1总线相连,此单元还包含一组16位寄存器,可通过APB1总线对其进行读写操作。另一部分由一组可编程计数器组成,分成两个主要模块。第一个模块是RTC的预分频模块,它可编程产生最长为1秒的RTC时间基准TR_CLK。RTC的预分频模块包含了一个20位的可编程分频器。如果在RTC_CR寄存器中设置了相应的允许位,则在每个TR_CLK周期中RTC产生一个中断。第二个模块是一个32位的可编程计数器,可被初始化为当前的系统时间。系统时间按TR_CLK周期累加并与存储在RTC_ALR寄存器中的可编程时间相比较,如果RTC_CR控制寄存器中设置了相应允许位,比较匹配时将产生一个闹钟中断。
RTC模块和时钟配置系统是在后备区域,即在系统复位或从待机模式唤醒后RTC的设置和时间维持不变。但是在系统复位后,会自动禁止访问后备寄存器和RTC,以防止对后备区域的意外写操作。所以在要设置时间之前,先要取消备份区域写保护。
12.2 相关寄存器
12.2.1 控制寄存器1:RTC_CRH
Bit 2:允许溢出中断位
0:屏蔽溢出中断
1:允许溢出中断
Bit 1:允许闹钟中断
0:屏蔽闹钟中断
1:允许闹钟中断
Bit 0:允许秒中断
0:屏蔽秒中断
1:允许秒中断
12.2.2 控制寄存器2:RTC_CRL
Bit 5:RTC操作关闭
0:写操作未完成
1:操作已完成
Bit 4:配置标志
0:退出配置模式
1:进入配置模式
Bit 3:RTC同步标志
RTC_CNT寄存器和RTC_DIV寄存器由软件更新或清0时,此位由硬件置1。在APB1复位后,或APB1时钟停止后,此位必须由软件清0。要进行任何的读操作之前,用户程序必须等待这位被硬件置1
0:寄存器尚未被同步
1:寄存器已经被同步
Bit 2:溢出标志
当32位可编程计数器溢出时,此位由硬件置1。此位只能由软件清0
0:无溢出
1:32位可编程计数器溢出
Bit 1:闹钟标志
当32位可编程计数器达到RTC_ALR寄存器所设置的预定值,此位由硬件置1。此位只能由软件清0。
0:无闹钟
1:有闹钟
Bit 0:秒标志
当32位可编程预分频器溢出时,此位由硬件置1,同时RTC计数器加1。此位只能由软件清除。
0:秒标志条件不成立
1:秒标志条件成立
12.2.3 预分频装载寄存器1:RTC_PRLH
Bit 3~Bit 0:RTC预分频装载值高位
12.2.4 预分频装载寄存器2:RTC_PRLL
Bit 15~Bit 0:RTC预分频装载值低位
注:RTC时钟频率根据预分频寄存器的值有如下计算公式。
其中RTCCLK代表的RTC的输入时钟,一般默认32.768kHz。
12.3 实验例程
功能:读取RTC的日期显示在LCD上面。
(1)创建rtc.h文件输入以下代码。
- /*********************************************************************************************************
- RTC 驱 动 文 件
- *********************************************************************************************************/
- #ifndef _RTC_H_
- #define _RTC_H_
- #include "sys.h"
- /*********************************************************************************************************
- 数 据 结 构
- *********************************************************************************************************/
- typedef struct
- {
- u8 year; //年
- u8 month; //月
- u8 date; //日
- u8 hour; //时
- u8 minute; //分
- u8 second; //秒
- }RTC_Data;
- extern RTC_Data RTC_Time;
- /*********************************************************************************************************
- 函 数 列 表
- *********************************************************************************************************/
- void RTC_Init( void ) ; //RTC初始化
- void RTC_Set_Time( u8 year, u8 month, u8 date, u8 hour, u8 minute, u8 second ) ; //设置时间
- void RTC_Get_Time( void ) ; //获取时间
- #endif
复制代码
(2)创建rtc.c文件并输入以下代码。
- #include "rtc.h"
- /***************************************************
- Name :RTC_Init
- Fuction :RTC初始化
- Parameter :None
- Return :None
- ***************************************************/
- void RTC_Init()
- {
- if( BKP->DR1!=0x5050 )
- {
- RCC->APB1ENR |= 1<<28 ; //使能PWR时钟
- RCC->APB1ENR |= 1<<27 ; //使能BKP时钟,RTC校准在BKP相关寄存器中
- PWR->CR |= 1<<8 ; //取消BKP相关寄存器写保护
- RCC->BDCR |= 1<<16 ; //备份区域软复位
- RCC->BDCR &= ~( 1<<16 ) ; //备份区域软复位结束
- RCC->BDCR |= 1<<0 ; //开启外部低速振荡器
- while( ( RCC->BDCR&0x02 )!=0x02 ) ; //等待外部时钟就绪
- RCC->BDCR |= 1<<8 ; //LSI作为RTC时钟
- RCC->BDCR |= 1<<15 ; //RTC时钟使能
- while( !( RTC->CRL&( 1<<5 ) ) ) ; //等待RTC寄存器最后一次操作完成
- while( !( RTC->CRL&( 1<<3 ) ) ) ; //等待RTC寄存器同步完成
- RTC->CRH &= ~( 7<<0 ) ; //不允许中断,CRH寄存器低三位有效
- while( !( RTC->CRL&( 1<<5 ) ) ) ; //等待RTC寄存器最后一次操作完成
- RTC->CRL |= 1<<4 ; //进入配置模式
- RTC->PRLH = 0 ;
- RTC->PRLL = 32767 ; //设定分频值
- RTC->CRL &= ~( 1<<4 ) ; //退出配置模式
- while( !( RTC->CRL&( 1<<5 ) ) ) ; //等待RTC寄存器最后一次操作完成
- BKP->DR1 = 0x5050 ;
- }
- else
- {
- while( !( RTC->CRL&( 1<<3 ) ) ) ; //等待RTC寄存器同步
- while( !( RTC->CRL&( 1<<5 ) ) ) ; //等待RTC寄存器操作完成
- }
- }
- /***************************************************
- Name :Is_Leap_Year
- Function :闰年判定
- Parameter :
- year:年份
- Return :闰年
- ***************************************************/
- u8 Is_Leap_Year( u16 year )
- {
- //必须能被4整除
- if( year%4==0 )
- {
- if( year%100==0 )
- {
- if( year%400==0 )
- return 1 ;
- else
- return 0 ;
- }
- else
- return 1 ;
- }
- else
- return 0;
- }
- /***************************************************
- Name :RTC_Set_Time
- Fuction :设置时间
- Parameter :
- year:年
- month:月
- date:日
- hour:时
- minute:分
- second:秒
- Return :None
- ***************************************************/
- u8 mon_table[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } ;
- void RTC_Set_Time( u8 year, u8 month, u8 date, u8 hour, u8 minute, u8 second )
- {
- u16 t ;
- u32 seccount = 0 ;
- //把所有年份的秒钟相加
- for( t=1970; t<year+2000; t++ )
- {
- if( Is_Leap_Year( t ) )
- seccount += 31622400 ; //闰年的秒钟数
- else
- seccount += 31536000 ; //平年的秒钟数
- }
- month -= 1 ;
- for( t=0; t<month; t++ ) //把前面月份的秒钟数相加
- {
- seccount += ( u32 )mon_table[ t ]*86400 ; //月份秒钟数相加
- if( Is_Leap_Year( year+2000 )&&( t==1 ) )
- seccount += 86400 ; //闰年2月份增加一天的秒钟数
- }
- seccount += ( date-1 )*86400 ; //把前面日期的秒钟数相加
- seccount += hour*3600 ; //小时秒钟数
- seccount += minute*60 ; //分钟秒钟数
- seccount += second ; //最后的秒钟加上去
- //设置时钟
- RCC->APB1ENR |= 1<<28 ; //使能电源时钟
- RCC->APB1ENR |= 1<<27 ; //使能备份时钟
- PWR->CR |= 1<<8 ; //取消备份区写保护
- RTC->CRL |= 1<<4 ; //允许配置
- RTC->CNTL = seccount&0xFFFF ;
- RTC->CNTH = seccount>>16 ;
- RTC->CRL &= ~( 1<<4 ) ; //配置更新
- while( ( RTC->CRL&0x20 )!=0x20 ) ; //等待RTC寄存器操作完成
- RTC_Get_Time() ; //设置完之后更新一下数据
- }
- /***************************************************
- Name :RTC_Get_Time
- Fuction :获取时间
- Parameter :None
- Return :None
- ***************************************************/
- RTC_Data RTC_Time;
- void RTC_Get_Time()
- {
- u16 daycnt=0;
- u32 timecount=0;
- u32 temp=0;
- u16 temp1=0;
- //得到计数器中的值
- timecount = RTC->CNTH ;
- timecount <<= 16 ;
- timecount += RTC->CNTL ;
- //得到天数
- 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
- break ;
- }
- else
- temp -= 365; //平年
- temp1 ++ ;
- }
- RTC_Time.year = temp1-2000 ; //得到年份
- temp1 = 0 ;
- //超过了一个月
- while( temp>=28 )
- {
- if( Is_Leap_Year( RTC_Time.year+2000 )&&( temp1==1 ) )//当年是不是闰年/2月份
- {
- if( temp>=29 )
- temp -= 29 ;//闰年的秒钟数
- else
- break;
- }
- else
- {
- if( temp>=mon_table[ temp1 ] )
- temp -= mon_table[ temp1 ] ; //平年
- else
- break ;
- }
- temp1 ++ ;
- }
- RTC_Time.month = temp1+1 ; //得到月份
- RTC_Time.date = temp+1 ; //得到日期
- }
- temp = timecount%86400 ; //得到秒钟数
- RTC_Time.hour = temp/3600 ; //小时
- RTC_Time.minute = ( temp%3600 )/60 ; //分钟
- RTC_Time.second = ( temp%3600 )%60 ; //秒钟
- }
复制代码 (3)创建1.c文件并输入以下代码。
- #include "sys.h"
- #include "delay.h"
- #include "usart1.h"
- #include "lcd.h"
- #include "rtc.h"
- int main()
- {
- u8 Str[ 50 ] ;
- STM32_Clock_Init( 9 ) ; //STM32时钟初始化
- SysTick_Init( 72 ) ; //SysTick初始化
- USART1_Init( 72, 115200 ) ; //初始化串口1波特率115200
- LCD_Init() ; //LCD初始化
- RTC_Init() ;
- RTC_Set_Time( 20, 12, 10, 10, 8, 0 ) ;
- while( 1 )
- {
- RTC_Get_Time() ;
- sprintf( ( char * )Str, "20%02d-%02d-%02d %02d:%02d:%02d", RTC_Time.year, RTC_Time.month, RTC_Time.date, RTC_Time.hour, RTC_Time.minute, RTC_Time.second ) ;
- LCD_ShowString( 10, 10, Str ) ;
- delay_ms( 500 ) ;
- }
- }
复制代码
|