From adc64cccacfd93f04f17dd2f01f278c020b8831d Mon Sep 17 00:00:00 2001 From: Russ Butler Date: Thu, 12 Jul 2018 11:25:07 -0500 Subject: [PATCH] Update low power ticker wrapper Update the low power ticker wrapper code so it does not violate any properties of the ticker specification. In specific this patch fixes the following: - Prevent spurious interrupts - Fire interrupt only when the ticker times increments to or past the value set by ticker_set_interrupt - Disable interrupts when ticker_init is called --- hal/LowPowerTickerWrapper.cpp | 290 +++++++++++++++++++++++++++++++++ hal/LowPowerTickerWrapper.h | 239 +++++++++++++++++++++++++++ hal/mbed_lp_ticker_api.c | 15 +- hal/mbed_lp_ticker_wrapper.cpp | 173 ++++++++------------ hal/mbed_lp_ticker_wrapper.h | 78 +++++++++ 5 files changed, 688 insertions(+), 107 deletions(-) create mode 100644 hal/LowPowerTickerWrapper.cpp create mode 100644 hal/LowPowerTickerWrapper.h create mode 100644 hal/mbed_lp_ticker_wrapper.h diff --git a/hal/LowPowerTickerWrapper.cpp b/hal/LowPowerTickerWrapper.cpp new file mode 100644 index 00000000000..fdbdf2c01f4 --- /dev/null +++ b/hal/LowPowerTickerWrapper.cpp @@ -0,0 +1,290 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018 ARM Limited + * + * 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 "hal/LowPowerTickerWrapper.h" +#include "platform/Callback.h" + +LowPowerTickerWrapper::LowPowerTickerWrapper(const ticker_data_t *data, const ticker_interface_t *interface, uint32_t min_cycles_between_writes, uint32_t min_cycles_until_match) + : _intf(data->interface), _min_count_between_writes(min_cycles_between_writes + 1), _min_count_until_match(min_cycles_until_match + 1), _suspended(false) +{ + core_util_critical_section_enter(); + + this->data.interface = interface; + this->data.queue = data->queue; + _reset(); + + core_util_critical_section_exit(); +} + +void LowPowerTickerWrapper::irq_handler(ticker_irq_handler_type handler) +{ + core_util_critical_section_enter(); + + if (_suspended) { + if (handler) { + handler(&data); + } + core_util_critical_section_exit(); + return; + } + + if (_pending_fire_now || _match_check(_intf->read())) { + _timeout.detach(); + _pending_timeout = false; + _pending_match = false; + _pending_fire_now = false; + if (handler) { + handler(&data); + } + } else { + // Spurious interrupt + _intf->clear_interrupt(); + } + + core_util_critical_section_exit(); +} + +void LowPowerTickerWrapper::suspend() +{ + core_util_critical_section_enter(); + + // Wait until rescheduling is allowed + while (!_set_interrupt_allowed) { + timestamp_t current = _intf->read(); + if (((current - _last_actual_set_interrupt) & _mask) >= _min_count_between_writes) { + _set_interrupt_allowed = true; + } + } + + _reset(); + _suspended = true; + + core_util_critical_section_exit(); +} + +void LowPowerTickerWrapper::resume() +{ + core_util_critical_section_enter(); + + _suspended = false; + + core_util_critical_section_exit(); +} + +bool LowPowerTickerWrapper::timeout_pending() +{ + core_util_critical_section_enter(); + + bool pending = _pending_timeout; + + core_util_critical_section_exit(); + return pending; +} + +void LowPowerTickerWrapper::init() +{ + core_util_critical_section_enter(); + + _reset(); + _intf->init(); + + core_util_critical_section_exit(); +} + +void LowPowerTickerWrapper::free() +{ + core_util_critical_section_enter(); + + _reset(); + _intf->free(); + + core_util_critical_section_exit(); +} + +uint32_t LowPowerTickerWrapper::read() +{ + core_util_critical_section_enter(); + + timestamp_t current = _intf->read(); + if (_match_check(current)) { + _intf->fire_interrupt(); + } + + core_util_critical_section_exit(); + return current; +} + +void LowPowerTickerWrapper::set_interrupt(timestamp_t timestamp) +{ + core_util_critical_section_enter(); + + _last_set_interrupt = _intf->read(); + _cur_match_time = timestamp; + _pending_match = true; + _schedule_match(_last_set_interrupt); + + core_util_critical_section_exit(); +} + +void LowPowerTickerWrapper::disable_interrupt() +{ + core_util_critical_section_enter(); + + _intf->disable_interrupt(); + + core_util_critical_section_exit(); +} + +void LowPowerTickerWrapper::clear_interrupt() +{ + core_util_critical_section_enter(); + + _intf->clear_interrupt(); + + core_util_critical_section_exit(); +} + +void LowPowerTickerWrapper::fire_interrupt() +{ + core_util_critical_section_enter(); + + _pending_fire_now = 1; + _intf->fire_interrupt(); + + core_util_critical_section_exit(); +} + +const ticker_info_t *LowPowerTickerWrapper::get_info() +{ + + core_util_critical_section_enter(); + + const ticker_info_t *info = _intf->get_info(); + + core_util_critical_section_exit(); + return info; +} + +void LowPowerTickerWrapper::_reset() +{ + MBED_ASSERT(core_util_in_critical_section()); + + _timeout.detach(); + _pending_timeout = false; + _pending_match = false; + _pending_fire_now = false; + _set_interrupt_allowed = true; + _cur_match_time = 0; + _last_set_interrupt = 0; + _last_actual_set_interrupt = 0; + + const ticker_info_t *info = _intf->get_info(); + if (info->bits >= 32) { + _mask = 0xffffffff; + } else { + _mask = ((uint64_t)1 << info->bits) - 1; + } + + // Round us_per_tick up + _us_per_tick = (1000000 + info->frequency - 1) / info->frequency; +} + +void LowPowerTickerWrapper::_timeout_handler() +{ + core_util_critical_section_enter(); + _pending_timeout = false; + + timestamp_t current = _intf->read(); + if (_ticker_match_interval_passed(_last_set_interrupt, current, _cur_match_time)) { + _intf->fire_interrupt(); + } else { + _schedule_match(current); + } + + core_util_critical_section_exit(); +} + +bool LowPowerTickerWrapper::_match_check(timestamp_t current) +{ + MBED_ASSERT(core_util_in_critical_section()); + + if (!_pending_match) { + return false; + } + return _ticker_match_interval_passed(_last_set_interrupt, current, _cur_match_time); +} + +uint32_t LowPowerTickerWrapper::_lp_ticks_to_us(uint32_t ticks) +{ + MBED_ASSERT(core_util_in_critical_section()); + + // Add 4 microseconds to round up the micro second ticker time (which has a frequency of at least 250KHz - 4us period) + return _us_per_tick * ticks + 4; +} + +void LowPowerTickerWrapper::_schedule_match(timestamp_t current) +{ + MBED_ASSERT(core_util_in_critical_section()); + + // Check if _intf->set_interrupt is allowed + if (!_set_interrupt_allowed) { + if (((current - _last_actual_set_interrupt) & _mask) >= _min_count_between_writes) { + _set_interrupt_allowed = true; + } + } + + uint32_t cycles_until_match = (_cur_match_time - _last_set_interrupt) & _mask; + bool too_close = cycles_until_match < _min_count_until_match; + + if (!_set_interrupt_allowed) { + + // Can't use _intf->set_interrupt so use microsecond Timeout instead + uint32_t ticks = cycles_until_match < _min_count_until_match ? cycles_until_match : _min_count_until_match; + _timeout.attach_us(mbed::callback(this, &LowPowerTickerWrapper::_timeout_handler), _lp_ticks_to_us(ticks)); + _pending_timeout = true; + return; + } + + if (!too_close) { + + // Schedule LP ticker + _intf->set_interrupt(_cur_match_time); + current = _intf->read(); + _last_actual_set_interrupt = current; + _set_interrupt_allowed = false; + + // Check for overflow + uint32_t new_cycles_until_match = (_cur_match_time - current) & _mask; + if (new_cycles_until_match > cycles_until_match) { + // Overflow so fire now + _intf->fire_interrupt(); + return; + } + + // Update variables with new time + cycles_until_match = new_cycles_until_match; + too_close = cycles_until_match < _min_count_until_match; + } + + if (too_close) { + + // Low power ticker incremented to less than _min_count_until_match + // so low power ticker may not fire. Use Timeout to ensure it does fire. + uint32_t ticks = cycles_until_match < _min_count_until_match ? cycles_until_match : _min_count_until_match; + _timeout.attach_us(mbed::callback(this, &LowPowerTickerWrapper::_timeout_handler), _lp_ticks_to_us(ticks)); + _pending_timeout = true; + return; + } +} diff --git a/hal/LowPowerTickerWrapper.h b/hal/LowPowerTickerWrapper.h new file mode 100644 index 00000000000..aac8d70d171 --- /dev/null +++ b/hal/LowPowerTickerWrapper.h @@ -0,0 +1,239 @@ + +/** \addtogroup hal */ +/** @{*/ +/* mbed Microcontroller Library + * Copyright (c) 2018 ARM Limited + * + * 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. + */ +#ifndef MBED_LOW_POWER_TICKER_WRAPPER_H +#define MBED_LOW_POWER_TICKER_WRAPPER_H + +#include "device.h" + +#include "hal/ticker_api.h" +#include "hal/us_ticker_api.h" +#include "drivers/Timeout.h" + + +class LowPowerTickerWrapper { +public: + + + /** + * Create a new wrapped low power ticker object + * + * @param data Low power ticker data to wrap + * @param interface new ticker interface functions + * @param min_cycles_between_writes The number of whole low power clock periods + * which must complete before subsequent calls to set_interrupt + * @param min_cycles_until_match The minimum number of whole low power clock periods + * from the current time for which the match timestamp passed to set_interrupt is + * guaranteed to fire. + * + * N = min_cycles_between_writes + * + * 0 1 N - 1 N N + 1 N + 2 N + 3 + * |-------|------...------|-------|-------|-------|-------| + * ^ ^ + * | | + * set_interrupt Next set_interrupt allowed + * + * N = min_cycles_until_match + * + * 0 1 N - 1 N N + 1 N + 2 N + 3 + * |-------|------...------|-------|-------|-------|-------| + * ^ ^ + * | | + * set_interrupt Earliest match timestamp allowed + * + * + */ + + LowPowerTickerWrapper(const ticker_data_t *data, const ticker_interface_t *interface, uint32_t min_cycles_between_writes, uint32_t min_cycles_until_match); + + /** + * Interrupt handler called by the underlying driver/hardware + * + * @param handler The callback which would normally be called by the underlying driver/hardware + */ + void irq_handler(ticker_irq_handler_type handler); + + /** + * Suspend wrapper operation and pass through interrupts. + * + * This stops to wrapper layer from using the microsecond ticker. + * This should be called before using the low power ticker APIs directly. + */ + void suspend(); + + /** + * Resume wrapper operation and filter interrupts normally + */ + void resume(); + + /** + * Check if a Timeout object is being used + * + * @return true if Timeout is used for scheduling false otherwise + */ + bool timeout_pending(); + + /* + * Implementation of ticker_init + */ + void init(); + + /* + * Implementation of free + */ + void free(); + + /* + * Implementation of read + */ + uint32_t read(); + + /* + * Implementation of set_interrupt + */ + void set_interrupt(timestamp_t timestamp); + + /* + * Implementation of disable_interrupt + */ + void disable_interrupt(); + + /* + * Implementation of clear_interrupt + */ + void clear_interrupt(); + + /* + * Implementation of fire_interrupt + */ + void fire_interrupt(); + + /* + * Implementation of get_info + */ + const ticker_info_t *get_info(); + + ticker_data_t data; + +private: + mbed::Timeout _timeout; + const ticker_interface_t *const _intf; + + /* + * The number of low power clock cycles which must pass between subsequent + * calls to intf->set_interrupt + */ + const uint32_t _min_count_between_writes; + + /* + * The minimum number of low power clock cycles in the future that + * a match value can be set to and still fire + */ + const uint32_t _min_count_until_match; + + /* + * Flag to indicate if the timer is suspended + */ + bool _suspended; + + /* + * _cur_match_time is valid and Timeout is scheduled to fire + */ + bool _pending_timeout; + + /* + * set_interrupt has been called and _cur_match_time is valid + */ + bool _pending_match; + + /* + * The function LowPowerTickerWrapper::fire_interrupt has been called + * and an interrupt is expected. + */ + bool _pending_fire_now; + + /* + * It is safe to call intf->set_interrupt + */ + bool _set_interrupt_allowed; + + /* + * Last value written by LowPowerTickerWrapper::set_interrupt + */ + timestamp_t _cur_match_time; + + /* + * Time of last call to LowPowerTickerWrapper::set_interrupt + */ + uint32_t _last_set_interrupt; + + /* + * Time of last call to intf->set_interrupt + */ + uint32_t _last_actual_set_interrupt; + + /* + * Mask of valid bits from intf->read() + */ + uint32_t _mask; + + /* + * Microsecond per low power tick (rounded up) + */ + uint32_t _us_per_tick; + + + void _reset(); + + /** + * Set the low power ticker match time when hardware is ready + * + * This event is scheduled to set the lp timer after the previous write + * has taken effect and it is safe to write a new value without blocking. + * If the time has already passed then this function fires and interrupt + * immediately. + */ + void _timeout_handler(); + + /* + * Check match time has passed + */ + bool _match_check(timestamp_t current); + + /* + * Convert low power ticks to approximate microseconds + * + * This value is always larger or equal to exact value. + */ + uint32_t _lp_ticks_to_us(uint32_t); + + /* + * Schedule a match interrupt to fire at the correct time + * + * @param current The current low power ticker time + */ + void _schedule_match(timestamp_t current); + +}; + +#endif + +/** @}*/ + + diff --git a/hal/mbed_lp_ticker_api.c b/hal/mbed_lp_ticker_api.c index fbf72c33411..477c1b13875 100644 --- a/hal/mbed_lp_ticker_api.c +++ b/hal/mbed_lp_ticker_api.c @@ -14,11 +14,10 @@ * limitations under the License. */ #include "hal/lp_ticker_api.h" +#include "hal/mbed_lp_ticker_wrapper.h" #if DEVICE_LPTICKER -void lp_ticker_set_interrupt_wrapper(timestamp_t timestamp); - static ticker_event_queue_t events = { 0 }; static ticker_irq_handler_type irq_handler = ticker_irq_handler; @@ -28,11 +27,7 @@ static const ticker_interface_t lp_interface = { .read = lp_ticker_read, .disable_interrupt = lp_ticker_disable_interrupt, .clear_interrupt = lp_ticker_clear_interrupt, -#if LPTICKER_DELAY_TICKS > 0 - .set_interrupt = lp_ticker_set_interrupt_wrapper, -#else .set_interrupt = lp_ticker_set_interrupt, -#endif .fire_interrupt = lp_ticker_fire_interrupt, .get_info = lp_ticker_get_info, .free = lp_ticker_free, @@ -45,7 +40,11 @@ static const ticker_data_t lp_data = { const ticker_data_t *get_lp_ticker_data(void) { +#if LPTICKER_DELAY_TICKS > 0 + return get_lp_ticker_wrapper_data(&lp_data); +#else return &lp_data; +#endif } ticker_irq_handler_type set_lp_ticker_irq_handler(ticker_irq_handler_type ticker_irq_handler) @@ -59,9 +58,13 @@ ticker_irq_handler_type set_lp_ticker_irq_handler(ticker_irq_handler_type ticker void lp_ticker_irq_handler(void) { +#if LPTICKER_DELAY_TICKS > 0 + lp_ticker_wrapper_irq_handler(irq_handler); +#else if (irq_handler) { irq_handler(&lp_data); } +#endif } #endif diff --git a/hal/mbed_lp_ticker_wrapper.cpp b/hal/mbed_lp_ticker_wrapper.cpp index 9c1a2b47289..403141fceaa 100644 --- a/hal/mbed_lp_ticker_wrapper.cpp +++ b/hal/mbed_lp_ticker_wrapper.cpp @@ -13,144 +13,115 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "hal/lp_ticker_api.h" +#include "hal/mbed_lp_ticker_wrapper.h" #if DEVICE_LPTICKER && (LPTICKER_DELAY_TICKS > 0) -#include "Timeout.h" -#include "mbed_critical.h" - -static const timestamp_t min_delta = LPTICKER_DELAY_TICKS; +#include "hal/LowPowerTickerWrapper.h" +#include "platform/mbed_critical.h" +// Do not use SingletonPtr since this must be initialized in a critical section +static LowPowerTickerWrapper *ticker_wrapper; +static uint64_t ticker_wrapper_data[(sizeof(LowPowerTickerWrapper) + 7) / 8]; static bool init = false; -static bool pending = false; -static bool timeout_pending = false; -static timestamp_t last_set_interrupt = 0; -static timestamp_t last_request = 0; -static timestamp_t next = 0; -static timestamp_t mask; -static timestamp_t reschedule_us; +static void lp_ticker_wrapper_init() +{ + ticker_wrapper->init(); +} -// Do not use SingletonPtr since this must be initialized in a critical section -static mbed::Timeout *timeout; -static uint64_t timeout_data[sizeof(mbed::Timeout) / 8]; +static uint32_t lp_ticker_wrapper_read() +{ + return ticker_wrapper->read(); +} -/** - * Initialize variables - */ -static void init_local() +static void lp_ticker_wrapper_set_interrupt(timestamp_t timestamp) { - MBED_ASSERT(core_util_in_critical_section()); + ticker_wrapper->set_interrupt(timestamp); +} - const ticker_info_t *info = lp_ticker_get_info(); - if (info->bits >= 32) { - mask = 0xffffffff; - } else { - mask = ((uint64_t)1 << info->bits) - 1; - } +static void lp_ticker_wrapper_disable_interrupt() +{ + ticker_wrapper->disable_interrupt(); +} - // Round us_per_tick up - timestamp_t us_per_tick = (1000000 + info->frequency - 1) / info->frequency; +static void lp_ticker_wrapper_clear_interrupt() +{ + ticker_wrapper->clear_interrupt(); +} - // Add 1 tick to the min delta for the case where the clock transitions after you read it - // Add 4 microseconds to round up the micro second ticker time (which has a frequency of at least 250KHz - 4us period) - reschedule_us = (min_delta + 1) * us_per_tick + 4; +static void lp_ticker_wrapper_fire_interrupt() +{ + ticker_wrapper->fire_interrupt(); +} - timeout = new (timeout_data) mbed::Timeout(); +static const ticker_info_t *lp_ticker_wrapper_get_info() +{ + return ticker_wrapper->get_info(); } -/** - * Call lp_ticker_set_interrupt with a value that is guaranteed to fire - * - * Assumptions - * -Only one low power clock tick can pass from the last read (last_read) - * -The closest an interrupt can fire is max_delta + 1 - * - * @param last_read The last value read from lp_ticker_read - * @param timestamp The timestamp to trigger the interrupt at - */ -static void set_interrupt_safe(timestamp_t last_read, timestamp_t timestamp) +static void lp_ticker_wrapper_free() { - MBED_ASSERT(core_util_in_critical_section()); - uint32_t delta = (timestamp - last_read) & mask; - if (delta < min_delta + 2) { - timestamp = (last_read + min_delta + 2) & mask; - } - lp_ticker_set_interrupt(timestamp); + ticker_wrapper->free(); } -/** - * Set the low power ticker match time when hardware is ready - * - * This event is scheduled to set the lp timer after the previous write - * has taken effect and it is safe to write a new value without blocking. - * If the time has already passed then this function fires and interrupt - * immediately. - */ -static void set_interrupt_later() +static const ticker_interface_t lp_interface = { + lp_ticker_wrapper_init, + lp_ticker_wrapper_read, + lp_ticker_wrapper_disable_interrupt, + lp_ticker_wrapper_clear_interrupt, + lp_ticker_wrapper_set_interrupt, + lp_ticker_wrapper_fire_interrupt, + lp_ticker_wrapper_free, + lp_ticker_wrapper_get_info +}; + +void lp_ticker_wrapper_irq_handler(ticker_irq_handler_type handler) { core_util_critical_section_enter(); - timestamp_t current = lp_ticker_read(); - if (_ticker_match_interval_passed(last_request, current, next)) { - lp_ticker_fire_interrupt(); - } else { - set_interrupt_safe(current, next); - last_set_interrupt = lp_ticker_read(); + if (!init) { + // Force ticker to initialize + get_lp_ticker_data(); } - timeout_pending = false; + + ticker_wrapper->irq_handler(handler); core_util_critical_section_exit(); } -/** - * Wrapper around lp_ticker_set_interrupt to prevent blocking - * - * Problems this function is solving: - * 1. Interrupt may not fire if set earlier than LPTICKER_DELAY_TICKS low power clock cycles - * 2. Setting the interrupt back-to-back will block - * - * This wrapper function prevents lp_ticker_set_interrupt from being called - * back-to-back and blocking while the first write is in progress. This function - * avoids that problem by scheduling a timeout event if the lp ticker is in the - * middle of a write operation. - * - * @param timestamp Time to call ticker irq - * @note this is a utility function and it's not required part of HAL implementation - */ -extern "C" void lp_ticker_set_interrupt_wrapper(timestamp_t timestamp) +const ticker_data_t *get_lp_ticker_wrapper_data(const ticker_data_t *data) { core_util_critical_section_enter(); if (!init) { - init_local(); + ticker_wrapper = new (ticker_wrapper_data) LowPowerTickerWrapper(data, &lp_interface, LPTICKER_DELAY_TICKS, LPTICKER_DELAY_TICKS); init = true; } - timestamp_t current = lp_ticker_read(); - if (pending) { - // Check if pending should be cleared - if (((current - last_set_interrupt) & mask) >= min_delta) { - pending = false; - } + core_util_critical_section_exit(); + + return &ticker_wrapper->data; +} + +void lp_ticker_wrapper_suspend() +{ + if (!init) { + // Force ticker to initialize + get_lp_ticker_data(); } - if (pending || timeout_pending) { - next = timestamp; - last_request = current; - if (!timeout_pending) { - timeout->attach_us(set_interrupt_later, reschedule_us); - timeout_pending = true; - } - } else { - // Schedule immediately if nothing is pending - set_interrupt_safe(current, timestamp); - last_set_interrupt = lp_ticker_read(); - pending = true; + ticker_wrapper->suspend(); +} + +void lp_ticker_wrapper_resume() +{ + if (!init) { + // Force ticker to initialize + get_lp_ticker_data(); } - core_util_critical_section_exit(); + ticker_wrapper->resume(); } #endif diff --git a/hal/mbed_lp_ticker_wrapper.h b/hal/mbed_lp_ticker_wrapper.h new file mode 100644 index 00000000000..4b317e36c27 --- /dev/null +++ b/hal/mbed_lp_ticker_wrapper.h @@ -0,0 +1,78 @@ + +/** \addtogroup hal */ +/** @{*/ +/* mbed Microcontroller Library + * Copyright (c) 2018 ARM Limited + * + * 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. + */ +#ifndef MBED_LP_TICKER_WRAPPER_H +#define MBED_LP_TICKER_WRAPPER_H + +#include "device.h" + +#if DEVICE_LPTICKER + +#include "hal/ticker_api.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*ticker_irq_handler_type)(const ticker_data_t *const); + +/** + * Interrupt handler for the wrapped lp ticker + * + * @param handler the function which would normally be called by the + * lp ticker handler when it is not wrapped + */ +void lp_ticker_wrapper_irq_handler(ticker_irq_handler_type handler); + +/** + * Get wrapped lp ticker data + * + * @param data hardware low power ticker object + * @return wrapped low power ticker object + */ +const ticker_data_t *get_lp_ticker_wrapper_data(const ticker_data_t *data); + +/** + * Suspend the wrapper layer code + * + * Pass through all interrupts to the low power ticker and stop using + * the microsecond ticker. + */ +void lp_ticker_wrapper_suspend(void); + +/** + * Resume the wrapper layer code + * + * Resume operation of the wrapper layer. Interrupts will be filtered + * as normal and the microsecond timer will be used for interrupts scheduled + * too quickly back-to-back. + */ +void lp_ticker_wrapper_resume(void); + +/**@}*/ + +#ifdef __cplusplus +} +#endif + +#endif + +#endif + +/** @}*/