mirror of https://github.com/ARMmbed/mbed-os.git
Fix for #5840
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
parent
b59005154b
commit
5d6c5dd81d
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue