Skip to content

Commit 06eefcb

Browse files
authored
Merge pull request #6536 from c1728p9/sync_lp_ticker_support
Add handling for synchronized low power tickers
2 parents 099e54b + 17892cb commit 06eefcb

File tree

2 files changed

+162
-0
lines changed

2 files changed

+162
-0
lines changed

hal/mbed_lp_ticker_api.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
#if DEVICE_LOWPOWERTIMER
1919

20+
void lp_ticker_set_interrupt_wrapper(timestamp_t timestamp);
21+
2022
static ticker_event_queue_t events = { 0 };
2123

2224
static ticker_irq_handler_type irq_handler = ticker_irq_handler;
@@ -26,7 +28,11 @@ static const ticker_interface_t lp_interface = {
2628
.read = lp_ticker_read,
2729
.disable_interrupt = lp_ticker_disable_interrupt,
2830
.clear_interrupt = lp_ticker_clear_interrupt,
31+
#if LOWPOWERTIMER_DELAY_TICKS > 0
32+
.set_interrupt = lp_ticker_set_interrupt_wrapper,
33+
#else
2934
.set_interrupt = lp_ticker_set_interrupt,
35+
#endif
3036
.fire_interrupt = lp_ticker_fire_interrupt,
3137
.get_info = lp_ticker_get_info,
3238
};

hal/mbed_lp_ticker_wrapper.cpp

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/* mbed Microcontroller Library
2+
* Copyright (c) 2018 ARM Limited
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
#include "hal/lp_ticker_api.h"
17+
18+
#if DEVICE_LOWPOWERTIMER && (LOWPOWERTIMER_DELAY_TICKS > 0)
19+
20+
#include "Timeout.h"
21+
#include "mbed_critical.h"
22+
23+
static const timestamp_t min_delta = LOWPOWERTIMER_DELAY_TICKS;
24+
25+
static bool init = false;
26+
static bool pending = false;
27+
static bool timeout_pending = false;
28+
static timestamp_t last_set_interrupt = 0;
29+
static timestamp_t last_request = 0;
30+
static timestamp_t next = 0;
31+
32+
static timestamp_t mask;
33+
static timestamp_t reschedule_us;
34+
35+
// Do not use SingletonPtr since this must be initialized in a critical section
36+
static mbed::Timeout *timeout;
37+
static uint64_t timeout_data[sizeof(mbed::Timeout) / 8];
38+
39+
/**
40+
* Initialize variables
41+
*/
42+
static void init_local()
43+
{
44+
MBED_ASSERT(core_util_in_critical_section());
45+
46+
const ticker_info_t* info = lp_ticker_get_info();
47+
if (info->bits >= 32) {
48+
mask = 0xffffffff;
49+
} else {
50+
mask = ((uint64_t)1 << info->bits) - 1;
51+
}
52+
53+
// Round us_per_tick up
54+
timestamp_t us_per_tick = (1000000 + info->frequency - 1) / info->frequency;
55+
56+
// Add 1 tick to the min delta for the case where the clock transitions after you read it
57+
// Add 4 microseconds to round up the micro second ticker time (which has a frequency of at least 250KHz - 4us period)
58+
reschedule_us = (min_delta + 1) * us_per_tick + 4;
59+
60+
timeout = new (timeout_data) mbed::Timeout();
61+
}
62+
63+
/**
64+
* Call lp_ticker_set_interrupt with a value that is guaranteed to fire
65+
*
66+
* Assumptions
67+
* -Only one low power clock tick can pass from the last read (last_read)
68+
* -The closest an interrupt can fire is max_delta + 1
69+
*
70+
* @param last_read The last value read from lp_ticker_read
71+
* @param timestamp The timestamp to trigger the interrupt at
72+
*/
73+
static void set_interrupt_safe(timestamp_t last_read, timestamp_t timestamp)
74+
{
75+
MBED_ASSERT(core_util_in_critical_section());
76+
uint32_t delta = (timestamp - last_read) & mask;
77+
if (delta < min_delta + 2) {
78+
timestamp = (last_read + min_delta + 2) & mask;
79+
}
80+
lp_ticker_set_interrupt(timestamp);
81+
}
82+
83+
/**
84+
* Set the low power ticker match time when hardware is ready
85+
*
86+
* This event is scheduled to set the lp timer after the previous write
87+
* has taken effect and it is safe to write a new value without blocking.
88+
* If the time has already passed then this function fires and interrupt
89+
* immediately.
90+
*/
91+
static void set_interrupt_later()
92+
{
93+
core_util_critical_section_enter();
94+
95+
timestamp_t current = lp_ticker_read();
96+
if (_ticker_match_interval_passed(last_request, current, next)) {
97+
lp_ticker_fire_interrupt();
98+
} else {
99+
set_interrupt_safe(current, next);
100+
last_set_interrupt = lp_ticker_read();
101+
}
102+
timeout_pending = false;
103+
104+
core_util_critical_section_exit();
105+
}
106+
107+
/**
108+
* Wrapper around lp_ticker_set_interrupt to prevent blocking
109+
*
110+
* Problems this function is solving:
111+
* 1. Interrupt may not fire if set earlier than LOWPOWERTIMER_DELAY_TICKS low power clock cycles
112+
* 2. Setting the interrupt back-to-back will block
113+
*
114+
* This wrapper function prevents lp_ticker_set_interrupt from being called
115+
* back-to-back and blocking while the first write is in progress. This function
116+
* avoids that problem by scheduling a timeout event if the lp ticker is in the
117+
* middle of a write operation.
118+
*
119+
* @param timestamp Time to call ticker irq
120+
* @note this is a utility function and it's not required part of HAL implementation
121+
*/
122+
extern "C" void lp_ticker_set_interrupt_wrapper(timestamp_t timestamp)
123+
{
124+
core_util_critical_section_enter();
125+
126+
if (!init) {
127+
init_local();
128+
init = true;
129+
}
130+
131+
timestamp_t current = lp_ticker_read();
132+
if (pending) {
133+
// Check if pending should be cleared
134+
if (((current - last_set_interrupt) & mask) >= min_delta) {
135+
pending = false;
136+
}
137+
}
138+
139+
if (pending || timeout_pending) {
140+
next = timestamp;
141+
last_request = current;
142+
if (!timeout_pending) {
143+
timeout->attach_us(set_interrupt_later, reschedule_us);
144+
timeout_pending = true;
145+
}
146+
} else {
147+
// Schedule immediately if nothing is pending
148+
set_interrupt_safe(current, timestamp);
149+
last_set_interrupt = lp_ticker_read();
150+
pending = true;
151+
}
152+
153+
core_util_critical_section_exit();
154+
}
155+
156+
#endif

0 commit comments

Comments
 (0)