#pragma once #include #include #include #include "uart_config.hpp" #include "uart_vcp.hpp" namespace uart { namespace detail { template struct always_false : std::false_type { }; template inline constexpr auto always_false_v = always_false::value; template static constexpr size_t cntDigits() { T num = Limit; size_t cnt = 0; do { num /= Base; ++cnt; } while(num > 0); return cnt; } template static constexpr size_t maxNumDigits() { constexpr T MinVal = std::numeric_limits::min(); constexpr T MaxVal = std::numeric_limits::max(); constexpr T MinDigits = cntDigits(); constexpr T MaxDigits = cntDigits(); return (MinDigits < MaxDigits) ? MaxDigits : MinDigits; } } // namespace detail template class Uart { public: // Constructing a uart object does not initialize the driver to allow different specializations with the same // back-end to exists at the same time // Note that init must be called every time when switching specializations with the same back-end Uart() = default; // Moving and copying uart objects is not supported Uart(const Uart&) = delete; Uart(Uart&&) = delete; Uart& operator=(const Uart&) = delete; Uart& operator=(Uart&&) = delete; // Before using the uart init must be called static void init() { Driver::init(); } static void txByte(const uint8_t& byte) { Driver::txByte(byte); } static bool rxByte(uint8_t& byte) { return Driver::rxByte(byte); } static bool peek(uint8_t& byte) { return Driver::peek(byte); } static bool peek() { return Driver::peek(); } static void flushTx() { Driver::flushTx(); } static void txString(const char* str) { static_assert(Driver::DATA_BITS == DataBits::EIGHT, "Strings are only supported with 8 data bits"); while(char ch = *str++) txByte(ch); } template static void txNumber(const T& val) { static_assert(std::is_integral_v, "Only supported on integral types"); static_assert(Base >= 2, "Numbers with base less than 2 make no sense"); static_assert(Base <= 16, "Numbers with base higher than 16 are not supported"); static_assert(Padding <= detail::maxNumDigits(), "Cannot pad more than maximum length of number"); constexpr char AlphaChar = (LowerCase) ? 'a' : 'A'; constexpr size_t NumDigits = detail::maxNumDigits(); T digits = val; if(digits < 0) { digits = -digits; txByte('-'); } uint8_t buffer[NumDigits]; uint8_t* bufEnd = buffer + NumDigits - 1; do { const uint8_t lastDigit = digits % Base; *bufEnd-- = (lastDigit < 10) ? ('0' + lastDigit) : (AlphaChar + lastDigit - 10); digits /= Base; } while(digits > 0); if(Padding > 0) { size_t strLen = buffer + NumDigits - (bufEnd + 1); if(Padding > strLen) { for(size_t i = Padding; i > strLen && bufEnd >= buffer; --i) { *bufEnd-- = PadChar; } } } for(uint8_t* buf = bufEnd + 1; buf < buffer + NumDigits; ++buf) { txByte(*buf); } } ////////////////////////////////////////////////////////////////////////// // Output stream overloads Uart& operator<<(const char* str) { txString(str); return *this; } Uart& operator<<(const char& val) { txByte(val); return *this; } Uart& operator<<(const signed char& val) { txNumber(val); return *this; } Uart& operator<<(const unsigned char& val) { txNumber(val); return *this; } Uart& operator<<(const short& val) { txNumber(val); return *this; } Uart& operator<<(const unsigned short& val) { txNumber(val); return *this; } Uart& operator<<(const int& val) { txNumber(val); return *this; } Uart& operator<<(const unsigned int& val) { txNumber(val); return *this; } Uart& operator<<(const long& val) { txNumber(val); return *this; } Uart& operator<<(const unsigned long& val) { txNumber(val); return *this; } Uart& operator<<(const long long& val) { txNumber(val); return *this; } Uart& operator<<(const unsigned long long& val) { txNumber(val); return *this; } template Uart& operator<<(float) const { static_assert(detail::always_false_v, "Not supported by hardware"); } template Uart& operator<<(double) const { static_assert(detail::always_false_v, "Not supported by hardware"); } template Uart& operator<<(long double) const { static_assert(detail::always_false_v, "Not supported by hardware"); } Uart& operator<<(const bool& val) { txString(val ? "true" : "false"); return *this; } Uart& operator<<(const void* val) { txString("0x"); txNumber(reinterpret_cast(val)); return *this; } ////////////////////////////////////////////////////////////////////////// // Input stream overloads template Uart& operator>>(char&) const { static_assert(detail::always_false_v, "Not implemented"); } template Uart& operator>>(unsigned char&) const { static_assert(detail::always_false_v, "Not implemented"); } template Uart& operator>>(short&) const { static_assert(detail::always_false_v, "Not implemented"); } template Uart& operator>>(unsigned short&) const { static_assert(detail::always_false_v, "Not implemented"); } template Uart& operator>>(int&) const { static_assert(detail::always_false_v, "Not implemented"); } template Uart& operator>>(unsigned int&) const { static_assert(detail::always_false_v, "Not implemented"); } template Uart& operator>>(long&) const { static_assert(detail::always_false_v, "Not implemented"); } template Uart& operator>>(unsigned long&) const { static_assert(detail::always_false_v, "Not implemented"); } template Uart& operator>>(long long&) const { static_assert(detail::always_false_v, "Not implemented"); } template Uart& operator>>(unsigned long long&) const { static_assert(detail::always_false_v, "Not implemented"); } template Uart& operator>>(float&) const { static_assert(detail::always_false_v, "Not supported by hardware"); } template Uart& operator>>(double&) const { static_assert(detail::always_false_v, "Not supported by hardware"); } template Uart& operator>>(long double&) const { static_assert(detail::always_false_v, "Not supported by hardware"); } template Uart& operator>>(bool&) const { static_assert(detail::always_false_v, "Not implemented"); } template Uart& operator>>(const void*&) const { static_assert(detail::always_false_v, "Not implemented"); } }; template> using Vcp = Uart>; } // namespace uart