RTC实时时钟(STM32)

Cortex-M4-实时时钟

实时时钟概述

实时时钟介绍

英文缩写:RTC。显示年、月、日、时、分、秒、星期,自动计算闰年,能够区分每个月的天数。
RTC特点:能从RTC获取到具体的日期时间,断掉后再开机时间仍然准确。
RTC模块分为两种,一种集成在芯片内部,另外一种是外接RTC芯片。
芯片集成:
1.外设、模块功能
集成→直接用内部寄存器/寄存器配置
没有集成→外接模块
2.协议
集成
(USART,IIC,SPI…)直接用芯片内部控制器进行控制

没有集成
1.IO模拟。
2.芯片内有没有读写时序一致的协议
例: 8080协议→驱动屏幕,内部没有集成8080
FSMC:静态存储器,读写时序与8080一致
用FSMC模拟8080驱动屏幕

STM32内部实时时钟介绍

BCD:二进制的十进制码
BIN:二进制
HEX:十六进制
BCD码表示时间:
15:39
0001 0101:0011 1001
  实时时钟 (RTC) 是一个独立的 BCD 定时器/计数器。 RTC 提供一个日历时钟、两个可编程闹钟中断,以及一个具有中断功能的周期性可编程唤醒标志。 RTC 还包含用于管理低功耗模式的自动唤醒单元。
  两个 32 位寄存器包含二进码十进数格式 (BCD) 的秒、分钟、小时( 12 或 24 小时制)、星期几、日期、月份和年份。此外,还可提供二进制格式的亚秒值。
  系统可以自动将月份的天数补偿为 28、 29(闰年)、 30 和 31 天。并且还可以进行夏令时(在夏季的某一天会在凌晨时分跳过一小时,而后会在冬季补回来)补偿。
  其它 32 位寄存器还包含可编程的闹钟亚秒、 秒、分钟、小时、星期几和日期。此外,还可以使用数字校准功能对晶振精度的偏差进行补偿。
  上电复位后,所有 RTC 寄存器都会受到保护,以防止可能的非正常写访问。
  无论器件状态如何(运行模式、低功耗模式或处于复位状态),只要电源电压保持在工作范围内, RTC 便不会停止工作

STM32内部实时时钟特点

RTC实时时钟(STM32)_第1张图片

RTC的电源部分
RTC实时时钟(STM32)_第2张图片
LSI,LSE,HSE 都可提供时钟给RTC,但对于某些器件来说,可能没有外部晶振提供时钟,所以最好使用内部低速时钟提供时钟源。
RTC实时时钟(STM32)_第3张图片

RTC实时时钟(STM32)_第4张图片
当主电源 VDD 断电时,可通过 VBAT (纽扣电池)电压为实时时钟 (RTC)、 RTC 备份寄存器和备份 SRAM(BKP SRAM) 供电。
访问RTC&RTC备份域数据、需要按照以下步骤

  1. 将 RCC_APB1ENR 寄存器中的 PWREN 位置 2.使能电源接口时钟
  2. 电源控制器中的PWR_CR的DBP位值1(使能对备份域的访问)
  3. 选择RTC时钟源(LSI LSE HSE)
    1.配置RCC_BDCR中的RTCSEL[1:0]、选择时钟源
    2.如果选择了HSE、需要配置RCC_CFGR中的RTCPRE[4:0]位进行分频
  4. 对RCC_BDCR中的RTCCEN[15]位进行编程、使能RTC时钟

STM32内部实时时钟框架

RTC实时时钟(STM32)_第5张图片
RTC基本日历功能框架分析
基本日历功能主要就是让日历模块正常工作(1秒1秒地计数),我们从日历时间读取出当前实时时间日期。也就是说,日历的工作频率就是1HZ。

RTC实时时钟(STM32)_第6张图片
RTC寄存器写保护
上电复位后、所有的RTC寄存器受到写保护、取消写保护必须按照以下顺序将对应的密钥写入关键字寄存器(RTC_WPR)
1)0xCA写入RTC_WPR
2) 0x53写入 RTC_WPR
RTC进入初始化模式(设置日历寄存器要注意)
要编程包括时间格式和预分频器配置在内的初始时间和日期日历值,需按照以下顺序操作:

  1. 将 RTC_ISR 寄存器中的 INIT 位置 1 以进入初始化模式。在此模式下,日历计数器将停止工作并且其值可更新。
  2. 轮询 RTC_ISR 寄存器中的INITF 位。当 INITF置1时进入初始化阶段模式。大约需要2个RTCCLK 时钟周期(由于时钟同步)。
  3. 要为日历计数器生成 1 Hz 时钟,应首先编程 RTC_PRER 寄存器中的同步预分频系数,然后编程异步预分频系数。即使只需要更改这两个字段中之一,也必须对 RTC_PRER寄存器执行两次单独的写访问。
  4. 在影子寄存器( RTC_TR 和 RTC_DR)中加载初始时间和日期值,然后通过 RTC_CR寄存器中的 FMT 位配置时间格式( 12 或 24 小时制)。
  5. 通过清零 INIT 位退出初始化模式。随后,自动加载实际日历计数器值,在 4 个 RTCCLK时钟周期后重新开始计数。

RTC同步(读取日历值要注意)
  每次将日历寄存器中的值复制到RTC_SSR、RTC_TR和RTC_DR影子寄存器时,RTC_ISR寄存器中的 RSF 位都会置1(日历影子寄存器已同步 )。每两个 RTCCLK 周期执行一次复制。为确保这 3 个值来自同一时刻点,读取 RTC_SSR 或 RTC_TR 时会锁定高阶日历影子寄存器中的值,直到读取RTC_DR。为避免软件对日历执行读访问的时间间隔小于 2 个 RTCCLK 周期:第一次读取日历之后必须通过软件将 RSF 清零,并且软件必须等待到 RSF 置 1 之后才可再次读取RTC_SSR、 RTC_TR 和 RTC_DR 寄存器。

/*
函 数 名:Rtc_Config
函数功能:实时时钟RTC初始化
返 回 值:无
形    参:无
备    注:
*/
void Rtc_Config(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);		//打开电源接口时钟
	PWR_BackupAccessCmd(ENABLE);													//启动寄存器访问
	RCC_LSICmd(ENABLE);																		//打开LSI时钟
//	RCC_LSEConfig(RCC_LSE_ON);														//打开LSE时钟
	RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);								//选择LSI时钟为RTC时钟源
	RCC_RTCCLKCmd(ENABLE);																//RTC时钟使能
	RTC_WriteProtectionCmd(DISABLE);											//取消写保护			
	
	RTC_EnterInitMode();
	RTC_InitTypeDef rtc_InitTypeDef;											//初始化结构体
	rtc_InitTypeDef.RTC_AsynchPrediv = 0x7f;								//异步分频
	rtc_InitTypeDef.RTC_HourFormat = RTC_HourFormat_24;		//24小时格式
	rtc_InitTypeDef.RTC_SynchPrediv = 0xff;								//同步分频
	RTC_Init(&rtc_InitTypeDef);														//RTC初始化
	
	Rtc_Set_Data(22,9,2,5);
	Rtc_Set_Time(RTC_H12_PM,17,0,0);

	RTC_ExitInitMode();
}

/*
函 数 名:Rtc_Set_Time
函数功能:设置初始时间
返 回 值:ErrorStatus 设置成功&失败的标志
形    参:u8 h12,u8 hour,u8 min,u8 sec
备    注:
*/
ErrorStatus Rtc_Set_Time(u8 h12,u8 hour,u8 min,u8 sec)
{
	RTC_TimeTypeDef rtc_TimeTypeDef;											//时间结构体
	rtc_TimeTypeDef.RTC_H12 = h12;												//AM 或 24 小时制 
	rtc_TimeTypeDef.RTC_Hours = hour;											//小时
	rtc_TimeTypeDef.RTC_Minutes = min;										//分钟
	rtc_TimeTypeDef.RTC_Seconds = sec;										//秒
	return RTC_SetTime(RTC_Format_BIN,&rtc_TimeTypeDef);	//BIN格式,时间初始化 
}

/*
函 数 名:Rtc_Set_Data
函数功能:设置初始日期
返 回 值:ErrorStatus 设置成功&失败的标志
形    参:u8 year,u8 month,u8 date,u8 weekday
备    注:
*/
ErrorStatus Rtc_Set_Data(u8 year,u8 month,u8 date,u8 weekday)
{
	RTC_DateTypeDef rtc_DateTypeDef;												//日期结构体
	rtc_DateTypeDef.RTC_Year = year;														//年
	rtc_DateTypeDef.RTC_Month = month;														//月
	rtc_DateTypeDef.RTC_Date = date;														//日
	rtc_DateTypeDef.RTC_WeekDay = weekday;													//星期
	return RTC_SetDate(RTC_Format_BIN,&rtc_DateTypeDef);						//BIN格式,日期初始化
}

/*
函 数 名:RTC_print
函数功能:时间打印
返 回 值:无
形    参:无
备    注:
*/
void RTC_print(void)
{
	RTC_TimeTypeDef RTC_Time;
	RTC_DateTypeDef RTC_Data;
	
	RTC_GetTime(RTC_Format_BIN,&RTC_Time);
	RTC_GetDate(RTC_Format_BIN,&RTC_Data);
		
	printf("20%d年%d月%d日星期%d ",RTC_Data.RTC_Year,RTC_Data.RTC_Month,RTC_Data.RTC_Date,RTC_Data.RTC_WeekDay);
	printf("%d:%d:%d\r\n",RTC_Time.RTC_Hours,RTC_Time.RTC_Minutes,RTC_Time.RTC_Seconds);
}

RTC可编程闹钟

可编程闹钟介绍

  RTC 单元提供两个可编程闹钟,即闹钟 A 和闹钟 B。
  可通过将RTC_CR寄存器中的ALRAE和ALRBE位置1来使能可编程闹钟功能。如果日历亚秒、秒、分钟、小时、日期或日分别与闹钟寄存器 RTC_ALRMASSR/RTC_ALRMAR和RTC_ALRMBSSR/RTC_ALRMBR 中编程的值相匹配ALRAF 和 ALRBF 标志会被置为 1可通过RTC_ALRMAR 和 RTC_ALRMBR 寄存器的 MSKx 位以及 RTC_ALRMASSR和 RTC_ALRMBSSR 寄存器的 MASKSSx 位单独选择各日历字段。可通过 RTC_CR 寄存器中 的 ALRAIE 和 ALRBIE 位使能闹钟中断
  闹钟 A 和闹钟 B(如果已通过 RTC_CR 寄存器中的位 OSEL[0:1] 使能)可连接到 RTC_ALARM 输出。可通过 RTC_CR 寄存器的 POL 位配置 RTC_ALARM 极性。

编程闹钟

要对可编程的闹钟(闹钟 A 或闹钟 B)进行编程或更新,必须执行类似的步骤:

  1. 将 RTC_CR 寄存器中的 ALRAE 或 ALRBE 位清零以禁止闹钟 A 或闹钟 B。
  2. 轮询 RTC_ISR 寄存器中的 ALRAWF 或 ALRBWF 位,直到其中一个置 1,以确保闹钟
    寄存器可以访问。大约需要 2 个 RTCCLK 时钟周期(由于时钟同步)。
  3. 编程闹钟 A 或闹钟 B 寄存器(RTC_ALRMASSR/RTC_ALRMAR 或 RTC_ALRMBSSR/
    RTC_ALRMBR)。
  4. 将 RTC_CR 寄存器中的 ALRAE 或 ALRBE 位置 1 以再次使能闹钟 A 或闹钟 B。

闹钟中断

RTC实时时钟(STM32)_第7张图片

u8 alarm_flag;
/*
函 数 名:ALarm_Config
函数功能:RTC闹钟初始化
返 回 值:无
形    参:无
备    注:
*/
void ALarm_Config(u32 week)
{
	RTC_AlarmCmd(RTC_Alarm_A,DISABLE);		//关闭闹钟A
	
	RTC_AlarmTypeDef rtc_AlarmTypeDef;
	rtc_AlarmTypeDef.RTC_AlarmDateWeekDay = week;									
	rtc_AlarmTypeDef.RTC_AlarmDateWeekDaySel = RTC_AlarmDateWeekDaySel_WeekDay;		//每固定星期报警
	rtc_AlarmTypeDef.RTC_AlarmMask = RTC_AlarmMask_None;							//与日历时间完全匹配					
	rtc_AlarmTypeDef.RTC_AlarmTime = Rtc_alarmA_time(RTC_H12_PM,17,0,10);
	RTC_SetAlarm(RTC_Format_BIN,RTC_Alarm_A,&rtc_AlarmTypeDef);
	
	EXTI_InitTypeDef EXTI17_InitTypeDef;
	EXTI17_InitTypeDef.EXTI_Line = EXTI_Line17;								//外部中断线17
	EXTI17_InitTypeDef.EXTI_LineCmd = ENABLE;								//外部中断使能
	EXTI17_InitTypeDef.EXTI_Mode = EXTI_Mode_Interrupt;						//中断寄存器模式
	EXTI17_InitTypeDef.EXTI_Trigger = EXTI_Trigger_Rising;					//上升沿触发
	EXTI_Init(&EXTI17_InitTypeDef);
	
	RTC_ITConfig(RTC_IT_ALRA,ENABLE);										//打开ALARMA中断
	NVIC_InitTypeDef nvic_InitTypeDef;										//中断初始化
	nvic_InitTypeDef.NVIC_IRQChannel 					= RTC_Alarm_IRQn;	//中断号
	nvic_InitTypeDef.NVIC_IRQChannelPreemptionPriority 	= 0;				//占先优先级
  	nvic_InitTypeDef.NVIC_IRQChannelSubPriority 		= 2;				//次级优先级
	nvic_InitTypeDef.NVIC_IRQChannelCmd 				= ENABLE;			//NVIC中断响应
	NVIC_Init(&nvic_InitTypeDef);											//初始化NVIC中断
	
	RTC_AlarmCmd(RTC_Alarm_A,ENABLE);										//闹钟使能
}

/*
函 数 名:Rtc_alarmA_time
函数功能:设置闹钟时间
返 回 值:RTC_TimeTypeDef 闹钟时间结构体
形    参:u8 h12,u8 hour,u8 min,u8 sec,RTC_TimeTypeDef* RTC_TimeStruct
备    注:
*/

RTC_TimeTypeDef Rtc_alarmA_time(u8 h12,u8 hour,u8 min,u8 sec)
{
	RTC_TimeTypeDef Alarm_Time;
	Alarm_Time.RTC_H12 = h12;
	Alarm_Time.RTC_Hours = hour;
	Alarm_Time.RTC_Minutes = min;
	Alarm_Time.RTC_Seconds = sec;
	return Alarm_Time;
}

/*
函 数 名:RTC_Alarm_IRQHandler
函数功能:RTC闹钟中断服务函数
返 回 值:无
形    参:无
备    注:
*/
void RTC_Alarm_IRQHandler(void)
{
	if(RTC_GetFlagStatus(RTC_FLAG_ALRAF))
	{
		RTC_ClearFlag(RTC_FLAG_ALRAF);
		alarm_flag = 1;
	}
	EXTI->PR |= (0x01 << 17);				//挂起寄存器写1清零
}

你可能感兴趣的:(Crotex-M4笔记,stm32,单片机)