This commit fixes #5840. Fix verified by running mbed_hal-lp_ticker test suite with preloaded RTC counter such that it wrapped in the middle of the suite.
Also removes explicit sleep blocking from the us_ticker implementation, since sleep blocking for us tickers is done at mbed HAL level now. This was causing one of the lp_ticker tests to fail.
pull/5854/head
Steven Cooreman 2018-01-15 15:56:55 +01:00
parent b59005154b
commit 5d6c5dd81d
5 changed files with 177 additions and 156 deletions

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -26,15 +26,18 @@
#include <stdint.h>
#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);

View File

@ -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);
}