/*! * \file utils/timer_delay.h * \brief * A CRTP timer delay utility * * \copyright Copyright (C) 2021 Christos Choutouridis * *
License
* 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. *
*/ #ifndef TBX_UTILS_TIMER_DELAY_H_ #define TBX_UTILS_TIMER_DELAY_H_ #include #include #include 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 class timer_delay { _CRTP_IMPL(Impl_t); using value_t = volatile Counter_t; using marker_t = std::make_signed_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_ */