Time Fundamentals
Unix Timestamps
A Unix timestamp is the total number of seconds elapsed since 00:00:00 UTC on January 1, 1970, ignoring leap seconds. It is stored as a 32-bit or 64-bit integer. All time zones use the same timestamp value, with local time calculated by adding a timezone offset.
UTC/GMT
GMT (Greenwich Mean Time) is a timekeeping system based on Earth's rotation, dividing a full rotation into 24 hours. UTC (Coordinated Universal Time) is a more precise atomic clock-based system that adjusts via leap seconds to align with Earth's rotation.
Time Conversion
The C standard library <time.h> provides functions like mktime() (converts struct tm to timestamp) and localtime() (converts timestamp to local time struct) for time conversion.
BKP (Backup Registers) Overview
Purpose
Backup registers store user data that remains intact when VDD power is lost, powered by a VBAT backup battery. A tamper (intrusion) event clears all backup register contents. The BKP also supports RTC clock calibration and signal output.
Structure
- Data Registers: 16-bit registers for data storage (10 registers on F103 mid-capacity, 42 on F103 high-capacity/F407).
- Control/Status Registers: Manage BKP operation and report status.
- RTC Calibration Register: Adjusts RTC clock accuracy.
- Tamper Detection: A rising/falling edge on the TAMPER pin (PC13 on F103) triggers data clearing for security.
- Clock Output: Outputs RTC calibration, alarm, or second pulses via PC13.
RTC (Real-Time Clock) Overview
Purpose
RTC is an independent timer that provides clock and calendar functionality. It remains operational with VBAT power and retains data during system resets.
Architecture
- Clock Source: Three options: HSE/128 (8MHz/128), LSE (32.768kHz, external crystal), or LSI (40kHz, internal RC).
- Prescaler: 20-bit programmable prescaler to generate a 1Hz second signal.
- 32-bit Counter: Tracks Unix timestamps for calendar calculations.
- Interrupts: Supports second, counter overflow, and alarm interrupts.
Operation Notes
- To access BKP/RTC, enable PWR and BKP clocks via
RCC_APB1ENR, then enable access viaPWR_CR.DBP. - Wait for RSF (register sync flag) before reading RTC registers if the APB1 interface was disabled.
- Anter configuration mode (
RTC_CRL.CNF) to write to RTC_PR, RTC_CNT, or RTC_ALR registers. - Check RTOFF (register update flag) before writing to ensure previous writes are complete.
HAL Configuration Steps
- Enable clock sources (HSE/LSE) and configure system clock.
- Configure RCC peripheral clocks, selecting LSE as RTC clock source.
- Initialize RTC instance with prescaler and output settings.
- Anable BKP access and RTC clock.
Platform-Specific Details for STM32F407
F407 has enhanced RTC features including:
- 24-hour/12-hour format support.
- Calendar functionality (year, month, day, weekday).
- Dual alarm support.
- Periodic wakeup timer.
- Programmible output signal polarity and type.
Practical Examples
Clock Configuration (F103)
#include "stm32f1xx_hal.h"
RCC_OscInitTypeDef rcc_osc;
RCC_ClkInitTypeDef rcc_clk;
RCC_PeriphCLKInitTypeDef rcc_periph;
void configure_system_clock(void)
{
HAL_StatusTypeDef ret;
rcc_osc.OscillatorType = RCC_OSCILLATORTYPE_HSE | RCC_OSCILLATORTYPE_LSE;
rcc_osc.HSEState = RCC_HSE_ON;
rcc_osc.LSEState = RCC_LSE_ON;
rcc_osc.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
rcc_osc.PLL.PLLState = RCC_PLL_ON;
rcc_osc.PLL.PLLSource = RCC_PLLSOURCE_HSE;
rcc_osc.PLL.PLLMUL = RCC_PLL_MUL9;
ret = HAL_RCC_OscConfig(&rcc_osc);
if (ret != HAL_OK) while(1);
rcc_clk.ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
rcc_clk.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
rcc_clk.AHBCLKDivider = RCC_HCLK_DIV1;
rcc_clk.APB1CLKDivider = RCC_HCLK_DIV2;
rcc_clk.APB2CLKDivider = RCC_HCLK_DIV1;
ret = HAL_RCC_ClockConfig(&rcc_clk, FLASH_LATENCY_2);
if (ret != HAL_OK) while(1);
rcc_periph.PeriphClockSelection = RCC_PERIPHCLK_RTC;
rcc_periph.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
HAL_RCCEx_PeriphCLKConfig(&rcc_periph);
}
Clock Configuration (F407)
#include "stm32f4xx_hal.h"
RCC_OscInitTypeDef rcc_osc;
RCC_ClkInitTypeDef rcc_clk;
RCC_PeriphCLKInitTypeDef rcc_periph;
uint8_t configure_clock_f407(uint16_t pllm, uint16_t plln, uint16_t pllp, uint16_t pllq)
{
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
rcc_osc.OscillatorType = RCC_OSCILLATORTYPE_HSE | RCC_OSCILLATORTYPE_LSE;
rcc_osc.HSEState = RCC_HSE_ON;
rcc_osc.LSEState = RCC_LSE_ON;
rcc_osc.PLL.PLLState = RCC_PLL_ON;
rcc_osc.PLL.PLLSource = RCC_PLLSOURCE_HSE;
rcc_osc.PLL.PLLM = pllm;
rcc_osc.PLL.PLLN = plln;
rcc_osc.PLL.PLLP = pllp;
rcc_osc.PLL.PLLQ = pllq;
if (HAL_RCC_OscConfig(&rcc_osc) != HAL_OK) return 1;
rcc_clk.ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
rcc_clk.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
rcc_clk.AHBCLKDivider = RCC_HCLK_DIV1;
rcc_clk.APB1CLKDivider = RCC_HCLK_DIV4;
rcc_clk.APB2CLKDivider = RCC_HCLK_DIV2;
if (HAL_RCC_ClockConfig(&rcc_clk, FLASH_LATENCY_5) != HAL_OK) return 1;
if (HAL_GetREVID() == 0x1001) __HAL_FLASH_PREFETCH_BUFFER_ENABLE();
rcc_periph.PeriphClockSelection = RCC_PERIPHCLK_RTC;
rcc_periph.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
HAL_RCCEx_PeriphCLKConfig(&rcc_periph);
return 0;
}
Time Library Usage (F103)
#include "stm32f1xx_hal.h"
#include <time.h>
RTC_HandleTypeDef rtc_handle;
struct tm time_struct;
time_t timestamp;
void init_rtc(void)
{
rtc_handle.Instance = RTC;
rtc_handle.Init.AsynchPrediv = RTC_AUTO_1_SECOND;
rtc_handle.Init.OutPut = RTC_OUTPUTSOURCE_NONE;
HAL_RTC_Init(&rtc_handle);
time_struct.tm_year = 2024 - 1900;
time_struct.tm_mon = 7;
time_struct.tm_mday = 11;
time_struct.tm_hour = 14;
time_struct.tm_min = 29;
time_struct.tm_sec = 30;
timestamp = mktime(&time_struct);
__HAL_RTC_WRITEPROTECTION_DISABLE(&rtc_handle);
WRITE_REG(rtc_handle.Instance->CNTH, timestamp >> 16);
WRITE_REG(rtc_handle.Instance->CNTL, timestamp & 0x0000FFFF);
__HAL_RTC_WRITEPROTECTION_ENABLE(&rtc_handle);
}
struct tm* get_current_time(void)
{
timestamp = (READ_REG(rtc_handle.Instance->CNTH) << 16) |
(READ_REG(rtc_handle.Instance->CNTL) & 0x0000FFFF);
return localtime(×tamp);
}
void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc)
{
__HAL_RCC_RTC_ENABLE();
__HAL_RCC_BKP_CLK_ENABLE();
}
Time Library Usage (F407)
#include "stm32f4xx_hal.h"
RTC_HandleTypeDef rtc_handle;
RTC_TimeTypeDef time_cfg;
RTC_DateTypeDef date_cfg;
void init_rtc_f407(void)
{
rtc_handle.Instance = RTC;
rtc_handle.Init.HourFormat = RTC_HOURFORMAT_24;
rtc_handle.Init.AsynchPrediv = 0x7F;
rtc_handle.Init.SynchPrediv = 0xFF;
rtc_handle.Init.OutPut = RTC_OUTPUT_DISABLE;
rtc_handle.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_LOW;
rtc_handle.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
HAL_RTC_Init(&rtc_handle);
time_cfg.Hours = 17;
time_cfg.Minutes = 46;
time_cfg.Seconds = 20;
time_cfg.TimeFormat = RTC_HOURFORMAT12_AM;
time_cfg.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
time_cfg.StoreOperation = RTC_STOREOPERATION_RESET;
HAL_RTC_SetTime(&rtc_handle, &time_cfg, RTC_FORMAT_BIN);
date_cfg.WeekDay = 2;
date_cfg.Month = 9;
date_cfg.Date = 3;
date_cfg.Year = 24;
HAL_RTC_SetDate(&rtc_handle, &date_cfg, RTC_FORMAT_BIN);
}
void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc)
{
__HAL_RCC_RTC_ENABLE();
HAL_PWR_EnableBkUpAccess();
__HAL_RCC_PWR_CLK_ENABLE();
}
Alarm Functionality (F103)
struct tm alarm_struct;
void set_rtc_alarm(void)
{
alarm_struct.tm_year = 2024 - 1900;
alarm_struct.tm_mon = 7;
alarm_struct.tm_mday = 11;
alarm_struct.tm_hour = 14;
alarm_struct.tm_min = 29;
alarm_struct.tm_sec = 40;
timestamp = mktime(&alarm_struct);
__HAL_RTC_WRITEPROTECTION_DISABLE(&rtc_handle);
WRITE_REG(rtc_handle.Instance->ALRH, timestamp >> 16);
WRITE_REG(rtc_handle.Instance->ALRL, timestamp & 0x0000FFFF);
__HAL_RTC_WRITEPROTECTION_ENABLE(&rtc_handle);
HAL_Delay(20);
__HAL_RTC_ALARM_CLEAR_FLAG(&rtc_handle, RTC_FLAG_ALRAF);
__HAL_RTC_ALARM_ENABLE_IT(&rtc_handle, RTC_IT_ALRA);
__HAL_RTC_ALARM_EXTI_ENABLE_IT();
__HAL_RTC_ALARM_EXTI_ENABLE_RISING_EDGE();
}
void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc)
{
__HAL_RCC_RTC_ENABLE();
__HAL_RCC_BKP_CLK_ENABLE();
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);
HAL_NVIC_SetPriority(RTC_Alarm_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn);
}
void RTC_Alarm_IRQHandler(void)
{
HAL_RTC_AlarmIRQHandler(&rtc_handle);
}
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
// Alarm callback implementation
}
Alarm Functionality (F407)
void set_alarm(uint8_t week, uint8_t hour, uint8_t min, uint8_t sec)
{
RTC_AlarmTypeDef alarm_handle;
alarm_handle.AlarmTime.Hours = hour;
alarm_handle.AlarmTime.Minutes = min;
alarm_handle.AlarmTime.Seconds = sec;
alarm_handle.AlarmTime.SubSeconds = 0;
alarm_handle.AlarmTime.TimeFormat = RTC_HOURFORMAT12_AM;
alarm_handle.AlarmMask = RTC_ALARMMASK_NONE;
alarm_handle.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_NONE;
alarm_handle.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_WEEKDAY;
alarm_handle.AlarmDateWeekDay = week;
alarm_handle.Alarm = RTC_ALARM_A;
HAL_RTC_SetAlarm_IT(&rtc_handle, &alarm_handle, RTC_FORMAT_BIN);
HAL_NVIC_SetPriority(RTC_Alarm_IRQn, 1, 2);
HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn);
}
void RTC_Alarm_IRQHandler(void)
{
HAL_RTC_AlarmIRQHandler(&rtc_handle);
}
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
// Alarm callback implementation
}
Seconds Interrupt (F103)
void enable_second_interrupt(void)
{
HAL_RTCEx_SetSecond_IT(&rtc_handle);
}
void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc)
{
__HAL_RCC_RTC_ENABLE();
__HAL_RCC_BKP_CLK_ENABLE();
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);
HAL_NVIC_SetPriority(RTC_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(RTC_IRQn);
}
void RTC_IRQHandler(void)
{
HAL_RTCEx_RTCIRQHandler(&rtc_handle);
}
void HAL_RTCEx_RTCEventCallback(RTC_HandleTypeDef *hrtc)
{
// Second interrupt callback implementation
}
Periodic Wakeup Timer (F407)
void set_wakeup_timer(uint8_t clk_div, uint16_t reload)
{
__HAL_RTC_WAKEUPTIMER_CLEAR_FLAG(&rtc_handle, RTC_FLAG_WUTF);
HAL_RTCEx_SetWakeUpTimer_IT(&rtc_handle, reload, clk_div);
HAL_NVIC_SetPriority(RTC_WKUP_IRQn, 1, 2);
HAL_NVIC_EnableIRQ(RTC_WKUP_IRQn);
}
void RTC_WKUP_IRQHandler(void)
{
HAL_RTCEx_WakeUpTimerIRQHandler(&rtc_handle);
}
void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)
{
// Wakeup timer callback implementation
}
Output Functionality
For F103:
void configure_rtc_output(void)
{
rtc_handle.Init.OutPut = RTC_OUTPUTSOURCE_SECOND; // Output second pulses
HAL_RTC_Init(&rtc_handle);
}
For F407:
void configure_rtc_output_f407(void)
{
rtc_handle.Init.OutPut = RTC_OUTPUT_SECOND;
rtc_handle.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
rtc_handle.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
HAL_RTC_Init(&rtc_handle);
}
Backup Register Read/Write
void write_backup_registers(void)
{
for (uint8_t i = 1; i <= 10; i++)
{
HAL_RTCEx_BKUPWrite(&rtc_handle, i, i * 0x123);
}
}
void read_backup_registers(void)
{
for (uint8_t i = 1; i <= 10; i++)
{
uint16_t value = HAL_RTCEx_BKUPRead(&rtc_handle, i);
// Process value
}
}
Tamper (Intrusion) Event Handling
#include "stm32f1xx_hal.h"
RTC_TamperTypeDef tamper_handle;
void init_tamper_detection(void)
{
tamper_handle.Tamper = RTC_TAMPER_1;
tamper_handle.Trigger = RTC_TAMPERTRIGGER_HIGHLEVEL;
__HAL_RTC_TAMPER_CLEAR_FLAG(&rtc_handle, RTC_FLAG_TAMP1F);
HAL_RTCEx_SetTamper_IT(&rtc_handle, &tamper_handle);
}
void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc)
{
__HAL_RCC_RTC_ENABLE();
__HAL_RCC_BKP_CLK_ENABLE();
HAL_NVIC_SetPriority(TAMPER_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(TAMPER_IRQn);
}
void TAMPER_IRQHandler(void)
{
HAL_RTCEx_TamperIRQHandler(&rtc_handle);
}
void HAL_RTCEx_Tamper1EventCallback(RTC_HandleTypeDef *hrtc)
{
HAL_RTCEx_DeactivateTamper(&rtc_handle, RTC_TAMPER_1);
// Intrusion event handling
}