|
- /*!
- * \file utils/timer_delay.h
- * \brief
- * A CRTP timer delay utility
- *
- * \copyright Copyright (C) 2021 Christos Choutouridis <christos@choutouridis.net>
- *
- * <dl class=\"section copyright\"><dt>License</dt><dd>
- * The MIT License (MIT)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- * </dd></dl>
- */
-
- #ifndef TBX_UTILS_TIMER_DELAY_H_
- #define TBX_UTILS_TIMER_DELAY_H_
-
- #include <core/core.h>
- #include <core/crtp.h>
-
- #include <type_traits>
-
- namespace tbx {
-
- /*!
- * \class timer_delay
- * \brief
- * A CRTP hw timer based, delay implementation.
- *
- * CRTP requirements:
- * - int set_frequency_impl (size_t freq, Counter_t ticks)
- * Initialize and start the hw timer with tick frequency \c freq and reload value \c ticks
- * - volatile Counter_t* get_value_ptr_impl (Counter_t discard)
- * Return a pointer to hw timer counter value register. The \c discard argument should discarded.
- *
- * \tparam Impl_t The derived class
- * \tparam Counter_t The hw timer's type
- */
- template <typename Impl_t, typename Counter_t>
- class timer_delay {
- _CRTP_IMPL(Impl_t);
- using value_t = volatile Counter_t;
- using marker_t = std::make_signed_t<std::remove_cv_t<Counter_t>>;
-
- //! \name CRTP requirements
- //! @{
- private:
- int set_frequency (size_t freq, Counter_t ticks) {
- return impl().set_frequency_impl(freq, ticks);
- }
- volatile Counter_t* get_value_ptr (Counter_t discard = Counter_t{}) {
- return impl().get_value_ptr_impl (discard);
- }
- //! @}
-
- //! \name Object lifetime
- //! @{
- protected:
- //! \brief
- //! Create and initialize
- //! \param freq The required hw timer's frequency
- //! \param ticks The required timer's reload value
- timer_delay (size_t freq, Counter_t ticks) {
- init (freq, ticks);
- }
- timer_delay() noexcept = default; //!< Default object is valid, but non-usable
- timer_delay(const timer_delay&) = delete; //!< No copies
- timer_delay& operator=(const timer_delay&) = delete; //!< No copies
- //! \note
- //! We are not initializing the timer via default ctor, in order to be able to
- //! declare a timer_delay object globally and initialize it after the call to main().
- //! @}
-
- private:
- //! Period to frequency compile time tool
- constexpr Counter_t period2freq (double period) noexcept {
- return (Counter_t)(1 / period);
- }
-
- /*!
- * \brief Return the systems best approximation for ticks per msec
- * \return The calculated value or zero if no calculation can apply
- */
- Counter_t ticks_per_msec () {
- Counter_t tck = (Counter_t)(frequency / period2freq(0.001));
- return (tck <= 1) ? 1 : tck;
- }
-
- /*!
- * \brief Return the systems best approximation for ticks per usec
- * \return The calculated value or zero if no calculation can apply
- */
- Counter_t ticks_per_usec () {
- Counter_t tck = (Counter_t)(frequency / period2freq(0.000001));
- return (tck <= 1) ? 1 : tck;
- }
-
- /*!
- * \brief Return the systems best approximation for ticks per usec
- * \return The calculated value or zero if no calculation can apply
- */
- Counter_t ticks_per_100nsec () {
- Counter_t tck = (Counter_t)(frequency / period2freq(0.0000001));
- return (tck <= 1) ? 1 : tck;
- }
-
- public:
- /*!
- * \brief
- * Initializes both object members and hw timer.
- *
- * \param freq The required hw timer's frequency
- * \param ticks The required timer's reload value
- * \return
- */
- bool init (size_t freq, Counter_t ticks) {
- if (set_frequency(freq, ticks))
- return false;
- volatile Counter_t* v = get_value_ptr();
-
- value = (v != nullptr) ? v : value;
- frequency = freq;
- max_ticks = ticks;
- tp1ms = ticks_per_msec();
- tp1us = ticks_per_usec();
- tp100ns= ticks_per_100nsec();
- return true;
- }
-
- /*!
- * \brief
- * A code based delay implementation, using hw timer for timing.
- * This is NOT accurate but it ensures that the time passed is always
- * more than the requested value.
- * The delay values are multiplications of 1 msec.
- * \param msec Time in msec for delay
- */
- void delay_ms (int msec) {
- marker_t m, m2, m1 = (marker_t)*value;
-
- msec *= tp1ms;
-
- // Eat the time difference from msec value.
- do {
- m2 = (marker_t)(*value);
- m = m2 - m1;
- msec -= (m>=0) ? m : max_ticks + m;
- m1 = m2;
- } while (msec>0);
- }
-
- /*!
- * \brief
- * A code based delay implementation, using hw timer for timing.
- * This is NOT accurate but it ensures that the time passed is always
- * more than the requested value.
- * The delay values are multiplications of 1 usec.
- * \param usec Time in usec for delay
- */
- void delay_us (int usec) {
- marker_t m, m2, m1 = (marker_t)*value;
-
- usec *= tp1us;
- if ((marker_t)(*value) - m1 > usec) // Very small delays may return here.
- return;
-
- // Eat the time difference from usec value.
- do {
- m2 = (marker_t)(*value);
- m = m2 - m1;
- usec -= (m>=0) ? m : max_ticks + m;
- m1 = m2;
- } while (usec>0);
- }
-
- /*!
- * \brief
- * A code based delay implementation, using hw timer for timing.
- * This is NOT accurate but it ensures that the time passed is always
- * more than the requested value.
- * The delay values are multiplications of 100 nsec.
- * \param _100nsec Time in 100nsec for delay
- */
- void delay_100ns (int _100nsec) {
- marker_t m, m2, m1 = (marker_t)*value;
-
- _100nsec *= tp100ns;
- if ((marker_t)(*value) - m1 > _100nsec) // Very small delays may return here.
- return;
-
- // Eat the time difference from _100nsec value.
- do {
- m2 = (marker_t)(*value);
- m = m2 - m1;
- _100nsec -= (m>=0) ? m : max_ticks + m;
- m1 = m2;
- } while (_100nsec>0);
- }
-
- /*!
- * \brief
- * A code based polling version delay implementation, using hw timer for timing.
- * This is NOT accurate but it ensures that the time passed is always
- * more than the requested value.
- * The delay values are multiplications of 1 msec.
- * \param msec Time in msec for delay
- * \return The status of ongoing delay
- * \arg false: Delay time has passed
- * \arg true: Delay is ongoing, keep calling
- */
- bool check_msec (int msec) {
- static marker_t m1=-1, cnt;
- marker_t m, m2;
-
- if (m1 == -1) {
- m1 = *value;
- cnt = tp1ms * msec;
- }
-
- // Eat the time difference from msec value.
- if (cnt>0) {
- m2 = (marker_t)(*value);
- m = m2-m1;
- cnt -= (m>=0) ? m : max_ticks + m;
- m1 = m2;
- return 1; // wait
- } else {
- m1 = -1;
- return 0; // do not wait any more
- }
- }
-
- /*!
- * \brief
- * A code based polling version delay implementation, using hw timer for timing.
- * This is NOT accurate but it ensures that the time passed is always
- * more than the requested value.
- * The delay values are multiplications of 1 usec.
- * \param usec Time in usec for delay
- * \return The status of ongoing delay
- * \arg false: Delay time has passed
- * \arg true: Delay is ongoing, keep calling
- */
- bool check_usec (int usec) {
- static marker_t m1=-1, cnt;
- marker_t m, m2;
-
- if (m1 == -1) {
- m1 = *value;
- cnt = tp1us * usec;
- }
-
- // Eat the time difference from usec value.
- if (cnt>0) {
- m2 = (marker_t)(*value);
- m = m2-m1;
- cnt -= (m>=0) ? m : max_ticks + m;
- m1 = m2;
- return 1; // wait
- } else {
- m1 = -1;
- return 0; // do not wait any more
- }
- }
-
-
- /*!
- * \brief
- * A code based polling version delay implementation, using hw timer for timing.
- * This is NOT accurate but it ensures that the time passed is always
- * more than the requested value.
- * The delay values are multiplications of 100 nsec.
- * \param
- * _100nsec Time in 100nsec for delay
- * \return The status of ongoing delay
- * \arg false: Delay time has passed
- * \arg true: Delay is ongoing, keep calling
- */
- bool check_100nsec (int _100nsec) {
- static marker_t m1=-1, cnt;
- marker_t m, m2;
-
- if (m1 == -1) {
- m1 = *value;
- cnt = tp100ns * _100nsec;
- }
-
- // Eat the time difference from _100nsec value.
- if (cnt>0) {
- m2 = (marker_t)(*value);
- m = m2-m1;
- cnt -= (m>=0) ? m : max_ticks + m;
- m1 = m2;
- return 1; // wait
- }
- else {
- m1 = -1;
- return 0; // do not wait any more
- }
- }
-
- private:
- static constexpr Counter_t zero = 0; //!< A always zero place
-
- value_t* value {(value_t*)&zero}; //!< Pointer to hw timer's counter register
- //!< We initialize it to &zero to avoid nullptr dereference
- size_t frequency{}; //!< The frequency of the timer
- Counter_t max_ticks{}; //!< The reload value of the timer
- Counter_t tp1ms{}; //!< ticks per ms temporary variable
- Counter_t tp1us{}; //!< ticks per us temporary variable
- Counter_t tp100ns{}; //!< ticks per 100ns temporary variable
- };
-
-
- } // namespace tbx
-
- #endif /* TBX_UTILS_TIMER_DELAY_H_ */
|