环境:
Stm32CubeMX V5.6.0
stm32F412RETx芯片,芯片的VBAT引脚接电池
RTC使用的外部晶振,内部的振荡器配置后断电后不跑,原因未知,下面直接上使用外部晶振的方案
配置RTC使用外部晶振,外部晶振是32.768KHz的
配置RTC,启用日历和时钟
stm32CubeMX默认填入的分频是使用32.768KHz的,但是内部振荡器是32KHz的,所以估计都是推荐使用外部晶振,使用内部振荡器的自己按公式重新计算分频值
公式如下:
RTC时钟频率 = RTC时钟源 / ((Asynchronous Predivider value + 1) * (Synchronous Predivider value + 1))
套用一下公式
32.768KHz / ((127+1)*(255+1)) = 1Hz,也就是1秒
年是从1970年算起的,其他月日时分秒以及24或者12小时制没什么好说的
数据格式Data Format,我使用Binary data format
stm32定义了RTC_FORMAT_BIN和RTC_FORMAT_BCD两种格式
RTC_FORMAT_BIN:为10进制,RTC_FORMAT_BCD为16进制,开始我也是这样认为的,然后并不是
看一下stm32f4xx_hal_rtc.h头文件中对月份的定义:
/** @defgroup RTC_Month_Date_Definitions RTC Month Date Definitions
* @{
*/
/* Coded in BCD format */
#define RTC_MONTH_JANUARY ((uint8_t)0x01)
#define RTC_MONTH_FEBRUARY ((uint8_t)0x02)
#define RTC_MONTH_MARCH ((uint8_t)0x03)
#define RTC_MONTH_APRIL ((uint8_t)0x04)
#define RTC_MONTH_MAY ((uint8_t)0x05)
#define RTC_MONTH_JUNE ((uint8_t)0x06)
#define RTC_MONTH_JULY ((uint8_t)0x07)
#define RTC_MONTH_AUGUST ((uint8_t)0x08)
#define RTC_MONTH_SEPTEMBER ((uint8_t)0x09)
#define RTC_MONTH_OCTOBER ((uint8_t)0x10)
#define RTC_MONTH_NOVEMBER ((uint8_t)0x11)
#define RTC_MONTH_DECEMBER ((uint8_t)0x12)
/** @defgroup RTC_WeekDay_Definitions RTC WeekDay Definitions
* @{
*/
#define RTC_WEEKDAY_MONDAY ((uint8_t)0x01)
#define RTC_WEEKDAY_TUESDAY ((uint8_t)0x02)
#define RTC_WEEKDAY_WEDNESDAY ((uint8_t)0x03)
#define RTC_WEEKDAY_THURSDAY ((uint8_t)0x04)
#define RTC_WEEKDAY_FRIDAY ((uint8_t)0x05)
#define RTC_WEEKDAY_SATURDAY ((uint8_t)0x06)
#define RTC_WEEKDAY_SUNDAY ((uint8_t)0x07)
比如12月份RTC_MONTH_DECEMBER的值是等于0x12的,转成10进制等于18,也就是RTC_FORMAT_BCD也不是16进制,只是用了16进制的样式,0x12不看0x直接看数字部分,就是12月份
再来看看stm32内部的两种格式的转换函数
/**
* @brief Converts a 2 digit decimal to BCD format.
* @param Value Byte to be converted
* @retval Converted byte
*/
uint8_t RTC_ByteToBcd2(uint8_t Value)
{
uint32_t bcdhigh = 0U;
while(Value >= 10U)
{
bcdhigh++;
Value -= 10U;
}
return ((uint8_t)(bcdhigh << 4U) | Value);
}
/**
* @brief Converts from 2 digit BCD to Binary.
* @param Value BCD value to be converted
* @retval Converted word
*/
uint8_t RTC_Bcd2ToByte(uint8_t Value)
{
uint32_t tmp = 0U;
tmp = ((uint8_t)(Value & (uint8_t)0xF0) >> (uint8_t)0x4) * 10;
return (tmp + (Value & (uint8_t)0x0F));
}
转换函数代码自己看不说明,总之觉得很奇怪的用法
配置完成,生成工程
找到static void MX_RTC_Init(void)函数,在 HAL_RTC_Init后,使用RTC的后备寄存器保存是否初始化过时间的标志,RTC的后备寄存器也是后备电源供电的,所以主电源断电后数据还在,也可以用这些寄存器保存数据,读写函数如下:
void HAL_RTCEx_BKUPWrite(RTC_HandleTypeDef *hrtc, uint32_t BackupRegister, uint32_t Data);
uint32_t HAL_RTCEx_BKUPRead(RTC_HandleTypeDef *hrtc, uint32_t BackupRegister);
MX_RTC_Init函数修改如下:
static void MX_RTC_Init(void)
{
/* USER CODE BEGIN RTC_Init 0 */
/* USER CODE END RTC_Init 0 */
RTC_TimeTypeDef sTime = {0};
RTC_DateTypeDef sDate = {0};
/* USER CODE BEGIN RTC_Init 1 */
/* USER CODE END RTC_Init 1 */
/** Initialize RTC Only
*/
hrtc.Instance = RTC;
hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
hrtc.Init.AsynchPrediv = 127;
hrtc.Init.SynchPrediv = 255;
hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
if (HAL_RTC_Init(&hrtc) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN Check_RTC_BKUP */
// 检查后备寄存器
uint32_t iSetFlag = 0x5053;// 这个0x5053值自己随意
if(iSetFlag != HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0))
{ //
/* USER CODE END Check_RTC_BKUP */
/** Initialize RTC and set the Time and Date
*/
sTime.Hours = 9;
sTime.Minutes = 45;
sTime.Seconds = 0;
sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
sTime.StoreOperation = RTC_STOREOPERATION_RESET;
if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK)
{
Error_Handler();
}
sDate.WeekDay = RTC_WEEKDAY_WEDNESDAY;
sDate.Month = RTC_MONTH_MAY;
sDate.Date = 13;
sDate.Year = 50;
if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN RTC_Init 2 */
// 设置完时钟,设置标志
HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR0, iSetFlag);
}
else
{
}
{
// 获取一次时间显示
RTC_TimeTypeDef sTime;
HAL_StatusTypeDef status = HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
RTC_DateTypeDef sDate;
status = HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
printf("MX_RTC_Init:Date time: %d-%02d-%02d weekData:%d %02d:%02d:%02d\r\n"
, 1970 + sDate.Year, sDate.Month, sDate.Date, sDate.WeekDay
, sTime.Hours, sTime.Minutes, sTime.Seconds);
}
/* USER CODE END RTC_Init 2 */
}
获取时间日期HAL_RTC_GetTime和HAL_RTC_GetDate有要求先后顺序的,看函数定义处的说明:
You must call HAL_RTC_GetDate() after HAL_RTC_GetTime() to unlock the values in the higher-order calendar shadow registers to ensure consistency between the time and date values. Reading RTC current time locks the values in calendar shadow registers until Current date is read.
上google翻译的结果:
必须在HAL_RTC_GetTime()之后调用HAL_RTC_GetDate()才能解锁高阶日历影子寄存器中的值,以确保时间和日期值之间的一致性。 读取RTC当前时间将锁定日历影子寄存器中的值,直到读取当前日期为止。
基本配置完成,断主电源后,在次上电,查看输出的时间是正常的
我用F103芯片配置过,发现F103的芯片日期断电后就没有了
参考:
https://blog.csdn.net/qq_24381977/article/details/94460855