@@ -38,8 +38,8 @@ | |||||
#include <cstring> | #include <cstring> | ||||
#include <cstdlib> | #include <cstdlib> | ||||
#include <algorithm> | |||||
#include <algorithm> | |||||
#include <utility> | #include <utility> | ||||
#include <atomic> | #include <atomic> | ||||
@@ -38,6 +38,8 @@ | |||||
#include <cont/equeue.h> | #include <cont/equeue.h> | ||||
#include <utils/shared.h> | #include <utils/shared.h> | ||||
#include <utils/print.h> | |||||
#include <utils/timer_delay.h> | |||||
#include <drv/cli_device.h> | #include <drv/cli_device.h> | ||||
#include <drv/gpio.h> | #include <drv/gpio.h> | ||||
@@ -0,0 +1,314 @@ | |||||
/*! | |||||
* \file utils/print.h | |||||
* \brief | |||||
* A CRTP base class to provide print interface | |||||
* | |||||
* \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_PRINT_H_ | |||||
#define TBX_UTILS_PRINT_H_ | |||||
#include <core/core.h> | |||||
#include <core/crtp.h> | |||||
#include <cstring> | |||||
#include <math.h> | |||||
#include <string_view> | |||||
#include <type_traits> | |||||
#include <utility> | |||||
#include <limits> | |||||
namespace tbx { | |||||
/*! | |||||
* \class Print | |||||
* \brief | |||||
* A CRTP print interface | |||||
* | |||||
* Requirements: | |||||
* - size_t write_impl(const Char_t* buffer, size_t size) : Return the number of \c Char_t written | |||||
* - size_t write_(const Char_t ch) : Return the number of \c Char_t written (normally one). | |||||
* | |||||
* \tparam Impl_t The derived type | |||||
* \tparam Char_t The char type to use | |||||
*/ | |||||
template <typename Impl_t, typename Char_t> | |||||
class Print { | |||||
_CRTP_IMPL(Impl_t); | |||||
public: | |||||
using value_type = Char_t; | |||||
using pointer_type = Char_t*; | |||||
using iterator_type = Char_t*; | |||||
using const_iterator_type = const Char_t*; | |||||
using difference_type = std::ptrdiff_t; | |||||
using size_type = size_t; | |||||
using str_view_t = std::basic_string_view<Char_t>; | |||||
//! Enumerator for number base formating | |||||
enum class Base { | |||||
BIN =2, OCT =8, DEC =10, HEX =16 | |||||
}; | |||||
private: | |||||
//! \name CRTP requirements | |||||
//! @{ | |||||
size_t write_(const Char_t* buffer, size_t size) { | |||||
return impl().write_impl(buffer, size); | |||||
} | |||||
size_t write_(const Char_t ch) { | |||||
return impl().write_impl(ch); | |||||
} | |||||
//! @} | |||||
protected: | |||||
Print() noexcept = default; //!< Construct from derived only | |||||
private: | |||||
//! Helper tool to convert strong enums to their underlying type | |||||
template <typename E> | |||||
constexpr typename std::underlying_type_t<E> value(E e) noexcept { | |||||
return static_cast<typename std::underlying_type_t<E>>(e); | |||||
} | |||||
size_t print_unsigned(unsigned long n, Base base); // integer conversion base tool | |||||
size_t print_double(double number, uint8_t digits); // double conversion base tool | |||||
public: | |||||
/*! | |||||
* \brief | |||||
* Prints a string view | |||||
* \param str The string view to print | |||||
* \return The number of printed \c Char_t | |||||
*/ | |||||
size_t print(const str_view_t str) { | |||||
return write_(str.data(), str.size()); | |||||
} | |||||
/*! | |||||
* \brief | |||||
* Prints a string | |||||
* \param str Pointer to string to print | |||||
* \return The number of printed \c Char_t | |||||
*/ | |||||
size_t print(const Char_t* str) { | |||||
if (str == nullptr) | |||||
return 0; | |||||
return write_(str, std::strlen(str)); | |||||
} | |||||
/*! | |||||
* \brief | |||||
* Prints a buffer of size \c size. If there is a null termination | |||||
* before the end of the buffer, prints up to termination. | |||||
* | |||||
* \param str Pointer to string buffer to print | |||||
* \param size The size of buffer | |||||
* \return The number of printed \c Char_t | |||||
*/ | |||||
size_t print(const Char_t* str, size_t size) { | |||||
if (str == nullptr) | |||||
return 0; | |||||
return write_(str, size); | |||||
} | |||||
/*! | |||||
* \brief | |||||
* Prints a \c Char_t | |||||
* \param ch The Char_t to print | |||||
* \return The number of printed \c Char_t | |||||
*/ | |||||
size_t print(Char_t ch) { | |||||
return write_ (ch); | |||||
} | |||||
/*! | |||||
* \brief | |||||
* Convert and print a long. | |||||
* \param n The number to print | |||||
* \param base The number base to use for conversion. | |||||
* \return The number of printed \c Char_t | |||||
*/ | |||||
size_t print(long n, Base base =Base::DEC) { | |||||
size_t cnt =0; | |||||
if (n < 0) { | |||||
n = -n; | |||||
cnt = write_ ('-'); | |||||
} | |||||
return cnt + print_unsigned((unsigned long)n, base); | |||||
} | |||||
/*! | |||||
* \brief | |||||
* Convert and print an int. | |||||
* \param n The number to print | |||||
* \param base The number base to use for conversion. | |||||
* \return The number of printed \c Char_t | |||||
*/ | |||||
size_t print(int n, Base base= Base::DEC) { | |||||
return print ((long)n, base); | |||||
} | |||||
/*! | |||||
* \brief | |||||
* Convert and print an unsigned long. | |||||
* \param n The number to print | |||||
* \param base The number base to use for conversion. | |||||
* \return The number of printed \c Char_t | |||||
*/ | |||||
size_t print(unsigned long n, Base base= Base::DEC) { | |||||
return print_unsigned ((unsigned long)n, base); | |||||
} | |||||
/*! | |||||
* \brief | |||||
* Convert and print an unsigned int. | |||||
* \param n The number to print | |||||
* \param base The number base to use for conversion. | |||||
* \return The number of printed \c Char_t | |||||
*/ | |||||
size_t print(unsigned int n, Base base= Base::DEC) { | |||||
return print_unsigned ((unsigned long)n, base); | |||||
} | |||||
/*! | |||||
* \brief | |||||
* Convert and print adouble | |||||
* \param n The number to print | |||||
* \param digits The number of decimal digits to print | |||||
* \return The number of printed \c Char_t | |||||
*/ | |||||
size_t print(double n, uint8_t digits = 2) { | |||||
return print_double (n, digits); | |||||
} | |||||
/*! | |||||
* \brief | |||||
* Perfect forwarder to print functionality with a new line termination | |||||
* \tparam Ts The types of parameters | |||||
* \param args The arguments to pass | |||||
* \return The number of printed \c Char_t | |||||
*/ | |||||
template <typename ...Ts> | |||||
size_t println(Ts&& ...args) { | |||||
size_t r = print (std::forward<Ts>(args)...); | |||||
r += write_ ('\n'); | |||||
return r; | |||||
} | |||||
}; | |||||
/*! | |||||
* \brief | |||||
* Converts and prints an unsigned long | |||||
* | |||||
* \tparam Impl_t The derived type | |||||
* \tparam Char_t The char type to use | |||||
* | |||||
* \param n The number to print | |||||
* \param base The number base to use | |||||
* \return The number of printed \c Char_t | |||||
*/ | |||||
template <typename Impl_t, typename Char_t> | |||||
size_t Print<Impl_t, Char_t>::print_unsigned(unsigned long n, Base base) { | |||||
Char_t buf[8 *sizeof(Char_t) * sizeof(long) + 1]; // Assumes 8-bit chars plus zero byte. | |||||
Char_t *str = &buf[sizeof(buf) - 1]; | |||||
*str = '\0'; | |||||
do { | |||||
Char_t c = n % value(base); | |||||
n /= value(base); | |||||
*--str = c < 10 ? c + '0' : c + 'A' - 10; | |||||
} while(n); | |||||
return write_(str, std::strlen(str)); | |||||
} | |||||
/*! | |||||
* \brief | |||||
* Converts and prints a double | |||||
* | |||||
* \note | |||||
* Internally, this implementation uses a long to store the integer part of the number. | |||||
* Thus overflows for numbers bigger than std::numeric_limits<long>::max() / min(). | |||||
* For these numbers it prints "ovf" instead. | |||||
* | |||||
* \tparam Impl_t The derived type | |||||
* \tparam Char_t The char type to use | |||||
* | |||||
* \param n The number to print | |||||
* \param digits The number of decimal digits to print | |||||
* \return The number of printed \c Char_t | |||||
*/ | |||||
template <typename Impl_t, typename Char_t> | |||||
size_t Print<Impl_t, Char_t>::print_double(double number, uint8_t digits) { | |||||
size_t n = 0; | |||||
if (std::isnan(number)) return print("nan"); | |||||
if (std::isinf(number)) return print("inf"); | |||||
if (number > (double)std::numeric_limits<long>::max()) | |||||
return print ("ovf"); | |||||
if (number < (double)std::numeric_limits<long>::min()) | |||||
return print ("-ovf"); | |||||
// Handle negative numbers | |||||
if (number < 0.0) { | |||||
n += write_ ('-'); | |||||
number = -number; | |||||
} | |||||
// Round correctly so that print(1.999, 2) prints as "2.00" | |||||
double rounding = 0.5; | |||||
for (uint8_t i=0; i<digits; ++i) | |||||
rounding /= 10.0; | |||||
number += rounding; | |||||
// Extract the integer part of the number and print it | |||||
unsigned long int_part = (unsigned long)number; | |||||
double remainder = number - (double)int_part; | |||||
n += print(int_part); | |||||
// Print the decimal point, but only if there are digits beyond | |||||
if (digits > 0) { | |||||
n += print('.'); | |||||
} | |||||
// Extract digits from the remainder one at a time | |||||
while (digits-- > 0) { | |||||
remainder *= 10.0; | |||||
unsigned int toPrint = (unsigned int)(remainder); | |||||
n += print(toPrint); | |||||
remainder -= toPrint; | |||||
} | |||||
return n; | |||||
} | |||||
} | |||||
#endif /* TBX_UTILS_PRINT_H_ */ |
@@ -0,0 +1,333 @@ | |||||
/*! | |||||
* \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_ */ |