STM32-RTC real-time clock

Table of Contents

RTC real time clock

Functional block diagram

UNIX timestamp

Initialization structure

RTC time structure

RTC date structure

RTC alarm clock structure

Entering and exiting configuration functions

Experiment 1: Display Calendar

General configuration

RTC configuration

Test session

Experimental phenomena

Experimental session 2: Alarm clock

General configuration

RTC configuration

Test session

Experimental phenomena


RTC real-time clock

The RTC peripheral of STM32 is essentially a timer that continues to run after power failure. Similar to the general-purpose timer TIM peripheral, it can time and trigger interrupts.

Power outage refers to when the power supply VDD is disconnected, in order for the RTC peripherals to continue running after power outage, a lithium battery must be connected to supply power to the STM32 RTC and backup card through the Vbat pin. When the main power supply VDD is valid, VDD supplies power to the RTC peripherals. When CDD is powered off, Vbat supplies power to the RTC peripherals. However, no matter what power supply is used, the RTC data is stored in the backup domain belonging to the RTC. If the main power supply VDD and Vbat are both powered off, all data saved in the backup domain will be lost. In addition to the registers of the RTC module, the backup domain also has 42 16-bit registers that can save user program data when VDD is powered off. The data will not be cleared when the system is reset or the power is reset.

From the timer characteristics of RTC, it is a 32-bit up counter. There are three clock sources: HSE/128, LSI and LSE.

Using LSI or HSE/8 clock source, both clock sources will be affected when the main power supply VDD is powered off, so the normal operation of RTC cannot be guaranteed. ThereforeRTC generally uses LSE. In designs, the frequency is usually 32.768kHz.

When the main power supply VDD is valid and in standby mode, RTC can also configure the STM32 to exit standby mode when an alarm event occurs.

Functional block diagram

The light gray part belongs to the backup domain, which can continue to run under the driver of Vbat when VDD is powered off. Includes RTC divider, counter, and alarm clock controller.

If the VDD power supply is valid, RTC can trigger RTC_Second (second interrupt), RTC_Overflow (overflow event) and RTC_Alarm (alarm interrupt). It can be analyzed from the structure diagram that the timer overflow interrupt cannot be configured as an interrupt.

If STM32 is in standby mode, it can exit standby mode by an alarm event or WKUP event (external wake-up event, belonging to the EXTI module, not RTC).

The alarm event is triggered when the value of the counter RTC_CNT is equal to the value of the alarm register RTC_ALR.

All registers in the backup domain are 16-bit, and the registers related to RTC control are no exception. The 32-bit counter RTC_CNT consists of two registers, RTC_CNTL and RTC_CNTH.

When configuring the clock of the RTC module, the input RTCCLK of 32768Hz is usually divided by 32768 to obtain the actual clock driving the counter TR_CLK = RTCCLK / 32768 =1Hz. The counting period is 1s. The counter counts under the drive of TR_CLK, that is, the counter per second The value of RTC_CNT + 1.

Due to the existence of the backup domain, the RTC core has the characteristics of being completely independent of the APB1 interface, so access to the RTC register must comply with certain rules.

After the system is reset, access to the backup register and RTC is prohibited by default to prevent accidental writes to the backup area (BKP). Do the following to enable access to the backing registers and RTC:

Set the RCC_APB1ENR: PWREN, BKPEN bits to enable the power supply and backup interface clock.

Setting the PWR_CR:DBP bit enables access to the backing register and RTC.

After setting the backup register to be writable, when accessing the RTC through the APB1 interface for the first time, due to the difference in clock frequency, you must wait for synchronization between APB1 and the RTC peripheral to ensure that the read RTC register value is correct. If the RTC peripheral interface of APB1 is not closed after synchronization, there is no need to synchronize again.

If the core wants to perform any write operation on the RTC register, after the core writes the write instruction, the RTC module starts the formal write operation to the RTC register after 3 RTCCLK clocks. Since the frequency of RTCCLK is much lower than the core frequency, the RTC shutdown operation flag RTOFF must be checked after each operation. When this flag is set to 1, the write operation is officially completed.

Of course, the above operations all have corresponding library functions and do not require specific reference to registers.

UNIX timestamp

RTC_CNT is a 32-bit register, and the maximum value that can be stored is 2^32-1, which is approximately equal to 136 years.

If the value of the counter read at a certain time is the number of seconds in two days, and the counter is set to 0 at the time of 2011.1.1 0:0:0, it can be calculated that it is the time of 2011.1.3 0:0:0, and the counter will be at 2011 + Overflowed around 136 years. The time when the timer is set to 0 is the first year of counting, and the number of seconds relative to the first year of counting is the timestamp (the value of the counter).

Most operating systems use timestamps and timing years to calculate the current time. There is a standard: UNIX timestamp and UNIX timing years.

The first year of the UNIX clock is set to 1970.1.1 0:0:0 Greenwich Time.

In this timing system, signed 32-bit integer variables are used to save UNIX timestamps, so the highest bit represents the sign. The range that the timestamp can be displayed is smaller, and it will overflow at 2038.1.19 3:14:07 .

Searchable on the web: UNIX timestamp. Can be viewed in real time.

Initialization structure

The STM32 HAL library provides complete functions for RTC control.

typedef struct {
     uint32_t AsynchPrediv; /* Configure the asynchronous frequency division factor of RTC_CLK (0x00~0x7F), specifically configured by RTC_PRER: PREDIV_A[6:0] */
     uint32_t OutPut; /* RTCEx output channel setting, specify which signal is used as RTC output, prohibit output/alarm clock A output/alarm clock B output/wake-up output */
 } RTC_InitTypeDef;

RTC time structure

Used to set the initial time, the RTC time register RTC_TR is configured.

 typedef struct {
     uint8_t Hours; /* Hour settings. In 12-hour format, 0~11; in 24-hour format, 0~23 */
     uint8_t Minutes; /* Minute setting, 0~59 */
     uint8_t Seconds; /* Seconds setting, 0~59 */
 } RTC_TimeTypeDef;

RTC date structure

Used to set the initial date, the RTC date register RTC_DR is configured.

typedef struct {
     uint8_t WeekDay; /* Day of the week setting, 1~7 */
     uint8_t Month; /* Month setting, 1~12 */
     uint8_t Date; /* Date setting, 1~31 */
     uint8_t Year; /* Year setting, 0~99 */
 } RTC_DateTypeDef;

RTC alarm clock structure

Used to set the alarm time, the setting format is [week/date]-[hour]-[minute]-[second], 4 fields, each field can be set to valid or invalid (MASK). If the [Weekday/Date] field is masked, the alarm will sound every day.

typedef struct {
     RTC_TimeTypeDef AlarmTime; /* Set the value of the RTC time register: hours/minutes/seconds */
     uint32_t Alarm; /* RTC alarm clock selection: alarm clock A, alarm clock B */
 } RTC_AlarmTypeDef;

Enter and exit configuration function

/**
 * @brief Enter RTC configuration mode.
 * @param None
 * @retval None
 */
 void RTC_EnterConfigMode(void)
 {
     /* Set CNF bit to enter configuration mode */
     RTC->CRL |= RTC_CRL_CNF;
 }

/*
 * @brief Exit RTC configuration mode.
 * @param None
 * @retval None
 */
 void RTC_ExitConfigMode(void)
 {
     /* Clear the CNF bit to exit configuration mode */
     RTC->CRL &= (uint16_t)~((uint16_t)RTC_CRL_CNF);
 }

Experiment session 1: Show calendar

General configuration

USART1: with interrupt, supports printf output.

RTC configuration

RTC_HandleTypeDef hrtc;

/*RTC init function */
void MX_RTC_Init(void)
{
    RTC_TimeTypeDef sTime = {0};
    RTC_DateTypeDef DateToUpdate = {0};

    /* USER CODE BEGIN RTC_Init 1 */
    /* Determine whether to power on for the first time */
    if (HAL_RTCEx_BKUPRead( & amp;hrtc, RTC_BKP_DR1) != 0x5050)
    {
        HAL_RTCEx_BKUPWrite( & amp;hrtc, RTC_BKP_DR1, 0x5050); // Insert BKP value judgment
        /* USER CODE END RTC_Init 1 */

        /** Initialize RTC Only
        */
        hrtc.Instance = RTC;
        hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND;
        hrtc.Init.OutPut = RTC_OUTPUTSOURCE_NONE;
        if (HAL_RTC_Init( & amp;hrtc) != HAL_OK)
        {
            Error_Handler();
        }

        /** Initialize RTC and set the Time and Date
        */
        sTime.Hours = 0x0;
        sTime.Minutes = 0x0;
        sTime.Seconds = 0x0;
        if (HAL_RTC_SetTime( & amp;hrtc, & amp;sTime, RTC_FORMAT_BCD) != HAL_OK)
        {
            Error_Handler();
        }

        DateToUpdate.WeekDay = RTC_WEEKDAY_MONDAY;
        DateToUpdate.Month = RTC_MONTH_JANUARY;
        DateToUpdate.Date = 0x1;
        DateToUpdate.Year = 0x0;
        if (HAL_RTC_SetDate( & amp;hrtc, & amp;DateToUpdate, RTC_FORMAT_BCD) != HAL_OK)
        {
            Error_Handler();
        }

    /* USER CODE BEGIN RTC_Init 2 */
    }
    /* USER CODE END RTC_Init 2 */
}

void HAL_RTC_MspInit(RTC_HandleTypeDef *rtcHandle)
{
    if (rtcHandle->Instance == RTC)
    {
        HAL_PWR_EnableBkUpAccess(); // Cancel the write protection of the BKP area to save and time the time.
        /* Enable BKP CLK enable for backup registers */
        __HAL_RCC_BKP_CLK_ENABLE(); // Turn on the BKP clock
        /*RTC clock enable */
        __HAL_RCC_RTC_ENABLE();
    }
}

void HAL_RTC_MspDeInit(RTC_HandleTypeDef *rtcHandle)
{
    if (rtcHandle->Instance == RTC)
    {
        __HAL_RCC_RTC_DISABLE();
    }
}

Test session

#include "string.h"

uint8_t RxBuffer[20];

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart == & amp;huart1)
    {
    }
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart == & amp;huart1)
    {
//A5 5A 00 01 01 00 00 00
if(RxBuffer[0]==0xA5 & amp; & amp; RxBuffer[1]==0x5A)
{
RTC_DateTypeDef RtcDate;
RTC_TimeTypeDef RtcTime;
\t\t
RtcTime.Hours = RxBuffer[5];
RtcTime.Minutes = RxBuffer[6];
RtcTime.Seconds = RxBuffer[7];
\t\t\t
if (HAL_RTC_SetTime( & amp;hrtc, & amp;RtcTime, RTC_FORMAT_BCD) != HAL_OK)
{
Error_Handler();
}
\t\t\t
// Automatically correct the day of the week internally
RtcDate.WeekDay = RTC_WEEKDAY_MONDAY;
RtcDate.Month = RxBuffer[3];
RtcDate.Date = RxBuffer[4];
RtcDate.Year = RxBuffer[2];
\t\t\t
if (HAL_RTC_SetDate( & amp;hrtc, & amp;RtcDate, RTC_FORMAT_BCD) != HAL_OK)
{
Error_Handler();
}

memset(RxBuffer, 0, sizeof(RxBuffer));
HAL_UART_Receive_IT( & amp;huart1, (uint8_t *) & amp;RxBuffer, 8);
}
    }
}

void test(void)
{
RTC_DateTypeDef RtcDate;
RTC_TimeTypeDef RtcTime;
\t
HAL_UART_Receive_IT( & amp;huart1, (uint8_t *) & amp;RxBuffer, 8);
while(1)
{
HAL_RTC_GetTime( & amp;hrtc, & amp;RtcTime, RTC_FORMAT_BIN); // Read the time value
HAL_RTC_GetDate( & amp;hrtc, & amp;RtcDate, RTC_FORMAT_BIN); // Be sure to read the time first and then the date, so that the day of the week parameters can be corrected
printf("Real-time time: d- d- d d: d: d Week: -\r\
", 2000 + RtcDate.Year, RtcDate.Month,
RtcDate.Date, RtcTime.Hours, RtcTime.Minutes, RtcTime.Seconds, RtcDate.WeekDay);//Display date and time
HAL_Delay(1000);
}
}

Experimental Phenomenon

Experimental Session 2: Alarm Clock

General configuration

USART1: with interrupt, supports printf output.

Buzzer configuration.

RTC configuration

RTC_HandleTypeDef hrtc;

void MX_RTC_Init(void)
{
    RTC_TimeTypeDef sTime = {0};
    RTC_DateTypeDef DateToUpdate = {0};
    RTC_AlarmTypeDef sAlarm = {0};

    /* USER CODE BEGIN RTC_Init 1 */
    /* Determine whether to power on for the first time */
    if (HAL_RTCEx_BKUPRead( & amp;hrtc, RTC_BKP_DR1) != 0x5050)
    {
        HAL_RTCEx_BKUPWrite( & amp;hrtc, RTC_BKP_DR1, 0x5050); // Insert BKP value judgment
    /* USER CODE END RTC_Init 1 */

        /** Initialize RTC Only
        */
        hrtc.Instance = RTC;
        hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND;
        hrtc.Init.OutPut = RTC_OUTPUTSOURCE_ALARM;
        if (HAL_RTC_Init( & amp;hrtc) != HAL_OK)
        {
            Error_Handler();
        }

        /** Initialize RTC and set the Time and Date
        */
        sTime.Hours = 0x0;
        sTime.Minutes = 0x0;
        sTime.Seconds = 0x0;
        if (HAL_RTC_SetTime( & amp;hrtc, & amp;sTime, RTC_FORMAT_BCD) != HAL_OK)
        {
            Error_Handler();
        }

        DateToUpdate.WeekDay = RTC_WEEKDAY_MONDAY;
        DateToUpdate.Month = RTC_MONTH_JANUARY;
        DateToUpdate.Date = 0x1;
        DateToUpdate.Year = 0x0;
        if (HAL_RTC_SetDate( & amp;hrtc, & amp;DateToUpdate, RTC_FORMAT_BCD) != HAL_OK)
        {
            Error_Handler();
        }

        /** Enable the Alarm A
        */
        sAlarm.AlarmTime.Hours = 0x0;
        sAlarm.AlarmTime.Minutes = 0x1;
        sAlarm.AlarmTime.Seconds = 0x0;
        sAlarm.Alarm = RTC_ALARM_A;
        if (HAL_RTC_SetAlarm_IT( & amp;hrtc, & amp;sAlarm, RTC_FORMAT_BCD) != HAL_OK)
        {
            Error_Handler();
        }

    /* USER CODE BEGIN RTC_Init 2 */
    }

    /* USER CODE END RTC_Init 2 */
}

void HAL_RTC_MspInit(RTC_HandleTypeDef *rtcHandle)
{
    if (rtcHandle->Instance == RTC)
    {
        HAL_PWR_EnableBkUpAccess();
        /* Enable BKP CLK enable for backup registers */
        __HAL_RCC_BKP_CLK_ENABLE();
        /*RTC clock enable */
        __HAL_RCC_RTC_ENABLE();

        /*RTC interrupt Init */
        HAL_NVIC_SetPriority(RTC_Alarm_IRQn, 0, 0);
        HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn);
    }
}

void HAL_RTC_MspDeInit(RTC_HandleTypeDef *rtcHandle)
{
    if (rtcHandle->Instance == RTC)
    {
        __HAL_RCC_RTC_DISABLE();

        /*RTC interrupt Deinit */
        HAL_NVIC_DisableIRQ(RTC_Alarm_IRQn);
    }
}

Test session

#include "string.h"

uint8_t RxBuffer[20];

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart == & amp;huart1)
    {
    }
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart == & amp;huart1)
    {
//A5 5A 00 01 01 00 00 00
if(RxBuffer[0]==0xA5 & amp; & amp; RxBuffer[1]==0x5A)
{
RTC_DateTypeDef RtcDate;
RTC_TimeTypeDef RtcTime;
RTC_AlarmTypeDef RtcAlarm;
\t\t\t
RtcTime.Hours = RxBuffer[5];
RtcTime.Minutes = RxBuffer[6];
RtcTime.Seconds = RxBuffer[7];
\t\t\t
if (HAL_RTC_SetTime( & amp;hrtc, & amp;RtcTime, RTC_FORMAT_BCD) != HAL_OK)
{
Error_Handler();
}
\t\t\t
// Automatically correct the day of the week internally
RtcDate.WeekDay = RTC_WEEKDAY_MONDAY;
RtcDate.Month = RxBuffer[3];
RtcDate.Date = RxBuffer[4];
RtcDate.Year = RxBuffer[2];
\t\t\t
if (HAL_RTC_SetDate( & amp;hrtc, & amp;RtcDate, RTC_FORMAT_BCD) != HAL_OK)
{
Error_Handler();
}

\t\t\t
RtcAlarm.AlarmTime.Hours = RxBuffer[5];
RtcAlarm.AlarmTime.Minutes = RxBuffer[6];
RtcAlarm.AlarmTime.Seconds = RxBuffer[7] + 0x10;
RtcAlarm.Alarm = RTC_ALARM_A;
if (HAL_RTC_SetAlarm_IT( & amp;hrtc, & amp;RtcAlarm, RTC_FORMAT_BCD) != HAL_OK)
{
Error_Handler();
}
  
memset(RxBuffer, 0, sizeof(RxBuffer));
HAL_UART_Receive_IT( & amp;huart1, (uint8_t *) & amp;RxBuffer, 8);
}
    }
}

void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
HAL_GPIO_TogglePin(LED_G_GPIO_Port, LED_G_Pin);
}

void test(void)
{
RTC_DateTypeDef RtcDate;
RTC_TimeTypeDef RtcTime;
\t
initialization
\t
HAL_UART_Receive_IT( & amp;huart1, (uint8_t *) & amp;RxBuffer, 8);
while(1)
{
HAL_RTC_GetTime( & amp;hrtc, & amp;RtcTime, RTC_FORMAT_BIN); // Read the time value
HAL_RTC_GetDate( & amp;hrtc, & amp;RtcDate, RTC_FORMAT_BIN); // Be sure to read the time first and then the date, so that the day of the week parameters can be corrected
printf("Real-time time: d- d- d d: d: d Week: -\r\
", 2000 + RtcDate.Year, RtcDate.Month,
RtcDate.Date, RtcTime.Hours, RtcTime.Minutes, RtcTime.Seconds, RtcDate.WeekDay);//Display date and time
HAL_Delay(1000);
}
}

Experimental phenomena

Power on and run, and the LED will be off by default. After one minute the LED turns green.

Send A55A231101000000 through the serial port debugging assistant, and the LED will turn off after 10 seconds.