From 34428f9d4cf1efa7bd05324be74966510e106c99 Mon Sep 17 00:00:00 2001 From: Kevin Bracey Date: Thu, 13 Feb 2020 11:21:20 +0200 Subject: [PATCH] Add Chrono support to Ticker et al --- drivers/LowPowerTicker.h | 9 +- drivers/LowPowerTimeout.h | 40 ++++++-- drivers/LowPowerTimer.h | 4 +- drivers/Ticker.h | 76 ++++++++++----- drivers/TickerDataClock.h | 170 ++++++++++++++++++++++++++++++++++ drivers/Timeout.h | 64 ++++++++++++- drivers/Timer.h | 45 +++++---- drivers/TimerEvent.h | 44 +++++++-- drivers/source/Ticker.cpp | 63 +++++++++---- drivers/source/Timeout.cpp | 38 +++++++- drivers/source/Timer.cpp | 89 +++++++++--------- drivers/source/TimerEvent.cpp | 23 +++-- hal/mbed_ticker_api.c | 15 +++ hal/ticker_api.h | 8 ++ 14 files changed, 551 insertions(+), 137 deletions(-) create mode 100644 drivers/TickerDataClock.h diff --git a/drivers/LowPowerTicker.h b/drivers/LowPowerTicker.h index 9a2a93cdf4..09ce9ec3c1 100644 --- a/drivers/LowPowerTicker.h +++ b/drivers/LowPowerTicker.h @@ -19,7 +19,6 @@ #include "platform/platform.h" #include "drivers/Ticker.h" -#include "platform/NonCopyable.h" #if defined (DEVICE_LPTICKER) || defined(DOXYGEN_ONLY) @@ -40,14 +39,10 @@ namespace mbed { * * @note Synchronization level: Interrupt safe */ -class LowPowerTicker : public Ticker, private NonCopyable { +class LowPowerTicker : public TickerBase { public: - LowPowerTicker() : Ticker(get_lp_ticker_data()) - { - } - - virtual ~LowPowerTicker() + LowPowerTicker() : TickerBase(get_lp_ticker_data()) { } }; diff --git a/drivers/LowPowerTimeout.h b/drivers/LowPowerTimeout.h index 91c60b7c10..5a1352e16b 100644 --- a/drivers/LowPowerTimeout.h +++ b/drivers/LowPowerTimeout.h @@ -21,9 +21,8 @@ #if DEVICE_LPTICKER || defined(DOXYGEN_ONLY) -#include "hal/lp_ticker_api.h" -#include "drivers/LowPowerTicker.h" -#include "platform/NonCopyable.h" +#include "drivers/LowPowerClock.h" +#include "drivers/Timeout.h" namespace mbed { /** @@ -36,14 +35,37 @@ namespace mbed { * * @note Synchronization level: Interrupt safe */ -class LowPowerTimeout : public LowPowerTicker, private NonCopyable { -#if !defined(DOXYGEN_ONLY) -private: - virtual void handler(void) +class LowPowerTimeout : public TimeoutBase { +public: + LowPowerTimeout(); + + /** Clock to use with attach_absolute, guaranteeing running only while attached or manually locked */ + using clock = LowPowerClock; + + /** Clock to use with attach_absolute, running always */ + using steady_clock = LowPowerClock; + + /** @copydoc TimeoutBase::scheduled_time() */ + LowPowerClock::time_point scheduled_time() const { - _function.call(); + /* Massage from virtual TickerDataClock::time_point used internally to true LowPowerClock::time_point */ + return LowPowerClock::time_point{TimeoutBase::scheduled_time().time_since_epoch()}; + } + + /** Attach a function to be called by the Timeout, specifying the absolute time + * + * @param func pointer to the function to be called + * @param abs_time the absolute time for the call, referenced to LowPowerClock + * + * @note setting @a abs_time to a time in the past means the event will be scheduled immediately + * resulting in an instant call to the function. + */ + template + void attach_absolute(F &&func, LowPowerClock::time_point abs_time) + { + /* Massage from true LowPowerClock::time_point to virtual TickerDataClock::time_point used internally */ + TimeoutBase::attach_absolute(std::forward(func), TickerDataClock::time_point{abs_time.time_since_epoch()}); } -#endif }; /** @}*/ diff --git a/drivers/LowPowerTimer.h b/drivers/LowPowerTimer.h index 4356036237..2000ef9ca8 100644 --- a/drivers/LowPowerTimer.h +++ b/drivers/LowPowerTimer.h @@ -36,10 +36,10 @@ namespace mbed { * * @note Synchronization level: Interrupt safe */ -class LowPowerTimer : public Timer, private NonCopyable { +class LowPowerTimer : public TimerBase { public: - LowPowerTimer() : Timer(get_lp_ticker_data()) + LowPowerTimer() : TimerBase(get_lp_ticker_data()) { } diff --git a/drivers/Ticker.h b/drivers/Ticker.h index 61238d264e..1de8838eaa 100644 --- a/drivers/Ticker.h +++ b/drivers/Ticker.h @@ -17,7 +17,9 @@ #ifndef MBED_TICKER_H #define MBED_TICKER_H +#include #include +#include "drivers/TickerDataClock.h" #include "drivers/TimerEvent.h" #include "platform/Callback.h" #include "platform/mbed_toolchain.h" @@ -25,6 +27,7 @@ #include "hal/lp_ticker_api.h" namespace mbed { + /** * \defgroup drivers_Ticker Ticker class * \ingroup drivers-public-api-ticker @@ -42,6 +45,7 @@ namespace mbed { * // Toggle the blinking LED after 5 seconds * * #include "mbed.h" + * using namespace std::chrono; * * Ticker timer; * DigitalOut led1(LED1); @@ -54,26 +58,20 @@ namespace mbed { * } * * int main() { - * timer.attach(&attime, 5); + * timer.attach(&attime, 5us); * while(1) { * if(flip == 0) { * led1 = !led1; * } else { * led2 = !led2; * } - * ThisThread::sleep_for(200); + * ThisThread::sleep_for(200ms); * } * } * @endcode */ -class Ticker : public TimerEvent, private NonCopyable { - +class TickerBase : public TimerEvent, private NonCopyable { public: - Ticker(); - - // When low power ticker is in use, then do not disable deep sleep. - Ticker(const ticker_data_t *data); - /** Attach a function to be called by the Ticker, specifying the interval in seconds * * The method forwards its arguments to attach_us() rather than copying them which @@ -82,17 +80,34 @@ public: * possible given attach_us() expects an integer value for the callback interval. * @param func pointer to the function to be called * @param t the time between calls in seconds + * @deprecated Pass a chrono duration, not a float second count. For example use `10ms` rather than `0.01f`. */ #if defined(__ICCARM__) + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not a float second count. For example use `10ms` rather than `0.01f`.") MBED_FORCEINLINE template #else template MBED_FORCEINLINE + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not a float second count. For example use `10ms` rather than `0.01f`.") #endif void attach(F &&func, float t) { - attach_us(std::forward(func), t * 1000000.0f); + auto float_interval = std::chrono::duration(t); + attach(std::forward(func), std::chrono::duration_cast(float_interval)); } + /** Attach a function to be called by the Ticker, specifying the interval in microseconds + * + * @param func pointer to the function to be called + * @param t the time between calls in micro-seconds + * + * @note setting @a t to a value shorter than it takes to process the ticker callback + * causes the system to hang. Ticker callback is called constantly with no time + * for threads scheduling. + * @deprecated Pass a chrono duration, not an integer microsecond count. For example use `10ms` rather than `10000`. + * + */ + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer microsecond count. For example use `10ms` rather than `10000`.") + void attach_us(Callback func, us_timestamp_t t); /** Attach a function to be called by the Ticker, specifying the interval in microseconds * @@ -104,13 +119,7 @@ public: * for threads scheduling. * */ - void attach_us(Callback func, us_timestamp_t t); - - - virtual ~Ticker() - { - detach(); - } + void attach(Callback func, std::chrono::microseconds t); /** Detach the function */ @@ -118,16 +127,41 @@ public: #if !defined(DOXYGEN_ONLY) protected: - void setup(us_timestamp_t t); - virtual void handler(); + TickerBase(const ticker_data_t *data); + TickerBase(const ticker_data_t *data, bool lock_deepsleep); -protected: - us_timestamp_t _delay; /**< Time delay (in microseconds) for resetting the multishot callback. */ + ~TickerBase() + { + detach(); + } + + /** Attach a function to be called by the Ticker, specifying the absolute call time + * + * If used, handler must be overridden, as TickerBase::handler would attempt + * to reschedule. This is done by `TimeoutBase` (used by `Timeout` and `LowPowerTimeout`). + * + * @param func pointer to the function to be called + * @param abs_time the time for the call + * + * @note setting @a abs_time to a time in the past means the event will be scheduled immediately + * resulting in an instant call to the function. + */ + void attach_absolute(Callback func, TickerDataClock::time_point abs_time); + + void handler() override; + std::chrono::microseconds _delay; /**< Time delay (in microseconds) for resetting the multishot callback. */ Callback _function; /**< Callback. */ bool _lock_deepsleep; /**< Flag which indicates if deep sleep should be disabled. */ #endif +private: + void setup(std::chrono::microseconds t); + void setup_absolute(TickerDataClock::time_point t); }; +class Ticker : public TickerBase { +public: + Ticker(); +}; /** @}*/ } // namespace mbed diff --git a/drivers/TickerDataClock.h b/drivers/TickerDataClock.h new file mode 100644 index 0000000000..5c894442f4 --- /dev/null +++ b/drivers/TickerDataClock.h @@ -0,0 +1,170 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2019 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * 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_TICKERDATACLOCK_H +#define MBED_TICKERDATACLOCK_H + +#include +#include "hal/ticker_api.h" + +namespace mbed { +/** + * \defgroup drivers_TickerDataClock TickerDataClock class + * \ingroup drivers-public-api-ticker + * @{ + */ + +/** + * A partial implementation of a C++11 Clock representing a HAL ticker. + * + * This class allows us to create chrono time_points for objects like Timer, + * with the limitation that the tickers are not singletons. This means: + * + * * the now() function is not static - this will limit + * use with some algorithms, + * * there is no distinction between time_points for different + * tickers + * + * This "pseudo-Clock" approach has been endorsed by Howard Hinnant + * (designer of Chrono) here: + * https://stackoverflow.com/questions/56400313/why-does-the-c-standard-require-the-clocknow-function-to-be-static + * + * TickerDataClock::time_point values should only be used with mbed APIs specifically taking + * them, not passed to generic templated chrono algorithms, and it is up to the user to use them + * in conjunction with the correct TickerDataClock. + * + * operators for `->` and conversion to `ticker_data_t *` are provided allowing + * TickerDataClock to be easily substituted in place of a `ticker_data_t *`. + */ +class TickerDataClock { +public: + /** Construct a TickerDataClock referring to a ticker_data_t */ + constexpr TickerDataClock(const ticker_data_t *ticker) : _ticker(ticker) + { + } + + /* duration is C++11 standard microseconds, so representation will be 64-bit signed integer */ + using duration = std::chrono::microseconds; + using rep = duration::rep; + using period = duration::period; + using time_point = std::chrono::time_point; + static const bool is_steady = false; + + /** Initialize a ticker and set the event handler + * + * @param handler A handler to be set + */ + void set_handler(ticker_event_handler handler) + { + return ticker_set_handler(_ticker, handler); + } + + /** Remove an event from the queue + * + * @param obj The event object to be removed from the queue + */ + void remove_event(ticker_event_t *obj) + { + ticker_remove_event(_ticker, obj); + } + + /** Insert an event to the queue + * + * The event will be executed in timestamp - ticker_read_us() us. + * + * @note If an event is inserted with a timestamp less than the current + * timestamp then the event will be scheduled immediately resulting in + * an instant call to event handler. + * + * @param obj The event object to be inserted to the queue + * @param timestamp The event's timestamp + * @param id The event object + */ + void insert_event(ticker_event_t *obj, time_point timestamp, uint32_t id) + { + ticker_insert_event_us(_ticker, obj, timestamp.time_since_epoch().count(), id); + } + + /** Read the current (absolute) ticker's timestamp + * + * @warning Return an absolute timestamp counting from the initialization of the + * ticker. + * + * @return The current timestamp + */ + time_point now() const + { + return time_point(duration(ticker_read_us(_ticker))); + } + + /** Read the next event's timestamp + * + * @param timestamp The timestamp object. + * @return 1 if timestamp is pending event, 0 if there's no event pending + */ + int get_next_timestamp(time_point *timestamp) const + { + us_timestamp_t t; + int ret = ticker_get_next_timestamp_us(_ticker, &t); + *timestamp = time_point(duration(t)); + return ret; + } + + /** Suspend this ticker + * + * When suspended reads will always return the same time and no + * events will be dispatched. When suspended the common layer + * will only ever call the interface function clear_interrupt() + * and that is only if ticker_irq_handler is called. + * + */ + void suspend() + { + ticker_suspend(_ticker); + } + + /** Resume this ticker + * + * When resumed the ticker will ignore any time that has passed + * and continue counting up where it left off. + */ + void resume() + { + ticker_resume(_ticker); + } + + constexpr const ticker_data_t *operator->() const + { + return _ticker; + } + constexpr operator const ticker_data_t *() const + { + return _ticker; + } + +private: + const ticker_data_t *const _ticker; +}; + +inline TickerDataClock::time_point get_time_point(const ticker_event_t &event) +{ + return TickerDataClock::time_point{TickerDataClock::duration{event.timestamp}}; +} + +/** @}*/ + +} +#endif /* MBED_TICKERDATACLOCK_H */ diff --git a/drivers/Timeout.h b/drivers/Timeout.h index bd312a3ef7..52a46de21e 100644 --- a/drivers/Timeout.h +++ b/drivers/Timeout.h @@ -17,8 +17,8 @@ #ifndef MBED_TIMEOUT_H #define MBED_TIMEOUT_H +#include "drivers/HighResClock.h" #include "drivers/Ticker.h" -#include "platform/NonCopyable.h" namespace mbed { /** @@ -57,14 +57,72 @@ namespace mbed { * } * @endcode */ -class Timeout : public Ticker, private NonCopyable { +class TimeoutBase : public TickerBase { +public: + /** Return time remaining until callback + * + * @return time remaining until callback is due + * + * @note if the callback is overdue, or has already run, the returned value will be negative + */ + std::chrono::microseconds remaining_time() const + { + return scheduled_time() - _ticker_data.now(); + } #if !defined(DOXYGEN_ONLY) protected: - virtual void handler(); + using TickerBase::TickerBase; + ~TimeoutBase() = default; + + void handler() final; + + /** Return scheduled callback time + * + * @return scheduled callback time + * + * @note if the callback is overdue, or has already run, the returned value will be in the past + */ + TickerDataClock::time_point scheduled_time() const + { + return get_time_point(event); + } #endif }; +class Timeout : public TimeoutBase { +public: + Timeout(); + + /** Clock to use with attach_absolute, guaranteeing running only while attached or manually locked */ + using clock = HighResClock; + + /** Clock to use with attach_absolute, running always */ + using steady_clock = SteadyHighResClock; + + /** @copydoc TimeoutBase::scheduled_time() */ + HighResClock::time_point scheduled_time() const + { + /* Massage from virtual TickerDataClock::time_point used internally to true HighResClock::time_point */ + return HighResClock::time_point{TimeoutBase::scheduled_time().time_since_epoch()}; + } + + /** Attach a function to be called by the Timeout, specifying the absolute time + * + * @param func pointer to the function to be called + * @param abs_time the absolute time for the call, referenced to HighResClock + * + * @note setting @a abs_time to a time in the past means the event will be scheduled immediately + * resulting in an instant call to the function. + */ + template + void attach_absolute(F &&func, HighResClock::time_point abs_time) + { + /* Massage from true HighResClock::time_point to virtual TickerDataClock::time_point used internally */ + TimeoutBase::attach_absolute(std::forward(func), TickerDataClock::time_point{abs_time.time_since_epoch()}); + } +}; + /** @}*/ } // namespace mbed diff --git a/drivers/Timer.h b/drivers/Timer.h index 03474cc6f6..cb8768f0cf 100644 --- a/drivers/Timer.h +++ b/drivers/Timer.h @@ -18,7 +18,7 @@ #define MBED_TIMER_H #include "platform/platform.h" -#include "hal/ticker_api.h" +#include "drivers/TickerDataClock.h" #include "platform/NonCopyable.h" namespace mbed { @@ -51,13 +51,9 @@ namespace mbed { * } * @endcode */ -class Timer : private NonCopyable { +class TimerBase : private NonCopyable { public: - Timer(); - Timer(const ticker_data_t *data); - ~Timer(); - /** Start the timer */ void start(); @@ -76,40 +72,57 @@ public: * * @returns Time passed in seconds */ - float read(); + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Floating point operators should normally be avoided for code size. If really needed, you can use `duration{elapsed_time()}.count()`") + float read() const; /** Get the time passed in milliseconds * * @returns Time passed in milliseconds */ - int read_ms(); + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Use the Chrono-based elapsed_time method. If integer milliseconds are needed, you can use `duration_cast(elapsed_time()).count()`") + int read_ms() const; /** Get the time passed in microseconds * * @returns Time passed in microseconds */ - int read_us(); + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Use the Chrono-based elapsed_time method. If integer microseconds are needed, you can use `elapsed_time().count()`") + int read_us() const; /** An operator shorthand for read() */ - operator float(); + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Floating point operators should normally be avoided for code size. If really needed, you can use `duration{elapsed_time()}.count()`") + operator float() const; /** Get in a high resolution type the time passed in microseconds. * Returns a 64 bit integer. */ - us_timestamp_t read_high_resolution_us(); + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Use the Chrono-based elapsed_time method. If integer microseconds are needed, you can use `elapsed_time().count()`") + us_timestamp_t read_high_resolution_us() const; + + /** Get in a high resolution type the time passed in microseconds. + * Returns a 64 bit integer chrono duration. + */ + std::chrono::microseconds elapsed_time() const; #if !defined(DOXYGEN_ONLY) protected: - us_timestamp_t slicetime(); - int _running; // whether the timer is running - us_timestamp_t _start; // the start time of the latest slice - us_timestamp_t _time; // any accumulated time from previous slices - const ticker_data_t *_ticker_data; + TimerBase(const ticker_data_t *data); + TimerBase(const ticker_data_t *data, bool lock_deepsleep); + ~TimerBase(); + std::chrono::microseconds slicetime() const; + TickerDataClock::time_point _start; // the start time of the latest slice + std::chrono::microseconds _time; // any accumulated time from previous slices + TickerDataClock _ticker_data; bool _lock_deepsleep; // flag that indicates if deep sleep should be disabled + bool _running = false; // whether the timer is running }; #endif +class Timer : public TimerBase { +public: + Timer(); +}; /** @}*/ } // namespace mbed diff --git a/drivers/TimerEvent.h b/drivers/TimerEvent.h index ff00ef3d3f..13d9dc190b 100644 --- a/drivers/TimerEvent.h +++ b/drivers/TimerEvent.h @@ -18,9 +18,12 @@ #define MBED_TIMEREVENT_H #include "hal/ticker_api.h" +#include "platform/mbed_toolchain.h" #include "platform/NonCopyable.h" +#include "drivers/TickerDataClock.h" namespace mbed { + /** * \defgroup drivers_TimerEvent TimerEvent class * \ingroup drivers-public-api-ticker @@ -32,8 +35,8 @@ namespace mbed { * @note Synchronization level: Interrupt safe */ class TimerEvent : private NonCopyable { -public: - TimerEvent(); +#if !defined(DOXYGEN_ONLY) +protected: TimerEvent(const ticker_data_t *data); /** The handler registered with the underlying timer interrupt @@ -44,10 +47,8 @@ public: /** Destruction removes it... */ - virtual ~TimerEvent(); + ~TimerEvent(); -#if !defined(DOXYGEN_ONLY) -protected: // The handler called to service the timer event of the derived class virtual void handler() = 0; @@ -62,9 +63,38 @@ protected: * Ticker's present timestamp is used for reference. For timestamps * from the past the event is scheduled after ticker's overflow. * For reference @see convert_timestamp + * + * @deprecated use `insert(std::chrono::microseconds timestamp)` */ + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer microsecond count. For example use `5ms` rather than `5000`.") void insert(timestamp_t timestamp); + /** Set relative timestamp of the internal event. + * @param timestamp event's us timestamp + * + * @warning + * Do not insert more than one timestamp. + * The same @a event object is used for every @a insert/insert_absolute call. + * + * @warning + * Ticker's present timestamp is used for reference. For timestamps + * from the past the event is scheduled after ticker's overflow. + * For reference @see convert_timestamp + */ + void insert(std::chrono::microseconds timestamp); + + /** Set absolute timestamp of the internal event. + * @param timestamp event's us timestamp + * + * @warning + * Do not insert more than one timestamp. + * The same @a event object is used for every @a insert/insert_absolute call. + * + * @deprecated use `insert_absolute(TickerDataClock::time_point timestamp)` + */ + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono time_point, not an integer microsecond count. For example use `_ticker_data.now() + 5ms` rather than `ticker_read_us(_ticker_data) + 5000`.") + void insert_absolute(us_timestamp_t timestamp); + /** Set absolute timestamp of the internal event. * @param timestamp event's us timestamp * @@ -72,7 +102,7 @@ protected: * Do not insert more than one timestamp. * The same @a event object is used for every @a insert/insert_absolute call. */ - void insert_absolute(us_timestamp_t timestamp); + void insert_absolute(TickerDataClock::time_point timestamp); /** Remove timestamp. */ @@ -80,7 +110,7 @@ protected: ticker_event_t event; - const ticker_data_t *_ticker_data; + TickerDataClock _ticker_data; #endif }; diff --git a/drivers/source/Ticker.cpp b/drivers/source/Ticker.cpp index 47b4960295..68f128625d 100644 --- a/drivers/source/Ticker.cpp +++ b/drivers/source/Ticker.cpp @@ -16,25 +16,26 @@ */ #include "drivers/Ticker.h" -#include "drivers/TimerEvent.h" -#include "hal/ticker_api.h" -#include "platform/mbed_critical.h" +#include "hal/us_ticker_api.h" +#include "platform/CriticalSectionLock.h" #include "platform/mbed_power_mgmt.h" +using namespace std::chrono; + namespace mbed { -Ticker::Ticker() : TimerEvent(), _delay(0), _lock_deepsleep(true) +TickerBase::TickerBase(const ticker_data_t *data) : TickerBase(data, !data->interface->runs_in_deep_sleep) { } // When low power ticker is in use, then do not disable deep sleep. -Ticker::Ticker(const ticker_data_t *data) : TimerEvent(data), _delay(0), _lock_deepsleep(!data->interface->runs_in_deep_sleep) +TickerBase::TickerBase(const ticker_data_t *data, bool lock_deepsleep) : TimerEvent(data), _lock_deepsleep(lock_deepsleep) { } -void Ticker::detach() +void TickerBase::detach() { - core_util_critical_section_enter(); + CriticalSectionLock lock; remove(); // unlocked only if we were attached (we locked it) and this is not low power ticker if (_function && _lock_deepsleep) { @@ -42,36 +43,64 @@ void Ticker::detach() } _function = 0; - core_util_critical_section_exit(); } -void Ticker::setup(us_timestamp_t t) +void TickerBase::setup(microseconds t) { - core_util_critical_section_enter(); remove(); _delay = t; - insert_absolute(_delay + ticker_read_us(_ticker_data)); - core_util_critical_section_exit(); + insert_absolute(_ticker_data.now() + t); } -void Ticker::handler() +void TickerBase::setup_absolute(TickerDataClock::time_point t) { - insert_absolute(event.timestamp + _delay); + remove(); + insert_absolute(t); +} + +void TickerBase::handler() +{ + insert_absolute(get_time_point(event) + _delay); if (_function) { _function(); } } -void Ticker::attach_us(Callback func, us_timestamp_t t) +void TickerBase::attach(Callback func, microseconds t) { - core_util_critical_section_enter(); + CriticalSectionLock lock; // lock only for the initial callback setup and this is not low power ticker if (!_function && _lock_deepsleep) { sleep_manager_lock_deep_sleep(); } _function = func; setup(t); - core_util_critical_section_exit(); +} + +void TickerBase::attach_us(Callback func, us_timestamp_t t) +{ + CriticalSectionLock lock; + // lock only for the initial callback setup and this is not low power ticker + if (!_function && _lock_deepsleep) { + sleep_manager_lock_deep_sleep(); + } + _function = func; + setup(microseconds{t}); +} + +void TickerBase::attach_absolute(Callback func, TickerDataClock::time_point abs_time) +{ + CriticalSectionLock lock; + // lock only for the initial callback setup and this is not low power ticker + if (!_function && _lock_deepsleep) { + sleep_manager_lock_deep_sleep(); + } + _function = func; + setup_absolute(abs_time); +} + +Ticker::Ticker() : TickerBase(get_us_ticker_data(), true) +{ } } // namespace mbed diff --git a/drivers/source/Timeout.cpp b/drivers/source/Timeout.cpp index 42141c4c48..d890e0ef67 100644 --- a/drivers/source/Timeout.cpp +++ b/drivers/source/Timeout.cpp @@ -15,14 +15,44 @@ * limitations under the License. */ #include "drivers/Timeout.h" +#include "drivers/LowPowerTimeout.h" +#include "drivers/HighResClock.h" +#include "drivers/LowPowerClock.h" +#include "drivers/RealTimeClock.h" + +using namespace std::chrono; namespace mbed { -void Timeout::handler() +void TimeoutBase::handler() { - Callback local = _function; - detach(); - local.call(); + if (_function) { + if (_lock_deepsleep) { + sleep_manager_unlock_deep_sleep(); + } + _function(); + _function = nullptr; + } } +Timeout::Timeout() : TimeoutBase(get_us_ticker_data(), true) +{ +} + +/* A few miscellaneous out-of-line static members from various related classes, + * just to save them getting needing their own cpp file for one line. + * (In C++17 could avoid the need for this by making the members inline). + */ +const bool HighResClock::is_steady; +const bool SteadyHighResClock::is_steady; +const bool LowPowerClock::is_steady; +mstd::once_flag SteadyHighResClock::init; +const bool RealTimeClock::is_steady; + +#if DEVICE_LPTICKER +LowPowerTimeout::LowPowerTimeout() : TimeoutBase(get_lp_ticker_data(), true) +{ +} +#endif + } // namespace mbed diff --git a/drivers/source/Timer.cpp b/drivers/source/Timer.cpp index d4f2276a41..233731ca48 100644 --- a/drivers/source/Timer.cpp +++ b/drivers/source/Timer.cpp @@ -17,105 +17,108 @@ #include "drivers/Timer.h" #include "hal/ticker_api.h" #include "hal/us_ticker_api.h" +#include "platform/CriticalSectionLock.h" #include "platform/mbed_critical.h" #include "platform/mbed_power_mgmt.h" +using std::milli; +using std::micro; +using namespace std::chrono; + namespace mbed { -Timer::Timer() : _running(), _start(), _time(), _ticker_data(get_us_ticker_data()), _lock_deepsleep(true) +TimerBase::TimerBase(const ticker_data_t *data) : TimerBase(data, !data->interface->runs_in_deep_sleep) +{ +} + +TimerBase::TimerBase(const ticker_data_t *data, bool lock_deepsleep) : _ticker_data(data), _lock_deepsleep(lock_deepsleep) { reset(); } -Timer::Timer(const ticker_data_t *data) : _running(), _start(), _time(), _ticker_data(data), - _lock_deepsleep(!data->interface->runs_in_deep_sleep) +TimerBase::~TimerBase() { - reset(); -} - -Timer::~Timer() -{ - core_util_critical_section_enter(); if (_running) { if (_lock_deepsleep) { sleep_manager_unlock_deep_sleep(); } } - _running = 0; - core_util_critical_section_exit(); } -void Timer::start() +void TimerBase::start() { - core_util_critical_section_enter(); + CriticalSectionLock lock; if (!_running) { if (_lock_deepsleep) { sleep_manager_lock_deep_sleep(); } - _start = ticker_read_us(_ticker_data); - _running = 1; + _start = _ticker_data.now(); + _running = true; } - core_util_critical_section_exit(); } -void Timer::stop() +void TimerBase::stop() { - core_util_critical_section_enter(); + CriticalSectionLock lock; _time += slicetime(); if (_running) { if (_lock_deepsleep) { sleep_manager_unlock_deep_sleep(); } } - _running = 0; - core_util_critical_section_exit(); + _running = false; } -int Timer::read_us() +int TimerBase::read_us() const { - return read_high_resolution_us(); + return duration(elapsed_time()).count(); } -float Timer::read() +float TimerBase::read() const { - return (float)read_high_resolution_us() / 1000000.0f; + return duration(elapsed_time()).count(); } -int Timer::read_ms() +int TimerBase::read_ms() const { - return read_high_resolution_us() / 1000; + return duration_cast>(elapsed_time()).count(); } -us_timestamp_t Timer::read_high_resolution_us() +us_timestamp_t TimerBase::read_high_resolution_us() const { - core_util_critical_section_enter(); - us_timestamp_t time = _time + slicetime(); - core_util_critical_section_exit(); - return time; + return duration_cast>(elapsed_time()).count(); } -us_timestamp_t Timer::slicetime() +microseconds TimerBase::elapsed_time() const { - us_timestamp_t ret = 0; - core_util_critical_section_enter(); + CriticalSectionLock lock; + return _time + slicetime(); +} + +microseconds TimerBase::slicetime() const +{ + CriticalSectionLock lock; + microseconds ret; if (_running) { - ret = ticker_read_us(_ticker_data) - _start; + ret = _ticker_data.now() - _start; } - core_util_critical_section_exit(); return ret; } -void Timer::reset() +void TimerBase::reset() { - core_util_critical_section_enter(); - _start = ticker_read_us(_ticker_data); - _time = 0; - core_util_critical_section_exit(); + CriticalSectionLock lock; + _start = _ticker_data.now(); + _time = 0s; } -Timer::operator float() +TimerBase::operator float() const +{ + return duration(elapsed_time()).count(); +} + +Timer::Timer() : TimerBase(get_us_ticker_data(), true) { - return read(); } } // namespace mbed diff --git a/drivers/source/TimerEvent.cpp b/drivers/source/TimerEvent.cpp index be3037e62f..7861ee06f0 100644 --- a/drivers/source/TimerEvent.cpp +++ b/drivers/source/TimerEvent.cpp @@ -19,21 +19,18 @@ #include #include "hal/us_ticker_api.h" -namespace mbed { +using namespace std::chrono; -TimerEvent::TimerEvent() : event(), _ticker_data(get_us_ticker_data()) -{ - ticker_set_handler(_ticker_data, (&TimerEvent::irq)); -} +namespace mbed { TimerEvent::TimerEvent(const ticker_data_t *data) : event(), _ticker_data(data) { - ticker_set_handler(_ticker_data, (&TimerEvent::irq)); + _ticker_data.set_handler(irq); } void TimerEvent::irq(uint32_t id) { - TimerEvent *timer_event = (TimerEvent *)id; + TimerEvent *timer_event = reinterpret_cast(id); timer_event->handler(); } @@ -48,14 +45,24 @@ void TimerEvent::insert(timestamp_t timestamp) ticker_insert_event(_ticker_data, &event, timestamp, (uint32_t)this); } +void TimerEvent::insert(microseconds timestamp) +{ + insert_absolute(_ticker_data.now() + timestamp); +} + void TimerEvent::insert_absolute(us_timestamp_t timestamp) { ticker_insert_event_us(_ticker_data, &event, timestamp, (uint32_t)this); } +void TimerEvent::insert_absolute(TickerDataClock::time_point timestamp) +{ + _ticker_data.insert_event(&event, timestamp, (uint32_t)this); +} + void TimerEvent::remove() { - ticker_remove_event(_ticker_data, &event); + _ticker_data.remove_event(&event); } } // namespace mbed diff --git a/hal/mbed_ticker_api.c b/hal/mbed_ticker_api.c index 55aeb7685c..5f363651bb 100644 --- a/hal/mbed_ticker_api.c +++ b/hal/mbed_ticker_api.c @@ -465,6 +465,21 @@ int ticker_get_next_timestamp(const ticker_data_t *const data, timestamp_t *time return ret; } +int ticker_get_next_timestamp_us(const ticker_data_t *const data, us_timestamp_t *timestamp) +{ + int ret = 0; + + /* if head is NULL, there are no pending events */ + core_util_critical_section_enter(); + if (data->queue->head != NULL) { + *timestamp = data->queue->head->timestamp; + ret = 1; + } + core_util_critical_section_exit(); + + return ret; +} + void ticker_suspend(const ticker_data_t *const ticker) { core_util_critical_section_enter(); diff --git a/hal/ticker_api.h b/hal/ticker_api.h index 9d3d7ddc91..28017feea1 100644 --- a/hal/ticker_api.h +++ b/hal/ticker_api.h @@ -187,6 +187,14 @@ us_timestamp_t ticker_read_us(const ticker_data_t *const ticker); */ int ticker_get_next_timestamp(const ticker_data_t *const ticker, timestamp_t *timestamp); +/** Read the next event's timestamp + * + * @param ticker The ticker object. + * @param timestamp The timestamp object. + * @return 1 if timestamp is pending event, 0 if there's no event pending + */ +int ticker_get_next_timestamp_us(const ticker_data_t *const ticker, us_timestamp_t *timestamp); + /** Suspend this ticker * * When suspended reads will always return the same time and no