1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/cpu/efm32/periph/rtc_series0.c
Benjamin Valentin bad385ab7c cpu/efm32: RTC Series 0: use RTC helper functions
By using the RTC helper functions instead of POSIX mktime()/gmtime()
we can not only extend the RTC range beyond Y2038.

For tests/periph_rtc:

before:

   text	   data	    bss	    dec	    hex	filename
  28028	    248	   2472	  30748	   781c stk3700/tests_periph_rtc.elf

after:

   text	   data	    bss	    dec	    hex	filename
  19400	    144	   2424	  21968	   55d0 stk3700/tests_periph_rtc.elf

fixes #13277
2020-11-09 17:57:57 +01:00

178 lines
3.9 KiB
C

/*
* Copyright (C) 2016-2017 Bas Stottelaar <basstottelaar@gmail.com>
*
* 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 <basstottelaar@gmail.com>
* @}
*/
#include <time.h>
#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();
}