Bare metal C Ch15 RTC - Hongjayeong-02/Basic_SoC_implementation GitHub Wiki

Ch15. Real-Time Clock (RTC)

ํ•œ ์ค„ ์š”์•ฝ: LSI๋ฅผ RTC clock source๋กœ ์‚ฌ์šฉํ•ด ์‹œ๊ฐ„/๋‚ ์งœ๋ฅผ ์„ค์ •ํ•˜๊ณ  BCD ๊ฐ’์„ ์ฝ์–ด 1์ดˆ๋งˆ๋‹ค UART๋กœ ์ถœ๋ ฅํ•œ๋‹ค.

1. ํ•ต์‹ฌ ์ด๋ก 

RTC๋Š” ์ผ๋ฐ˜-purpose timer์™€ ๋‹ฌ๋ฆฌ backup domain์— ์†ํ•˜๋Š” peripheral์ด๋‹ค. Notion์—์„œ๋Š” main system๊ณผ ๋ถ„๋ฆฌ๋œ ์˜์—ญ์—์„œ ์‹œ๊ฐ„์„ ์œ ์ง€ํ•˜๋Š” ์žฅ์น˜๋กœ ์ •๋ฆฌํ–ˆ๋‹ค.

์ดˆ๊ธฐํ™”์— ํ•„์š”ํ•œ ์ถ”๊ฐ€ ์ ˆ์ฐจ

RTC register๋Š” ์ผ๋ฐ˜ peripheral๋ณด๋‹ค ๋ณดํ˜ธ ์ ˆ์ฐจ๊ฐ€ ๋งŽ๋‹ค.

PWR clock enable
   โ†“
DBP = backup domain write enable
   โ†“
LSI enable + ready wait
   โ†“
RTC clock source select
   โ†“
RTC enable
   โ†“
WPR unlock sequence
   โ†“
Initialization mode
   โ†“
Time / Date / Prescaler ์„ค์ •
   โ†“
Initialization mode exit
   โ†“
Synchronization wait
   โ†“
WPR lock

Write protection key

0xCA
  โ†“
0x53

์ˆœ์„œ๋กœ RTC->WPR์— ๊ธฐ๋กํ•ด write protection์„ ํ•ด์ œํ•œ๋‹ค.

BCD

RTC์˜ Time/Date field๋Š” BCD ํ˜•์‹์„ ์‚ฌ์šฉํ•œ๋‹ค.

์˜ˆ:

0x59 โ†’ decimal 59

๋ณ€ํ™˜ ์ฝ”๋“œ๋Š”:

((value >> 4U) * 10U) + (value & 0x0FU)

ํ•ต์‹ฌ register

Register | ์—ญํ•  -- | -- RTC->TR | Time RTC->DR | Date RTC->ISR | init/synchronization flags RTC->PRER | prescaler RTC->WPR | write protection RCC->BDCR | backup-domain / RTC clock PWR->CR | backup domain write access

2. ๋™์ž‘ ํ๋ฆ„

LSI enable
   โ”‚
   โ–ผ
Backup Domain ์ ‘๊ทผ ํ—ˆ์šฉ
   โ”‚
   โ–ผ
RTC source = LSI
   โ”‚
   โ–ผ
WPR unlock
   โ”‚
   โ–ผ
INIT mode
   โ”‚
   โ”œโ”€ Date
   โ”œโ”€ Time
   โ””โ”€ Prescaler
   โ”‚
   โ–ผ
INIT exit + sync
   โ”‚
   โ–ผ
TR / DR read
   โ”‚
   โ–ผ
BCD โ†’ Decimal
   โ”‚
   โ–ผ
UART output

3. ์‹ค์ œ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ


Ch15_rtc/

โ”œโ”€โ”€ Inc/rtc.h

โ”œโ”€โ”€ Src/rtc.c

โ”œโ”€โ”€ Inc/uart.h

โ”œโ”€โ”€ Src/uart.c

โ”œโ”€โ”€ Inc/systick.h

โ”œโ”€โ”€ Src/systick.c

โ”œโ”€โ”€ Src/main.c

syscalls.c, sysmem.c, startup file, linker script, CubeIDE metadata/build output์€ Ch04์—์„œ ๋‹ค๋ฃฌ ๊ณตํ†ต ๊ธฐ๋ฐ˜์ด๋ฏ€๋กœ ์ด ํŽ˜์ด์ง€์—์„œ๋Š” ๋ฐ˜๋ณตํ•˜์ง€ ์•Š๋Š”๋‹ค. ์•„๋ž˜์—๋Š” ์ด chapter ๊ธฐ๋Šฅ ๊ตฌํ˜„์— ์ง์ ‘ ์‚ฌ์šฉํ•œ source/header๋ฅผ ์ „๋ถ€ ๊ธฐ๋กํ•œ๋‹ค.

4. ์ „์ฒด ์ฝ”๋“œ

Inc/rtc.h

#ifndef RTC_H__
#define RTC_H__

#include <stdint.h> #include "stm32f4xx.h"

void rtc_init(void);

uint8_t rtc_convert_bcd2dec(uint8_t value);

uint32_t rtc_date_get_day(void); uint32_t rtc_date_get_month(void); uint32_t rtc_date_get_year(void);

uint32_t rtc_time_get_hour(void); uint32_t rtc_time_get_minute(void); uint32_t rtc_time_get_second(void);

#endif

Src/rtc.c

#include "rtc.h"

#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)

#define RTC_WRITE_PROTECTION_KEY_1 ((uint8_t)0xCAU) #define RTC_WRITE_PROTECTION_KEY_2 ((uint8_t)0x53U) #define RTC_INIT_MASK 0xFFFFFFFFU #define ISR_INITF (1U << 6) #define ISR_RSF (1U << 5)

#define WEEKDAY_FRIDAY ((uint8_t)0x05U) #define TIME_FORMAT_PM (1U << 22) #define CR_FMT (1U << 6)

#define RTC_ASYNCH_PREDIV ((uint32_t)0x7F) #define RTC_SYNCH_PREDIV ((uint32_t)0x00F9)

static void _rtc_enable_init_mode(void) { RTC->ISR = RTC_INIT_MASK; } static void _rtc_disable_init_mode(void) { RTC->ISR = (uint32_t)(~RTC_INIT_MASK); } static uint8_t _rtc_isActiveflag_init(void) { return (uint8_t)((RTC->ISR & ISR_INITF) == ISR_INITF); } static uint8_t _rtc_isActiveflag_rs(void) { return (uint8_t)((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) { RTC->ISR &= ~ISR_RSF; while (_rtc_isActiveflag_rs() != 1U) {} return 1U; }

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

static void rtc_date_config(uint32_t WeekDay, uint32_t Day, uint32_t Month, uint32_t Year) { uint32_t temp; 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) { uint32_t temp; 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); }

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); }

void rtc_init(void) { RCC->APB1ENR |= PWREN; PWR->CR |= CR_DBP;

RCC-&gt;CSR |= CSR_LSION;
while ((RCC-&gt;CSR &amp; CSR_LSIRDY) != CSR_LSIRDY) {}

RCC-&gt;BDCR |= BDCR_BDRST;
RCC-&gt;BDCR &amp;= ~BDCR_BDRST;

RCC-&gt;BDCR &amp;= ~(1U &lt;&lt; 8);
RCC-&gt;BDCR |= (1U &lt;&lt; 9);

RCC-&gt;BDCR |= BDCR_RTCEN;

RTC-&gt;WPR = RTC_WRITE_PROTECTION_KEY_1;
RTC-&gt;WPR = RTC_WRITE_PROTECTION_KEY_2;

if (rtc_init_seq() != 1U) { /* ์‹คํŒจ ์ฒ˜๋ฆฌ ์ƒ๋žต */ }

/* ์›ํ•˜๋Š” ์ดˆ๊ธฐ ๋‚ ์งœ/์‹œ๊ฐ„์œผ๋กœ ๋ฐ”๊ฟ”๋„ ๋จ (BCD๋กœ ์ง€์ •) */
rtc_date_config(WEEKDAY_FRIDAY, 0x19, 0x09, 0x26);
rtc_time_config(TIME_FORMAT_PM, 0x11, 0x59, 0x55);

RTC-&gt;CR |= CR_FMT;

rtc_set_asynch_prescaler(RTC_ASYNCH_PREDIV);
rtc_set_synch_prescaler(RTC_SYNCH_PREDIV);

exit_init_seq();

RTC-&gt;WPR = 0xFF;

}

uint8_t rtc_convert_bcd2dec(uint8_t value) { return (uint8_t)(((value >> 4U) * 10U) + (value & 0x0FU)); }

uint32_t rtc_time_get_hour(void) { return ((RTC->TR & (RTC_TR_HT | RTC_TR_HU)) >> RTC_TR_HU_Pos); } uint32_t rtc_time_get_minute(void) { return ((RTC->TR & (RTC_TR_MNT | RTC_TR_MNU)) >> RTC_TR_MNU_Pos); } uint32_t rtc_time_get_second(void) { return ((RTC->TR & (RTC_TR_ST | RTC_TR_SU)) >> RTC_TR_SU_Pos); }

uint32_t rtc_date_get_day(void) { return ((RTC->DR & (RTC_DR_DT | RTC_DR_DU)) >> RTC_DR_DU_Pos); } uint32_t rtc_date_get_month(void) { return ((RTC->DR & (RTC_DR_MT | RTC_DR_MU)) >> RTC_DR_MU_Pos); } uint32_t rtc_date_get_year(void) { return ((RTC->DR & (RTC_DR_YT | RTC_DR_YU)) >> RTC_DR_YU_Pos); }

Inc/uart.h

#ifndef UART_H
#define UART_H

#include "stm32f4xx.h"

void uart_init(void); void uart_write_string(const char *s); int __io_putchar(int ch);

#endif

Src/uart.c

#include <stdint.h>
#include "uart.h"

#define GPIODEN (1U << 3) #define USART3EN (1U << 18)

#define DBG_UART_BAUDRATE 115200U #define SYS_FREQ 16000000U #define APB1_CLK SYS_FREQ

#define CR1_TE (1U << 3) #define CR1_UE (1U << 13) #define SR_TXE (1U << 7)

static void uart_write(int ch); static uint16_t compute_uart_bd(uint32_t periph_clk, uint32_t baudrate); static void uart_set_baudrate(uint32_t periph_clk, uint32_t baudrate);

int __io_putchar(int ch) { uart_write(ch); return ch; }

void uart_init(void) { RCC->AHB1ENR |= GPIODEN;

GPIOD-&gt;MODER &amp;= ~(0x3UL &lt;&lt; (8 * 2));
GPIOD-&gt;MODER |=  (0x2UL &lt;&lt; (8 * 2));
GPIOD-&gt;MODER &amp;= ~(0x3UL &lt;&lt; (9 * 2));
GPIOD-&gt;MODER |=  (0x2UL &lt;&lt; (9 * 2));

GPIOD-&gt;AFR[1] &amp;= ~(0xFUL &lt;&lt; 0);
GPIOD-&gt;AFR[1] |=  (0x7UL &lt;&lt; 0);   /* PD8 -&gt; AF7 (USART3_TX) */
GPIOD-&gt;AFR[1] &amp;= ~(0xFUL &lt;&lt; 4);
GPIOD-&gt;AFR[1] |=  (0x7UL &lt;&lt; 4);   /* PD9 -&gt; AF7 (USART3_RX) */

RCC-&gt;APB1ENR |= USART3EN;

uart_set_baudrate(APB1_CLK, DBG_UART_BAUDRATE);

USART3-&gt;CR1 = CR1_TE;
USART3-&gt;CR1 |= CR1_UE;

}

static void uart_write(int ch) { while (!(USART3->SR & SR_TXE)) { } USART3->DR = (ch & 0xFF); }

void uart_write_string(const char *s) { while (*s) uart_write((int)*s++); }

static uint16_t compute_uart_bd(uint32_t periph_clk, uint32_t baudrate) { return (uint16_t)((periph_clk + (baudrate / 2U)) / baudrate); }

static void uart_set_baudrate(uint32_t periph_clk, uint32_t baudrate) { USART3->BRR = compute_uart_bd(periph_clk, baudrate); }

Inc/systick.h

#ifndef SYSTICK_H_
#define SYSTICK_H_

#include <stdint.h> #include "stm32f4xx.h"

void systick_msec_delay(uint32_t delay);

#endif /* SYSTICK_H_ */

Src/systick.c

#include "systick.h"

#define CTRL_ENABLE (1UL << 0) #define CTRL_CLKSRC (1UL << 2) #define CTRL_COUNTFLAG (1UL << 16)

/* ์ฝ”์–ดํด๋Ÿญ 16MHz(๊ธฐ๋ณธ HSI) ๊ธฐ์ค€ 1ms๋‹น ํด๋Ÿญ ์‚ฌ์ดํด ์ˆ˜ */ #define ONE_MSEC_LOAD 16000UL

void systick_msec_delay(uint32_t delay) { SysTick->LOAD = ONE_MSEC_LOAD - 1UL; SysTick->VAL = 0UL; SysTick->CTRL = CTRL_CLKSRC; SysTick->CTRL |= CTRL_ENABLE;

for (uint32_t i = 0; i &lt; delay; i++)
{
    while ((SysTick-&gt;CTRL &amp; CTRL_COUNTFLAG) == 0UL) { }
}

SysTick-&gt;CTRL = 0UL;

}

Src/main.c

#include <stdio.h>
#include "rtc.h"

extern void uart_init(void); void systick_msec_delay(uint32_t delay);

static void display_rtc_calendar(void) { uint8_t hh = rtc_convert_bcd2dec((uint8_t)rtc_time_get_hour()); uint8_t mm = rtc_convert_bcd2dec((uint8_t)rtc_time_get_minute()); uint8_t ss = rtc_convert_bcd2dec((uint8_t)rtc_time_get_second()); uint8_t mon = rtc_convert_bcd2dec((uint8_t)rtc_date_get_month()); uint8_t day = rtc_convert_bcd2dec((uint8_t)rtc_date_get_day()); uint8_t yy = rtc_convert_bcd2dec((uint8_t)rtc_date_get_year());

printf("Time : %02d:%02d:%02d  Date : %02d-%02d-20%02d\r\n",
       hh, mm, ss, mon, day, yy);

}

int main(void) { uart_init(); rtc_init();

while (1)
{
    display_rtc_calendar();
    systick_msec_delay(1000);
}

}

5. ์‹ค์Šต ๊ฒฐ๊ณผ

Notion ๊ธฐ๋ก ๊ธฐ์ค€ ์‹ค์ œ ๋ณด๋“œ ๊ฒ€์ฆ ์™„๋ฃŒ.

  • ์ดˆ/๋ถ„/์‹œ ์ฆ๊ฐ€ ํ™•์ธ

  • ๋‚ ์งœ rollover 09-19 โ†’ 09-20 ํ™•์ธ

  • UART๋กœ 1์ดˆ๋งˆ๋‹ค calendar ์ถœ๋ ฅ ํ™•์ธ

text = 2260 bytes
data = 0 bytes
bss  = 2560 bytes
warning ์—†์Œ

Notion์—๋Š” ์ดˆ๊ธฐํ™” ์งํ›„ ์ž ๊น 01-01-2000์ด ์ถœ๋ ฅ๋  ์ˆ˜ ์žˆ๋Š” ํ˜„์ƒ์„ RTC shadow register ๋™๊ธฐํ™” ํŠน์„ฑ์œผ๋กœ ๊ธฐ๋กํ•ด๋‘์—ˆ๋‹ค.

6. ํ•ต์‹ฌ ์ •๋ฆฌ

  • RTC๋Š” backup domain ์ ‘๊ทผ๊ณผ write-protection ํ•ด์ œ ์ ˆ์ฐจ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

  • Time/Date๋Š” BCD field๋กœ ์ €์žฅ๋œ๋‹ค.

  • ์ดˆ๊ธฐํ™” ๋ชจ๋“œ์™€ synchronization ์ ˆ์ฐจ๋ฅผ ๊ฑฐ์ณ์•ผ ํ•œ๋‹ค.

โš ๏ธ **GitHub.com Fallback** โš ๏ธ