Bare metal C Ch15 RTC - Hongjayeong-02/Basic_SoC_implementation GitHub Wiki
ํ ์ค ์์ฝ: LSI๋ฅผ RTC clock source๋ก ์ฌ์ฉํด ์๊ฐ/๋ ์ง๋ฅผ ์ค์ ํ๊ณ BCD ๊ฐ์ ์ฝ์ด 1์ด๋ง๋ค UART๋ก ์ถ๋ ฅํ๋ค.
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
0xCA
โ
0x53
์์๋ก RTC->WPR์ ๊ธฐ๋กํด write protection์ ํด์ ํ๋ค.
RTC์ Time/Date field๋ BCD ํ์์ ์ฌ์ฉํ๋ค.
์:
0x59 โ decimal 59
๋ณํ ์ฝ๋๋:
((value >> 4U) * 10U) + (value & 0x0FU)
LSI enable
โ
โผ
Backup Domain ์ ๊ทผ ํ์ฉ
โ
โผ
RTC source = LSI
โ
โผ
WPR unlock
โ
โผ
INIT mode
โ
โโ Date
โโ Time
โโ Prescaler
โ
โผ
INIT exit + sync
โ
โผ
TR / DR read
โ
โผ
BCD โ Decimal
โ
โผ
UART output
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๋ฅผ ์ ๋ถ ๊ธฐ๋กํ๋ค.
#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
#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->CSR |= CSR_LSION;
while ((RCC->CSR & CSR_LSIRDY) != CSR_LSIRDY) {}
RCC->BDCR |= BDCR_BDRST;
RCC->BDCR &= ~BDCR_BDRST;
RCC->BDCR &= ~(1U << 8);
RCC->BDCR |= (1U << 9);
RCC->BDCR |= BDCR_RTCEN;
RTC->WPR = RTC_WRITE_PROTECTION_KEY_1;
RTC->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->CR |= CR_FMT;
rtc_set_asynch_prescaler(RTC_ASYNCH_PREDIV);
rtc_set_synch_prescaler(RTC_SYNCH_PREDIV);
exit_init_seq();
RTC->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); }
#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
#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->MODER &= ~(0x3UL << (8 * 2));
GPIOD->MODER |= (0x2UL << (8 * 2));
GPIOD->MODER &= ~(0x3UL << (9 * 2));
GPIOD->MODER |= (0x2UL << (9 * 2));
GPIOD->AFR[1] &= ~(0xFUL << 0);
GPIOD->AFR[1] |= (0x7UL << 0); /* PD8 -> AF7 (USART3_TX) */
GPIOD->AFR[1] &= ~(0xFUL << 4);
GPIOD->AFR[1] |= (0x7UL << 4); /* PD9 -> AF7 (USART3_RX) */
RCC->APB1ENR |= USART3EN;
uart_set_baudrate(APB1_CLK, DBG_UART_BAUDRATE);
USART3->CR1 = CR1_TE;
USART3->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);
}
#ifndef SYSTICK_H_
#define SYSTICK_H_
#include <stdint.h>
#include "stm32f4xx.h"
void systick_msec_delay(uint32_t delay);
#endif /* SYSTICK_H_ */
#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 < delay; i++)
{
while ((SysTick->CTRL & CTRL_COUNTFLAG) == 0UL) { }
}
SysTick->CTRL = 0UL;
}
#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);
}
}
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 ๋๊ธฐํ ํน์ฑ์ผ๋ก ๊ธฐ๋กํด๋์๋ค.
RTC๋ backup domain ์ ๊ทผ๊ณผ write-protection ํด์ ์ ์ฐจ๊ฐ ํ์ํ๋ค.
Time/Date๋ BCD field๋ก ์ ์ฅ๋๋ค.
์ด๊ธฐํ ๋ชจ๋์ synchronization ์ ์ฐจ๋ฅผ ๊ฑฐ์ณ์ผ ํ๋ค.