/* * Copyright (C) 2015 Lari Lehtomäki * Copyright (C) 2016 Laksh Bhatia * * 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 * directory for more details. */ /** * @ingroup cpu_stm32l1 * @{ * @file * @brief Low-level RTC driver implementation * @author Lari Lehtomäki * @author Laksh Bhatia * @} */ #include #include "cpu.h" #include "periph/rtc.h" #include "periph_conf.h" /* guard file in case no RTC device was specified */ #if RTC_NUMOF #define RTC_WRITE_PROTECTION_KEY1 (0xCA) #define RTC_WRITE_PROTECTION_KEY2 (0x53) #define RTC_SYNC_PRESCALER (0xff) /**< prescaler for 32.768 kHz oscillator */ #define RTC_ASYNC_PRESCALER (0x7f) /**< prescaler for 32.768 kHz oscillator */ #define MCU_YEAR_OFFSET (100) /**< struct tm counts years since 1900 but RTC has only two-digit year hence the offset of 100 years. */ typedef struct { rtc_alarm_cb_t cb; /**< callback called from RTC interrupt */ void *arg; /**< argument passed to the callback */ } rtc_state_t; static rtc_state_t rtc_callback; static uint8_t byte2bcd(uint8_t value); /** * @brief Initializes the RTC to use LSE (external 32.768 kHz oscillator) as a * clock source. Verify that your board has this oscillator. If other clock * source is used, then also the prescaler constants should be changed. */ void rtc_init(void) { rtc_poweron(); /* Unlock RTC write protection */ RTC->WPR = RTC_WRITE_PROTECTION_KEY1; RTC->WPR = RTC_WRITE_PROTECTION_KEY2; /* Enter RTC Init mode */ RTC->ISR = 0; RTC->ISR |= RTC_ISR_INIT; while ( (RTC->ISR & RTC_ISR_INITF) == 0 ); /* Set 24-h clock */ RTC->CR |= RTC_CR_FMT; /* Timestamps enabled */ RTC->CR |= RTC_CR_TSE; /* Configure the RTC PRER */ RTC->PRER = RTC_SYNC_PRESCALER; RTC->PRER |= (RTC_ASYNC_PRESCALER << 16); /* Exit RTC init mode */ RTC->ISR &= (uint32_t)~RTC_ISR_INIT; /* Enable RTC write protection */ RTC->WPR = 0xff; } int rtc_set_time(struct tm *time) { /* Enable write access to RTC registers */ periph_clk_en(APB1, RCC_APB1ENR_PWREN); PWR->CR |= PWR_CR_DBP; /* Unlock RTC write protection */ RTC->WPR = RTC_WRITE_PROTECTION_KEY1; RTC->WPR = RTC_WRITE_PROTECTION_KEY2; /* Enter RTC Init mode */ RTC->ISR |= RTC_ISR_INIT; while ( (RTC->ISR & RTC_ISR_INITF) == 0 ); RTC->DR = ( (((uint32_t)byte2bcd(time->tm_year - MCU_YEAR_OFFSET) << 16) & (RTC_DR_YT | RTC_DR_YU) ) | (((uint32_t)byte2bcd(time->tm_mon+1)<< 8) & (RTC_DR_MT | RTC_DR_MU) ) | (((uint32_t)byte2bcd(time->tm_mday) << 0) & (RTC_DR_DT | RTC_DR_DU) ) ); RTC->TR = ( (((uint32_t)byte2bcd(time->tm_hour) << 16) & (RTC_TR_HT | RTC_TR_HU) ) | (((uint32_t)byte2bcd(time->tm_min) << 8) & (RTC_TR_MNT| RTC_TR_MNU)) | (((uint32_t)byte2bcd(time->tm_sec) << 0) & (RTC_TR_ST | RTC_TR_SU) ) ); /* Exit RTC init mode */ RTC->ISR &= (uint32_t)~RTC_ISR_INIT; /* Enable RTC write protection */ RTC->WPR = RTC_WPR_KEY; return 0; } int rtc_get_time(struct tm *time) { time->tm_year = MCU_YEAR_OFFSET; time->tm_year +=(((RTC->DR & RTC_DR_YT) >> 20) * 10) + ((RTC->DR & RTC_DR_YU) >> 16); time->tm_mon = (((RTC->DR & RTC_DR_MT) >> 12) * 10) + ((RTC->DR & RTC_DR_MU) >> 8) - 1; time->tm_mday = (((RTC->DR & RTC_DR_DT) >> 4) * 10) + ((RTC->DR & RTC_DR_DU) >> 0); time->tm_hour = (((RTC->TR & RTC_TR_HT) >> 20) * 10) + ((RTC->TR & RTC_TR_HU) >> 16); if ( RTC->TR & RTC_TR_PM ) time->tm_hour += 12; time->tm_min = (((RTC->TR & RTC_TR_MNT) >> 12) * 10) + ((RTC->TR & RTC_TR_MNU) >> 8); time->tm_sec = (((RTC->TR & RTC_TR_ST) >> 4) * 10) + ((RTC->TR & RTC_TR_SU) >> 0); return 0; } int rtc_set_alarm(struct tm *time, rtc_alarm_cb_t cb, void *arg) { /* Enable write access to RTC registers */ periph_clk_en(APB1, RCC_APB1ENR_PWREN); PWR->CR |= PWR_CR_DBP; /* Unlock RTC write protection */ RTC->WPR = RTC_WRITE_PROTECTION_KEY1; RTC->WPR = RTC_WRITE_PROTECTION_KEY2; /* Enter RTC Init mode */ RTC->ISR |= RTC_ISR_INIT; while ( (RTC->ISR & RTC_ISR_INITF) == 0 ); RTC->CR &= ~(RTC_CR_ALRAE); while ( (RTC->ISR & RTC_ISR_ALRAWF) == 0 ); RTC->ALRMAR &= ~(RTC_ALRMAR_MSK1 | RTC_ALRMAR_MSK2 | RTC_ALRMAR_MSK3 | RTC_ALRMAR_MSK4); RTC->ALRMAR = ( (((uint32_t)byte2bcd(time->tm_mday) << 24) & (RTC_ALRMAR_DT | RTC_ALRMAR_DU) ) | (((uint32_t)byte2bcd(time->tm_hour) << 16) & (RTC_ALRMAR_HT | RTC_ALRMAR_HU) ) | (((uint32_t)byte2bcd(time->tm_min) << 8) & (RTC_ALRMAR_MNT| RTC_ALRMAR_MNU)) | (((uint32_t)byte2bcd(time->tm_sec) << 0) & (RTC_ALRMAR_ST | RTC_ALRMAR_SU) ) ); /* Enable Alarm A */ RTC->CR |= RTC_CR_ALRAE; RTC->CR |= RTC_CR_ALRAIE; RTC->ISR &= ~(RTC_ISR_ALRAF); /* Exit RTC init mode */ RTC->ISR &= (uint32_t)~RTC_ISR_INIT; /* Enable RTC write protection */ RTC->WPR = 0xFF; EXTI->IMR |= EXTI_IMR_MR17; EXTI->RTSR |= EXTI_RTSR_TR17; NVIC_SetPriority(RTC_Alarm_IRQn, 5); NVIC_EnableIRQ(RTC_Alarm_IRQn); rtc_callback.cb = cb; rtc_callback.arg = arg; return 0; } int rtc_get_alarm(struct tm *time) { time->tm_year = MCU_YEAR_OFFSET; time->tm_year +=(((RTC->DR & RTC_DR_YT) >> 20) * 10) + ((RTC->DR & RTC_DR_YU) >> 16); time->tm_mon = (((RTC->DR & RTC_DR_MT) >> 12) * 10) + ((RTC->DR & RTC_DR_MU) >> 8) - 1; time->tm_mday = (((RTC->ALRMAR & RTC_ALRMAR_DT) >> 28) * 10) + ((RTC->ALRMAR & RTC_ALRMAR_DU) >> 24); time->tm_hour = (((RTC->ALRMAR & RTC_ALRMAR_HT) >> 20) * 10) + ((RTC->ALRMAR & RTC_ALRMAR_HU) >> 16); if ( (RTC->ALRMAR & RTC_ALRMAR_PM) && (RTC->CR & RTC_CR_FMT) ) time->tm_hour += 12; time->tm_min = (((RTC->ALRMAR & RTC_ALRMAR_MNT) >> 12) * 10) + ((RTC->ALRMAR & RTC_ALRMAR_MNU) >> 8); time->tm_sec = (((RTC->ALRMAR & RTC_ALRMAR_ST) >> 4) * 10) + ((RTC->ALRMAR & RTC_ALRMAR_SU) >> 0); return 0; } void rtc_clear_alarm(void) { /* Disable Alarm A */ RTC->CR &= RTC_CR_ALRAE; RTC->CR &= RTC_CR_ALRAIE; rtc_callback.cb = NULL; rtc_callback.arg = NULL; } void rtc_poweron(void) { /* Enable write access to RTC registers */ periph_clk_en(APB1, RCC_APB1ENR_PWREN); PWR->CR |= PWR_CR_DBP; /* Reset RTC domain */ RCC->CSR |= RCC_CSR_RTCRST; RCC->CSR &= ~(RCC_CSR_RTCRST); /* Enable the LSE clock (external 32.768 kHz oscillator) */ RCC->CSR &= ~(RCC_CSR_LSEON); RCC->CSR &= ~(RCC_CSR_LSEBYP); RCC->CSR |= RCC_CSR_LSEON; while ( (RCC->CSR & RCC_CSR_LSERDY) == 0 ); /* Switch RTC to LSE clock source */ RCC->CSR &= ~(RCC_CSR_RTCSEL); RCC->CSR |= RCC_CSR_RTCSEL_LSE; /* Enable the RTC */ RCC->CSR |= RCC_CSR_RTCEN; } void rtc_poweroff(void) { /* Enable write access to RTC registers */ periph_clk_en(APB1, RCC_APB1ENR_PWREN); PWR->CR |= PWR_CR_DBP; /* Reset RTC domain */ RCC->CSR |= RCC_CSR_RTCRST; RCC->CSR &= ~(RCC_CSR_RTCRST); /* Disable the RTC */ RCC->CSR &= ~RCC_CSR_RTCEN; /* Disable LSE clock */ RCC->CSR &= ~(RCC_CSR_LSEON); } void isr_rtc_alarm(void) { if ((RTC->ISR & RTC_ISR_ALRAF) && (rtc_callback.cb != NULL)) { rtc_callback.cb(rtc_callback.arg); RTC->ISR &= ~RTC_ISR_ALRAF; } cortexm_isr_end(); } /** * Convert a number from unsigned to BCD * * @param[in] value to be converted * @return BCD representation of the value */ static uint8_t byte2bcd(uint8_t value) { uint8_t bcdhigh = 0; while (value >= 10) { bcdhigh++; value -= 10; } return ((uint8_t)(bcdhigh << 4) | value); } #endif /* RTC_NUMOF */