RTC

RTC: Introduction


RTC: RTC and Backup Registers

The STM32 contains two power domains:

The RTC and the Independent Watchdog are located within the backup domain.


RTC: RTC and Backup Registers

Backup Registers
Memory locations that can be used to hold critical data values during the Standby mode when the main power domain is switched off.

In low power mode both the RTC and the independent watchdog can be kept running and may be used to wake up the system or to perform a chip reset.


RTC: RTC and Wakeup


RTC : Backup Registers And Tamper Pin


RTC : Backup Registers And Tamper Pin


RTC : Calendar


RTC : Main Features


RTC : Calendar Features

image.png

image.png


RTC : Programmable Alarm


RTC : Periodic auto-wakeup


RTC : Timestamp functionality


RTC : Tamper detection


RTC : Clock Source


image.png

image.png

RTC Calendar

image.png

#define PWREN                 (1U<<28)
#define CR_DBP                (1U<<8)

#define CSR_LSION             (1U<<0)
#define CSR_LSIRDY            (1U<<1)

#define BDCR_BDRST            (1U<<16)
#define BDCR_RTCEN            (1U<<15)

/* Write protection keys */
#define RTC_WRITE_PROTECTION_ENABLE_1  ((uint8_t)0xCAU)
#define RTC_WRITE_PROTECTION_ENABLE_2  ((uint8_t)0x53U)

/* INIT / status flags */
#define RTC_INIT_MASK         0xFFFFFFFFU
#define ISR_INITF             (1U<<6)
#define ISR_RSF               (1U<<5)

/* Date/time helpers (BCD packed nibble masks in screenshots) */
#define WEEKDAY_FRIDAY        ((uint8_t)0x05U)
#define MONTH_DECEMBER        ((uint8_t)0x12U)
#define TIME_FORMAT_PM        (1U<<22)
#define CR_FMT                (1U<<6)

/* Prescaler values (from screenshots) */
#define RTC_ASYNCH_PREDIV     ((uint32_t)0x7F)
#define RTC_SYNCH_PREDIV      ((uint32_t)0xF9)

/* ------------- Static helpers ------------- */
uint8_t rtc_convert_bin2bcd(uint8_t value)
{
    return (uint8_t)((((value) / 10U) << 4U) | ((value) % 10U));
}

uint8_t rtc_convert_bcd2bin(uint8_t value)
{
    return (uint8_t)(((uint8_t)((value) & (uint8_t)0xF0U) >> (uint8_t)0x4U) * 10U + ((value) & (uint8_t)0x0FU));
}

uint32_t rtc_date_get_day(void)
{
    return (uint32_t)((READ_BIT(RTC->DR, (RTC_DR_DT | RTC_DR_DU))) >> RTC_DR_DU_Pos);
}

uint32_t rtc_date_get_year(void)
{
    return (uint32_t)((READ_BIT(RTC->DR, (RTC_DR_YT | RTC_DR_YU))) >> RTC_DR_YU_Pos);
}

uint32_t rtc_date_get_month(void)
{
    return (uint32_t)((READ_BIT(RTC->DR, (RTC_DR_MT | RTC_DR_MU))) >> RTC_DR_MU_Pos);
}

uint32_t rtc_time_get_second(void)
{
    return (uint32_t)(READ_BIT(RTC->TR, (RTC_TR_ST | RTC_TR_SU)) >> RTC_TR_SU_Pos);
}

uint32_t rtc_time_get_minute(void)
{
    return (uint32_t)((READ_BIT(RTC->TR, (RTC_TR_MNT | RTC_TR_MNU))) >> RTC_TR_MNU_Pos);
}

uint32_t rtc_time_get_hour(void)
{
    return (uint32_t)((READ_BIT(RTC->TR, (RTC_TR_HT | RTC_TR_HU))) >> RTC_TR_HU_Pos);
}

static void rtc_set_asynch_prescaler(uint32_t AsynchPrescaler)
{
    MODIFY_REG(RTC->PRER, RTC_PRER_PREDIV_A, (AsynchPrescaler << RTC_PRER_PREDIV_A_Pos));
}

static void rtc_set_synch_prescaler(uint32_t SynchPrescaler)
{
    MODIFY_REG(RTC->PRER, RTC_PRER_PREDIV_S, (SynchPrescaler << RTC_PRER_PREDIV_S_Pos));
}

static void rtc_date_config(uint32_t WeekDay, uint32_t Day, uint32_t Month, uint32_t Year)
{
    register uint32_t temp = 0U;

    temp =  (WeekDay << RTC_DR_WDU_Pos)
          | (((Year  & 0xF0U) << (RTC_DR_YT_Pos - 4U)) | ((Year  & 0x0FU) << RTC_DR_YU_Pos))
          | (((Month & 0xF0U) << (RTC_DR_MT_Pos - 4U)) | ((Month & 0x0FU) << RTC_DR_MU_Pos))
          | (((Day   & 0xF0U) << (RTC_DR_DT_Pos - 4U)) | ((Day   & 0x0FU) << RTC_DR_DU_Pos));

    MODIFY_REG(RTC->DR,
               (RTC_DR_WDU | RTC_DR_MT | RTC_DR_MU | RTC_DR_DT | RTC_DR_DU | RTC_DR_YT | RTC_DR_YU),
               temp);
}

static void rtc_time_config(uint32_t Format12_24, uint32_t Hours, uint32_t Minutes, uint32_t Seconds)
{
    register uint32_t temp = 0U;

    temp =  Format12_24
          | (((Hours   & 0xF0U) << (RTC_TR_HT_Pos - 4U)) | ((Hours   & 0x0FU) << RTC_TR_HU_Pos))
          | (((Minutes & 0xF0U) << (RTC_TR_MNT_Pos - 4U)) | ((Minutes & 0x0FU) << RTC_TR_MNU_Pos))
          | (((Seconds & 0xF0U) << (RTC_TR_ST_Pos  - 4U)) | ((Seconds & 0x0FU) << RTC_TR_SU_Pos));

    MODIFY_REG(RTC->TR,
               (RTC_TR_PM | RTC_TR_HT | RTC_TR_HU | RTC_TR_MNT | RTC_TR_MNU | RTC_TR_ST | RTC_TR_SU),
               temp);
}

/* -------- Init mode control & flags -------- */
static void _rtc_enable_init_mode(void)
{
    RTC->ISR = RTC_INIT_MASK;
}

static void _rtc_disable_init_mode(void)
{
    RTC->ISR = ~RTC_INIT_MASK;
}

static uint8_t _rtc_isActiveflag_init(void)
{
    return ((RTC->ISR & ISR_INITF) == ISR_INITF);
}

static uint8_t _rtc_isActiveflag_rs(void)
{
    return ((RTC->ISR & ISR_RSF) == ISR_RSF);
}

static uint8_t rtc_init_seq(void)
{
    _rtc_enable_init_mode();
    while(_rtc_isActiveflag_init() != 1U) {}
    return 1U;
}

static uint8_t wait_for_synchro(void)
{
    /* Clear RSF */
    RTC->ISR &= ~ISR_RSF;
    /* Wait until registers synchronized */
    while(_rtc_isActiveflag_rs() != 1U) {}
    return 1U;
}

static uint8_t exit_init_seq(void)
{
    _rtc_disable_init_mode();
    return wait_for_synchro();
}

void rtc_init(void)
{
    /* Enable clock access to PWR */
    RCC->APB1ENR |= PWREN;

    /* Enable Backup access to config RTC */
    PWR->CR |= CR_DBP;

    /* Enable Low Speed Internal (LSI) */
    RCC->CSR |= CSR_LSION;

    /* Wait for LSI to be ready */
    while((RCC->CSR & CSR_LSIRDY) != CSR_LSIRDY) {}

    /* Force backup domain reset */
    RCC->BDCR |= BDCR_BDRST;

    /* Release backup domain reset */
    RCC->BDCR &= ~BDCR_BDRST;

    /* Set RTC clock source to LSI*/
    RCC->BDCR &= ~(1U<<8);
    RCC->BDCR |=  (1U<<9);

    /* Enable the RTC */
    RCC->BDCR |= BDCR_RTCEN;

    /* Disable RTC registers write protection */
    RTC->WPR = RTC_WRITE_PROTECTION_ENABLE_1;
    RTC->WPR = RTC_WRITE_PROTECTION_ENABLE_2;

    /* Enter initialization mode */
    if(rtc_init_seq() != 1U)
    {
        /* Error handling placeholder */
    }

    /* Set desired date : Friday December 29th 2016 (BCD: 0x29 day, 0x16 year) */
    rtc_date_config(WEEKDAY_FRIDAY, 0x29U, MONTH_DECEMBER, 0x16U);

    /* Set desired time : 11:59:55 PM (BCD: 0x11 hour, 0x59 minute, 0x55 second) */
    rtc_time_config(TIME_FORMAT_PM, 0x11U, 0x59U, 0x55U);

    /* Set hour format */
    RTC->CR |= CR_FMT;

    /* Set Asynch prescaler */
    rtc_set_asynch_prescaler(RTC_ASYNCH_PREDIV);

    /* Set Synch prescaler */
    rtc_set_synch_prescaler(RTC_SYNCH_PREDIV);

    /* Exit the initialization mode */
    exit_init_seq();

    /* Re-enable (lock) RTC registers write protection (commonly 0xFF) */
    RTC->WPR = 0xFFU;
}

#include "stdio.h"

uint8_t time_buff[20] = {0};
uint8_t date_buff[20] = {0};

static void display_rtc_calendar(void)
{
    /* Display format :  hh : mm : ss */
    sprintf((char*)time_buff, "%.2d : %.2d : %.2d",
            rtc_convert_bcd2bin(rtc_time_get_hour()),
            rtc_convert_bcd2bin(rtc_time_get_minute()),
            rtc_convert_bcd2bin(rtc_time_get_second()));

    /* Display format :  mm : dd : yy */
    sprintf((char*)date_buff, "%.2d - %.2d - %.2d",
            rtc_convert_bcd2bin(rtc_date_get_month()),
            rtc_convert_bcd2bin(rtc_date_get_day()),
            rtc_convert_bcd2bin(rtc_date_get_year()));
}

int main(void)
{
    rtc_init();

    while(1)
    {
        display_rtc_calendar();
        for (int i =0; i < 330000; i++) {}
    }
}

RTC Alarm

RTC Timestamp

RTC Wakeup Timer

Standby and Wakeup

#define PWR_MODE_STANDBY        (PWR_CR_PDDS)
#define WK_PIN                  (1U<<0)

static void set_power_mode(uint32_t pwr_mode);

void wakeup_pin_init(void)
{
    //Enable clock for GPIOA
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;

    //Set PA0 as input pin
    GPIOA->MODER &= ~(1U<<0);
    GPIOA->MODER &= ~(1U<<1);

    //No pull
    GPIOA->PUPDR &= ~(1U<<0);
    GPIOA->PUPDR &= ~(1U<<1);
}

void standby_wakeup_pin_setup(void)
{
    /*Wait for wakeup pin to be released*/
    while(get_wakeup_pin_state() == 0){}

    /*Disable wakeup pin*/
    PWR->CSR &= ~(1U<<8);

    /*Clear all wakeup flags*/
    PWR->CR |= (1U<<2);

    /*Enable wakeup pin*/
    PWR->CSR |= (1U<<8);

    /*Enter StandBy mode*/
    set_power_mode(PWR_MODE_STANDBY);

    /*Set SLEEPDEEP bit in the CortexM System Control Register*/
    SCB->SCR |= (1U<<2);

    /*Wait for interrupt*/
    __WFI();
}

void get_wakeup_pin_state(void)
{
    return ((GPIOA->IDR & WK_PIN) == WK_PIN);
}

static void set_power_mode(uint32_t pwr_mode)
{
    MODIFY_REG(PWR->CR,
               (PWR_CR_PDDS | PWR_CR_LPDS | PWR_CR_FPDS | PWR_CR_LPLVDS | PWR_CR_MRLVDS),
               pwr_mode);
}

// ...main.c

uint8_t g_btn_press;
static void check_reset_source(void);
void clock_config(void);
static void led_blink_forever(void);

/*Press the blue push-button to enter StandBy mode*/
/**@note: PA0 is wakeup pin and it is active low
 * connect a jumper wire from PA0 to ground in normal mode,
 * pull out jumper wire and connect it to 3.3v to cause a change in logic which will
 * in turn trigger the WAKEUP**/

int main(void)
{
    uart2_tx_init();
    wakeup_pin_init();
    check_reset_source();
    gpio_interrupt_init(); // init the button interrupt
    led_init();

    led_blink_forever();
    while(1)
    {
    }
}

static void led_blink_forever(void)
{
    while(1)
    {
        GPIOA->ODR ^= (1U << 5);
        for(int i = 0; i < 90000; i++){}
    }
}

static void check_reset_source(void)
{
    /*Enable clock access to PWR*/
    RCC->APB1ENR |= RCC_APB1ENR_PWREN;

    if ((PWR->CSR & PWR_CSR_SBF) == (PWR_CSR_SBF))
    {
        /*Clear Standby flag*/
        PWR->CR |= PWR_CR_CSBF;

        led_blink();
        printf("System resume from Standby.....\n\r");

        /*Wait for wakeup pin to be released*/
        while (get_wakeup_pin_state() == 0) {}
    }

    /*Check and Clear Wakeup flag*/
    if ((PWR->CSR & PWR_CSR_WUF) == PWR_CSR_WUF)
    {
        PWR->CR |= PWR_CR_CWUF;
    }
}

static void btn_callback(void)
{
    standby_wakeup_pin_setup();
}

void EXTI15_10_IRQHandler(void)
{
    if ((EXTI->PR & EXTI_IMR_IM13) == (EXTI_IMR_IM13))
    {
        /*Clear EXTI flag*/
        EXTI->PR = EXTI_IMR_IM13;

        //Do something...
        btn_callback();
    }
}

RTC Wakeup Timer Driver