STM32F407 的实时时钟(RTC)是一个独立的定时器。STM32 的 RTC 模块拥有一组连续
计数的计数器,在相对应的软件配置下,可提供时钟日历的功能。修改计数器的值可以重新设
置系统的当前时间和日期。
RTC是个独立的定时器。RTC模块拥有一个连续计数的计数器,在相应的软件配置下,可以提供时钟日历的功能。修改计数器的值可以重新设置当前时间和日期 RTC还包含用于管理低功耗模式的自动唤醒单元。在断电情况下 RTC仍可以独立运行 只要芯片的备用电源一直供电,RTC上的时间会一直走。RTC实质是一个掉电后还继续运行的定时器,从定时器的角度来看,相对于通用定时器TIM外设,它的功能十分简单,只有计时功能(也可以触发中断)。但其高级指出也就在于掉电之后还可以正常运行。
无论器件状态如何(运行模式、低功耗模式或处于复位状态),只要电源电压保持在工作范围内,RTC使不会停止工作。
① 时钟源
STM32F407 的 RTC 时钟源(RTCCLK)通过时钟控制器,可以从 LSE 时钟、LSI 时钟以及 HSE 时钟三者中选择其一。一般我们选择 LSE(外部 32.768KHz 晶振)作为时钟源(RTCCLK)。外部晶振具有精度高,但价格高,RC振荡器精度低,但成本也低。
③ 时间和日期相关寄存器
该部分包括三个影子寄存器,RTC_SSR(亚秒)、RTC_TR(时间)、RTC_DR(日期)。实时时钟一般表示为:时/分/秒/亚秒。RTC_TR 寄存器用于存储时/分/秒时间数据,可读可写(即
可设置或者获取时间)。RTC_DR 寄存器用于存储日期数据,包括年/月/日/星期,可读可写(即
可设置或者获取日期)。RTC_SSR 寄存器用于存储亚秒级的时间,这样我们可以获取更加精确
的时间数据。
④ 可编程闹钟
STM32F407 提供两个可编程闹钟:闹钟 A(ALARM_A)和闹钟 B(ALARM_B)。通过
RTC_CR 寄存器的 ALRAE 和 ALRBE 位置 1 来使能闹钟。当亚秒、秒、分、小时、日期分别与
闹钟寄存器RTC_ALRMASSR/RTC_ALRMAR和RTC_ALRMBSSR/RTC_ALRMBR中的值匹配
时,则可以产生闹钟(需要适当配置)。本章我们将利用闹钟 A 产生闹铃,即设置
RTC_ALRMASSR 和 RTC_ALRMAR 即可。
(1)读取系统时间函数
/*读取系统时间*/
HAL_StatusTypeDef HAL_RTC_GetTime(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format)
*hrtc RTC结构体参数 例:&hi2c2
RTC_TimeTypeDef *sTime: 获取RTC时间的结构体,
Format: 获取时间的格式
RTC_FORMAT_BIN 使用16进制
RTC_FORMAT_BCD 使用BCD进制
(2)读取系统日期函数
/*读取系统日期*/
HAL_StatusTypeDef HAL_RTC_GetDate(RTC_HandleTypeDef *hrtc, RTC_DateTypeDef *sDate, uint32_t Format)
*hrtc RTC结构体参数 例:&hi2c2
RTC_DateTypeDef *sTime: 获取RTC日期的结构体,
Format: 获取日期的格式
RTC_FORMAT_BIN 使用16进制
RTC_FORMAT_BCD 使用BCD进制
(3)结构体介绍
typedef struct
{
uint8_t Hours; /*!< Specifies the RTC Time Hour.*/
uint8_t Minutes; /*!< Specifies the RTC Time Minutes*/
uint8_t Seconds; /*!< Specifies the RTC Time Seconds*/
} RTC_TimeTypeDef;
typedef struct
{
uint8_t WeekDay; /*!< Specifies the RTC Date WeekDay */
uint8_t Month; /*!< Specifies the RTC Date Month (in BCD format)*/
uint8_t Date; /*!< Specifies the RTC Date*/
uint8_t Year; /*!< Specifies the RTC Date Year*/
} RTC_DateTypeDef;
#include "stdio.h"
int fputc(int ch,FILE *f){
uint8_t temp[1]={ch};
HAL_UART_Transmit(&huart1,temp,1,2);
return ch;
}
/* Get the RTC current Time */
HAL_RTC_GetTime(&hrtc, &GetTime, RTC_FORMAT_BIN);
/* Get the RTC current Date */
HAL_RTC_GetDate(&hrtc, &GetData, RTC_FORMAT_BIN);
/* Display date Format : yy/mm/dd */
printf("%02d/%02d/%02d\r\n",2000 + GetData.Year, GetData.Month, GetData.Date);
/* Display time Format : hh:mm:ss */
printf("%02d:%02d:%02d\r\n",GetTime.Hours, GetTime.Minutes, GetTime.Seconds);
printf("\r\n");
HAL_Delay(1000);
但是呢,在hal库中生成的代码,每次断电就RTC时间会重置,每次上电都会重新初始化时间。因为HAL库设置了一个BKP寄存器保存一个标志。每次单片机启动时都读取这个标志并判断是不是预先设定的值:如度果不是就初始化RTC并设置时间,再设置标志为预期值;如果是预期值就跳过初始化和时间设置,继续执行后面的程序所以这里我们只需要每次上电执行RTC初始化之前,将标志设置为预期值即可
在rtc.c中的RTC_Init修改为以下内容即可,但请注意,需要修改时间
/**
* @brief RTC时间设置
* @param hour,min,sec: 小时,分钟,秒钟
* @param ampm : AM/PM, 0=AM/24H; 1=PM/12H
* @retval 0,成功
* 其他,异常状态
*/
HAL_StatusTypeDef rtc_set_time(uint8_t hour, uint8_t min, uint8_t sec, uint8_t ampm)
{
RTC_TimeTypeDef rtc_time_handle;
rtc_time_handle.Hours = hour;
rtc_time_handle.Minutes = min;
rtc_time_handle.Seconds = sec;
rtc_time_handle.TimeFormat = ampm;
rtc_time_handle.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
rtc_time_handle.StoreOperation = RTC_STOREOPERATION_RESET;
return HAL_RTC_SetTime(&hrtc, &rtc_time_handle, RTC_FORMAT_BIN);
}
/**
* @brief RTC日期设置
* @param year,month,date : 年(0~99),月(1~12),日(0~31)
* @param week : 星期(1~7,0,非法!)
* @retval 0,成功
* 其他,异常状态
*/
HAL_StatusTypeDef rtc_set_date(uint8_t year, uint8_t month, uint8_t date, uint8_t week)
{
RTC_DateTypeDef rtc_date_handle;
rtc_date_handle.Date = date;
rtc_date_handle.Month = month;
rtc_date_handle.WeekDay = week;
rtc_date_handle.Year = year;
return HAL_RTC_SetDate(&hrtc, &rtc_date_handle, RTC_FORMAT_BIN);
}
void MX_RTC_Init(void)
{
uint16_t bkpflag = 0;
hrtc.Instance = RTC;
hrtc.Init.HourFormat = RTC_HOURFORMAT_24; /* RTC 设置为 24 小时格式 */
hrtc.Init.AsynchPrediv = 0x7F; /* RTC 异步分频系数(1~0x7F) */
hrtc.Init.SynchPrediv = 0xFF; /* RTC 同步分频系数(0~0x7FFF) */
hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
/* 检查是不是第一次配置时钟 */
bkpflag = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0); /* 读取 BKP0 的值 */
HAL_RTC_Init(&hrtc);
if ((bkpflag != 0x5050) && (bkpflag != 0x5051)) /* 之前未初始化过, 重新配置 */
{
rtc_set_time(15, 46, 0, RTC_HOURFORMAT12_AM);/* 设置时间, 根据实际时间修改 */
rtc_set_date(24, 2, 2, 5); /* 设置日期 */
}
}