目录
一、介绍
1.简介
二、RTC原理和代码详解:
第一步设置RTC.
1前提:
2时钟
第二步APB1接口
1.预分频模块
2可编程计数器模块
第三步:主函数
实时时钟是一个独立的定时器,可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。
在系统复位或从待机模式唤醒后,RTC的设置和时间维持不变。
框图代码分析:
CPU和RTC独立。我们要通过APB1操纵RTC。
后备域前期配置
RTC可提供时钟日历的功能,我们每次复位如果它都重新计时显然不符合实际需要。所以stm32芯片将它和cpu独立。内部加上一个存储器外部附上一个独立电源。使其成为一个真正的计时工具。
所以当我们使用RTC时我们需要先打开独立供电区(PWR)和后备域(存储器位于后备域)(BKP)
然后设置后备域允许访问。
让后备域复位。
数据手册相关叙述:
系统复位后,对后备寄存器和RTC的访问被禁止,这是为了防止对后备区域(BKP)的意外写操
作。执行以下操作将使能对后备寄存器和RTC的访问:
● 设置寄存器RCC_APB1ENR的PWREN和BKPEN位,使能电源和后备接口时钟
● 设置寄存器PWR_CR的DBP位,使能对后备寄存器和RTC的访问。
TC_PRL、RTC_ALR、RTC_CNT和RTC_DIV寄存器仅能通过备份域复位信号复位
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能PWR和BKP外设时钟
PWR_BackupAccessCmd(ENABLE); //使能后备寄存器访问
BKP_DeInit(); //复位备份区域 (让其有个初始值)
PCLK1:时钟来源APB1最大36MHz。HSE时钟除以128
RTCCLK:LSI振荡器时钟(40kHz)或LSE振荡器时钟(32.768kHz)驱动
代码1:
VDD掉电的情况下,需要尽量保持时钟来源不会会受到影响,所以一般选择LSE振荡器时钟。
先设置外部低速时钟。
设置完毕,我们要确保LSE振荡器时钟正常。通过while循环检查相关标志位。
选择LSE,使能。
RCC_LSEConfig(RCC_LSE_ON); //设置外部低速晶振(LSE)
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET&&temp<250) //检查指定的RCC标志位设置否,等待低速晶振就绪
{
temp++;
delay_ms(10);
}
if(temp>=250)return 1;//初始化时钟失败,晶振有问题
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //设置RTC时钟(RTCCLK),选择LSE作为RTC时//(RCC_RTCCLKSource_LSI 和 RCC_RTCCLKSource_HSE_Div128)
RCC_RTCCLKCmd(ENABLE); //使能RTC时钟
RTC设置完后,我们终于可以通过过APB1接口控制RTC了
APB1和RTC时钟同步才能数据交换,如果最近一次写操作持续等待它完成。
同时数据手册也规定了配置寄存器步骤
1. 查询RTOFF位,直到RTOFF的值变为’1’
2. 置CNF值为1,进入配置模式
3. 对一个或多个RTC寄存器进行写操作
4. 清除CNF标志位,退出配置模式
5. 查询RTOFF,直至RTOFF位变为’1’以确认写操作已经完成
RTC_WaitForSynchro(); //等待RTC寄存器同步
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
它可编程产生最长为1秒的RTC时间基准TR_CLK.
RTC的预分频模块包含了一个20位的可编程分频器(RTC预分频器)。如果在RTC_CR寄存器中设置了相应的允许位,则在每个实时时钟(RTC)TR_CLK周期中RTC产生一个中断(秒中断)
从图上得,TR_CLK这条线产生了一个中断
于是乎,我们通过APB1写第一条语句使能这个中断,并等待它写完
预分频器我们也设置一下(1s)这是第二个语句同样等待它写完。
同理再写个时间初始值。
代码:
RTC_EnterConfigMode();/// 允许配置
RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能RTC秒中断,写操作,断电不保存
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
RTC_SetPrescaler(32767); //设置RTC预分频的值
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
RTC_Set(2021,1,14,17,42,55); //这个函数得自己写
BKP_WriteBackupRegister(BKP_DR1, 0X5050);
RTC_ExitConfigMode(); //退出配置模式
我们需要想一个问题,这样设置断不断电不久没区别了。所以我们要通过一些代码让他重复进入有区别。
代码:
if (BKP_ReadBackupRegister(BKP_DR1) != 0x5050)
{
BKP_WriteBackupRegister(BKP_DR1, 0X5050);
}
根据手册得秒中断使能是不保存到独立存储器的,换而言之,我们需要重新配置秒中断。
代码:
else//系统继续计时
{
RTC_WaitForSynchro(); //等待最近一次对RTC寄存器的写操作完成
RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能RTC秒中断
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
}
RTC_NVIC_Config();
RCC_ClearFlag();//清除复位标志
由于我们用到了中断我们自然需要配置NVIC ,以及中断函数
代码:
static void RTC_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn; //RTC全局中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级1位,从优先级3位
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //先占优先级0位,从优先级4位
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能该通道中断
NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
}
void RTC_IRQHandler(void)
{
if (RTC_GetITStatus(RTC_IT_SEC) != RESET)//秒钟中断
{
RTC_ClearITPendingBit(RTC_IT_SEC|RTC_IT_OW); //清闹钟中断
time_bz=1;//用个变量记录一下
}
}
是一个32位的可编程计数器,可被初始化为当前的系统时间。系统时间按TR_CLK周期累加并与存储在RTC_ALR寄存器中的可编程时间相比较,如果RTC_CR控制寄存器中设置了相应允许位,比较匹配时将产生一个闹钟中断。
我们前面设置的预分频模块产生的总时间累计在这
代码:
RTC_GetCounter();
主函数:
while(tim_bz==1)
{
tim_bz=0;
(u32)a=RTC_GetCounter();
h=a/3600;
min=(a%3600)/60;
s=(a%60)/60;
printf();
}