STM32 Realtime Clock Example

Real-time clock

A real-time clock (RTC) is an electronic device (most often in the form of an integrated circuit) that measures the passage of time.

Although the term often refers to the devices in the personal computer, servers and embedded system, RTCs are present in almost any electronic device which needs to keep accurate time.

Components Required:

  • STM32F303CB/STM32 blue pill boards – 1
  • ST-LINK – 2
  • FTDI – 2
  • Breadboard – 2
  • Jumper wires

A Real-Time Clock (RTC) is a timing element dedicated to keeping time. In many applications, especially where precise timed-operations are needed to be performed, an RTC is a very useful tool. Examples of such applications apart from clocks and watches include washing machines, medicine dispensers, data loggers, etc. Basically, an RTC is a timer-counter but unlike other timers of an MCU, it is much more accurate. Previous to this post, we explored STM32 timers but those were useful for applications like PWM generation, time-bases and other waveform-related tasks. Those were not suitable for precise time-keeping. In most 8-bit MCUs like the regular PICs and AVRs, there are no built-in RTC modules and so we need to use dedicated RTC chips like the popular DS1302 or PCF8563 when we need an onboard precise time-keeping device. Those chips also need some additional circuitry, wiring and circuit board space. At present, however, most modern MCUs come packed with literally every possible hardware a designer may think of. It is only up to a designer to decide which resources to use from a modern-era micro to meet a specific design goal. Gone are the days when MCUs were manufactured for application-specific requirements and also gone are the days of implementing and involving multiple assets in a design. Thus cost, time and space are dramatically reduced, resulting in smarter, sleeker and smaller affordable devices. Fortunately, STM32s are on that list of those modern era microcontrollers. STM32 MCUs come with built-in RTC modules that require no additional hardware support. This tutorial covers basic features of STM32’s internal RTC and how to use it for time-keeping applications.

Feature of STM32 RTC Block

The embedded RTC of an STM32 micro is an independent binary-coded-decimal (BCD) timer counter. The RTC core consists of counters, Prescaler, clock dividers, alarm data registers, etc. Like with any standard RTC chip, the embedded RTC can be used to provide a full-featured software-based calendar along with alarm functions. However more needs to be done on the software end rather than the hardware end. When using RTC chips, it is only required to read or write individual date-time registers. In an STM32 micro, we need to do more than that as no separate date-time registers exist.

Resetting or waking up the MCU from a sleep/standby mode does not reinitializes time once set. It gets even better if there is a battery backup on the battery backup (VBAT) pin. All DVDs of an STM32 can be turned off or in other words, the entire MCU core can be fully shut down but the battery backup keeps the RTC and the backup domain running. Thus time is not varied or lost during powered down and sleep modes. Key features of the STM32 embedded RTC are highlighted below:

  • Programmable Prescaler: division factor up to.
  • 32-bit programmable counter for long-term measurement
  • Two separate clocks: PCLK1 for the APB1 interface and RTC clock.
  • The RTC clock source could be any of the following ones
    • HSE clock divided by 128.
    • LSE oscillator clock.
    • LSI oscillator clock.

And now, let’s build this system step-by-step

Step1: Open CubeIDE & Create New Project

Step2: Choose The Target MCU & Double-Click Its Name

Step3: Configure The RTC Peripheral

CODE

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <string.h>
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
char time[30];
char date[30];
RTC_TimeTypeDef sTime;
 RTC_DateTypeDef sDate;


Set date and time

static void MX_RTC_Init(void)
{

  /* USER CODE BEGIN RTC_Init 0 */

  /* USER CODE END RTC_Init 0 */

  RTC_TimeTypeDef sTime = {0};
  RTC_DateTypeDef sDate = {0};

  /* USER CODE BEGIN RTC_Init 1 */

  /* USER CODE END RTC_Init 1 */
  /** 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 */
    
  /* USER CODE END Check_RTC_BKUP */

  /** Initialize RTC and set the Time and Date 
  */
  sTime.Hours = 0x10;
  sTime.Minutes = 0x20;
  sTime.Seconds = 0x30;
  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_MONDAY;
  sDate.Month = RTC_MONTH_MARCH;
  sDate.Date = 0x16;
  sDate.Year = 0x21;

  if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BCD) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN RTC_Init 2 */

  /* USER CODE END RTC_Init 2 */

}

Get Date and time

{
    /* USER CODE END WHILE */
HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);


sprintf(date,"Date: %02d.%02d.%02d\t",sDate.Date,sDate.Month,sDate.Year);
sprintf(time,"Time: %02d.%02d.%02d\r\n",sTime.Hours,sTime.Minutes,sTime.Seconds);



HAL_UART_Transmit(&huart3, (uint8_t *)date, sizeof(date), 300);
HAL_UART_Transmit(&huart3, (uint8_t *)time, sizeof(time), 300);
HAL_Delay(1000);
  }

Final code

/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <string.h>
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
char time[30];
char date[30];
/* USER CODE END PTD */
RTC_TimeTypeDef sTime;
 RTC_DateTypeDef sDate;

while (1)
  {
    /* USER CODE END WHILE */
HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);


sprintf(date,"Date: %02d.%02d.%02d\t",sDate.Date,sDate.Month,sDate.Year);
sprintf(time,"Time: %02d.%02d.%02d\r\n",sTime.Hours,sTime.Minutes,sTime.Seconds);



HAL_UART_Transmit(&huart3, (uint8_t *)date, sizeof(date), 300);
HAL_UART_Transmit(&huart3, (uint8_t *)time, sizeof(time), 300);
HAL_Delay(1000);
  }

static void MX_RTC_Init(void)
{

  /* USER CODE BEGIN RTC_Init 0 */

  /* USER CODE END RTC_Init 0 */

  RTC_TimeTypeDef sTime = {0};
  RTC_DateTypeDef sDate = {0};

  /* USER CODE BEGIN RTC_Init 1 */

  /* USER CODE END RTC_Init 1 */
  /** 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 */
    
  /* USER CODE END Check_RTC_BKUP */

  /** Initialize RTC and set the Time and Date 
  */
  sTime.Hours = 0x10;
  sTime.Minutes = 0x20;
  sTime.Seconds = 0x30;
  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_MONDAY;
  sDate.Month = RTC_MONTH_MARCH;
  sDate.Date = 0x16;
  sDate.Year = 0x21;

  if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BCD) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN RTC_Init 2 */

  /* USER CODE END RTC_Init 2 */

}

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *

%d bloggers like this: