打印
[STM32F1]

STM32基础篇——RTC时钟实验

[复制链接]
1234|18
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本次试验通过STM32 的内部实时时钟, RTC 中断 printf 输出时间,实现一个简单的时钟。顺带介绍 BKP 的使用。试验目标:1.  STM32 内部时钟结构
2. 时钟的计算方式。

沙发
aizaixiyuanqian|  楼主 | 2017-12-28 21:50 | 只看该作者
STM32内部RTC时钟简介
STM32备份寄存器是 42 个 16 位的寄存器,可用来存储 84 个字节的用户应用程序数据。他们处在备份域里,当 VDD 电源被切断,他们仍然由 VBAT 维持供电。当系统在待机模式下被唤醒,或系统复位或电源复位时,他们也不会被复位。而 STM32 的内部 RTC 时钟就在备份寄存器中。所以我们得到一个结论,就是要操作 RTC 时钟就要操作备份寄存器。

使用特权

评论回复
板凳
aizaixiyuanqian|  楼主 | 2017-12-28 21:52 | 只看该作者
RTC 的结构框图:

使用特权

评论回复
地板
aizaixiyuanqian|  楼主 | 2017-12-28 21:54 | 只看该作者
从框图中我们可以看出,其实 RTC 时钟里面存储时钟信号的只是一个 32 位的寄存器,如果按秒来计算的话可以存储可以记录 4294967296 秒,约合 136 年左右,作为一般应用,这已经是足够了的。但是从这里看出我们要具体知道现在的时间是哪年哪月哪日,还有时分秒,那么就要自己进行处理了,将读取出来的计数值,转换为我们熟悉的年月日时分秒。接下来我们来看一下怎么操作 RTC 时钟。

使用特权

评论回复
5
aizaixiyuanqian|  楼主 | 2017-12-28 21:55 | 只看该作者
BKP 简介:
备份寄存器是 42 个 16 位的寄存器,可用来存储 84 个字节的用户应用程序数据。他们处在备份域里,当 VDD 电源被切断,他们仍然由 VBAT 维持供电。当系统在待机模式下被唤醒,或系统复位或电源复位时,他们也不会被复位。此外,BKP 控制寄存器用来管理侵入检测和 RTC 校准功能。 复位后,对备份寄存器和RTC 的访问被禁止,并且备份域被保护以防止可能存在的意外的写操作。执行以下操作可以使能对备份寄存器和 RTC 的访问。
1. 通过设置寄存器 RCC_APB1ENR 的 PWREN 和 BKPEN 位来打开电源和后备接口的时钟。
2.电源控制寄存器(PWR_CR)的 DBP 位来使能对后备寄存器和 RTC 的访问。

使用特权

评论回复
6
aizaixiyuanqian|  楼主 | 2017-12-28 21:57 | 只看该作者
CRTC 时钟的操作
1.  打开相应的时钟
操作 RTC,就要操作备份寄存器,需要打开一个是备份区域时钟。而一般操作 RTC 的话还会用到一个时钟, 就是电源时钟, 在电源控制里面有操作 RTC 的一些设置,所以我们还有将电源控制的时钟打开。所以代码为:
/* 使能 PWR 电源时钟和 BKP 备份区域外设时钟 */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);//打开电源时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP,ENABLE);//打开存储器时钟

使用特权

评论回复
7
aizaixiyuanqian|  楼主 | 2017-12-28 21:58 | 只看该作者
2.使能备份寄存器操作
可以使用 PWR_BackupAccessCmd()函数(从开头的 PWR 我们就知道这个设置是在电源控制部分设置的,所以我们要打开电源控制的时钟) 。
它只有一个参数,也就是设置的状态,我们要使能所以设为: ENABLE。

使用特权

评论回复
8
aizaixiyuanqian|  楼主 | 2017-12-28 21:58 | 只看该作者
3.复位备份寄存器
当然这个操作不要每次都执行,因为备份区域的复位将导致之前存在的数据丢失,所以要不要复位,要看情况而定。
可以使用 BKP_DeInit()函数。

使用特权

评论回复
9
aizaixiyuanqian|  楼主 | 2017-12-28 22:07 | 只看该作者
4.设置外部低速时钟
我们要使用外部的低速时钟来控制 RTC,代码为:RCC_LSEConfig(RCC_LSE_ON); //设置外部低速晶振(LSE),使用外设低速晶振。在开启外部低速时钟的时候,我们还要确定它是否成功起振,之后才能够接着操作,所以我们还要检测,外部低速时钟是否开启。代码为:
/* 检查指定的 RCC 标志位设置与否,等待低速晶振(LSE)就绪 */
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);等待它起振好了之后将它作为 RTC 的时钟,代码为:
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //设置 RTC 时钟(RTCCLK),选择 LSE 作为 RTC 时钟

使用特权

评论回复
10
aizaixiyuanqian|  楼主 | 2017-12-28 22:08 | 只看该作者
5.使能 RTC  时钟打开 RTC 时钟,代码为:RCC_RTCCLKCmd(ENABLE); //使能 RTC 时钟在对 RTC 操作的时候,注意连续操作的时候还要检测它是否执行完成,才能够接着对起进行操作,所以操作完之后再检测是否操作完成。代码为:RTC_WaitForLastTask(); //等待最近一次对 RTC 寄存器的写操作完成。

使用特权

评论回复
11
aizaixiyuanqian|  楼主 | 2017-12-28 22:10 | 只看该作者
6. 等待 RTC 时钟寄存器同步
代码为:RTC_WaitForSynchro(); //等待 RTC 寄存器同步
7.开启秒中断
我们要读取时钟秒更新一次,所以我们开启秒中断。
代码为RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能 RTC 秒中断然后等待操作完成。
RTC_WaitForLastTask(); //等待最近一次对 RTC 寄存器的写操作完

使用特权

评论回复
12
aizaixiyuanqian|  楼主 | 2017-12-28 22:12 | 只看该作者
8.然后设置 RTC  时钟的预分频
我们要进行 32767 分频。所以代码为: RTC_SetPrescaler(32767); //设置 RTC 预分频的值然后等待操作完成: RTC_WaitForLastTask(); //等待最近一次对 RTC 寄存器的写操作完成。

使用特权

评论回复
13
aizaixiyuanqian|  楼主 | 2017-12-28 22:17 | 只看该作者
9.设置初始化时间
也就是要初始化的时钟存入到 32 位寄存器,这里原理就是,我们直接将对应的时间数据转换为 16 进制数写入到 32 位寄存器中,每当这个 32 位寄存器加 1,那么比 0 时 0分 0 秒,多了一秒就是 0 时 0 分 1 秒。因此到时候我们直接读取寄存器值即可。

使用特权

评论回复
14
aizaixiyuanqian|  楼主 | 2017-12-28 22:19 | 只看该作者
RTC 初始化程序如下:
void rtc_init()
{
NVIC_InitTypeDef NVIC_InitStructure; //中断结构体定义
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);//打开电源时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP,ENABLE);//打开存储器时钟
PWR_BackupAccessCmd(ENABLE);//使能或者失能 RTC 和后备寄存器访问
BKP_DeInit();//将外设 BKP 的全部寄存器重设为缺省值
RCC_LSEConfig(RCC_LSE_ON);//设置外部低速晶振(LSE)
while(RCC_GetFlagStatus(RCC_FLAG_LSERDY)==RESET);//检查指定的 RCC 标志位设置与否
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);//设置 RTC 时钟(RTCCLK)
RCC_RTCCLKCmd(ENABLE);//使能或者失能 RTC 时钟
RTC_WaitForSynchro(); //等待 RTC 寄存器同步
RTC_WaitForLastTask();//等待最近一次对 RTC 寄存器的写操作完成
RTC_ITConfig(RTC_IT_SEC,ENABLE);//使能或者失能指定的 RTC 中断
RTC_WaitForLastTask();//等待最近一次对 RTC 寄存器的写操作完成
RTC_SetPrescaler(32767);//设置预分频 使用外部晶振为 32.768K,要想 1s 中断则预分频数设置为 32767,系统会在此数字基础上加 1
RTC_WaitForLastTask();//等待最近一次对 RTC 寄存器的写操作完成
/* 设置 NVIC 参数 */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn; //打开 RTC 的全局中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级为 0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //响应优先级为 1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能
NVIC_Init(&NVIC_InitStructure);
}

使用特权

评论回复
15
aizaixiyuanqian|  楼主 | 2017-12-28 22:19 | 只看该作者
RTC  时间写入初始化
void clockinit_RTC()
{
if(BKP_ReadBackupRegister(BKP_DR1)!=0XA5A5)//从指定的后备寄存器中读出数据
{
rtc_init();
RTC_WaitForLastTask();//等待最近一次对 RTC 寄存器的写操作完成
RTC_SetCounter(0x9f92);//设置 RTC 计数器的值 11:20:50
RTC_WaitForLastTask();
BKP_WriteBackupRegister(BKP_DR1,0xA5A5);
}
else
{
RTC_WaitForSynchro();//等待 RTC 寄存器同步
RTC_WaitForLastTask();//等待写 RTC 寄存器完成
RTC_ITConfig(RTC_IT_SEC,ENABLE);//使能 RTC 秒中断
RTC_WaitForLastTask();//等待写 RTC 寄存器完成
}
RCC_ClearFlag();//清除复位标志;
}

使用特权

评论回复
16
aizaixiyuanqian|  楼主 | 2017-12-28 22:20 | 只看该作者
RTC 中断函数:
void RTC_IRQHandler() //RTC 实时时钟
{
if(RTC_GetITStatus((RTC_IT_SEC))!=RESET)
{
RTC_ClearITPendingBit(RTC_IT_SEC);
flag=1;
}
}

使用特权

评论回复
17
aizaixiyuanqian|  楼主 | 2017-12-28 22:22 | 只看该作者
主函数
int main()
{
u16 hour=0,min=0,sec=0;u32 time=0;
rtc_init(); //RTC 配置初始化
clockinit_RTC(); //时间初始化
printf_init(); //printf 初始化
while(1)
{
if(flag==1)  //时钟秒中断
{
flag=0;  //清零
time=RTC_GetCounter();//获取 RTC 计数器的值
hour=time/3600; //以秒为单位计算时间
min=(time%3600)/60;
sec=time%60;
printf("RTC time is: %0.2d:%0.2d:%0.2d\r\n",hour,min,sec);
}
}
}

使用特权

评论回复
18
aizaixiyuanqian|  楼主 | 2017-12-28 22:22 | 只看该作者
好了,今天的试验就到这里,感谢大家的关注。

使用特权

评论回复
19
mmuuss586| | 2017-12-28 22:51 | 只看该作者
感谢分享;

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

62

主题

1353

帖子

6

粉丝