diff --git a/targets/TARGET_Silicon_Labs/TARGET_EFM32/common/clocking.h b/targets/TARGET_Silicon_Labs/TARGET_EFM32/common/clocking.h index 022148984c..b9ad628edc 100644 --- a/targets/TARGET_Silicon_Labs/TARGET_EFM32/common/clocking.h +++ b/targets/TARGET_Silicon_Labs/TARGET_EFM32/common/clocking.h @@ -24,6 +24,8 @@ #ifndef MBED_CLOCKING_H #define MBED_CLOCKING_H +#include "em_cmu.h" + /* Clock definitions */ #define LFXO 0 #define HFXO 1 @@ -51,8 +53,8 @@ #ifndef CORE_CLOCK_SOURCE #define CORE_CLOCK_SOURCE HFRCO #if defined(_CMU_HFRCOCTRL_BAND_MASK) -#define HFRCO_FREQUENCY_ENUM _CMU_HFRCOCTRL_BAND_21MHZ -#define HFRCO_FREQUENCY 21000000 +#define HFRCO_FREQUENCY_ENUM _CMU_HFRCOCTRL_BAND_21MHZ +#define HFRCO_FREQUENCY 21000000 #elif defined(_CMU_HFRCOCTRL_FREQRANGE_MASK) #define HFRCO_FREQUENCY_ENUM cmuHFRCOFreq_32M0Hz #define HFRCO_FREQUENCY 32000000 @@ -103,4 +105,13 @@ # endif #endif +/* Adjust this to change speed of RTC and LP ticker ticks */ +#define RTC_CLOCKDIV cmuClkDiv_8 +/* Adjust this to match RTC_CLOCKDIV as integer value */ +#define RTC_CLOCKDIV_INT 8U +/* Adjust this to match RTC_CLOCKDIV as shift for 1 second worth of ticks. + * E.g. with 32768 Hz crystal and CLOCKDIV of 8, 1 second is 4096 ticks. + * 4096 equals 1 << 12, so RTC_FREQ_SHIFT needs to be 12. */ +#define RTC_FREQ_SHIFT 12U + #endif diff --git a/targets/TARGET_Silicon_Labs/TARGET_EFM32/lp_ticker.c b/targets/TARGET_Silicon_Labs/TARGET_EFM32/lp_ticker.c index 684ed188c9..9c08b32090 100644 --- a/targets/TARGET_Silicon_Labs/TARGET_EFM32/lp_ticker.c +++ b/targets/TARGET_Silicon_Labs/TARGET_EFM32/lp_ticker.c @@ -25,14 +25,25 @@ #include "clocking.h" #if DEVICE_LOWPOWERTIMER +/******************************************************************************* + * The Silicon Labs lp_ticker implementation is mapped on top of an extended RTC + * API, since the RTC is available in the lowest energy modes. By default, the + * RTC counter is configured to run at 4kHz, giving us a quarter-ms resolution + * for the low power timer, which should be good enough for a low power use + * case. + * + * On Silicon Labs devices, the lowest width RTC implementation has a 24-bit + * counter, which gets extended with a further 32-bit software counter. This + * gives 56 bits of actual width, which with the default speed maps to + * 557462 years before the extended RTC counter wraps around. We are pretty + * certain no device is going to have that amount of uptime. + * (At max speed the wraparound is at 69730 years, which is unlikely as well) + ******************************************************************************/ + #include "rtc_api.h" #include "rtc_api_HAL.h" #include "lp_ticker_api.h" - #include "mbed_critical.h" -#if (defined RTCC_COUNT) && (RTCC_COUNT > 0) -#include "em_rtcc.h" -#endif static int rtc_reserved = 0; @@ -57,135 +68,55 @@ void lp_ticker_free() } } -#ifndef RTCC_COUNT - -/* RTC API */ - void lp_ticker_set_interrupt(timestamp_t timestamp) { - uint64_t timestamp_ticks; - uint64_t current_ticks = RTC_CounterGet(); - timestamp_t current_time = ((uint64_t)(current_ticks * 1000000) / (LOW_ENERGY_CLOCK_FREQUENCY / RTC_CLOCKDIV_INT)); - - /* Initialize RTC */ - lp_ticker_init(); + uint64_t rtc_compare_value; + uint64_t current_ticks = rtc_get_full(); + timestamp_t current_time = lp_ticker_read(); /* calculate offset value */ timestamp_t offset = timestamp - current_time; - if(offset > 0xEFFFFFFF) offset = 100; + + /* If the requested timestamp is too far in the future, we might not be able + * to set the interrupt accurately due to potentially having ticked between + * calculating the timestamp to set and us calculating the offset. */ + if(offset > 0xFFFF0000) offset = 100; /* map offset to RTC value */ // ticks = offset * RTC frequency div 1000000 - timestamp_ticks = ((uint64_t)offset * (LOW_ENERGY_CLOCK_FREQUENCY / RTC_CLOCKDIV_INT)) / 1000000; - timestamp_ticks += current_ticks; + rtc_compare_value = ((uint64_t)offset * (LOW_ENERGY_CLOCK_FREQUENCY / RTC_CLOCKDIV_INT)) / 1000000; - /* RTC has 24 bit resolution */ - timestamp_ticks &= 0xFFFFFF; - - /* check for RTC limitation */ - if((timestamp_ticks - RTC_CounterGet()) >= 0x800000) timestamp_ticks = RTC_CounterGet() + 2; - - /* Set callback */ - RTC_FreezeEnable(true); - RTC_CompareSet(0, (uint32_t)timestamp_ticks); - RTC_IntEnable(RTC_IF_COMP0); - RTC_FreezeEnable(false); -} - -void lp_ticker_fire_interrupt(void) -{ - RTC_IntSet(RTC_IFS_COMP0); -} - -inline void lp_ticker_disable_interrupt() -{ - RTC_IntDisable(RTC_IF_COMP0); -} - -inline void lp_ticker_clear_interrupt() -{ - RTC_IntClear(RTC_IF_COMP0); -} - -timestamp_t lp_ticker_read() -{ - lp_ticker_init(); - - uint64_t ticks_temp; - uint64_t ticks = RTC_CounterGet(); - - /* ticks = counter tick value - * timestamp = value in microseconds - * timestamp = ticks * 1.000.000 / RTC frequency - */ - - ticks_temp = (ticks * 1000000) / (LOW_ENERGY_CLOCK_FREQUENCY / RTC_CLOCKDIV_INT); - return (timestamp_t) (ticks_temp & 0xFFFFFFFF); -} - -#else - -/* RTCC API */ - -void lp_ticker_set_interrupt(timestamp_t timestamp) -{ - uint64_t timestamp_ticks; - uint64_t current_ticks = RTCC_CounterGet(); - timestamp_t current_time = ((uint64_t)(current_ticks * 1000000) / (LOW_ENERGY_CLOCK_FREQUENCY / RTC_CLOCKDIV_INT)); - - /* Initialize RTC */ - lp_ticker_init(); - - /* calculate offset value */ - timestamp_t offset = timestamp - current_time; - if(offset > 0xEFFFFFFF) offset = 100; - - /* map offset to RTC value */ - // ticks = offset * RTC frequency div 1000000 - timestamp_ticks = ((uint64_t)offset * (LOW_ENERGY_CLOCK_FREQUENCY / RTC_CLOCKDIV_INT)) / 1000000; - // checking the rounding. If timeout is wanted between RTCC ticks, irq should be configured to - // trigger in the latter RTCC-tick. Otherwise ticker-api fails to send timer event to its client - if(((timestamp_ticks * 1000000) / (LOW_ENERGY_CLOCK_FREQUENCY / RTC_CLOCKDIV_INT)) < offset){ - timestamp_ticks++; + /* If RTC offset is less then 2 RTC ticks, the interrupt won't fire */ + if(rtc_compare_value < 2) { + rtc_compare_value = 2; } - timestamp_ticks += current_ticks; + rtc_compare_value += current_ticks; - /* RTCC has 32 bit resolution */ - timestamp_ticks &= 0xFFFFFFFF; - - /* check for RTCC limitation */ - if((timestamp_ticks - RTCC_CounterGet()) >= 0x80000000) timestamp_ticks = RTCC_CounterGet() + 2; - - /* init channel */ - RTCC_CCChConf_TypeDef ccchConf = RTCC_CH_INIT_COMPARE_DEFAULT; - RTCC_ChannelInit(0,&ccchConf); - /* Set callback */ - RTCC_ChannelCCVSet(0, (uint32_t)timestamp_ticks); - RTCC_IntEnable(RTCC_IF_CC0); + rtc_set_comp0_value(rtc_compare_value, true); } -void lp_ticker_fire_interrupt(void) +inline void lp_ticker_fire_interrupt(void) { - RTCC_IntSet(RTCC_IFS_CC0); + rtc_force_comp0(); } inline void lp_ticker_disable_interrupt() { - RTCC_IntDisable(RTCC_IF_CC0); + rtc_enable_comp0(false); } inline void lp_ticker_clear_interrupt() { - RTCC_IntClear(RTCC_IF_CC0); + /* No need to clear interrupt flag, since that already happens at RTC level */ } timestamp_t lp_ticker_read() { lp_ticker_init(); - + uint64_t ticks_temp; - uint64_t ticks = RTCC_CounterGet(); + uint64_t ticks = rtc_get_full(); /* ticks = counter tick value * timestamp = value in microseconds @@ -196,6 +127,4 @@ timestamp_t lp_ticker_read() return (timestamp_t) (ticks_temp & 0xFFFFFFFF); } -#endif /* RTCC */ - #endif diff --git a/targets/TARGET_Silicon_Labs/TARGET_EFM32/rtc_api.c b/targets/TARGET_Silicon_Labs/TARGET_EFM32/rtc_api.c index 41db7d1128..81e64cbc00 100644 --- a/targets/TARGET_Silicon_Labs/TARGET_EFM32/rtc_api.c +++ b/targets/TARGET_Silicon_Labs/TARGET_EFM32/rtc_api.c @@ -37,17 +37,21 @@ #include "em_rtcc.h" #endif -static bool rtc_inited = false; -static time_t time_base = 0; -static uint32_t useflags = 0; -static uint32_t time_extend = 0; +static bool rtc_inited = false; +static bool rtc_cancelled = false; +static time_t time_base = 0; +static uint32_t useflags = 0; +static uint32_t time_extend = 0; +static uint32_t extended_comp0 = 0; -static void (*comp0_handler)(void) = NULL; - -#ifndef RTCC_COUNT +static void (*comp0_handler)(void) = NULL; +#ifndef RTCC_PRESENT /* Using RTC API */ -#define RTC_NUM_BITS (24) + +#if RTC_CLOCKDIV_INT > 16 +#error invalid prescaler value RTC_CLOCKDIV_INT, since LP ticker resolution will exceed 1ms. +#endif void RTC_IRQHandler(void) { @@ -60,24 +64,31 @@ void RTC_IRQHandler(void) } if (flags & RTC_IF_COMP0) { RTC_IntClear(RTC_IF_COMP0); - if (comp0_handler != NULL) { + if (comp0_handler != NULL && ((time_extend == extended_comp0) || (rtc_cancelled))) { + rtc_cancelled = false; comp0_handler(); } } } -uint32_t rtc_get_32bit(void) -{ - uint32_t pending = (RTC_IntGet() & RTC_IF_OF) ? 1 : 0; - return (RTC_CounterGet() + ((time_extend + pending) << RTC_NUM_BITS)); -} - uint64_t rtc_get_full(void) { uint64_t ticks = 0; - ticks += time_extend; - ticks = ticks << RTC_NUM_BITS; - ticks += RTC_CounterGet(); + + do + { + ticks = RTC_CounterGet(); + + if (RTC_IntGet() & RTC_IF_OF) { + RTC_IntClear(RTC_IF_OF); + /* RTC has overflowed in a critical section, so handle the overflow here */ + time_extend += 1; + } + + ticks += (uint64_t)time_extend << RTC_BITS; + } + while ( (ticks & RTC_MAX_VALUE) != RTC_CounterGet() ); + return ticks; } @@ -131,10 +142,38 @@ void rtc_free_real(uint32_t flags) } } -#else +void rtc_enable_comp0(bool enable) +{ + RTC_FreezeEnable(true); + if (!enable) { + RTC_IntDisable(RTC_IF_COMP0); + } else { + RTC_IntEnable(RTC_IF_COMP0); + } + RTC_FreezeEnable(false); +} +void rtc_set_comp0_value(uint64_t value, bool enable) +{ + rtc_enable_comp0(false); + + /* Set callback */ + RTC_FreezeEnable(true); + extended_comp0 = (uint32_t) (value >> RTC_BITS); + RTC_CompareSet(0, (uint32_t) (value & RTC_MAX_VALUE)); + RTC_FreezeEnable(false); + + rtc_enable_comp0(enable); +} + +void rtc_force_comp0(void) +{ + rtc_cancelled = true; + RTC_IntSet(RTC_IFS_COMP0); +} + +#else /* Using RTCC API */ -#define RTCC_NUM_BITS (32) void RTCC_IRQHandler(void) { @@ -149,23 +188,30 @@ void RTCC_IRQHandler(void) if (flags & RTCC_IF_CC0) { RTCC_IntClear(RTCC_IF_CC0); - if (comp0_handler != NULL) { + if (comp0_handler != NULL && ((time_extend == extended_comp0) || (rtc_cancelled))) { comp0_handler(); } } } -uint32_t rtc_get_32bit(void) -{ - return RTCC_CounterGet(); -} - uint64_t rtc_get_full(void) { uint64_t ticks = 0; - ticks += time_extend; - ticks = ticks << RTCC_NUM_BITS; - ticks += RTCC_CounterGet(); + + do + { + ticks = RTCC_CounterGet(); + + if (RTCC_IntGet() & RTCC_IF_OF) { + RTCC_IntClear(RTCC_IF_OF); + /* RTCC has overflowed in critical section, so handle the rollover here */ + time_extend += 1; + } + + ticks += (uint64_t)time_extend << RTC_BITS; + } + while ( (ticks & RTC_MAX_VALUE) != RTCC_CounterGet() ); + return ticks; } @@ -184,10 +230,18 @@ void rtc_init_real(uint32_t flags) init.enable = 1; init.precntWrapOnCCV0 = false; init.cntWrapOnCCV1 = false; -#if RTC_CLOCKDIV_INT == 8 +#if RTC_CLOCKDIV_INT == 1 + init.presc = rtccCntPresc_1; +#elif RTC_CLOCKDIV_INT == 2 + init.presc = rtccCntPresc_2; +#elif RTC_CLOCKDIV_INT == 4 + init.presc = rtccCntPresc_4; +#elif RTC_CLOCKDIV_INT == 8 init.presc = rtccCntPresc_8; +#elif RTC_CLOCKDIV_INT == 16 + init.presc = rtccCntPresc_16; #else -#error invalid prescaler value RTC_CLOCKDIV_INT +#error invalid prescaler value RTC_CLOCKDIV_INT, since LP ticker resolution will exceed 1ms. #endif /* Enable Interrupt from RTC */ @@ -220,6 +274,35 @@ void rtc_free_real(uint32_t flags) } } +void rtc_enable_comp0(bool enable) +{ + if(!enable) { + RTCC_IntDisable(RTCC_IF_CC0); + } else { + RTCC_IntEnable(RTCC_IF_CC0); + } +} + +void rtc_set_comp0_value(uint64_t value, bool enable) +{ + rtc_enable_comp0(false); + + /* init channel */ + RTCC_CCChConf_TypeDef ccchConf = RTCC_CH_INIT_COMPARE_DEFAULT; + RTCC_ChannelInit(0,&ccchConf); + /* Set callback */ + extended_comp0 = (uint32_t) (value >> RTC_BITS); + RTCC_ChannelCCVSet(0, (uint32_t) (value & RTC_MAX_VALUE)); + + rtc_enable_comp0(enable); +} + +void rtc_force_comp0(void) +{ + rtc_cancelled = true; + RTCC_IntSet(RTCC_IFS_CC0); +} + #endif /* RTCC_COUNT */ void rtc_set_comp0_handler(uint32_t handler) diff --git a/targets/TARGET_Silicon_Labs/TARGET_EFM32/rtc_api_HAL.h b/targets/TARGET_Silicon_Labs/TARGET_EFM32/rtc_api_HAL.h index a265e13e47..702c42ccd7 100644 --- a/targets/TARGET_Silicon_Labs/TARGET_EFM32/rtc_api_HAL.h +++ b/targets/TARGET_Silicon_Labs/TARGET_EFM32/rtc_api_HAL.h @@ -26,15 +26,18 @@ #include #include "rtc_api.h" -#include "em_rtc.h" +#include "clocking.h" -#define RTC_CLOCKDIV cmuClkDiv_8 -#define RTC_CLOCKDIV_INT 8 -#define RTC_FREQ_SHIFT 12 +#define RTC_INIT_LPTIMER (1U << 1) +#define RTC_INIT_RTC (1U << 0) - -#define RTC_INIT_LPTIMER (1 << 1) -#define RTC_INIT_RTC (1 << 0) +#if defined(RTCC_PRESENT) +#define RTC_BITS (32U) +#define RTC_MAX_VALUE (0xFFFFFFFFUL) +#elif defined(RTC_PRESENT) +#define RTC_BITS (24U) +#define RTC_MAX_VALUE (0xFFFFFFUL) +#endif #ifdef __cplusplus extern "C" { @@ -42,6 +45,11 @@ extern "C" { /* Purpose of this file: extend rtc_api.h to include EFM-specific stuff*/ void rtc_set_comp0_handler(uint32_t handler); +void rtc_enable_comp0(bool enable); +void rtc_set_comp0_value(uint64_t value, bool enable); +void rtc_force_comp0(void); + +uint64_t rtc_get_full(void); void rtc_init_real(uint32_t flags); void rtc_free_real(uint32_t flags); diff --git a/targets/TARGET_Silicon_Labs/TARGET_EFM32/us_ticker.c b/targets/TARGET_Silicon_Labs/TARGET_EFM32/us_ticker.c index 93d5772bc6..74ac081768 100644 --- a/targets/TARGET_Silicon_Labs/TARGET_EFM32/us_ticker.c +++ b/targets/TARGET_Silicon_Labs/TARGET_EFM32/us_ticker.c @@ -25,11 +25,9 @@ #include "us_ticker_api.h" #include "device.h" #include "mbed_assert.h" -#include "mbed_sleep.h" #include "em_cmu.h" #include "em_timer.h" #include "clocking.h" -#include "sleep_api.h" /** * Timer functions for microsecond ticker. @@ -158,10 +156,6 @@ void us_ticker_set_interrupt(timestamp_t timestamp) uint32_t goal = timestamp; uint32_t trigger; - if((US_TICKER_TIMER->IEN & TIMER_IEN_CC0) == 0) { - //Timer was disabled, but is going to be enabled. Set sleep mode. - sleep_manager_lock_deep_sleep(); - } TIMER_IntDisable(US_TICKER_TIMER, TIMER_IEN_CC0); /* convert us delta value back to timer ticks */ @@ -215,10 +209,6 @@ void us_ticker_fire_interrupt(void) void us_ticker_disable_interrupt(void) { - if((US_TICKER_TIMER->IEN & TIMER_IEN_CC0) != 0) { - //Timer was enabled, but is going to get disabled. Clear sleepmode. - sleep_manager_unlock_deep_sleep(); - } /* Disable compare channel interrupts */ TIMER_IntDisable(US_TICKER_TIMER, TIMER_IEN_CC0); }