芯片是stm32f407ve
软件 stm32cubemx5.3.0 和keil5
F4的RTC提供了日历时钟和两个可编程闹钟中断,一个周期性可编程唤醒标志。这样很方便设置系统时间,并不会像F1的RTC那样要自己计算年月日时分秒。F4的RTC是一个独立的BCD定时计数器,系统可以自动将月份天数补偿为28、29(闰年)、30、31天。并且还可以进行夏令时补偿。
1、设置RCC
RTC设备因为其独特的运行方式(即掉电依旧运行)使用HSE分频时钟或者LSI的时候,在主电源VDD掉电的情况下,这两个时钟来源都会受到影响,资源消耗太大,小小的纽扣电池根本吃不消。没法保证RTC正常工作.所以RTC一般都时钟低速外部时钟LSE。
2、设置RTC
Activate Clock Source 激活时钟源
Activate calendar 激活日历
设置初始的时间、日期和格式
低速的外部晶振我使用的是32768Hz的,根据计算公式
RTC时钟频率 = RTC时钟源 / ((Asynchronous Predivider value + 1) * (Synchronous Predivider value + 1))
32.768KHz / ((127+1)*(255+1)) = 1Hz,也就是1秒
Binary data format 十进制
BCD data format BCD码进制
3、编写代码
生成工程后就可以读取时间和日期了
RTC_TimeTypeDef GetTime;
RTC_DateTypeDef GetData;
GUI_SetColor(GUI_BLACK);
GUI_SetBkColor(GUI_WHITE);
GUI_SetFont(pFont_22);
HAL_RTC_GetTime(&hrtc,&GetTime,RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc,&GetData,RTC_FORMAT_BIN);
Time2Asc(GetTime,aa);
GUI_DispStringAt(aa,300,10);
Date2Asc(GetData,aa);
GUI_DispStringAt(aa,380,10);
需要注意的是,必须在HAL_RTC_GetTime函数后面使用HAL_RTC_GetDate函数,否则是不能读出数据的。
4、RTC备份寄存器
修改RTC初始化函数
void MX_RTC_Init(void)
{
RTC_TimeTypeDef sTime = {0};
RTC_DateTypeDef sDate = {0};
/** 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 */
__HAL_RCC_PWR_CLK_ENABLE(); //开启后备电源时钟
HAL_PWR_EnableBkUpAccess(); //允许访问、RTC 备份寄存器
if (HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1) != 0xA2A2)
{
sTime.Hours = 0x0;
sTime.Minutes = 0x0;
sTime.Seconds = 0x0;
sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
sTime.StoreOperation = RTC_STOREOPERATION_RESET;
if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD) != HAL_OK)
{
Error_Handler();
}
sDate.WeekDay = RTC_WEEKDAY_FRIDAY;
sDate.Month = RTC_MONTH_JANUARY;
sDate.Date = 0x1;
sDate.Year = 0x00;
if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BCD) != HAL_OK)
{
Error_Handler();
}
HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, 0xA2A2);
}
else
{
}
/* USER CODE END Check_RTC_BKUP */
/** Initialize RTC and set the Time and Date
*/
// sTime.Hours = 0x0;
// sTime.Minutes = 0x0;
// sTime.Seconds = 0x0;
// sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
// sTime.StoreOperation = RTC_STOREOPERATION_RESET;
// if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD) != HAL_OK)
// {
// Error_Handler();
// }
// sDate.WeekDay = RTC_WEEKDAY_FRIDAY;
// sDate.Month = RTC_MONTH_JANUARY;
// sDate.Date = 0x1;
// sDate.Year = 0x2000;
// if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BCD) != HAL_OK)
// {
// Error_Handler();
// }
}
这样断电后或者复位后,时钟会保存下来,不会是复位值
遇到的问题
自己手动修改了日期之后,再读取时,读出来的年份总是错误的,其他的月份什么的没有问题,后来在保存日期之前,把星期也重新设置一下再保存,这样就没有问题
应该是因为用HAL_RTC_SetDate这个函数保存的时候,他是将整个RTC_DR寄存器都进行了赋值再保存的,如果不设置星期,那么对RTC_DR寄存器赋值的时候,WDU[2:0]会是一个未知的值,所以后面读取的时候可能会出现问题
assert_param(IS_RTC_YEAR(sDate->Year));
assert_param(IS_RTC_MONTH(sDate->Month));
assert_param(IS_RTC_DATE(sDate->Date));
datetmpreg = (((uint32_t)RTC_ByteToBcd2(sDate->Year) << 16U) | \
((uint32_t)RTC_ByteToBcd2(sDate->Month) << 8U) | \
((uint32_t)RTC_ByteToBcd2(sDate->Date)) | \
((uint32_t)sDate->WeekDay << 13U));
上面是HAL_RTC_SetDate函数中的一段语句,可以看出Week会左移13位,而week是uint8_t类型的数据,左移13位的话,正好会影响年份的值。
包括后面设置时间时,单独设置时间,或者单独设置12小时制再读出来就有问题,一起设置的话就可以,所以现在都是先读取一下,在修改某一项,再整体保存