STM32 Cortex-M4 RTC实时时钟保姆级别总结

22 篇文章 1 订阅
15 篇文章 1 订阅

一、概述

实时时钟的缩写是RTC,RTC是集成电路,通常称为时钟芯片。RTC通常情况下需要外接32.768kHz晶体,匹配电容、备份电源等元件。

作用:经常用来做时间的计算和显示来源。使用定时器,通过定时一秒钟也可以达到这个时间的计算和显示,定时器程序复位会回复到默认的数值。

RTC的优点是:只要优点,时间就会一直的走下去,就算程序复位,时间也不会重置,有纽扣电池,就算单片机没电,时间也会一直计算下去,就算没有纽扣电池,拔掉单片机所有的输入电源,RTC才会停止。

二、 特性

包含亚秒、秒、分钟、小时( 12/24 小时制)、星期几、日期、月份和年份的日历。软件可编程的夏令时补偿。

两个具有中断功能的可编程闹钟。可通过任意日历字段的组合驱动闹钟。
● 自动唤醒单元,可周期性地生成标志以触发自动唤醒中断。
● 参考时钟检测:可使用更加精确的第二时钟源(50 Hz 或 60 Hz)来提高日历的精确度。
● 利用亚秒级移位特性与外部时钟实现精确同步。
● 可屏蔽中断/事件:
— 闹钟 A
— 闹钟 B
— 唤醒中断
— 时间戳
— 入侵检测
● 数字校准电路(周期性计数器调整)
— 精度为 5 ppm
— 精度为 0.95 ppm,在数秒钟的校准窗口中获得
● 用于事件保存的时间戳功能( 1 个事件)
● 入侵检测:
— 2 个带可配置过滤器和内部上拉的入侵事件
● 20 个备份寄存器( 80 字节)。发生入侵检测时间时,可以复位备份寄存器。

三、RTC的时钟

   LSE:外部低速时钟

 

初始化配置

RTC寄存器是一个32位的寄出去你,除了当BYPSHAD=0时对日历影子寄存器执行的读访问之外,APB接口会在访问RTC寄存器时引入2个等待周期。

 日历配置

  系统复位后,应用可读取 RTC_ISR 寄存器中的 INITS 标志,以检查日历是否已初始化。如 
果该标志为 0,表明日历尚未初始化,因为年份字段设置为其上电复位时的默认值 (0x00)。
要在初始化之后读取日历,必须首先用软件检查 RTC_ISR 寄存器的 RSF 标志是否置 1。

复位RTC

 

 同步RTC

  初始化平移操作前,用户必须检查确认 SS[15] = 0,以确保不会发生上溢。对 RTC_SHIFTR 寄存器执行写操作以启动平移操作时,硬件会将 SHPF 标志置 1 以指示平 移操作挂起。完成平移操作时,硬件会将将该位清零。该同步功能与参考时钟检测功能不兼容:当 REFCKON=1 时,固件不能对 RTC_SHIFTR 执 行写操作。

RTC初始化步骤

1、选择时钟源

 在时钟树开启相应的时钟,顺序如下:

 

这一步的代码

void rtc_init()
{

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);//开电源
	PWR_BackupAccessCmd(ENABLE ); //使能后备寄存器访问
}

打开LSE后,需要进行精密校准,需要精密校准RTC的LSE时钟。

使能RTC时钟,RCC文件里面专门为RTC提供时钟函数 ;

 

 2、使能PWR时钟

使能PWR的电源接口时钟,

 想要激活RTC的写保护,那么必须显示使能PWR电源控制寄存器,因为这个控制器控制这RTC的写保护。

接下来,就要进行对备份区的使能操作,

 

3、使能后备区寄存器访问

后备区和RTC内存是一起,所以后备区也需要使能,作用:第一次初始化完后,写入一个值,用于记录已经初始化过RTC的时间了,系统重启或者复位,我们需要读取这个备份区的数据,判断是否需要重新设置初始化时间。

 

4、初始化RTC结构体

在设置时间格式、预分频、初始化时间和日期,需要先进入初始化模式,时间和日期的值才可以更新。

rtc.c

void rtc_init()
{
	RTC_InitTypeDef RTC_InitStructure;
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);//开电源
	PWR_BackupAccessCmd(ENABLE ); //使能后备寄存器访问
	if(RTC_ReadBackupRegister(RTC_BKP_DR0)!=0X11)
	{
		RCC_LSEConfig(RCC_LSE_ON);//LSE开启
		while(!RCC_GetFlagStatus(RCC_FLAG_LSERDY));//等待lse稳定
		RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);//设置LSE为RTC时钟
		RCC_RTCCLKCmd(ENABLE);
		rtc_SetTimeData(11,22,54,22,4,26,2);
		RTC_WriteBackupRegister(RTC_BKP_DR0,0X11);
	}
	 RTC_Set_AlarmA(2,11,38,20);
	 RTC_Set_AlarmB(2,16,21,0);
}

5、设置时间

	RTC_TimeTypeInitStructure.RTC_Hours=hour;
	RTC_TimeTypeInitStructure.RTC_Minutes=min;
	RTC_TimeTypeInitStructure.RTC_Seconds=sec;
	RTC_TimeTypeInitStructure.RTC_H12=RTC_H12_AM;

 6、时钟日期

现在相应的功能文件stm32f407x.rtc.h找到相应的时间和日期结构体函数;

 设置日期

	RTC_DateTypeInitStructure.RTC_Date=date;
	RTC_DateTypeInitStructure.RTC_Month=month;
	RTC_DateTypeInitStructure.RTC_WeekDay=week;
	RTC_DateTypeInitStructure.RTC_Year=year;

 7、备份区读写,用于判断是否初始化过时间

void rtc_init()
{
	RTC_InitTypeDef RTC_InitStructure;
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);//开电源
	PWR_BackupAccessCmd(ENABLE ); //使能后备寄存器访问
	if(RTC_ReadBackupRegister(RTC_BKP_DR0)!=0X11)
	{
		RCC_LSEConfig(RCC_LSE_ON);//LSE开启
		while(!RCC_GetFlagStatus(RCC_FLAG_LSERDY));//等待lse稳定
		RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);//设置LSE为RTC时钟
		RCC_RTCCLKCmd(ENABLE);
		rtc_SetTimeData(11,22,54,22,4,26,2);
		RTC_WriteBackupRegister(RTC_BKP_DR0,0X11);
	}
	 RTC_Set_AlarmA(2,11,38,20);
	 RTC_Set_AlarmB(2,16,21,0);
}

 完整代码

rtc.c

#include "rtc.h" 
#include "beep.h"
#include "lcd.h"  
#include "stdio.h"

u32 rct_AlarmA_flag;
RTC_TimeTypeDef RTC_TimeTypeInitStructure;
RTC_DateTypeDef RTC_DateTypeInitStructure;
RTC_AlarmTypeDef RTC_AlarmStruct;
//RTC时间设置函数
//hour,min,sec:时,分,秒设定值
//ampm:@RTC_AM_PM_Definitions:RTC_H12_AM/RTC_H12_PM
//返回值:SUCEE(1),成功;ERROR(0),进入初始化模式失败 

void rtc_SetTimeData(u8 hour,u8 min,u8 sec,u8 year,
	u8 month,u8 date,u8 week)
{
	RTC_TimeTypeInitStructure.RTC_Hours=hour;
	RTC_TimeTypeInitStructure.RTC_Minutes=min;
	RTC_TimeTypeInitStructure.RTC_Seconds=sec;
	RTC_TimeTypeInitStructure.RTC_H12=RTC_H12_AM;
	
  RTC_SetTime(RTC_Format_BIN,&RTC_TimeTypeInitStructure);
    
	RTC_DateTypeInitStructure.RTC_Date=date;
	RTC_DateTypeInitStructure.RTC_Month=month;
	RTC_DateTypeInitStructure.RTC_WeekDay=week;
	RTC_DateTypeInitStructure.RTC_Year=year;
    
 RTC_SetDate(RTC_Format_BIN,&RTC_DateTypeInitStructure);
}

void RTC_GetTimeDate(void)
{
	 static u8 temp=0;
	 RTC_GetTime(RTC_Format_BIN,&RTC_TimeTypeInitStructure);
	 RTC_GetDate(RTC_Format_BIN,&RTC_DateTypeInitStructure);
	if(temp !=RTC_TimeTypeInitStructure.RTC_Seconds)
	{
		temp=RTC_TimeTypeInitStructure.RTC_Seconds;
		printf("%d:%d:%d\r\n",RTC_TimeTypeInitStructure.RTC_Hours,RTC_TimeTypeInitStructure.RTC_Minutes,RTC_TimeTypeInitStructure.RTC_Seconds);
	}
	
}

void RTC_Set_AlarmA(u8 week,u8 hour,u8 min,u8 sec)
{
	NVIC_InitTypeDef NVIC_InitStruct;
	EXTI_InitTypeDef EXTI_InitStruct;
	RTC_AlarmCmd(RTC_Alarm_A, DISABLE);//关闭闹钟A
	
	RTC_TimeTypeInitStructure.RTC_Hours=hour;
	RTC_TimeTypeInitStructure.RTC_Minutes=min;
	RTC_TimeTypeInitStructure.RTC_Seconds=sec;
	RTC_TimeTypeInitStructure.RTC_H12=RTC_H12_AM;
	
	RTC_AlarmStruct.RTC_AlarmDateWeekDay=week;
	RTC_AlarmStruct.RTC_AlarmDateWeekDaySel=RTC_AlarmDateWeekDaySel_WeekDay;
	RTC_AlarmStruct.RTC_AlarmMask=RTC_AlarmMask_None; 
	RTC_AlarmStruct.RTC_AlarmTime=RTC_TimeTypeInitStructure;
	RTC_SetAlarm(RTC_Format_BIN, RTC_Alarm_A,&RTC_AlarmStruct);
	
	
	EXTI_InitStruct.EXTI_Line = EXTI_Line17;//中断线号0
	EXTI_InitStruct.EXTI_LineCmd = ENABLE; //中断线使能
	EXTI_InitStruct.EXTI_Mode   = EXTI_Mode_Interrupt;//中断模式
	EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;//上升沿触发
	EXTI_Init(&EXTI_InitStruct);
	
	NVIC_InitStruct.NVIC_IRQChannel =  RTC_Alarm_IRQn ;//中断源
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;//使能中断
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;//抢占优先级
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;//响应优先级
	NVIC_Init(&NVIC_InitStruct);
	
	RTC_ITConfig( RTC_IT_ALRA, ENABLE);//S使能闹钟A中断
	RTC_AlarmCmd(RTC_Alarm_A, ENABLE);//开启闹钟A
}

void RTC_Alarm_IRQHandler (void)
{
	if(RTC_GetFlagStatus(RTC_FLAG_ALRAF))
	{
		rct_AlarmA_flag=1;
		RTC_ClearITPendingBit(RTC_IT_ALRA);
		EXTI_ClearITPendingBit( EXTI_Line17);
	}
	else if(RTC_GetFlagStatus(RTC_FLAG_ALRBF ))
	{
		RTC_ClearITPendingBit(RTC_IT_ALRB);
		EXTI_ClearITPendingBit( EXTI_Line17);
	}
}

void RTC_Set_AlarmB(u8 week,u8 hour,u8 min,u8 sec)
{
	NVIC_InitTypeDef NVIC_InitStruct;
	EXTI_InitTypeDef EXTI_InitStruct;
	RTC_AlarmCmd(RTC_Alarm_B, DISABLE);//关闭闹钟B
	
	RTC_TimeTypeInitStructure.RTC_Hours=hour;
	RTC_TimeTypeInitStructure.RTC_Minutes=min;
	RTC_TimeTypeInitStructure.RTC_Seconds=sec;
	RTC_TimeTypeInitStructure.RTC_H12=RTC_H12_AM;
	
	RTC_AlarmStruct.RTC_AlarmDateWeekDay=week;
	RTC_AlarmStruct.RTC_AlarmDateWeekDaySel=RTC_AlarmDateWeekDaySel_WeekDay;
	RTC_AlarmStruct.RTC_AlarmMask=RTC_AlarmMask_None; 
	RTC_AlarmStruct.RTC_AlarmTime=RTC_TimeTypeInitStructure;
	RTC_SetAlarm(RTC_Format_BIN, RTC_Alarm_B,&RTC_AlarmStruct);
	
	
	EXTI_InitStruct.EXTI_Line = EXTI_Line17;//中断线号0
	EXTI_InitStruct.EXTI_LineCmd = ENABLE; //中断线使能
	EXTI_InitStruct.EXTI_Mode   = EXTI_Mode_Interrupt;//中断模式
	EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;//上升沿触发
	EXTI_Init(&EXTI_InitStruct);
	
	NVIC_InitStruct.NVIC_IRQChannel =  RTC_Alarm_IRQn ;//中断源
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;//使能中断
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;//抢占优先级
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;//响应优先级
	NVIC_Init(&NVIC_InitStruct);
	
	RTC_ITConfig( RTC_IT_ALRB, ENABLE);//S使能闹钟A中断
	RTC_AlarmCmd(RTC_Alarm_B, ENABLE);//开启闹钟A
}

void Rtc_Lcd_Display(void)
{
	char hour[10];
	char min[10];
	char sec[10];
	 RTC_GetTimeDate();
	sprintf(hour,"%.2d",RTC_TimeTypeInitStructure.RTC_Hours);
	sprintf(min,"%.2d",RTC_TimeTypeInitStructure.RTC_Minutes);
	sprintf(sec,"%.2d",RTC_TimeTypeInitStructure.RTC_Seconds);
	LCD_Show_Str_ChineseorChar(50,70,(u8 *)hour,BLACK,WHITE);
	LCD_Show_Str_ChineseorChar(80,70,(u8 *)min,BLACK,WHITE);
	LCD_Show_Str_ChineseorChar(110,70,(u8 *)sec,BLACK,WHITE);
}

void rtc_init()
{
	RTC_InitTypeDef RTC_InitStructure;
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);//开电源
	PWR_BackupAccessCmd(ENABLE ); //使能后备寄存器访问
	if(RTC_ReadBackupRegister(RTC_BKP_DR0)!=0X11)
	{
		RCC_LSEConfig(RCC_LSE_ON);//LSE开启
		while(!RCC_GetFlagStatus(RCC_FLAG_LSERDY));//等待lse稳定
		RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);//设置LSE为RTC时钟
		RCC_RTCCLKCmd(ENABLE);
		rtc_SetTimeData(11,22,54,22,4,26,2);
		RTC_WriteBackupRegister(RTC_BKP_DR0,0X11);
	}
	 RTC_Set_AlarmA(2,11,38,20);
	 RTC_Set_AlarmB(2,16,21,0);
}


 rtc.h

#ifndef __RTC_DECLS
#define __RTC_DECLS

#include "stm32f4xx.h" 
#include "io_bit.h"


 extern u32 rct_AlarmA_flag;

void beep_init(void);
void rtc_init(void);
void RTC_GetTimeDate(void);

void Rtc_Lcd_Display(void);

#endif

点赞加关注,打赏就更好了!!!

  • 5
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
以下是基于STM32F4xx的RTC实时时钟代码,包括初始化和设置时间的函数: ```c #include "stm32f4xx.h" void RTC_Init(void) { /* 使能PWR和BKP的时钟 */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); /* 解锁BKP区域 */ PWR_BackupAccessCmd(ENABLE); /* 复位BKP区域 */ BKP_DeInit(); /* 使能LSE时钟 */ RCC_LSEConfig(RCC_LSE_ON); while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) { } /* 选择LSE作为RTC时钟源 */ RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); /* 使能RTC时钟 */ RCC_RTCCLKCmd(ENABLE); /* 等待RTC同步 */ RTC_WaitForSynchro(); /* 设置RTC预分频器 */ RTC_SetPrescaler(32767); /* 等待RTC同步 */ RTC_WaitForSynchro(); } void RTC_SetTime(uint8_t hour, uint8_t minute, uint8_t second) { RTC_TimeTypeDef RTC_TimeStructure; /* 等待RTC同步 */ RTC_WaitForSynchro(); /* 设置RTC时间 */ RTC_TimeStructure.RTC_Hours = hour; RTC_TimeStructure.RTC_Minutes = minute; RTC_TimeStructure.RTC_Seconds = second; RTC_TimeStructure.RTC_H12 = RTC_H12_AM; RTC_SetTime(RTC_Format_BIN, &RTC_TimeStructure); /* 等待RTC同步 */ RTC_WaitForSynchro(); } ``` 在主函数中,可以先调用RTC初始化函数,然后再调用RTC设置时间函数,例如: ```c int main(void) { /* 初始化RTC */ RTC_Init(); /* 设置RTC时间为12:34:56 */ RTC_SetTime(12, 34, 56); while (1) { } } ``` 需要注意的是,RTC模块需要连接外部低速晶振(LSE),并且需要在STM32的RCC寄存器中设置LSE作为RTC时钟源。同时,还需要在PWR寄存器中解锁BKP区域,才能够使用RTC模块。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

丘比特惩罚陆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值