#pragma once #include "config.hpp" #define FORCE_INLINE __attribute__((always_inline)) namespace uart { enum class Mode { ASYNCHRONOUS, ASYNCHRONOUS_2X, SYNCHRONOUS_MASTER, SYNCHRONOUS_SLAVE, SPI, }; enum class Driven { INTERRUPT, BLOCKING, }; namespace detail { #if defined(__AVR_ATmega1284P__) struct Registers0 { static constexpr volatile auto *IO_REG = &UDR0; static constexpr volatile auto *CTRL_STAT_REG_A = &UCSR0A; static constexpr volatile auto *CTRL_STAT_REG_B = &UCSR0B; static constexpr volatile auto *CTRL_STAT_REG_C = &UCSR0C; static constexpr volatile auto *BAUD_REG_L = &UBRR0L; static constexpr volatile auto *BAUD_REG_H = &UBRR0H; }; enum class ControlFlagsA0 { MULTI_PROC_COMM_MODE = MPCM0, SPEED_2X = U2X0, PARITY_ERROR = UPE0, DATA_OVER_RUN = DOR0, FRAME_ERROR = FE0, DATA_REG_EMPTY = UDRE0, TRANSMIT_COMPLETE = TXC0, RECEIVE_COMPLETE = RXC0, }; enum class ControlFlagsB0 { TX_DATA_BIT_8 = TXB80, RX_DATA_BIT_8 = RXB80, CHAR_SIZE_2 = UCSZ02, TX_ENABLE = TXEN0, RX_ENABLE = RXEN0, DATA_REG_EMPTY_INT_ENABLE = UDRIE0, TX_INT_ENABLE = TXCIE0, RX_INT_ENABLE = RXCIE0, }; enum class ControlFlagsC0 { CLK_POLARITY = UCPOL0, CHAR_SIZE_0 = UCSZ00, CHAR_SIZE_1 = UCSZ01, STOP_BIT_SEL = USBS0, PARITY_MODE_0 = UPM00, PARITY_MODE_1 = UPM01, MODE_SEL_0 = UMSEL00, MODE_SEL_1 = UMSEL01, }; constexpr int operator<<(const int &lhs, const ControlFlagsA0 &rhs) { return lhs << static_cast(rhs); } constexpr int operator<<(const int &lhs, const ControlFlagsB0 &rhs) { return lhs << static_cast(rhs); } constexpr int operator<<(const int &lhs, const ControlFlagsC0 &rhs) { return lhs << static_cast(rhs); } #else #error "This chip is not supported" #endif template class Hardware { public: static void init() { constexpr auto baudVal = calcBaud(); *Registers::BAUD_REG_H = static_cast(baudVal >> 8); *Registers::BAUD_REG_L = static_cast(baudVal); constexpr auto dataBitsVal = calcDataBits(); constexpr auto parityVal = calcParity(); constexpr auto stopBitsVal = calcStopBits(); constexpr auto modeVal = calcMode(); constexpr auto enableRx = calcRxState(); constexpr auto enableTx = calcTxState(); constexpr uint8_t controlRegB = dataBitsVal.regBVal | enableRx | enableTx; constexpr uint8_t controlRegC = dataBitsVal.regCVal | parityVal | stopBitsVal | modeVal; *Registers::CTRL_STAT_REG_B = controlRegB; *Registers::CTRL_STAT_REG_C = controlRegC; } static void txByte(typename cfg::data_t byte) { while (!(*Registers::CTRL_STAT_REG_A & (1 << CtrlFlagsA::DATA_REG_EMPTY))) ; *Registers::IO_REG = byte; } private: struct DataBitsVal { uint8_t regCVal = 0; uint8_t regBVal = 0; }; static constexpr auto calcBaud() { // The actual formula is (F_CPU / (16 * baudRate)) - 1, but this one has the advantage of rounding correctly constexpr auto baudVal = (F_CPU + 8 * cfg::BAUD_RATE) / (16 * cfg::BAUD_RATE) - 1; return baudVal; } static constexpr auto calcDataBits() { DataBitsVal dataBitsVal; switch (cfg::DATA_BITS) { case DataBits::FIVE: dataBitsVal.regCVal = 0; break; case DataBits::SIX: dataBitsVal.regCVal = (1 << CtrlFlagsC::CHAR_SIZE_0); break; case DataBits::SEVEN: dataBitsVal.regCVal = (1 << CtrlFlagsC::CHAR_SIZE_1); break; case DataBits::EIGHT: dataBitsVal.regCVal = (1 << CtrlFlagsC::CHAR_SIZE_1) | (1 << CtrlFlagsC::CHAR_SIZE_0); break; case DataBits::NINE: dataBitsVal.regCVal = (1 << CtrlFlagsC::CHAR_SIZE_1) | (1 << CtrlFlagsC::CHAR_SIZE_0); dataBitsVal.regBVal = (1 << CtrlFlagsB::CHAR_SIZE_2); break; } return dataBitsVal; } static constexpr auto calcParity() { uint8_t parityVal = 0; if (cfg::PARITY == Parity::EVEN) parityVal = (1 << CtrlFlagsC::PARITY_MODE_1); else if (cfg::PARITY == Parity::ODD) parityVal = (1 << CtrlFlagsC::PARITY_MODE_1) | (1 << CtrlFlagsC::PARITY_MODE_0); return parityVal; } static constexpr auto calcStopBits() { uint8_t stopBitsVal = 0; if (cfg::STOP_BITS == StopBits::TWO) stopBitsVal = (1 << CtrlFlagsC::STOP_BIT_SEL); return stopBitsVal; } static constexpr auto calcMode() { static_assert(mode != Mode::SPI, "SPI mode can not be used with uart"); uint8_t modeVal = 0; if (mode == Mode::SYNCHRONOUS_MASTER || mode == Mode::SYNCHRONOUS_SLAVE) { modeVal = (1 << CtrlFlagsC::MODE_SEL_0); } return modeVal; } template static constexpr auto calcRxState() { uint8_t enableVal = 0; if (enable) enableVal = (1 << CtrlFlagsB::RX_ENABLE); return enableVal; } template static constexpr auto calcTxState() { uint8_t enableVal = 0; if (enable) enableVal = (1 << CtrlFlagsB::TX_ENABLE); return enableVal; } }; } // namespace detail template , Driven driven = Driven::INTERRUPT> class Hardware0 { public: using data_t = typename cfg::data_t; static constexpr auto DATA_BITS = cfg::DATA_BITS; static void init() { HardwareImpl::init(); } static void txByte(data_t byte) FORCE_INLINE { HardwareImpl::txByte(byte); } static data_t rxByte() {} static data_t peek() {} private: using HardwareImpl = detail::Hardware; }; } // namespace uart #undef FORCE_INLINE