Skip to content

Commit 2153046

Browse files
authored
Merge pull request #6471 from SiliconLabs/feature-hal-spec-ticker
Re-implement us_ticker and lp_ticker for Silicon Labs targets
2 parents c9ecc9b + fdb4a8e commit 2153046

File tree

9 files changed

+422
-601
lines changed

9 files changed

+422
-601
lines changed
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/***************************************************************************//**
2+
* @file rtc_burtc.c
3+
*******************************************************************************
4+
* @section License
5+
* <b>(C) Copyright 2018 Silicon Labs, http://www.silabs.com</b>
6+
*******************************************************************************
7+
*
8+
* SPDX-License-Identifier: Apache-2.0
9+
*
10+
* Licensed under the Apache License, Version 2.0 (the "License"); you may
11+
* not use this file except in compliance with the License.
12+
* You may obtain a copy of the License at
13+
*
14+
* http://www.apache.org/licenses/LICENSE-2.0
15+
*
16+
* Unless required by applicable law or agreed to in writing, software
17+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
18+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19+
* See the License for the specific language governing permissions and
20+
* limitations under the License.
21+
*
22+
******************************************************************************/
23+
24+
#include "device.h"
25+
#if DEVICE_RTC
26+
27+
/* Use BURTC on devices that have it, and don't use the RTCC abstraction */
28+
#if defined(BURTC_PRESENT) && !defined(RTCC_PRESENT)
29+
30+
#include "clocking.h"
31+
#include "em_cmu.h"
32+
#include "em_emu.h"
33+
#include "em_burtc.h"
34+
#include "rtc_api.h"
35+
36+
void rtc_init(void)
37+
{
38+
/* Only reset & configure the RTC if it has never run before */
39+
if(BUS_RegBitRead(&BURTC->CTRL, _BURTC_CTRL_RSTEN_SHIFT)) {
40+
EMU_EM4Init_TypeDef em4_init = EMU_EM4INIT_DEFAULT;
41+
#if (LOW_ENERGY_CLOCK_SOURCE == LFXO)
42+
em4_init.osc = emuEM4Osc_LFXO;
43+
#elif (LOW_ENERGY_CLOCK_SOURCE == LFRCO)
44+
em4_init.osc = emuEM4Osc_LFRCO;
45+
#else
46+
#error "Can't use BURTC on mbed with ULFRCO"
47+
#endif
48+
EMU_EM4Init(&em4_init);
49+
50+
BURTC_Init_TypeDef burtc_init = BURTC_INIT_DEFAULT;
51+
burtc_init.mode = burtcModeEM4;
52+
#if (LOW_ENERGY_CLOCK_SOURCE == LFXO)
53+
burtc_init.clkSel = burtcClkSelLFXO;
54+
#elif (LOW_ENERGY_CLOCK_SOURCE == LFRCO)
55+
burtc_init.clkSel = burtcClkSelLFRCO;
56+
#else
57+
#error "Can't use BURTC on mbed with ULFRCO"
58+
#endif
59+
burtc_init.clkDiv = burtcClkDiv_128;
60+
burtc_init.lowPowerMode = burtcLPEnable;
61+
62+
BURTC_Reset();
63+
BUS_RegBitWrite(&RMU->CTRL, _RMU_CTRL_BURSTEN_SHIFT, 0);
64+
BURTC_Init(&burtc_init);
65+
BURTC_RetRegSet(0, 0);
66+
BURTC_RetRegSet(1, 0);
67+
}
68+
}
69+
70+
void rtc_free(void)
71+
{
72+
/* Nothing to release here */
73+
}
74+
75+
int rtc_isenabled(void)
76+
{
77+
return (BUS_RegBitRead(&BURTC->CTRL, _BURTC_CTRL_RSTEN_SHIFT) == 0);
78+
}
79+
80+
time_t rtc_read(void)
81+
{
82+
uint32_t ts = 0;
83+
uint32_t ts2 = 1;
84+
do {
85+
if (BURTC->IF & BURTC_IF_OF) {
86+
BURTC_RetRegSet(1, BURTC_RetRegGet(1)+1);
87+
BURTC->IFC = BURTC_IFC_OF;
88+
}
89+
90+
ts = ts2;
91+
ts2 = (BURTC_CounterGet() >> 8) + BURTC_RetRegGet(0) + (BURTC_RetRegGet(1) << 16);
92+
} while (ts != ts2);
93+
return ts2;
94+
}
95+
96+
void rtc_write(time_t t)
97+
{
98+
BURTC_RetRegSet(0, t - (BURTC_CounterGet() >> 8));
99+
BURTC_RetRegSet(1, 0);
100+
if (BURTC->IF & BURTC_IF_OF) {
101+
BURTC->IFC = BURTC_IFC_OF;
102+
}
103+
}
104+
105+
#endif /* BURTC_PRESENT && !RTCC_PRESENT */
106+
#endif /* DEVICE_RTC */

targets/TARGET_Silicon_Labs/TARGET_EFM32/common/clocking.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,12 @@
106106
#endif
107107

108108
/* Adjust this to change speed of RTC and LP ticker ticks */
109-
#define RTC_CLOCKDIV cmuClkDiv_8
109+
#define RTC_CLOCKDIV cmuClkDiv_1
110110
/* Adjust this to match RTC_CLOCKDIV as integer value */
111-
#define RTC_CLOCKDIV_INT 8U
111+
#define RTC_CLOCKDIV_INT 1U
112112
/* Adjust this to match RTC_CLOCKDIV as shift for 1 second worth of ticks.
113113
* E.g. with 32768 Hz crystal and CLOCKDIV of 8, 1 second is 4096 ticks.
114114
* 4096 equals 1 << 12, so RTC_FREQ_SHIFT needs to be 12. */
115-
#define RTC_FREQ_SHIFT 12U
115+
#define RTC_FREQ_SHIFT 15U
116116

117117
#endif

targets/TARGET_Silicon_Labs/TARGET_EFM32/common/mbed_overrides.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,11 @@ void mbed_sdk_init()
117117
# error "Low energy clock selection not valid"
118118
#endif
119119

120+
#if defined(RTCC_PRESENT)
121+
/* Turn RTCC clock gate back on to keep RTC time correct */
122+
CMU_ClockEnable(cmuClock_RTCC, true);
123+
#endif
124+
120125
#if defined(EFM_BC_EN)
121126
/* Enable BC line driver to avoid garbage on CDC port */
122127
gpio_init_out_ex(&bc_enable, EFM_BC_EN, 1);

targets/TARGET_Silicon_Labs/TARGET_EFM32/lp_ticker.c

Lines changed: 91 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,17 @@
3232
* for the low power timer, which should be good enough for a low power use
3333
* case.
3434
*
35+
* Mapping of mbed APIs to Silicon Labs peripherals:
36+
* ---: Does not meet mbed API requirements
37+
* X : Implemented to provide mbed API functionality
38+
*
39+
* --------------------------------------------
40+
* | ------------- | RTCC | BURTC | RTC | TIMER |
41+
* | rtc_api | X | X | --- | ----- |
42+
* | lp_ticker_api | X | | X | ----- |
43+
* | us_ticker_api | --- | ----- | --- | X |
44+
* --------------------------------------------
45+
*
3546
* On Silicon Labs devices, the lowest width RTC implementation has a 24-bit
3647
* counter, which gets extended with a further 32-bit software counter. This
3748
* gives 56 bits of actual width, which with the default speed maps to
@@ -40,91 +51,114 @@
4051
* (At max speed the wraparound is at 69730 years, which is unlikely as well)
4152
******************************************************************************/
4253

43-
#include "rtc_api.h"
44-
#include "rtc_api_HAL.h"
54+
#if defined(RTC_PRESENT)
55+
#include "em_rtc.h"
56+
#include "em_cmu.h"
4557
#include "lp_ticker_api.h"
4658
#include "mbed_critical.h"
4759

48-
static int rtc_reserved = 0;
60+
#if RTC_CLOCKDIV_INT > 16
61+
#error invalid prescaler value RTC_CLOCKDIV_INT, since LP ticker resolution will exceed 1ms.
62+
#endif
63+
64+
#define RTC_BITS (24U)
65+
#define RTC_MAX_VALUE (0xFFFFFFUL)
66+
67+
static bool rtc_inited = false;
68+
69+
const ticker_info_t* lp_ticker_get_info(void)
70+
{
71+
static const ticker_info_t rtc_info = {
72+
LOW_ENERGY_CLOCK_FREQUENCY,
73+
RTC_BITS
74+
};
75+
return &rtc_info;
76+
}
77+
78+
void RTC_IRQHandler(void)
79+
{
80+
uint32_t flags;
81+
flags = RTC_IntGet();
82+
if ((flags & RTC_IF_COMP0) && rtc_inited) {
83+
RTC_IntClear(RTC_IF_COMP0);
84+
lp_ticker_irq_handler();
85+
}
86+
}
4987

5088
void lp_ticker_init()
5189
{
52-
if(!rtc_reserved) {
53-
core_util_critical_section_enter();
54-
rtc_init_real(RTC_INIT_LPTIMER);
55-
rtc_set_comp0_handler((uint32_t)lp_ticker_irq_handler);
56-
rtc_reserved = 1;
57-
core_util_critical_section_exit();
90+
core_util_critical_section_enter();
91+
if (!rtc_inited) {
92+
CMU_ClockEnable(cmuClock_RTC, true);
93+
94+
/* Initialize RTC */
95+
RTC_Init_TypeDef init = RTC_INIT_DEFAULT;
96+
init.enable = 1;
97+
/* Don't use compare register 0 as top value */
98+
init.comp0Top = 0;
99+
100+
/* Initialize */
101+
RTC_Init(&init);
102+
RTC_CounterSet(20);
103+
104+
/* Enable Interrupt from RTC */
105+
RTC_IntDisable(RTC_IF_COMP0);
106+
RTC_IntClear(RTC_IF_COMP0);
107+
NVIC_SetVector(RTC_IRQn, (uint32_t)RTC_IRQHandler);
108+
NVIC_EnableIRQ(RTC_IRQn);
109+
110+
rtc_inited = true;
111+
} else {
112+
/* Cancel current interrupt by virtue of calling init again */
113+
RTC_IntDisable(RTC_IF_COMP0);
114+
RTC_IntClear(RTC_IF_COMP0);
58115
}
116+
core_util_critical_section_exit();
59117
}
60118

61119
void lp_ticker_free()
62120
{
63-
if(rtc_reserved) {
64-
core_util_critical_section_enter();
65-
rtc_free_real(RTC_INIT_LPTIMER);
66-
rtc_reserved = 0;
67-
core_util_critical_section_exit();
121+
/* Disable the RTC if it was inited and is no longer in use by anyone. */
122+
if (rtc_inited) {
123+
NVIC_DisableIRQ(RTC_IRQn);
124+
RTC_Reset();
125+
CMU_ClockEnable(cmuClock_RTC, false);
126+
rtc_inited = false;
68127
}
69128
}
70129

71130
void lp_ticker_set_interrupt(timestamp_t timestamp)
72131
{
73-
uint64_t rtc_compare_value;
74-
uint64_t current_ticks = rtc_get_full();
75-
timestamp_t current_time = lp_ticker_read();
76-
77-
/* calculate offset value */
78-
timestamp_t offset = timestamp - current_time;
79-
80-
/* If the requested timestamp is too far in the future, we might not be able
81-
* to set the interrupt accurately due to potentially having ticked between
82-
* calculating the timestamp to set and us calculating the offset. */
83-
if(offset > 0xFFFF0000) offset = 100;
84-
85-
/* map offset to RTC value */
86-
// ticks = offset * RTC frequency div 1000000
87-
rtc_compare_value = ((uint64_t)offset * (LOW_ENERGY_CLOCK_FREQUENCY / RTC_CLOCKDIV_INT)) / 1000000;
88-
89-
/* If RTC offset is less then 2 RTC ticks, the interrupt won't fire */
90-
if(rtc_compare_value < 2) {
91-
rtc_compare_value = 2;
92-
}
93-
94-
rtc_compare_value += current_ticks;
95-
96-
rtc_set_comp0_value(rtc_compare_value, true);
132+
RTC_IntDisable(RTC_IF_COMP0);
133+
RTC_IntClear(RTC_IF_COMP0);
134+
RTC_FreezeEnable(true);
135+
RTC_CompareSet(0, (uint32_t) (timestamp & RTC_MAX_VALUE));
136+
RTC_FreezeEnable(false);
137+
RTC_IntEnable(RTC_IF_COMP0);
97138
}
98139

99-
inline void lp_ticker_fire_interrupt(void)
140+
void lp_ticker_fire_interrupt(void)
100141
{
101-
rtc_force_comp0();
142+
RTC_IntEnable(RTC_IF_COMP0);
143+
RTC_IntSet(RTC_IF_COMP0);
102144
}
103145

104-
inline void lp_ticker_disable_interrupt()
146+
void lp_ticker_disable_interrupt()
105147
{
106-
rtc_enable_comp0(false);
148+
RTC_IntDisable(RTC_IF_COMP0);
107149
}
108150

109-
inline void lp_ticker_clear_interrupt()
151+
void lp_ticker_clear_interrupt()
110152
{
111-
/* No need to clear interrupt flag, since that already happens at RTC level */
153+
RTC_IntClear(RTC_IF_COMP0);
112154
}
113155

114156
timestamp_t lp_ticker_read()
115157
{
116-
lp_ticker_init();
117-
118-
uint64_t ticks_temp;
119-
uint64_t ticks = rtc_get_full();
120-
121-
/* ticks = counter tick value
122-
* timestamp = value in microseconds
123-
* timestamp = ticks * 1.000.000 / RTC frequency
124-
*/
125-
126-
ticks_temp = (ticks * 1000000) / (LOW_ENERGY_CLOCK_FREQUENCY / RTC_CLOCKDIV_INT);
127-
return (timestamp_t) (ticks_temp & 0xFFFFFFFF);
158+
return (timestamp_t) RTC_CounterGet();
128159
}
129160

130-
#endif
161+
#elif defined(RTCC_PRESENT)
162+
/* lp_ticker api is implemented in rtc_rtcc.c */
163+
#endif /* RTC_PRESENT */
164+
#endif /* DEVICE_LPTICKER */

0 commit comments

Comments
 (0)