From 333706ccb9d4204b732637031d47fce93a77e1f4 Mon Sep 17 00:00:00 2001 From: "S.J.R. van Schaik" Date: Wed, 26 Jul 2017 11:30:04 +0200 Subject: [PATCH] stm32f0: rtc: set up the real-time clock and implement functions to get and set the time --- include/rtc.h | 7 ++ source/platform/stm32f0/Makefile | 1 + source/platform/stm32f0/rtc.c | 146 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 154 insertions(+) create mode 100644 include/rtc.h create mode 100644 source/platform/stm32f0/rtc.c diff --git a/include/rtc.h b/include/rtc.h new file mode 100644 index 0000000..7d6aae0 --- /dev/null +++ b/include/rtc.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +int rtc_init(struct tm *time); +int rtc_get_time(struct tm *time); +time_t rtc_time(void); diff --git a/source/platform/stm32f0/Makefile b/source/platform/stm32f0/Makefile index 6bea991..9865e32 100644 --- a/source/platform/stm32f0/Makefile +++ b/source/platform/stm32f0/Makefile @@ -1,2 +1,3 @@ obj-y += source/platform/stm32f0/gpio.o obj-y += source/platform/stm32f0/rcc.o +obj-y += source/platform/stm32f0/rtc.o diff --git a/source/platform/stm32f0/rtc.c b/source/platform/stm32f0/rtc.c new file mode 100644 index 0000000..6d76eec --- /dev/null +++ b/source/platform/stm32f0/rtc.c @@ -0,0 +1,146 @@ +#include +#include +#include + +#include +#include + +static void rtc_set_clock_source(enum rcc_osc clock_source) +{ + uint32_t reg; + + switch (clock_source) { + case RCC_LSE: + /* Turn on the LSE and wait until it stabilised. */ + RCC_BDCR |= RCC_BDCR_LSEON; + + while ((reg = (RCC_BDCR & RCC_BDCR_LSERDY)) == 0); + + /* Choose LSE as the RTC clock source. */ + RCC_BDCR &= ~BITS(8, 9); + RCC_BDCR |= BIT(8); + break; + case RCC_LSI: + /* Turn on the LSI and wait until it stabilised. */ + RCC_CSR |= RCC_CSR_LSION; + + while ((reg = (RCC_CSR & RCC_CSR_LSIRDY)) == 0); + + /* Choose LSI as the RTC clock source. */ + RCC_BDCR &= ~BITS(8, 9); + RCC_BDCR |= BIT(9); + break; + case RCC_HSE: + /* Turn on the HSE and wait until it stabilised. */ + RCC_CR |= RCC_CR_HSEON; + + while ((reg = (RCC_CR & RCC_CR_HSERDY)) == 0); + + /* Choose HSE as the RTC clock source. */ + RCC_BDCR &= ~BITS(8, 9); + RCC_BDCR |= BITS(8, 9); + break; + default: + /* Unusable clock sources. Turn off the clock source for RTC. */ + RCC_BDCR &= ~BITS(8, 9); + break; + } +} + +int rtc_get_time(struct tm *time) +{ + uint32_t rtc_tr, rtc_dr; + + rtc_tr = RTC_TR; + rtc_dr = RTC_DR; + + time->tm_sec = (unsigned char)(rtc_tr & 0xF); + time->tm_sec += (unsigned char)(((rtc_tr >> 4) & 0xF) * 10); + time->tm_min = (unsigned char)((rtc_tr >> 8) & 0xF); + time->tm_min += (unsigned char)(((rtc_tr >> 12) & 0xF) * 10); + time->tm_hour = (unsigned char)((rtc_tr >> 16) & 0xF); + time->tm_hour += (unsigned char)(((rtc_tr >> 20) & 0xF) * 10); + + time->tm_mday = (unsigned char)(rtc_dr & 0xF); + time->tm_mday += (unsigned char)(((rtc_dr >> 4) & 0x3) * 10); + time->tm_mon = (unsigned char)((rtc_dr >> 8) & 0xF); + time->tm_mon += (unsigned char)(((rtc_dr >> 12) & 0x1) * 10); + time->tm_year = (unsigned char)((rtc_dr >> 16) & 0xF); + time->tm_year += (unsigned char)(((rtc_dr >> 20) & 0xF) * 10); + time->tm_year += 100; + + return 0; +} + +static int rtc_set_time(struct tm *time) +{ + uint32_t rtc_tr, rtc_dr; + uint32_t year; + + if (!time) + return -1; + + year = time->tm_year - 100; + + rtc_tr = (time->tm_sec % 10); + rtc_tr |= (time->tm_sec / 10) << 4; + rtc_tr |= (time->tm_min % 10) << 8; + rtc_tr |= (time->tm_min / 10) << 12; + rtc_tr |= (time->tm_hour % 10) << 16; + rtc_tr |= (time->tm_hour / 10) << 20; + + rtc_dr = (time->tm_mday % 10); + rtc_dr |= (time->tm_mday / 10) << 4; + rtc_dr |= (time->tm_mon % 10) << 8; + rtc_dr |= (time->tm_mon / 10) << 12; + rtc_dr |= (year % 10) << 16; + rtc_dr |= (year / 10) << 20; + + RTC_TR = rtc_tr; + RTC_DR = rtc_dr; + + return 0; +} + +int rtc_init(struct tm *time) +{ + /* Turn on power block to enable unlocking. */ + rcc_periph_clock_enable(RCC_PWR); + pwr_disable_backup_domain_write_protect(); + + /* Reset the RTC. */ + RCC_BDCR |= RCC_BDCR_BDRST; + RCC_BDCR &= ~RCC_BDCR_BDRST; + + /* Enable the LSI and set it as the clock source for RTC. */ + rcc_osc_on(RCC_LSI); + rcc_wait_for_osc_ready(RCC_LSI); + rtc_set_clock_source(RCC_LSI); + + /* Enable the RTC clock */ + rcc_periph_clock_enable(RCC_RTC); + + /* Unlock the registers in the RTC domain. */ + rtc_unlock(); + + /* Enter init mode. */ + RTC_ISR |= RTC_ISR_INIT; + + while ((RTC_ISR & RTC_ISR_INITF) == 0); + + /* Configure the RTC. */ + rtc_set_prescaler(0x1ff, 0x7f); + rtc_set_time(time); + + /* Exit init mode. */ + RTC_ISR &= ~(RTC_ISR_INIT); + + /* Lock the registers. */ + rtc_lock(); + pwr_enable_backup_domain_write_protect(); + + /* Wait for the synchronisation. */ + rtc_wait_for_synchro(); + + return 0; +}