The STM32F1 microcontroller uses the HAL library and the LL library to jointly develop the RTC real-time clock to solve the problem of the 24-hour date remaining unchanged in standby mode.

Project requirements:

Use STM32F103CBT6 microcontroller to jointly develop the RTC real-time clock through HAL library development and LL library.
And the entire device is powered by batteries. Considering the size of the product, it does not use an external clock chip and a backup clock battery. The RTC clock uses the HSE of the microcontroller. After the microcontroller enters standby mode for more than 24 hours, it wakes up the microcontroller through PA0, and the year, month, day, hour, minute, and second remain unchanged. The display is normal and will not be cleared.

Problem analysis and description

Those who can read this article must be trapped because the STM32F1 series microcontroller does not have a separate register to store the year, month and day, and the library function automatically generated by the HAL library makes the RTC counter registers (RTC_CNTH and RTC_CNTL) that can originally count 138 years only The increment will be cleared when it reaches 23 hours, 59 minutes and 59 seconds. This means that the RTC clock of the HAL library will not increase the number of days of the date by 1 after entering standby mode for 24 hours, unless it is woken up by an alarm clock interrupt, and the microcontroller will never be allowed to Enter standby mode for 24 hours (I tried this method but it didn’t work).
Facts have proved that it is difficult for the STM32F1 series to use the HAL library alone to implement the RTC real-time clock and still operate normally after entering standby mode for 24 hours. The editor used the joint development method of HAL library + LL library. Let’s see below:

Solution:

Using the standard library will not cause the RTC counting registers (RTC_CNTH and RTC_CNTL) to be cleared when it reaches 23:59:59. In this way, is it possible to use the combination of the standard library and the HAL library to realize the RTC entering standby? The purpose of the mode is to run normally after waking up for 24 hours. But having said that, it is a little troublesome to transplant the standard library to the HAL library. Then the editor found that the LL library is more suitable. The LL library can be said to be more library than the standard library, and it can also be directly To operate the register, more importantly, it can be generated directly with STM32CubeMX and some of the peripherals can use the HAL library, only the RTC uses the LL library, so that it can be achieved In order to prevent the RTC counting registers (RTC_CNTH and RTC_CNTL) from being cleared when the count reaches 23 hours, 59 minutes and 59 seconds, they can keep increasing upwards. After solving this big problem, you can stand on the shoulders of giants and see the world. Refer to the implementation of the punctual atomic RTC real-time clock routine. Convert the year, month, day, hour, minute, and second to be set into seconds by setting the time function, and assign it directly to RTC counting register (RTC_CNTH and RTC_CNTL), whether in standby or not, the RTC counting register will continue to increase until the battery is completely exhausted.
The respective values of RTC_CNTH and RTC_CNTLPlease add a picture description

When you want to get the current time, read the seconds in the register, and convert the year, month, day, hour, minute, and second into years, months, days, hours, minutes, and seconds.
HAL library RTC configuration:
Please add a picture descriptionPlease add a picture description

Code display:

Shown below is rtc.c.

/* Includes ----------------------------------------------- -----------------------*/
#include "rtc.h"

/* USER CODE BEGIN 0 */

_calendar_obj calendar;//clock structure

const uint8_t table_week[12]={<!-- -->0,3,3,6,1,4,6,2,5,0,3,5}; //Monthly revised data table

const uint8_t mon_table[12]={<!-- -->31,28,31,30,31,30,31,31,30,31,30,31};//Month and date table in ordinary years

/* USER CODE END 0 */

/*RTC init function */
void MX_RTC_Init(void)
{<!-- -->

  /* USER CODE BEGIN RTC_Init 0 */

  /* USER CODE END RTC_Init 0 */

  LL_RTC_InitTypeDef RTC_InitStruct = {<!-- -->0};
// LL_RTC_TimeTypeDef RTC_TimeStruct = {0};

    LL_PWR_EnableBkUpAccess();
    /* Enable BKP CLK enable for backup registers */
    LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_BKP);
  /* Peripheral clock enable */
  LL_RCC_EnableRTC();

  /*RTC interrupt Init */
\t
  /* USER CODE BEGIN RTC_Init 1 */
\t
LL_RTC_EnableIT_SEC(RTC);//Enable second interrupt
NVIC_SetPriority(RTC_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
  NVIC_EnableIRQ(RTC_IRQn);
RTC_InitStruct.AsynchPrescaler = 32768;//Frequency division of 32.768Mhz clock
  LL_RTC_Init(RTC, & amp;RTC_InitStruct);
  LL_RTC_SetAsynchPrescaler(RTC,32768);
\t
  /* USER CODE END RTC_Init 1 */
  /** Initialize RTC and set the Time and Date
  */
  /** Initialize RTC and set the Time and Date
  */
  /* USER CODE BEGIN RTC_Init 2 */
\t
rtc_init_user();//Set the initial time value and update the time after each standby wake-up
\t
//if(LL_RTC_BKP_GetRegister(BKP,LL_RTC_BKP_DR1)!= 0x5555)
// {<!-- -->
// LL_RTC_TIME_Init(RTC, LL_RTC_FORMAT_BIN, & amp;RTC_TimeStruct);
// }
  /* USER CODE END RTC_Init 2 */

}

/* USER CODE BEGIN 1 */
void rtc_init_user(void)
{<!-- -->
if(LL_RTC_BKP_GetRegister(BKP,LL_RTC_BKP_DR1)!=0x7777)//Whether it is configured for the first time
{<!-- -->
RTC_Set(2023,10,17,16,28,0); //Set date and time
LL_RTC_BKP_SetRegister(BKP,LL_RTC_BKP_DR1,0x7777);//The mark has been initialized
}
\t
RTC_Get();//update time
}

void RTC_Set(uint16_t syear,uint8_t smon,uint8_t sday,uint8_t hour,uint8_t min,uint8_t sec)
{<!-- -->
uint16_t t;
uint32_t seccount=0;
\t
if(syear<1970||syear>2099) return;
for(t=1970;t<syear;t + + ) //Add the seconds of all years
{<!-- -->
if(Is_Leap_Year(t))seccount + =31622400;//The number of seconds in leap year
else seccount + =31536000; //The number of seconds in a normal year
}
smon-=1;
for(t=0;t<smon;t + + ) //Add the seconds in the previous month
{<!-- -->
seccount + =(uint32_t)mon_table[t]*86400;//Add the seconds in the month
if(Is_Leap_Year(syear) & amp; & amp;t==1)seccount + =86400;//Add the number of seconds to a day in February of a leap year
}
seccount + =(uint32_t)(sday-1)*86400;//Add the seconds of the previous date
seccount + =(uint32_t)hour*3600;//number of hours and seconds
  seccount + =(uint32_t)min*60; //minutes and seconds
seccount + =sec;//Add the last seconds
 
//Set the clock
    RCC->APB1ENR|=1<<28;//Enable power clock
    RCC->APB1ENR|=1<<27;//Enable backup clock
PWR->CR|=1<<8; //Cancel backup area write protection
//The above three steps are necessary!
RTC->CRL|=1<<4; //Allow configuration
RTC->CNTL=seccount &0xffff;
RTC->CNTH=seccount>>16;
RTC->CRL &=~(1<<4);//Configuration update
while(!(RTC->CRL & amp;(1<<5)));//Wait for the RTC register operation to complete
\t
RTC_Get();//Update the data after setting
}

//Get Time
void RTC_Get(void)
{<!-- -->
static uint16_t daycnt=0;
uint32_t timecount=0;
uint32_t temp=0;
uint16_t temp1=0;
 timecount=RTC->CNTH;//Get the value in the counter (number of seconds)
timecount<<=16;
timecount + =RTC->CNTL;
 temp=timecount/86400; //Get the number of days (corresponding to the number of seconds)
if(daycnt!=temp)//It’s more than one day
{<!-- -->
daycnt=temp;
temp1=1970; //Start from 1970
while(temp>=365)
{<!-- -->
if(Is_Leap_Year(temp1))//It is a leap year
{<!-- -->
if(temp>=366)temp-=366;//The number of seconds in leap year
else break;
}
else temp-=365; //Normal year
temp1++;
}
calendar.w_year=temp1;//Get the year
temp1=0;
while(temp>=28)//more than one month
{<!-- -->
if(Is_Leap_Year(calendar.w_year) & amp; & temp1==1)//Is the current year a leap year/February?
{<!-- -->
if(temp>=29)temp-=29;//The number of seconds in leap year
else break;
}
else
{<!-- -->
if(temp>=mon_table[temp1])temp-=mon_table[temp1];//Normal year
else break;
}
temp1++;
}
calendar.w_month=temp1 + 1; //Get the month
calendar.w_date=temp + 1; //Get the date
}
temp=timecount?400; //Get the number of seconds
calendar.hour=temp/3600; //Hour
calendar.min=(temp600)/60; //minutes
calendar.sec=(temp600)`; //Seconds
calendar.week=RTC_Get_Week(calendar.w_year,calendar.w_month,calendar.w_date);//Get the week
}


//Determine whether it is a leap year function
//Month 1 2 3 4 5 6 7 8 9 10 11 12
//Leap year 31 29 31 30 31 30 31 31 30 31 30 31
//Non-leap year 31 28 31 30 31 30 31 31 30 31 30 31
//year: year
//Return value: whether the year is a leap year. 1, yes. 0, no
uint8_t Is_Leap_Year(uint16_t year)
{<!-- -->
if(year%4==0) //Must be divisible by 4
{<!-- -->
if(year 0==0)
{<!-- -->
if(year@0==0)return 1;//If it ends with 00, it must be divisible by 400
else return 0;
}else return 1;
}else return 0;
}

//Get the day of the week it is now
//Function description: Enter the Gregorian calendar date to get the day of the week (only 1901-2099 is allowed)
//year,month,day: Gregorian calendar year, month and day
//Return value: week number
uint8_t RTC_Get_Week(uint16_t year,uint8_t month,uint8_t day)
{<!-- -->
uint16_t temp2;
uint8_t yearH,yearL;
\t
yearH=year/100; yearL=year 0;
// If it is the 21st century, add 100 to the year number
if (yearH>19)yearL + =100;
// Leap years are only counted after 1900
temp2=yearL + yearL/4;
temp2=temp2%7;
temp2=temp2 + day + table_week[month-1];
if (yearL%4==0 & amp; & amp;month<3)temp2--;
return(temp2%7);
}

/* USER CODE END 1 */
//The second interrupt is in line 210 of the file stm32f1xx_it., mainly used to refresh the time every second
void RTC_IRQHandler(void)
{<!-- -->
  /* USER CODE BEGIN RTC_IRQn 0 */
if(LL_RTC_IsActiveFlag_SEC(RTC))
{<!-- -->
LL_RTC_ClearFlag_SEC(RTC);
RTC_Get();//update time
SEGGER_RTT_printf(0,"666\r\\
");
}
  /* USER CODE END RTC_IRQn 0 */
  /* USER CODE BEGIN RTC_IRQn 1 */

  /* USER CODE END RTC_IRQn 1 */
}

First of all, I would like to thank all the viewers, I hope it can be helpful to you; secondly, I would like to thank all the seniors at CSDN. Every time I read your articles, I will feel enlightened. Finally, I hope the world will be better!