A C++ toolbox repo until the pair uTL/dTL arives
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

334 lines
12 KiB

  1. /*!
  2. * \file utils/timer_delay.h
  3. * \brief
  4. * A CRTP timer delay utility
  5. *
  6. * \copyright Copyright (C) 2021 Christos Choutouridis <christos@choutouridis.net>
  7. *
  8. * <dl class=\"section copyright\"><dt>License</dt><dd>
  9. * The MIT License (MIT)
  10. *
  11. * Permission is hereby granted, free of charge, to any person obtaining a copy
  12. * of this software and associated documentation files (the "Software"), to deal
  13. * in the Software without restriction, including without limitation the rights
  14. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  15. * copies of the Software, and to permit persons to whom the Software is
  16. * furnished to do so, subject to the following conditions:
  17. *
  18. * The above copyright notice and this permission notice shall be included in all
  19. * copies or substantial portions of the Software.
  20. *
  21. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  22. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  23. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  24. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  25. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  26. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  27. * SOFTWARE.
  28. * </dd></dl>
  29. */
  30. #ifndef TBX_UTILS_TIMER_DELAY_H_
  31. #define TBX_UTILS_TIMER_DELAY_H_
  32. #include <core/core.h>
  33. #include <core/crtp.h>
  34. #include <type_traits>
  35. namespace tbx {
  36. /*!
  37. * \class timer_delay
  38. * \brief
  39. * A CRTP hw timer based, delay implementation.
  40. *
  41. * CRTP requirements:
  42. * - int set_frequency_impl (size_t freq, Counter_t ticks)
  43. * Initialize and start the hw timer with tick frequency \c freq and reload value \c ticks
  44. * - volatile Counter_t* get_value_ptr_impl (Counter_t discard)
  45. * Return a pointer to hw timer counter value register. The \c discard argument should discarded.
  46. *
  47. * \tparam Impl_t The derived class
  48. * \tparam Counter_t The hw timer's type
  49. */
  50. template <typename Impl_t, typename Counter_t>
  51. class timer_delay {
  52. _CRTP_IMPL(Impl_t);
  53. using value_t = volatile Counter_t;
  54. using marker_t = std::make_signed_t<std::remove_cv_t<Counter_t>>;
  55. //! \name CRTP requirements
  56. //! @{
  57. private:
  58. int set_frequency (size_t freq, Counter_t ticks) {
  59. return impl().set_frequency_impl(freq, ticks);
  60. }
  61. volatile Counter_t* get_value_ptr (Counter_t discard = Counter_t{}) {
  62. return impl().get_value_ptr_impl (discard);
  63. }
  64. //! @}
  65. //! \name Object lifetime
  66. //! @{
  67. protected:
  68. //! \brief
  69. //! Create and initialize
  70. //! \param freq The required hw timer's frequency
  71. //! \param ticks The required timer's reload value
  72. timer_delay (size_t freq, Counter_t ticks) {
  73. init (freq, ticks);
  74. }
  75. timer_delay() noexcept = default; //!< Default object is valid, but non-usable
  76. timer_delay(const timer_delay&) = delete; //!< No copies
  77. timer_delay& operator=(const timer_delay&) = delete; //!< No copies
  78. //! \note
  79. //! We are not initializing the timer via default ctor, in order to be able to
  80. //! declare a timer_delay object globally and initialize it after the call to main().
  81. //! @}
  82. private:
  83. //! Period to frequency compile time tool
  84. constexpr Counter_t period2freq (double period) noexcept {
  85. return (Counter_t)(1 / period);
  86. }
  87. /*!
  88. * \brief Return the systems best approximation for ticks per msec
  89. * \return The calculated value or zero if no calculation can apply
  90. */
  91. Counter_t ticks_per_msec () {
  92. Counter_t tck = (Counter_t)(frequency / period2freq(0.001));
  93. return (tck <= 1) ? 1 : tck;
  94. }
  95. /*!
  96. * \brief Return the systems best approximation for ticks per usec
  97. * \return The calculated value or zero if no calculation can apply
  98. */
  99. Counter_t ticks_per_usec () {
  100. Counter_t tck = (Counter_t)(frequency / period2freq(0.000001));
  101. return (tck <= 1) ? 1 : tck;
  102. }
  103. /*!
  104. * \brief Return the systems best approximation for ticks per usec
  105. * \return The calculated value or zero if no calculation can apply
  106. */
  107. Counter_t ticks_per_100nsec () {
  108. Counter_t tck = (Counter_t)(frequency / period2freq(0.0000001));
  109. return (tck <= 1) ? 1 : tck;
  110. }
  111. public:
  112. /*!
  113. * \brief
  114. * Initializes both object members and hw timer.
  115. *
  116. * \param freq The required hw timer's frequency
  117. * \param ticks The required timer's reload value
  118. * \return
  119. */
  120. bool init (size_t freq, Counter_t ticks) {
  121. if (set_frequency(freq, ticks))
  122. return false;
  123. volatile Counter_t* v = get_value_ptr();
  124. value = (v != nullptr) ? v : value;
  125. frequency = freq;
  126. max_ticks = ticks;
  127. tp1ms = ticks_per_msec();
  128. tp1us = ticks_per_usec();
  129. tp100ns= ticks_per_100nsec();
  130. return true;
  131. }
  132. /*!
  133. * \brief
  134. * A code based delay implementation, using hw timer for timing.
  135. * This is NOT accurate but it ensures that the time passed is always
  136. * more than the requested value.
  137. * The delay values are multiplications of 1 msec.
  138. * \param msec Time in msec for delay
  139. */
  140. void delay_ms (int msec) {
  141. marker_t m, m2, m1 = (marker_t)*value;
  142. msec *= tp1ms;
  143. // Eat the time difference from msec value.
  144. do {
  145. m2 = (marker_t)(*value);
  146. m = m2 - m1;
  147. msec -= (m>=0) ? m : max_ticks + m;
  148. m1 = m2;
  149. } while (msec>0);
  150. }
  151. /*!
  152. * \brief
  153. * A code based delay implementation, using hw timer for timing.
  154. * This is NOT accurate but it ensures that the time passed is always
  155. * more than the requested value.
  156. * The delay values are multiplications of 1 usec.
  157. * \param usec Time in usec for delay
  158. */
  159. void delay_us (int usec) {
  160. marker_t m, m2, m1 = (marker_t)*value;
  161. usec *= tp1us;
  162. if ((marker_t)(*value) - m1 > usec) // Very small delays may return here.
  163. return;
  164. // Eat the time difference from usec value.
  165. do {
  166. m2 = (marker_t)(*value);
  167. m = m2 - m1;
  168. usec -= (m>=0) ? m : max_ticks + m;
  169. m1 = m2;
  170. } while (usec>0);
  171. }
  172. /*!
  173. * \brief
  174. * A code based delay implementation, using hw timer for timing.
  175. * This is NOT accurate but it ensures that the time passed is always
  176. * more than the requested value.
  177. * The delay values are multiplications of 100 nsec.
  178. * \param _100nsec Time in 100nsec for delay
  179. */
  180. void delay_100ns (int _100nsec) {
  181. marker_t m, m2, m1 = (marker_t)*value;
  182. _100nsec *= tp100ns;
  183. if ((marker_t)(*value) - m1 > _100nsec) // Very small delays may return here.
  184. return;
  185. // Eat the time difference from _100nsec value.
  186. do {
  187. m2 = (marker_t)(*value);
  188. m = m2 - m1;
  189. _100nsec -= (m>=0) ? m : max_ticks + m;
  190. m1 = m2;
  191. } while (_100nsec>0);
  192. }
  193. /*!
  194. * \brief
  195. * A code based polling version delay implementation, using hw timer for timing.
  196. * This is NOT accurate but it ensures that the time passed is always
  197. * more than the requested value.
  198. * The delay values are multiplications of 1 msec.
  199. * \param msec Time in msec for delay
  200. * \return The status of ongoing delay
  201. * \arg false: Delay time has passed
  202. * \arg true: Delay is ongoing, keep calling
  203. */
  204. bool check_msec (int msec) {
  205. static marker_t m1=-1, cnt;
  206. marker_t m, m2;
  207. if (m1 == -1) {
  208. m1 = *value;
  209. cnt = tp1ms * msec;
  210. }
  211. // Eat the time difference from msec value.
  212. if (cnt>0) {
  213. m2 = (marker_t)(*value);
  214. m = m2-m1;
  215. cnt -= (m>=0) ? m : max_ticks + m;
  216. m1 = m2;
  217. return 1; // wait
  218. } else {
  219. m1 = -1;
  220. return 0; // do not wait any more
  221. }
  222. }
  223. /*!
  224. * \brief
  225. * A code based polling version delay implementation, using hw timer for timing.
  226. * This is NOT accurate but it ensures that the time passed is always
  227. * more than the requested value.
  228. * The delay values are multiplications of 1 usec.
  229. * \param usec Time in usec for delay
  230. * \return The status of ongoing delay
  231. * \arg false: Delay time has passed
  232. * \arg true: Delay is ongoing, keep calling
  233. */
  234. bool check_usec (int usec) {
  235. static marker_t m1=-1, cnt;
  236. marker_t m, m2;
  237. if (m1 == -1) {
  238. m1 = *value;
  239. cnt = tp1us * usec;
  240. }
  241. // Eat the time difference from usec value.
  242. if (cnt>0) {
  243. m2 = (marker_t)(*value);
  244. m = m2-m1;
  245. cnt -= (m>=0) ? m : max_ticks + m;
  246. m1 = m2;
  247. return 1; // wait
  248. } else {
  249. m1 = -1;
  250. return 0; // do not wait any more
  251. }
  252. }
  253. /*!
  254. * \brief
  255. * A code based polling version delay implementation, using hw timer for timing.
  256. * This is NOT accurate but it ensures that the time passed is always
  257. * more than the requested value.
  258. * The delay values are multiplications of 100 nsec.
  259. * \param
  260. * _100nsec Time in 100nsec for delay
  261. * \return The status of ongoing delay
  262. * \arg false: Delay time has passed
  263. * \arg true: Delay is ongoing, keep calling
  264. */
  265. bool check_100nsec (int _100nsec) {
  266. static marker_t m1=-1, cnt;
  267. marker_t m, m2;
  268. if (m1 == -1) {
  269. m1 = *value;
  270. cnt = tp100ns * _100nsec;
  271. }
  272. // Eat the time difference from _100nsec value.
  273. if (cnt>0) {
  274. m2 = (marker_t)(*value);
  275. m = m2-m1;
  276. cnt -= (m>=0) ? m : max_ticks + m;
  277. m1 = m2;
  278. return 1; // wait
  279. }
  280. else {
  281. m1 = -1;
  282. return 0; // do not wait any more
  283. }
  284. }
  285. private:
  286. static constexpr Counter_t zero = 0; //!< A always zero place
  287. value_t* value {(value_t*)&zero}; //!< Pointer to hw timer's counter register
  288. //!< We initialize it to &zero to avoid nullptr dereference
  289. size_t frequency{}; //!< The frequency of the timer
  290. Counter_t max_ticks{}; //!< The reload value of the timer
  291. Counter_t tp1ms{}; //!< ticks per ms temporary variable
  292. Counter_t tp1us{}; //!< ticks per us temporary variable
  293. Counter_t tp100ns{}; //!< ticks per 100ns temporary variable
  294. };
  295. } // namespace tbx
  296. #endif /* TBX_UTILS_TIMER_DELAY_H_ */