Tip of the RTC iceberg | STM32F4 | RTC | CMSIS

What is a Real-time Clock?


Check out this article, Real-time clock from Wikipedia. It can get you started on what it is. In this tutorial, we will be talking about the integrated RTC peripheral on the STM32F401 microcontroller. 












RTC Peripheral on the STM32F401RE

The RTC is an independent Binary Coded Decimal counter. The RTC has two 32-bit registers that contain the
   
    > S
econds,
    > Minutes,
    Hours (12- or 24-hour format),
    Day (Day of Week),
    Date (Day of Month),
    Month,
    > Year,

expressed in binary-coded decimal format (BCD).

After the backup domain reset, all RTC registers are protected against possible parasitic write accesses. As long as the supply voltage remains in the operating range, the RTC never stops, regardless of the device status (Run mode, low-power mode, or under reset).

Registers used

    1. RTC Time Register (RTC_TR)
    2. RTC Date Register (RTC_DR)
    3. RTC Control Register (RTC_CR)
    4. RTC Initialization and Status Register (RTC_ISR)
    5. RTC Prescale Register (RTC_PRER)
    6. RTC Write Protection Register (RTC_WPR)
    7. PWR Power Control Register (PWR_CR)
    8. RCC Clock Configuration Register (RCC_CFGR)
    9. RCC Backup Domain Control Register (RCC_BDCR)

The RTC Peripheral registers used here are for Setting the Time, Date, Prescaler factor and for controlling and monitoring the status of the RTC Peripheral. In addition, we use the RTC Write Protection Register to protect the RTC registers against unwanted write access. We also need the Power Contol Register to enable Write Access and the RCC Backup Domain Control Register to select the clock source for RTC.

Walkthrough

Including Header files


In addition to the CMSIS header file and the stdio.h, we also include Delay.h and UART.h for creating user-specific time delays and use UART2 as a debug console respectively.

User Defined Functions


The RTC_Init() initializes the RTC peripheral to the penultimate stage. From there, we can set the Time and Date according to our requirements (mostly the current time) using the RTC_SetTime() and RTC_SetDate() functions. Once we call the RTC_Start() function, the clock starts ticking and it increments like a normal clock would do. Whenever we need the current Date and Time, a call to the RTC_Get_Date() and RTC_Get_Time() function returns the Seconds, Minutes, and Hours into a Time structure and Day, Date, Month and Year into a Date structure.

Structure Definition


This introduces two new structure types defined as RTC_Date and RTC_Time. RTC_Date is used to hold the date and weekday information while RTC_Time is used to hold the time and hour format information. Now we can access the Time and Date using these structures throughout the tutorial.

RTC Initialization


Disable Backup Domain Write protection by setting the 8th bit of the PWR_CR register. Divide the HSE clock by a factor of 25 to supply an input of 1 MHz to the RTC. Select HSE as the Clock source to RTC and Enable it in the RCC_BDCR register.

For unlocking the write protection of RTC Registers, write '0xCA' followed by '0x53' to the RTC_WPR register. Then set the RTC into Initialization Mode ( RCC_ISR Bit 7 ) and wait for the RTC to be set into Initialization mode by waiting for the Initialization Flag, INITF to be set (RCC_ISR Bit 6 ).

Clear the RTC Prescaler register of its reset value. Set the Asynchronous and Synchronous Prescaler factors, here '0x7C' and '0x1F3F' respectively.

Setting the Date


For setting the Date, create integer variables to hold the Units and Tens position for the Year, Month, and Day. Also, an integer to specify the weekday. The RTC_Set_Date() function accepts a struct of type RTC_Date as its argument whose members we have seen above. We will specify the year in two digits, hence we need to subtract 2000 from the year provided.

Consider the input year is 2023, 
                                                                    D->year = 23

Therefore,
                                                                    yt  =  23/10  = 2 (since it is an integer )
                                                                    yu = 23%10 = 3

Similarly for the rest of the variables. Then we will arrange these assigned variables into a 32-bit integer space by using bit shift operators. If you look at the second to last statement, you can see that the 

            Bits[23:20]: yt
            Bits[19:16] : yu
            Bits[15:13] : wd
            Bit  [12]      : mt
            Bits[11:8]   : mu
            Bits[5:4]     : dt
            Bits[3:0]     : du     

Then, write this value into the RTC Date Register (RTC_DR). This sets the Date as per requirement.

Setting the Time


Similar to RTC_SetDate(), we can go through RTC_SetTime() in the same way. But, in addition to the struct of type RTC_Time, there is an integer also passed as an argument, representing the time format used, that is 12 or 24-hour format. We take each assigned variable, shift it into a single 32-bit integer space, and then finally write it into the RTC Time Register (RTC_TR).

Starting the RTC Peripheral


Starting the RTC requires a few more steps. First, we Set the Hour Format and then Enable Time Stamp. Since we programmed the RTC registers in the previous steps, we needed the RTC Peripheral in the Initialization mode. But now we are going to activate the counter, so we need the RTC Peripheral to be reset into Free Running Mode (RCC_ISR Bit 7 ). Then we will enable Write Protection again, which we earlier disabled to modify the RTC registers, by writing a wrong key into the RTC_WPR register, say 0x55. Then we will block access to the RTC and RTC Backup registers by Disabling Write Access to the Backup Domain Registers in the PWR_CR register ( Bit 8 ).

Fetch the current Date


After starting the RTC Peripheral, we can call the RTC_Get_Date() function to reterieve the current Date. It is the reversal of the RTC_SetDate() function. First, we read the RTC Date Register and store the tens and units of year, month, and date into corresponding variables. Then, combine the tens and unit values to get the correct year, month, and date. Then, write these back into the struct passed as an argument to this function. This will store the Date in the struct of type RTC_Date.

Fetch the Current Time


Similar to the RTC_Get_Date() function, we can analyse the RTC_Get_Time() function.Instead of year, month and date, here we have hours, minutes and seconds. We repeat the same process we did before and we get the current time from the peripheral registers.

Test Run


For ease of demonstration, we will use the initialize the UART2 peripheral as a serial monitor, along with two character arrays of 100 bytes long, namely date[] and time[]. We will store the retrieved time and date data into these two buffer arrays to display it on the serial terminal. Create a struct X of type RTC_Time and set the time as 03:00:00 PM. Then create a struct Y of type RTC_Date and set the date as 03/09/2023 (DD/MM/YYYY). Call the Initialization function, Set the Time and Date and finally start the peripheral. From here on there will not be any write to the RTC Registers. But we can read from it always.

Infinity


Retrieve the Date and Time. Store the date into the date[] buffer and send it to the serial terminal. Similarly, store the time into the time[] buffer and send it to the serial terminal. Wait for a thousand milliseconds (or, 1 second ) and repeat this process.

Popular posts from this blog

Cifradopro: A baremetal Hardware Security Module using the STM32L4S5 Cortex-M4 MCU

SignGlove: Bridging the Communication Gap for Paralyzed Patients

Capturing images using the Digital Camera Interface | STM32L4 | DCMI | CMSIS