正点原子的hal库的RTC例程也有这个问题。
具体问题现象是:多次连续按复位,或者多次连续开关电源,出现RTC时间慢于实际时间。
在网上寻找无果:stm32 每次复位启动RTC时间都会变慢_stm32停机唤醒缓慢-优快云博客 这一片文章应该是年代久了,而且也不知道在说啥。
怀疑是HAL_RTC_Init(&hrtc)的问题,将该函数直接放在main函数中,rtc直接不动。
我的解决方法是:在MX_RTC_Init()函数中的HAL_RTC_Init(&hrtc)函数找到RTC_EnterInitMode(hrtc)函数,然后将这个函数内的__HAL_RTC_WRITEPROTECTION_DISABLE(hrtc)函数注释掉,图片如下。原因可能是在于该函数使得RTC几个寄存器的写保护消失,使得HAL_RTC_Init(&hrtc)重复对RTC的一些寄存器进行初始化。
试验后问题解决。
/*2025-3-17 21:22
=======================================================================*/
今天深挖了一下原理,导致stm32 每次复位或者开关电源,RTC时间都会变慢的原因是因为,在原版的HAL_RTC_Init(&hrtc)中有个向预分频装载计数器PRL重新写值的函数如下图:
我们将这两行代码注释掉就可以更精准的解决这个问题了。
STM32F1手册313页如是说:
因为一旦RTC_PRL寄存器被重写,RTC_DIV寄存器也会被硬件重新装载。而RTC_DIV寄存器是受RTC_CLK(该值一般为为LSE,晶振频率为32.768kHz)影响计数,进而产生我们需要的1Hz信号,给RTC_CNT寄存器计数的。一般来说RTC_DIV只会受到RTC_CNT加1而重新装载RTC_PRL的值,但是一旦我们复位,RTC_DIV寄存器被重新装载,就导致了时间变慢,我们从RTC_CNT中读取秒数也会变慢。
粘贴主要代码如下:
main函数中的MX_RTC_Init函数、我的RTC秒中断函数:
RTC_TimeTypeDef RTC_Time;
RTC_DateTypeDef RTC_Date;
uint8_t aShowTime[50] = {0};
uint8_t aShowDate[50] = {0};
void MX_RTC_Init(void)
{
RTC_TimeTypeDef sTime = {0};
RTC_DateTypeDef DateToUpdate = {0};
hrtc.Instance = RTC;
hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND;
hrtc.Init.OutPut = RTC_OUTPUTSOURCE_ALARM;
if (HAL_RTC_Init(&hrtc) != HAL_OK) {
Error_Handler();
}
/* USER CODE BEGIN Check_RTC_BKUP */
HAL_RTCEx_SetSecond_IT(&hrtc);
if (HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1) == 0x32F6) { // 如果已经初始化过,则直接返回,否则读取备份寄存器中的年月星期日期
hrtc.DateToUpdate.Year = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR2);
hrtc.DateToUpdate.Month = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR3);
hrtc.DateToUpdate.WeekDay = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR4);
hrtc.DateToUpdate.Date = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR5);
return;
} else { // 如果未初始化过,则进行初始化 并对BKP1寄存器标记
/* USER CODE END Check_RTC_BKUP */
/** Initialize RTC and set the Time and Date
*/
sTime.Hours = 21;
sTime.Minutes = 0;
sTime.Seconds = 0;
if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK) {
Error_Handler();
}
DateToUpdate.WeekDay = RTC_WEEKDAY_MONDAY;
DateToUpdate.Month = RTC_MONTH_MARCH;
DateToUpdate.Date = 17;
DateToUpdate.Year = 25;
if (HAL_RTC_SetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BIN) != HAL_OK) {
Error_Handler();
}
/* USER CODE BEGIN RTC_Init 2 */
HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, 0x32F6);
}
/* USER CODE END RTC_Init 2 */
}
void HAL_RTCEx_RTCEventCallback(RTC_HandleTypeDef *hrtc)
{
LED0_TOGGLE();
HAL_RTC_GetTime(hrtc, &RTC_Time, RTC_FORMAT_BIN);
HAL_RTC_GetDate(hrtc, &RTC_Date, RTC_FORMAT_BIN);
sprintf((char *)aShowTime, "%2d:%2d:%2d", RTC_Time.Hours, RTC_Time.Minutes, RTC_Time.Seconds);
sprintf((char *)aShowDate, "%2d-%2d-%2d", RTC_Date.Month, RTC_Date.Date, 2000 + RTC_Date.Year);
if (RTC_Time.Seconds == 0) { //每分钟记录当前时间到备份寄存器中
HAL_RTCEx_BKUPWrite(hrtc, RTC_BKP_DR2, RTC_Date.Year);
HAL_RTCEx_BKUPWrite(hrtc, RTC_BKP_DR3, RTC_Date.Month);
HAL_RTCEx_BKUPWrite(hrtc, RTC_BKP_DR4, RTC_Date.WeekDay);
HAL_RTCEx_BKUPWrite(hrtc, RTC_BKP_DR5, RTC_Date.Date);
}
}
main函数:
int main(void)
{
HAL_Init();
/* USER CODE BEGIN Init */
delay_init(72);
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_FSMC_Init();
MX_RTC_Init();
MX_TIM1_Init();
/* USER CODE BEGIN 2 */
LCD_Init();
LCD_ShowString(0, 0, 200, 12, 12, (uint8_t *)"HellowLCD!");
printf("HellowSerial!\r\n");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1) {
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
LCD_ShowString(0, 24, 200, 12, 12, aShowDate);
LCD_ShowString(0, 36, 200, 12, 12, aShowTime);
}
/* USER CODE END 3 */
}