4、RTC 实时时钟Demo(STM32F407)

您所在的位置:网站首页 SAMPLE电子 4、RTC 实时时钟Demo(STM32F407)

4、RTC 实时时钟Demo(STM32F407)

2024-07-11 00:45| 来源: 网络整理| 查看: 265

RTC是个独立的BCD定时器/计数器。RTC 提供一个日历时钟,两个可编程闹钟中断,以及一个具有中断功能的周期性可编程唤醒标志。RTC还包含用于管理低功耗模式的自动唤醒单元。

(RTC实质:一个掉电(主电源)后还继续运行(由VBAT供电)的32位的向上计数器,STM32F103没有分组的时间寄存器,需要自己解析,而F4有分组的时间寄存器。)

两个32位寄存器包含二进码十进制格式(BCD)的秒,分钟,小时(12或24小时制),星期几,日期,月份和年份。此外,还可以提供二进制的亚秒值。

系统可以自动将月份的天数补偿为28,29(闰年),30,31天。并且还可以进行夏令时补偿。

其他32位寄存器还包含可编程的闹钟亚秒,秒,分钟,小时,星期几和日期。

此外,还可以使用数字校准功能对晶振精度的偏差进行补偿。

上电复位后,所有的RTC寄存器都会受到保护,以防止可能的非正常写访问。

RTC模块和时钟配置是在后备区域,即在系统复位或者待机模式唤醒后RTC的设置和时间维持不变,只要后备区域供电正常,RTC将一直工作下去。但是在系统复位之后会自动禁止访问后备区域和RTC,以防止意外操作,所以在设置时间之前,要先取消后备区域写保护。

 RTC工作原理框图

 RTC时钟源:

 RTC BKP备份寄存器

提醒:一共有20个32位备份寄存器。常用来保存一些系统配置信息和相关标志位。

 RTC相关常用寄存器

RTC时间寄存器(RTC_TR)  RTC日期寄存器(RTC_DR)RTC亚秒寄存器(RTC_SSR)RTC控制寄存器(RTC_CR)RTC初始化和状态寄存器(RTC_ISR)RTC预分频寄存器(RTC_PRER)RTC唤醒定时器寄存器(RTC_WUTR)RTC闹钟A寄存器(RTC_ALRMAR)RTC闹钟A亚秒寄存器(RTC_ALRMASSR)RTC闹钟B寄存器( RTC_ALRMBR)RTC闹钟B亚秒寄存器(RTC_ALRMBSSR)RTC写保护寄存器(RTC_WPR)RTC备份寄存器(RTC_BKPxR)RTC时间戳时间寄存器(RTC_TSTR)RTC时间戳日期寄存器(RTC_TSDR)RTC时间戳亚秒寄存器(RTC_TSSSR)

RTC预分频寄存器(RTC_PRER)

 RTC时间寄存器(RTC_TR)  

 RTC日期寄存器(RTC_DR) 

 RTC亚秒寄存器(RTC_SSR)

 RTC控制寄存器(RTC_CR)

 RTC初始化和状态寄存器(RTC_ISR)

 RTC唤醒定时器寄存器(RTC_WUTR)

 RTC闹钟A/B寄存器(RTC_ALRMAR/ RTC_ALRMBR)

 RTC闹钟A/B亚秒寄存器(RTC_ALRMASSR/ RTC_ALRMBSSR)

 RTC写保护寄存器(RTC_WPR)

 程序源码

rtc.h 

#ifndef __RTC_H #define __RTC_H #include "sys.h" u8 My_RTC_Init(void); // RTC初始化 ErrorStatus RTC_Set_Time(u8 hour, u8 min, u8 sec, u8 ampm); // RTC时间设置 ErrorStatus RTC_Set_Date(u8 year, u8 month, u8 date, u8 week); // RTC日期设置 void RTC_Set_AlarmA(u8 week, u8 hour, u8 min, u8 sec); // 设置闹钟时间(按星期闹铃,24小时制) void RTC_Set_WakeUp(u32 wksel, u16 cnt); // 周期性唤醒定时器设置 #endif

rtc.c

#include "rtc.h" #include "led.h" #include "delay.h" #include "usart.h" NVIC_InitTypeDef NVIC_InitStructure; // RTC时间设置 // hour,min,sec:小时,分钟,秒钟 // ampm:@RTC_AM_PM_Definitions :RTC_H12_AM/RTC_H12_PM // 返回值:SUCEE(1),成功 // ERROR(0),进入初始化模式失败 ErrorStatus RTC_Set_Time(u8 hour, u8 min, u8 sec, u8 ampm) { RTC_TimeTypeDef RTC_TimeTypeInitStructure; RTC_TimeTypeInitStructure.RTC_Hours = hour; RTC_TimeTypeInitStructure.RTC_Minutes = min; RTC_TimeTypeInitStructure.RTC_Seconds = sec; RTC_TimeTypeInitStructure.RTC_H12 = ampm; return RTC_SetTime(RTC_Format_BIN, &RTC_TimeTypeInitStructure); } // RTC日期设置 // year,month,date:年(0~99),月(1~12),日(0~31) // week:星期(1~7,0,非法!) // 返回值:SUCEE(1),成功 // ERROR(0),进入初始化模式失败 ErrorStatus RTC_Set_Date(u8 year, u8 month, u8 date, u8 week) { RTC_DateTypeDef RTC_DateTypeInitStructure; RTC_DateTypeInitStructure.RTC_Date = date; RTC_DateTypeInitStructure.RTC_Month = month; RTC_DateTypeInitStructure.RTC_WeekDay = week; RTC_DateTypeInitStructure.RTC_Year = year; return RTC_SetDate(RTC_Format_BIN, &RTC_DateTypeInitStructure); } // RTC初始化 // 返回值:0,初始化成功; // 1,LSE开启失败; // 2,进入初始化模式失败; u8 My_RTC_Init(void) { RTC_InitTypeDef RTC_InitStructure; // 定义RTC初始化结构体 u16 retry = 0X1FFF; // 定义重试次数 RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); // 使能PWR时钟 PWR_BackupAccessCmd(ENABLE); // 使能后备寄存器访问 if (RTC_ReadBackupRegister(RTC_BKP_DR0) != 0x5053) // 检查是否第一次配置 { RCC_LSEConfig(RCC_LSE_ON); // 开启LSE(低速外部晶振) while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) // 检查指定的RCC标志位设置与否,等待低速晶振就绪 { retry++; delay_ms(10); } if (retry == 0) return 1; // LSE 开启失败. RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); // 设置RTC时钟(RTCCLK),选择LSE作为RTC时钟 RCC_RTCCLKCmd(ENABLE); // 使能RTC时钟 RTC_InitStructure.RTC_AsynchPrediv = 0x7F; // RTC异步分频系数(1~0X7F) RTC_InitStructure.RTC_SynchPrediv = 0xFF; // RTC同步分频系数(0~7FFF) RTC_InitStructure.RTC_HourFormat = RTC_HourFormat_24; // RTC设置为24小时格式 RTC_Init(&RTC_InitStructure); // 初始化RTC RTC_Set_Time(23, 59, 56, RTC_H12_AM); // 设置时间,23 点 59 分 56 秒 RTC_Set_Date(23, 11, 29, 3); // 设置日期,14 年 5 月 5 日星期 1 RTC_WriteBackupRegister(RTC_BKP_DR0, 0x5053); // 标记已经初始化过了 } return 0; } // 设置闹钟时间(按星期闹铃,24小时制) // week:星期几(1~7) @ref RTC_Alarm_Definitions // hour,min,sec:小时,分钟,秒钟 void RTC_Set_AlarmA(u8 week, u8 hour, u8 min, u8 sec) { EXTI_InitTypeDef EXTI_InitStructure; // 定义外部中断线结构体 RTC_AlarmTypeDef RTC_AlarmTypeInitStructure; // 定义RTC闹钟结构体 RTC_TimeTypeDef RTC_TimeTypeInitStructure; // 定义RTC时间结构体 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_AlarmTypeInitStructure.RTC_AlarmDateWeekDay = week; // 设置星期 RTC_AlarmTypeInitStructure.RTC_AlarmDateWeekDaySel = RTC_AlarmDateWeekDaySel_WeekDay; // 按星期闹 RTC_AlarmTypeInitStructure.RTC_AlarmMask = RTC_AlarmMask_None; // 精确匹配星期,时分秒 RTC_AlarmTypeInitStructure.RTC_AlarmTime = RTC_TimeTypeInitStructure; // 设置闹钟时间 RTC_SetAlarm(RTC_Format_BIN, RTC_Alarm_A, &RTC_AlarmTypeInitStructure); // 设置闹钟 RTC_ClearITPendingBit(RTC_IT_ALRA); // 清除RTC闹钟A的标志 EXTI_ClearITPendingBit(EXTI_Line17); // 清除LINE17上的中断标志位 RTC_ITConfig(RTC_IT_ALRA, ENABLE); // 开启闹钟A中断 RTC_AlarmCmd(RTC_Alarm_A, ENABLE); // 开启闹钟A EXTI_InitStructure.EXTI_Line = EXTI_Line17; // LINE17 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; // 中断事件 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; // 上升沿触发 EXTI_InitStructure.EXTI_LineCmd = ENABLE; // 使能LINE17 EXTI_Init(&EXTI_InitStructure); // 配置外部中断线 NVIC_InitStructure.NVIC_IRQChannel = RTC_Alarm_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; // 抢占优先级1 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02; // 子优先级2 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能外部中断通道 NVIC_Init(&NVIC_InitStructure); // 配置中断向量表 } // 周期性唤醒定时器设置 /*wksel: @ref RTC_Wakeup_Timer_Definitions #define RTC_WakeUpClock_RTCCLK_Div16 ((uint32_t)0x00000000) #define RTC_WakeUpClock_RTCCLK_Div8 ((uint32_t)0x00000001) #define RTC_WakeUpClock_RTCCLK_Div4 ((uint32_t)0x00000002) #define RTC_WakeUpClock_RTCCLK_Div2 ((uint32_t)0x00000003) #define RTC_WakeUpClock_CK_SPRE_16bits ((uint32_t)0x00000004) #define RTC_WakeUpClock_CK_SPRE_17bits ((uint32_t)0x00000006) */ // cnt:自动重装载值.减到0,产生中断. /*唤醒功能可以让RTC实时时钟在设定的时间点或间隔后触发中断, 从而唤醒系统或执行特定的任务。例如,在低功耗应用中, 系统可能会进入睡眠模式以节省能量,但仍然需要在某个时间点或间隔后执行某些任务, 比如更新显示、采集传感器数据等。通过设置唤醒功能, RTC实时时钟可以在预定的时间点或间隔后触发中断,从而唤醒系统并执行相应的任务。*/ void RTC_Set_WakeUp(u32 wksel, u16 cnt) { EXTI_InitTypeDef EXTI_InitStructure; RTC_WakeUpCmd(DISABLE); // 关闭WAKE UP RTC_WakeUpClockConfig(wksel); // 唤醒时钟选择 RTC_SetWakeUpCounter(cnt); // 设置WAKE UP自动重装载寄存器 RTC_ClearITPendingBit(RTC_IT_WUT); // 清除RTC WAKE UP的标志 EXTI_ClearITPendingBit(EXTI_Line22); // 清除LINE22上的中断标志位 RTC_ITConfig(RTC_IT_WUT, ENABLE); // 开启WAKE UP 定时器中断 RTC_WakeUpCmd(ENABLE); // 开启WAKE UP 定时器  EXTI_InitStructure.EXTI_Line = EXTI_Line22; // LINE22 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; // 中断事件 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; // 上升沿触发 EXTI_InitStructure.EXTI_LineCmd = ENABLE; // 使能LINE22 EXTI_Init(&EXTI_InitStructure); // 配置 NVIC_InitStructure.NVIC_IRQChannel = RTC_WKUP_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; // 抢占优先级1 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02; // 子优先级2 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能外部中断通道 NVIC_Init(&NVIC_InitStructure); // 配置NVIC中断向量表 } // RTC闹钟中断服务函数 void RTC_Alarm_IRQHandler(void) { if (RTC_GetFlagStatus(RTC_FLAG_ALRAF) == SET) // ALARM A中断? { RTC_ClearFlag(RTC_FLAG_ALRAF); // 清除中断标志 printf("ALARM A!\r\n"); } EXTI_ClearITPendingBit(EXTI_Line17); // 清除中断线17的中断标志 } // RTC WAKE UP中断服务函数 void RTC_WKUP_IRQHandler(void) { if (RTC_GetFlagStatus(RTC_FLAG_WUTF) == SET) // WK_UP中断? { RTC_ClearFlag(RTC_FLAG_WUTF); // 清除中断标志 LED1 = !LED1; } EXTI_ClearITPendingBit(EXTI_Line22); // 清除中断线22的中断标志 }

 main.c

#include "sys.h" #include "delay.h" #include "usart.h" #include "led.h" #include "lcd.h" #include "usmart.h" #include "rtc.h" int main(void) { RTC_TimeTypeDef RTC_TimeStruct; // 用于存储RTC的时间 RTC_DateTypeDef RTC_DateStruct; // 用于存储日期信息 u8 tbuf[40]; // 用于存储格式化后的时间和日期字符串 u8 t = 0; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 设置系统中断优先级分组2 delay_init(168); // 初始化延时函数 uart_init(115200); // 初始化串口波特率为115200 usmart_dev.init(84); // 初始化USMART LED_Init(); // 初始化LED LCD_Init(); // 初始化LCD My_RTC_Init(); // 初始化RTC RTC_Set_WakeUp(RTC_WakeUpClock_CK_SPRE_16bits, 0); // 配置WAKE UP中断,1秒钟中断一次 POINT_COLOR = RED; LCD_ShowString(30, 50, 200, 16, 16, "Explorer STM32F4"); LCD_ShowString(30, 70, 200, 16, 16, "RTC TEST"); LCD_ShowString(30, 90, 200, 16, 16, "ATOM@ALIENTEK"); LCD_ShowString(30, 110, 200, 16, 16, "2023/11/29"); while (1) { t++; if ((t % 10) == 0) // 每100ms更新一次显示数据 { RTC_GetTime(RTC_Format_BIN, &RTC_TimeStruct); // 获取当前的时间信息 // 将其格式化为字符串存储在tbuf中,然后通过LCD显示出来 sprintf((char *)tbuf, "Time:%02d:%02d:%02d", RTC_TimeStruct.RTC_Hours, RTC_TimeStruct.RTC_Minutes, RTC_TimeStruct.RTC_Seconds); LCD_ShowString(30, 140, 210, 16, 16, tbuf); RTC_GetDate(RTC_Format_BIN, &RTC_DateStruct); // 获取当前的日期信息 // 将其格式化为字符串存储在tbuf中,再通过LCD显示出来 sprintf((char *)tbuf, "Date:20%02d-%02d-%02d", RTC_DateStruct.RTC_Year, RTC_DateStruct.RTC_Month, RTC_DateStruct.RTC_Date); LCD_ShowString(30, 160, 210, 16, 16, tbuf); sprintf((char *)tbuf, "Week:%d", RTC_DateStruct.RTC_WeekDay); LCD_ShowString(30, 180, 210, 16, 16, tbuf); } if ((t % 20) == 0) LED0 = !LED0; // 每200ms,翻转一次LED0 delay_ms(10); // 当循环执行了10次,即经过了10次delay_ms(10)的暂停,总共的时间就是10毫秒乘以10次,即100毫秒 } }

效果视频

RTC实时时钟



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3