/* * Copyright (C) 2016-2017 Bas Stottelaar * * 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_efm32 * @ingroup drivers_periph_rtc * @{ * * @file * @brief RTC peripheral driver implementation * * @author Bas Stottelaar * @} */ #include #include "cpu.h" #include "periph_conf.h" #include "periph/rtc.h" #include "em_cmu.h" #include "em_rtc.h" #define RTC_MAX_VALUE (0xFFFFFF) #define RTC_SHIFT_VALUE (24U) typedef struct { rtc_alarm_cb_t alarm_cb; /**< callback called from RTC interrupt */ void *alarm_arg; /**< argument passed to the callback */ uint32_t alarm; /**< scheduled alarm (may be deferred) */ uint8_t overflows; /**< number of overflows */ } rtc_state_t; static rtc_state_t rtc_state; /** * @brief Actual implementation of rtc_set_alarm */ static void _set_alarm(void) { uint32_t overflows = (rtc_state.alarm >> RTC_SHIFT_VALUE); /* check if alarm is in reach of RTC counter, which basically means that the first 8 bits created by software now match */ if (overflows == rtc_state.overflows) { /* disable interrupt so it doesn't accidentally trigger */ RTC_IntDisable(RTC_IEN_COMP0); /* set compare register */ RTC_CompareSet(0, rtc_state.alarm & RTC_MAX_VALUE); /* (re-)enable the interrupt */ RTC_IntClear(RTC_IEN_COMP0); RTC_IntEnable(RTC_IEN_COMP0); } } void rtc_init(void) { /* prescaler of 32768 = 1 s of resolution and overflow each 194 days */ CMU_ClockDivSet(cmuClock_RTC, cmuClkDiv_32768); /* enable clocks */ CMU_ClockEnable(cmuClock_CORELE, true); CMU_ClockEnable(cmuClock_RTC, true); /* initialize the state */ rtc_state.overflows = 0; /* reset and initialize the peripheral */ RTC_Init_TypeDef init = RTC_INIT_DEFAULT; init.enable = false; init.comp0Top = false; RTC_Reset(); RTC_Init(&init); /* enable interrupts */ RTC_IntEnable(RTC_IEN_OF); NVIC_ClearPendingIRQ(RTC_IRQn); NVIC_EnableIRQ(RTC_IRQn); /* enable peripheral */ RTC_Enable(true); } int rtc_set_time(struct tm *time) { time_t timestamp = rtc_mktime(time); rtc_state.overflows = (timestamp >> RTC_SHIFT_VALUE); RTC->CNT = timestamp & RTC_MAX_VALUE; return 0; } int rtc_get_time(struct tm *time) { time_t timestamp = RTC_CounterGet(); timestamp = timestamp + (rtc_state.overflows << RTC_SHIFT_VALUE); rtc_localtime(timestamp, time); return 0; } int rtc_set_alarm(struct tm *time, rtc_alarm_cb_t cb, void *arg) { rtc_state.alarm_cb = cb; rtc_state.alarm_arg = arg; rtc_state.alarm = rtc_mktime(time); /* alarm may not be in reach of current time, so defer if needed */ _set_alarm(); return 0; } int rtc_get_alarm(struct tm *time) { rtc_localtime(rtc_state.alarm, time); return 0; } void rtc_clear_alarm(void) { rtc_state.alarm_cb = NULL; rtc_state.alarm_arg = NULL; rtc_state.alarm = 0; RTC_IntDisable(RTC_IEN_COMP0); } void rtc_poweron(void) { CMU_ClockEnable(cmuClock_RTC, true); } void rtc_poweroff(void) { CMU_ClockEnable(cmuClock_RTC, false); } void isr_rtc(void) { if ((RTC_IntGet() & RTC_IF_COMP0)) { if (rtc_state.alarm_cb != NULL) { rtc_state.alarm_cb(rtc_state.alarm_arg); } /* clear interrupt */ RTC_IntClear(RTC_IFC_COMP0); } if (RTC_IntGet() & RTC_IF_OF) { rtc_state.overflows++; /* check if alarm should be enabled now */ if (rtc_state.alarm_cb) { _set_alarm(); } /* clear interrupt */ RTC_IntClear(RTC_IFC_OF); } cortexm_isr_end(); }