00. 目录
01. RTC时钟简介
STM32F4 的实时时钟(RTC)相对于 STM32F1 来说,改进了不少,带了日历功能了,STM32F4 的 RTC,是一个独立的 BCD 定时器/计数器。RTC 提供一个日历时钟(包含年月日时分秒信息)、两个可编程闹钟(ALARM A 和 ALARM B)中断,以及一个具有中断功能的周
期性可编程唤醒标志。RTC 还包含用于管理低功耗模 式的自动唤醒单元。
两个 32 位寄存器(TR 和 DR)包含二进码十进数格式 (BCD) 的秒、分钟、小时(12 或24 小时制)、星期、日期、月份和年份。此外,还可提供二进制格式的亚秒值。
STM32F4 的 RTC 可以自动将月份的天数补偿为 28、29(闰年)、30 和 31 天。并且还可以进行夏令时 补偿。
RTC 模块和时钟配置是在后备区域,即在系统复位或从待机模式唤醒后 RTC 的设置和时间维持不变,只要后备区域供电正常,那么 RTC 将可以一直运行。但是在系统复位后,会自动禁止访问后备寄存器和 RTC,以防止对后备区域(BKP)的意外写操作。所以在要设置时间之前,先要取消备份区域(BKP)写保护。
02. 硬件设计
用到的硬件资源有:
1) 指示灯 DS0
2) 串口
3) TFTLCD 模块
4) RTC
03. RTC日历配置步骤
①使能PWR时钟:RCC_APB1PeriphClockCmd();
② 使能后备寄存器访问: PWR_BackupAccessCmd()
③ 配置RTC时钟源,使能RTC时钟:
RCC_RTCCLKConfig();
RCC_RTCCLKCmd();
如果使用LSE,要打开LSE:RCC_LSEConfig(RCC_LSE_ON);
④ 初始化RTC(同步/异步分频系数和时钟格式):RTC_Init ();
⑤ 设置时间:RTC_SetTime ();
⑥设置日期:RTC_SetDate();
04. 设置和获取日期示例
rtc.h
#ifndef __RTC_H__
#define __RTC_H__
#include "sys.h"
//RTC初始化
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 mon, u8 day, u8 week);
#endif /*__RTC_H__*/
rtc.c
#include "rtc.h"
#include "delay.h"
//RTC时间设置
ErrorStatus RTC_Set_Time(u8 hour, u8 min, u8 sec, u8 ampm)
{
RTC_TimeTypeDef RTC_TimeStruct;
RTC_TimeStruct.RTC_H12 = ampm;
RTC_TimeStruct.RTC_Hours = hour;
RTC_TimeStruct.RTC_Minutes = min;
RTC_TimeStruct.RTC_Seconds = sec;
return RTC_SetTime(RTC_Format_BIN,&RTC_TimeStruct);
}
//RTC日期设置
ErrorStatus RTC_Set_Date(u8 year, u8 mon, u8 day, u8 week)
{
RTC_DateTypeDef RTC_DateStruct;
RTC_DateStruct.RTC_Year = year;
RTC_DateStruct.RTC_Month = mon;
RTC_DateStruct.RTC_Date = day;
RTC_DateStruct.RTC_WeekDay = week;
return RTC_SetDate(RTC_Format_BIN, &RTC_DateStruct);
}
//RTC初始化
u8 MY_RTC_Init(void)
{
u16 retry = 0x1fff;
RTC_InitTypeDef RTC_InitStruct;
//使能PWR时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
//使能后备寄存器访问
PWR_BackupAccessCmd(ENABLE);
//判断是否第一次初始化
if (RTC_ReadBackupRegister(RTC_BKP_DR0) != 0x8080)
{
//开启LSE
RCC_LSEConfig(RCC_LSE_ON);
//等待LSE准备就绪
while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)
{
retry++;
delay_ms(10);
}
//开始LSE失败
if (0 == retry)
{
return 1;
}
//设置RTC时钟为LSE
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
//使能RTC时钟
RCC_RTCCLKCmd(ENABLE);
//初始化RTC
RTC_InitStruct.RTC_HourFormat = RTC_HourFormat_24;
RTC_InitStruct.RTC_AsynchPrediv = 0x7F;
RTC_InitStruct.RTC_SynchPrediv = 0xFF;
RTC_Init(&RTC_InitStruct);
//设置时间
RTC_Set_Time(16, 06, 50, RTC_H12_AM);
//设置日期
RTC_Set_Date(20, 9, 9, RTC_Weekday_Wednesday);
//标记已经初始化
RTC_WriteBackupRegister(RTC_BKP_DR0, 0x8080);
}
return 0;
}
main.c
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "beep.h"
#include "key.h"
#include "usmart.h"
#include "lcd.h"
#include "rtc.h"
//LED状态设置函数
void led_set(u8 sta)
{
LED1 = sta;
}
//函数参数调用测试函数
void test_fun(void(*ledset)(u8), u8 sta)
{
ledset(sta);
}
int main(void)
{
u8 buf[40];
u8 t = 0;
RTC_TimeTypeDef RTC_TimeStruct;
RTC_DateTypeDef RTC_DateStruct;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
delay_init(168);
uart_init(115200);
usmart_dev.init(84);
LED_Init();
LCD_Init();
//RTC初始化
MY_RTC_Init();
POINT_COLOR = RED;
LCD_ShowString(30,50,200,16,16,"Explorer STM32F4");
LCD_ShowString(30,70,200,16,16,"USMART TEST");
LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
LCD_ShowString(30,110,200,16,16,"2020/09/09");
while(1)
{
t++;
//每隔100ms更新显示
if (0 == t % 10)
{
RTC_GetTime(RTC_Format_BIN, &RTC_TimeStruct);
sprintf((char*)buf, "Time: %02d:%02d:%02d", RTC_TimeStruct.RTC_Hours, RTC_TimeStruct.RTC_Minutes, RTC_TimeStruct.RTC_Seconds);
LCD_ShowString(30, 140, 210, 16, 16, buf);
RTC_GetDate(RTC_Format_BIN, &RTC_DateStruct);
sprintf((char*)buf, "Date: 20%02d-%02d-%02d", RTC_DateStruct.RTC_Year, RTC_DateStruct.RTC_Month, RTC_DateStruct.RTC_Date);
LCD_ShowString(30, 160, 210, 16, 16, buf);
sprintf((char*)buf, "Week: %d", RTC_DateStruct.RTC_WeekDay);
LCD_ShowString(30, 180, 210, 16, 16, buf);
}
if (0 == t % 20)
{
LED1 = !LED1;
}
//睡眠10毫秒
delay_ms(10);
}
}
05. 设置闹钟中断示例
//设置闹钟时间(按星期闹铃,24小时制)
//week:星期几(1~7) @ref RTC_Alarm_Definitions
//hour,min,sec:小时,分钟,秒钟
void RTC_Set_AlarmA(u8 week,u8 hour,u8 min,u8 sec)
{
RTC_TimeTypeDef RTC_TimeStruct;
RTC_AlarmTypeDef RTC_AlarmStruct;
EXTI_InitTypeDef EXTI_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
//关闭闹钟A
RTC_AlarmCmd(RTC_Alarm_A, DISABLE);
//设置闹钟参数
RTC_TimeStruct.RTC_H12 = RTC_H12_AM;
RTC_TimeStruct.RTC_Hours = hour;
RTC_TimeStruct.RTC_Minutes = min;
RTC_TimeStruct.RTC_Seconds = sec;
RTC_AlarmStruct.RTC_AlarmTime = RTC_TimeStruct;
RTC_AlarmStruct.RTC_AlarmMask = RTC_AlarmMask_None;
RTC_AlarmStruct.RTC_AlarmDateWeekDaySel = RTC_AlarmDateWeekDaySel_WeekDay;
RTC_AlarmStruct.RTC_AlarmDateWeekDay = week;
RTC_SetAlarm(RTC_Format_BIN, RTC_Alarm_A, &RTC_AlarmStruct);
//清除闹钟A的中断标志
RTC_ClearITPendingBit(RTC_IT_ALRA);
//清除中断线17上的中断标志
EXTI_ClearITPendingBit(EXTI_Line17);
//开启闹钟A中断
RTC_ITConfig(RTC_IT_ALRA, ENABLE);
//开启闹钟
RTC_AlarmCmd(RTC_Alarm_A, ENABLE);
//初始化中断
EXTI_InitStruct.EXTI_Line = EXTI_Line17;
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 = 0x2;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0x2;
NVIC_Init(&NVIC_InitStruct);
}
//闹钟中断处理函数
void RTC_Alarm_IRQHandler(void)
{
if (RTC_GetFlagStatus(RTC_FLAG_ALRAF) == SET)
{
RTC_ClearFlag(RTC_FLAG_ALRAF);
printf("ALARM A!\r\n");
}
//清中断
EXTI_ClearITPendingBit(EXTI_Line17);
}
06. 设置唤醒中断示例
//周期性唤醒定时器设置
/*wksel: @ref RTC_Wakeup_Timer_Definitions
#define RTC_WakeUpClock_RTCCLK_Div16 ((uint32_t)0x00000000)
#define RTC_WakeUpClock_RTCCLK_Div8 ((uint32_t)0x00000001)
#define RTC_WakeUpClok_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,产生中断.
void RTC_Set_WakeUp(u32 wksel,u16 cnt)
{
EXTI_InitTypeDef EXTI_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
//关闭WakeUP
RTC_WakeUpCmd(DISABLE);
//配置WakeUP时钟分频系数或者来源
RTC_WakeUpClockConfig(wksel);
//设置WakeUP自动装载值
RTC_SetWakeUpCounter(cnt);
//清除中断标志
RTC_ClearITPendingBit(RTC_IT_WUT);
//清除中断线17上的中断标志
EXTI_ClearITPendingBit(EXTI_Line22);
//开启WakeUP中断
RTC_ITConfig(RTC_IT_WUT, ENABLE);
//使能WakeUp
RTC_WakeUpCmd(ENABLE);
//初始化中断
EXTI_InitStruct.EXTI_Line = EXTI_Line22;
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_WKUP_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0x2;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0x2;
NVIC_Init(&NVIC_InitStruct);
}
//唤醒中断处理函数
void RTC_WKUP_IRQHandler(void)
{
if (RTC_GetFlagStatus(RTC_FLAG_WUTF) == SET)
{
RTC_ClearFlag(RTC_FLAG_WUTF);
LED2 = !LED2;
}
//清中断
EXTI_ClearITPendingBit(EXTI_Line22);
}
07. 附录
7.1 【STM32】STM32系列教程汇总
08. 声明
该教程参考了正点原子的《STM32 F4 开发指南》