From 96c34313046117ffe55cbc5523f13461e1ee0867 Mon Sep 17 00:00:00 2001 From: Alexei Date: Mon, 25 Mar 2019 12:29:32 +0300 Subject: [PATCH] RTC for stm32f1 --- boards/bluepill/doc.txt | 1 + boards/common/stm32f103c8/Makefile.features | 1 + cpu/stm32_common/periph/rtc.c | 262 +++++++++++++++++++- 3 files changed, 263 insertions(+), 1 deletion(-) diff --git a/boards/bluepill/doc.txt b/boards/bluepill/doc.txt index 0b7140bd9d1c5..3b0bc94579601 100644 --- a/boards/bluepill/doc.txt +++ b/boards/bluepill/doc.txt @@ -47,6 +47,7 @@ flash][Flashsize]. | USB | no | | Timer | yes | | CAN | no | +| RTC | yes | ## Flashing diff --git a/boards/common/stm32f103c8/Makefile.features b/boards/common/stm32f103c8/Makefile.features index 057577d4b9913..309975061c263 100644 --- a/boards/common/stm32f103c8/Makefile.features +++ b/boards/common/stm32f103c8/Makefile.features @@ -2,6 +2,7 @@ FEATURES_PROVIDED += periph_adc FEATURES_PROVIDED += periph_i2c FEATURES_PROVIDED += periph_pwm +FEATURES_PROVIDED += periph_rtc FEATURES_PROVIDED += periph_spi FEATURES_PROVIDED += periph_timer FEATURES_PROVIDED += periph_uart diff --git a/cpu/stm32_common/periph/rtc.c b/cpu/stm32_common/periph/rtc.c index 143e85f5b344e..95d7ecbe458a4 100644 --- a/cpu/stm32_common/periph/rtc.c +++ b/cpu/stm32_common/periph/rtc.c @@ -3,6 +3,7 @@ * 2016 Laksh Bhatia * 2016-2017 OTA keys S.A. * 2017 Freie Universität Berlin + * 2019 Alexei Bezborodov * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level @@ -19,6 +20,7 @@ * @author Laksh Bhatia * @author Vincent Dupont * @author Hauke Petersen + * @author Alexei Bezborodov * @} */ @@ -27,7 +29,12 @@ #include "stmclk.h" #include "periph/rtc.h" -/* this implementation does not work for the stm32f1 */ +#define ENABLE_DEBUG (0) +#include "debug.h" + +/** Print out a message that function is not yet implementd */ +#define NOT_YET_IMPLEMENTED() DEBUG("%s not yet implemented\n", __func__) + #if !defined(CPU_FAM_STM32F1) /* map some CPU specific register names */ @@ -322,4 +329,257 @@ void ISR_NAME(void) cortexm_isr_end(); } +#elif defined(CPU_FAM_STM32F1) + +void rtc_init(void) +{ + DEBUG("[RTC rtc_init]\n"); + + // Enable APB1 clocks + RCC->APB1ENR |= RCC_APB1ENR_PWREN | RCC_APB1ENR_BKPEN; + + // Disable backup domain write protection + PWR->CR |= PWR_CR_DBP; + + if ((RCC->BDCR & RCC_BDCR_RTCEN) != RCC_BDCR_RTCEN) // if RTC clock disabled + { + DEBUG("[RTC initialize]\n"); + + // Resets the entire Backup domain + RCC->BDCR |= RCC_BDCR_BDRST; + // Reset not activated + RCC->BDCR &= ~RCC_BDCR_BDRST; + + //RTC clock enabled + //LSE oscillator clock used as RTC clock10: LSI oscillator clock used as RTC clock + RCC->BDCR |= RCC_BDCR_RTCEN | RCC_BDCR_RTCSEL_LSE; + + // Second interrupt is enabled. + RTC->CRH |= RTC_CRH_SECIE; + + // Enter configuration mode. + RTC->CRL |= RTC_CRL_CNF; + + //If the input clock frequency (fRTCCLK) is 32.768 kHz, write 7FFFh in this register to get a signal period of 1 second. + RTC->PRLH = 0; + RTC->PRLL = 0x8000; //тактирование от внешнего кварца + + // Calibration clock output disable (for enable in pin PC13 "AFIO->EVCR = 0xAD" and "BKP->RTCCR | = BKP_RTCCR_CCO | BKP_RTCCR_ASOS | BKP_RTCCR_ASOE;") + AFIO->EVCR = 0; + BKP->RTCCR = 0; + + // Exit configuration mode (start update of RTC registers). + RTC->CRL &= ~RTC_CRL_CNF; + + RCC->BDCR |= RCC_BDCR_LSEON; + while ((RCC->BDCR & RCC_BDCR_LSEON) != RCC_BDCR_LSEON){} + + // Wait registers synchronize flag + RTC->CRL &= (uint16_t)~RTC_CRL_RSF; + while((RTC->CRL & RTC_CRL_RSF) != RTC_CRL_RSF){} + } +} + +static void _rtc_enter_config_mode(void) +{ + // Wait until the RTOFF bit is 1 (no RTC register writes ongoing). + while ((RTC->CRL & RTC_CRL_RTOFF) == 0); + + // Enter configuration mode. + RTC->CRL |= RTC_CRL_CNF; +} + +static void _rtc_exit_config_mode(void) +{ + // Exit configuration mode. + RTC->CRL &= ~RTC_CRL_CNF; + + // Wait until the RTOFF bit is 1 (our RTC register write finished). + while ((RTC->CRL & RTC_CRL_RTOFF) == 0); +} + +static void _rtc_set_alarm_time(uint32_t alarm_time) +{ + _rtc_enter_config_mode(); + RTC->ALRL = (alarm_time & 0x0000ffff); + RTC->ALRH = (alarm_time & 0xffff0000) >> 16; + _rtc_exit_config_mode(); +} + +static void _rtc_enable_alarm(void) +{ + _rtc_enter_config_mode(); + RTC->CRH |= RTC_CRH_ALRIE; + _rtc_exit_config_mode(); +} + +static void _rtc_disable_alarm(void) +{ + _rtc_enter_config_mode(); + RTC->CRH &= ~RTC_CRH_ALRIE; + _rtc_exit_config_mode(); +} + +static uint32_t rtc_get_counter_val(void) +{ + return (RTC->CNTH << 16) | RTC->CNTL; +} + +static uint32_t rtc_get_alarm_val(void) +{ + return (RTC->ALRH << 16) | RTC->ALRL; +} + +static void _rtc_set_counter_val(uint32_t counter_val) +{ + _rtc_enter_config_mode(); + RTC->CNTH = (counter_val & 0xffff0000) >> 16; // CNT[31:16] + RTC->CNTL = counter_val & 0x0000ffff; // CNT[15:0] + _rtc_exit_config_mode(); +} + +// (UnixTime = 00:00:00 01.01.1970 = JD0 = 2440588) +#define JULIAN_DATE_BASE 2440588 + +// Julian day number calculation - https://en.wikipedia.org/wiki/Julian_day +static void _rtc_get_time_from_count(const uint32_t rtc_counter, struct tm *time) +{ + unsigned long t; + unsigned long t1, a, b, c, d, e, m; + int year = 0; + int mon = 0; + int wday = 0; + int mday = 0; + int hour = 0; + int min = 0; + int sec = 0; + uint64_t jd = 0;; + uint64_t jdn = 0; + + jd = ((rtc_counter + 43200) / (86400 >> 1)) + (2440587 <<1 ) + 1; + jdn = jd >> 1; + + t = rtc_counter; + t1 = t / 60; + sec = t - t1 * 60; + + t = t1; + t1 = t / 60; + min = t - t1 * 60; + + t = t1; + t1 = t / 24; + hour = t - t1 * 24; + + wday = jdn % 7; + + a = jdn + 32044; + b = (4 * a + 3) / 146097; + c = a - (146097 * b) / 4; + d = (4 * c + 3) / 1461; + e = c - (1461 * d) / 4; + m = (5 * e + 2) / 153; + mday = e - (153 * m + 2) / 5 + 1; + mon = m + 3 - 12 * (m / 10); + year = 100 * b + d - 4800 + (m / 10); + + time->tm_year = year - 1900; + time->tm_mon = mon; + time->tm_mday = mday; + time->tm_hour = hour; + time->tm_min = min; + time->tm_sec = sec; + time->tm_wday = wday; + + DEBUG("[RTC time_from_count] c=%lu; %04i-%02i-%02i %02i:%02i:%02i\n", rtc_counter, + time->tm_year + 1900, time->tm_mon + 1, time->tm_mday, + time->tm_hour, time->tm_min, time->tm_sec); +} + +// Julian day number calculation - https://en.wikipedia.org/wiki/Julian_day +uint32_t _rtc_get_count_from_time(struct tm *time) +{ + uint8_t a; + uint16_t y; + uint8_t m; + uint32_t result_count; + + a = (14 - time->tm_mon) / 12; + y = (time->tm_year + 1900) + 4800 - a; + m = time->tm_mon + (12 * a) - 3; + + result_count = time->tm_mday; + result_count += (153 * m + 2) / 5; + result_count += 365 * y; + result_count += y / 4; + result_count += -y / 100; + result_count += y / 400; + result_count = result_count - 32045; + result_count = result_count - JULIAN_DATE_BASE; + result_count *= 86400; + result_count += (time->tm_hour * 3600); + result_count += (time->tm_min * 60); + result_count += (time->tm_sec); + + DEBUG("[RTC count_from_time] c=%lu; %04i-%02i-%02i %02i:%02i:%02i\n", result_count, + time->tm_year + 1900, time->tm_mon + 1, time->tm_mday, + time->tm_hour, time->tm_min, time->tm_sec); + + return result_count; +} + +int rtc_set_time(struct tm *time) +{ + _rtc_set_counter_val(_rtc_get_count_from_time(time)); + return 0; +} + +int rtc_get_time(struct tm *time) +{ + uint32_t time_count_val = rtc_get_counter_val(); + _rtc_get_time_from_count(time_count_val, time); + return 0; +} + +int rtc_set_alarm(struct tm *time, rtc_alarm_cb_t cb, void *arg) +{ + (void)cb; + (void)arg; + if (cb != NULL) { + DEBUG("[RTC rtc_set_alarm: warning callback not used]\n"); + } + + uint32_t time_count_val = _rtc_get_count_from_time(time); + _rtc_disable_alarm(); + _rtc_set_alarm_time(time_count_val); + _rtc_enable_alarm(); + + return 0; +} + +int rtc_get_alarm(struct tm *time) +{ + uint32_t time_count_val = rtc_get_alarm_val(); + _rtc_get_time_from_count(time_count_val, time); + return 0; +} + +void rtc_clear_alarm(void) +{ + _rtc_disable_alarm(); + _rtc_set_alarm_time(0); +} + +void rtc_poweron(void) +{ + /* TODO implement */ + NOT_YET_IMPLEMENTED(); +} + +void rtc_poweroff(void) +{ + /* TODO implement */ + NOT_YET_IMPLEMENTED(); +} + #endif /* !CPU_FAM_STM32F1 */