Skip to content

Re-implement us_ticker and lp_ticker for Silicon Labs targets #6471

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions targets/TARGET_Silicon_Labs/TARGET_EFM32/burtc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/***************************************************************************//**
* @file rtc_burtc.c
*******************************************************************************
* @section License
* <b>(C) Copyright 2018 Silicon Labs, http://www.silabs.com</b>
*******************************************************************************
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/

#include "device.h"
#if DEVICE_RTC

/* Use BURTC on devices that have it, and don't use the RTCC abstraction */
#if defined(BURTC_PRESENT) && !defined(RTCC_PRESENT)

#include "clocking.h"
#include "em_cmu.h"
#include "em_emu.h"
#include "em_burtc.h"
#include "rtc_api.h"

void rtc_init(void)
{
/* Only reset & configure the RTC if it has never run before */
if(BUS_RegBitRead(&BURTC->CTRL, _BURTC_CTRL_RSTEN_SHIFT)) {
EMU_EM4Init_TypeDef em4_init = EMU_EM4INIT_DEFAULT;
#if (LOW_ENERGY_CLOCK_SOURCE == LFXO)
em4_init.osc = emuEM4Osc_LFXO;
#elif (LOW_ENERGY_CLOCK_SOURCE == LFRCO)
em4_init.osc = emuEM4Osc_LFRCO;
#else
#error "Can't use BURTC on mbed with ULFRCO"
#endif
EMU_EM4Init(&em4_init);

BURTC_Init_TypeDef burtc_init = BURTC_INIT_DEFAULT;
burtc_init.mode = burtcModeEM4;
#if (LOW_ENERGY_CLOCK_SOURCE == LFXO)
burtc_init.clkSel = burtcClkSelLFXO;
#elif (LOW_ENERGY_CLOCK_SOURCE == LFRCO)
burtc_init.clkSel = burtcClkSelLFRCO;
#else
#error "Can't use BURTC on mbed with ULFRCO"
#endif
burtc_init.clkDiv = burtcClkDiv_128;
burtc_init.lowPowerMode = burtcLPEnable;

BURTC_Reset();
BUS_RegBitWrite(&RMU->CTRL, _RMU_CTRL_BURSTEN_SHIFT, 0);
BURTC_Init(&burtc_init);
BURTC_RetRegSet(0, 0);
BURTC_RetRegSet(1, 0);
}
}

void rtc_free(void)
{
/* Nothing to release here */
}

int rtc_isenabled(void)
{
return (BUS_RegBitRead(&BURTC->CTRL, _BURTC_CTRL_RSTEN_SHIFT) == 0);
}

time_t rtc_read(void)
{
uint32_t ts = 0;
uint32_t ts2 = 1;
do {
if (BURTC->IF & BURTC_IF_OF) {
BURTC_RetRegSet(1, BURTC_RetRegGet(1)+1);
BURTC->IFC = BURTC_IFC_OF;
}

ts = ts2;
ts2 = (BURTC_CounterGet() >> 8) + BURTC_RetRegGet(0) + (BURTC_RetRegGet(1) << 16);
} while (ts != ts2);
return ts2;
}

void rtc_write(time_t t)
{
BURTC_RetRegSet(0, t - (BURTC_CounterGet() >> 8));
BURTC_RetRegSet(1, 0);
if (BURTC->IF & BURTC_IF_OF) {
BURTC->IFC = BURTC_IFC_OF;
}
}

#endif /* BURTC_PRESENT && !RTCC_PRESENT */
#endif /* DEVICE_RTC */
6 changes: 3 additions & 3 deletions targets/TARGET_Silicon_Labs/TARGET_EFM32/common/clocking.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,12 @@
#endif

/* Adjust this to change speed of RTC and LP ticker ticks */
#define RTC_CLOCKDIV cmuClkDiv_8
#define RTC_CLOCKDIV cmuClkDiv_1
/* Adjust this to match RTC_CLOCKDIV as integer value */
#define RTC_CLOCKDIV_INT 8U
#define RTC_CLOCKDIV_INT 1U
/* 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
#define RTC_FREQ_SHIFT 15U

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ void mbed_sdk_init()
# error "Low energy clock selection not valid"
#endif

#if defined(RTCC_PRESENT)
/* Turn RTCC clock gate back on to keep RTC time correct */
CMU_ClockEnable(cmuClock_RTCC, true);
#endif

#if defined(EFM_BC_EN)
/* Enable BC line driver to avoid garbage on CDC port */
gpio_init_out_ex(&bc_enable, EFM_BC_EN, 1);
Expand Down
148 changes: 91 additions & 57 deletions targets/TARGET_Silicon_Labs/TARGET_EFM32/lp_ticker.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,17 @@
* for the low power timer, which should be good enough for a low power use
* case.
*
* Mapping of mbed APIs to Silicon Labs peripherals:
* ---: Does not meet mbed API requirements
* X : Implemented to provide mbed API functionality
*
* --------------------------------------------
* | ------------- | RTCC | BURTC | RTC | TIMER |
* | rtc_api | X | X | --- | ----- |
* | lp_ticker_api | X | | X | ----- |
* | us_ticker_api | --- | ----- | --- | X |
* --------------------------------------------
*
* 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
Expand All @@ -40,91 +51,114 @@
* (At max speed the wraparound is at 69730 years, which is unlikely as well)
******************************************************************************/

#include "rtc_api.h"
#include "rtc_api_HAL.h"
#if defined(RTC_PRESENT)
#include "em_rtc.h"
#include "em_cmu.h"
#include "lp_ticker_api.h"
#include "mbed_critical.h"

static int rtc_reserved = 0;
#if RTC_CLOCKDIV_INT > 16
#error invalid prescaler value RTC_CLOCKDIV_INT, since LP ticker resolution will exceed 1ms.
#endif

#define RTC_BITS (24U)
#define RTC_MAX_VALUE (0xFFFFFFUL)

static bool rtc_inited = false;

const ticker_info_t* lp_ticker_get_info(void)
{
static const ticker_info_t rtc_info = {
LOW_ENERGY_CLOCK_FREQUENCY,
RTC_BITS
};
return &rtc_info;
}

void RTC_IRQHandler(void)
{
uint32_t flags;
flags = RTC_IntGet();
if ((flags & RTC_IF_COMP0) && rtc_inited) {
RTC_IntClear(RTC_IF_COMP0);
lp_ticker_irq_handler();
}
}

void lp_ticker_init()
{
if(!rtc_reserved) {
core_util_critical_section_enter();
rtc_init_real(RTC_INIT_LPTIMER);
rtc_set_comp0_handler((uint32_t)lp_ticker_irq_handler);
rtc_reserved = 1;
core_util_critical_section_exit();
core_util_critical_section_enter();
if (!rtc_inited) {
CMU_ClockEnable(cmuClock_RTC, true);

/* Initialize RTC */
RTC_Init_TypeDef init = RTC_INIT_DEFAULT;
init.enable = 1;
/* Don't use compare register 0 as top value */
init.comp0Top = 0;

/* Initialize */
RTC_Init(&init);
RTC_CounterSet(20);

/* Enable Interrupt from RTC */
RTC_IntDisable(RTC_IF_COMP0);
RTC_IntClear(RTC_IF_COMP0);
NVIC_SetVector(RTC_IRQn, (uint32_t)RTC_IRQHandler);
NVIC_EnableIRQ(RTC_IRQn);

rtc_inited = true;
} else {
/* Cancel current interrupt by virtue of calling init again */
RTC_IntDisable(RTC_IF_COMP0);
RTC_IntClear(RTC_IF_COMP0);
}
core_util_critical_section_exit();
}

void lp_ticker_free()
{
if(rtc_reserved) {
core_util_critical_section_enter();
rtc_free_real(RTC_INIT_LPTIMER);
rtc_reserved = 0;
core_util_critical_section_exit();
/* Disable the RTC if it was inited and is no longer in use by anyone. */
if (rtc_inited) {
NVIC_DisableIRQ(RTC_IRQn);
RTC_Reset();
CMU_ClockEnable(cmuClock_RTC, false);
rtc_inited = false;
}
}

void lp_ticker_set_interrupt(timestamp_t timestamp)
{
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 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
rtc_compare_value = ((uint64_t)offset * (LOW_ENERGY_CLOCK_FREQUENCY / RTC_CLOCKDIV_INT)) / 1000000;

/* If RTC offset is less then 2 RTC ticks, the interrupt won't fire */
if(rtc_compare_value < 2) {
rtc_compare_value = 2;
}

rtc_compare_value += current_ticks;

rtc_set_comp0_value(rtc_compare_value, true);
RTC_IntDisable(RTC_IF_COMP0);
RTC_IntClear(RTC_IF_COMP0);
RTC_FreezeEnable(true);
RTC_CompareSet(0, (uint32_t) (timestamp & RTC_MAX_VALUE));
RTC_FreezeEnable(false);
RTC_IntEnable(RTC_IF_COMP0);
}

inline void lp_ticker_fire_interrupt(void)
void lp_ticker_fire_interrupt(void)
{
rtc_force_comp0();
RTC_IntEnable(RTC_IF_COMP0);
RTC_IntSet(RTC_IF_COMP0);
}

inline void lp_ticker_disable_interrupt()
void lp_ticker_disable_interrupt()
{
rtc_enable_comp0(false);
RTC_IntDisable(RTC_IF_COMP0);
}

inline void lp_ticker_clear_interrupt()
void lp_ticker_clear_interrupt()
{
/* No need to clear interrupt flag, since that already happens at RTC level */
RTC_IntClear(RTC_IF_COMP0);
}

timestamp_t lp_ticker_read()
{
lp_ticker_init();

uint64_t ticks_temp;
uint64_t ticks = rtc_get_full();

/* 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);
return (timestamp_t) RTC_CounterGet();
}

#endif
#elif defined(RTCC_PRESENT)
/* lp_ticker api is implemented in rtc_rtcc.c */
#endif /* RTC_PRESENT */
#endif /* DEVICE_LPTICKER */
Loading