#pragma once #include #include #include #include #include "config.hpp" #include "hardware.hpp" #define FORCE_INLINE __attribute__((always_inline)) namespace uart { namespace detail { #if defined(__AVR_ATmega1284P__) /* The following works in avr-gcc 5.4.0, but is not legal C++, because ptr's are not legal constexpr's constexpr auto *foo = ptr; Workaround is to store the the address of the ptr in a uintptr_t and reinterpret_cast it at call site For this to work we need to temporarily disable the _SFR_MEM8 macro so that the register macro just gives the address */ #undef _SFR_MEM8 #define _SFR_MEM8 struct Registers1 { static constexpr uintptr_t IO_REG_ADDR = UDR1; static constexpr uintptr_t CTRL_STAT_REG_A_ADDR = UCSR1A; static constexpr uintptr_t CTRL_STAT_REG_B_ADDR = UCSR1B; static constexpr uintptr_t CTRL_STAT_REG_C_ADDR = UCSR1C; static constexpr uintptr_t BAUD_REG_L_ADDR = UBRR1L; static constexpr uintptr_t BAUD_REG_H_ADDR = UBRR1H; }; #undef _SFR_MEM8 #define _SFR_MEM8(mem_addr) _MMIO_BYTE(mem_addr) enum class ControlFlagsA1 { MULTI_PROC_COMM_MODE = MPCM1, SPEED_2X = U2X1, PARITY_ERROR = UPE1, DATA_OVER_RUN = DOR1, FRAME_ERROR = FE1, DATA_REG_EMPTY = UDRE1, TRANSMIT_COMPLETE = TXC1, RECEIVE_COMPLETE = RXC1, }; enum class ControlFlagsB1 { TX_DATA_BIT_8 = TXB81, RX_DATA_BIT_8 = RXB81, CHAR_SIZE_2 = UCSZ12, TX_ENABLE = TXEN1, RX_ENABLE = RXEN1, DATA_REG_EMPTY_INT_ENABLE = UDRIE1, TX_INT_ENABLE = TXCIE1, RX_INT_ENABLE = RXCIE1, }; enum class ControlFlagsC1 { CLK_POLARITY = UCPOL1, CHAR_SIZE_0 = UCSZ10, CHAR_SIZE_1 = UCSZ11, STOP_BIT_SEL = USBS1, PARITY_MODE_0 = UPM10, PARITY_MODE_1 = UPM11, MODE_SEL_0 = UMSEL10, MODE_SEL_1 = UMSEL11, }; // clang-format off constexpr int operator<<(const int &lhs, const ControlFlagsA1 &rhs) { return lhs << static_cast(rhs); } constexpr int operator<<(const int &lhs, const ControlFlagsB1 &rhs) { return lhs << static_cast(rhs); } constexpr int operator<<(const int &lhs, const ControlFlagsC1 &rhs) { return lhs << static_cast(rhs); } // clang-format on extern void (*fnRx1IntHandler)(); extern void (*fnDataReg1EmptyIntHandler)(); #define HAS_UART1 #endif } // namespace detail #ifdef HAS_UART1 template , Driven driven = Driven::INTERRUPT, Mode mode = Mode::ASYNCHRONOUS> class Hardware1 { public: using data_t = typename cfg::data_t; static constexpr auto DATA_BITS = cfg::DATA_BITS; static void init() FORCE_INLINE { HardwareImpl::init(); } static void txByte(const data_t &byte) FORCE_INLINE { HardwareImpl::txByteBlocking(byte); } static bool rxByte(data_t &byte) FORCE_INLINE { return HardwareImpl::rxByteBlocking(byte); } static bool peek(data_t &byte) FORCE_INLINE { static_cast(byte); static_assert(driven != Driven::BLOCKING, "Peek with data is not supported in blocking mode"); return false; } static bool peek() FORCE_INLINE { return HardwareImpl::peekBlocking(); } static void flushTx() FORCE_INLINE { while (!HardwareImpl::txEmpty()) ; while (!HardwareImpl::txComplete()) ; HardwareImpl::clearTxComplete(); } private: using HardwareImpl = detail::Hardware; }; template class Hardware1 { public: using data_t = typename cfg::data_t; static constexpr auto DATA_BITS = cfg::DATA_BITS; static void init() FORCE_INLINE { detail::fnRx1IntHandler = rxIntHandler; detail::fnDataReg1EmptyIntHandler = dataRegEmptyIntHandler; HardwareImpl::init(); sei(); } static void txByte(const data_t &byte) FORCE_INLINE { uint8_t tmpHead = (sm_txBuf.head + 1) % TX_BUFFER_SIZE; while (tmpHead == sm_txBuf.tail) ; sm_txBuf.buf[tmpHead] = byte; sm_txBuf.head = tmpHead; HardwareImpl::enableDataRegEmptyInt(); } static bool rxByte(data_t &byte) FORCE_INLINE { if (sm_rxBuf.head == sm_rxBuf.tail) return false; uint8_t tmpTail = (sm_rxBuf.tail + 1) % RX_BUFFER_SIZE; byte = sm_rxBuf.buf[tmpTail]; sm_rxBuf.tail = tmpTail; return true; } static bool peek(data_t &byte) FORCE_INLINE { if (sm_rxBuf.head == sm_rxBuf.tail) return false; uint8_t tmpTail = (sm_rxBuf.tail + 1) % RX_BUFFER_SIZE; byte = sm_rxBuf.buf[tmpTail]; return true; } static bool peek() FORCE_INLINE { return (sm_rxBuf.head != sm_rxBuf.tail); } static void flushTx() FORCE_INLINE { while (sm_txBuf.head != sm_txBuf.tail) ; while (!HardwareImpl::txEmpty()) ; while (!HardwareImpl::txComplete()) ; HardwareImpl::clearTxComplete(); } private: using HardwareImpl = detail::Hardware; static constexpr auto TX_BUFFER_SIZE = 16; static constexpr auto RX_BUFFER_SIZE = 16; static volatile detail::RingBuffer sm_txBuf; static volatile detail::RingBuffer sm_rxBuf; static void rxIntHandler() { auto data = HardwareImpl::rxByteInterrupt(); uint8_t tmpHead = (sm_rxBuf.head + 1) % RX_BUFFER_SIZE; if (tmpHead != sm_rxBuf.tail) { sm_rxBuf.head = tmpHead; sm_rxBuf.buf[tmpHead] = data; } else { // TODO: Handle overflow } } static void dataRegEmptyIntHandler() FORCE_INLINE { if (sm_txBuf.head != sm_txBuf.tail) { uint8_t tmpTail = (sm_txBuf.tail + 1) % TX_BUFFER_SIZE; sm_txBuf.tail = tmpTail; HardwareImpl::txByteInterrupt(sm_txBuf.buf[tmpTail]); } else HardwareImpl::disableDataRegEmptyInt(); } }; template volatile detail::RingBuffer::data_t, Hardware1::TX_BUFFER_SIZE> Hardware1::sm_txBuf = {0, 0, {0}}; template volatile detail::RingBuffer::data_t, Hardware1::RX_BUFFER_SIZE> Hardware1::sm_rxBuf = {0, 0, {0}}; #endif } // namespace uart #undef FORCE_INLINE