#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 { enum class SupportedHardware { ATmega1284P, }; template struct HardwareAbstraction { }; template <> struct HardwareAbstraction { struct Reg0 { static constexpr volatile auto *ioReg = &UDR0; static constexpr volatile auto *controlStatusRegA = &UCSR0A; static constexpr volatile auto *controlStatusRegB = &UCSR0B; static constexpr volatile auto *controlStatusRegC = &UCSR0C; static constexpr volatile auto *baudRateRegL = &UBRR0L; static constexpr volatile auto *baudRateRegH = &UBRR0H; }; struct Reg1 { static constexpr volatile auto *ioReg = &UDR1; static constexpr volatile auto *controlStatusRegA = &UCSR1A; static constexpr volatile auto *controlStatusRegB = &UCSR1B; static constexpr volatile auto *controlStatusRegC = &UCSR1C; static constexpr volatile auto *baudRateRegL = &UBRR1L; static constexpr volatile auto *baudRateRegH = &UBRR1H; }; template 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 * baudRate) / (16 * baudRate) - 1; return baudVal; } struct DataBitsVal { uint8_t ucsrcVal = 0; uint8_t ucsrbVal = 0; }; template static constexpr auto calcDataBits() { DataBitsVal dataBitsVal; switch (dataBits) { case DataBits::FIVE: dataBitsVal.ucsrcVal = 0; break; case DataBits::SIX: dataBitsVal.ucsrcVal = (1 << UCSZ00); break; case DataBits::SEVEN: dataBitsVal.ucsrcVal = (1 << UCSZ01); break; case DataBits::EIGHT: dataBitsVal.ucsrcVal = (1 << UCSZ01) | (1 << UCSZ00); break; case DataBits::NINE: dataBitsVal.ucsrcVal = (1 << UCSZ01) | (1 << UCSZ00); dataBitsVal.ucsrbVal = (1 << UCSZ02); break; } return dataBitsVal; } template static constexpr auto calcParity() { uint8_t parityVal = 0; if (parity == Parity::EVEN) parityVal = (1 << UPM01); else if (parity == Parity::ODD) parityVal = (1 << UPM01) | (1 << UPM00); return parityVal; } template static constexpr auto calcStopBits() { uint8_t stopBitsVal = 0; if (stopBits == StopBits::TWO) stopBitsVal = (1 << USBS0); return stopBitsVal; } template 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 << UMSEL00); } return modeVal; } template static constexpr auto calcRxState() { uint8_t enableVal = 0; if (enable) enableVal = (1 << RXEN0); return enableVal; } template static constexpr auto calcTxState() { uint8_t enableVal = 0; if (enable) enableVal = (1 << TXEN0); return enableVal; } static void setBaud(const uint16_t baudVal) { *Reg0::baudRateRegH = static_cast(baudVal >> 8); *Reg0::baudRateRegL = static_cast(baudVal); } template static void setControlRegA() { *Reg0::controlStatusRegA = regVal; } template static void setControlRegB() { *Reg0::controlStatusRegB = regVal; } template static void setControlRegC() { *Reg0::controlStatusRegC = regVal; } static void txByte(uint8_t byte) FORCE_INLINE { while (!(*Reg0::controlStatusRegA & (1 << UDRE0))) ; *Reg0::ioReg = byte; } }; static constexpr auto currentHardware = SupportedHardware::ATmega1284P; } // 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() { detail::HardwareAbstraction hal; hal.setBaud(hal.calcBaud()); constexpr auto dataBitsVal = hal.calcDataBits(); constexpr auto parityVal = hal.calcParity(); constexpr auto stopBitsVal = hal.calcStopBits(); constexpr auto modeVal = hal.calcMode(); constexpr auto enableRx = hal.calcRxState(); constexpr auto enableTx = hal.calcTxState(); constexpr uint8_t ucsr0b = dataBitsVal.ucsrbVal | enableRx | enableTx; constexpr uint8_t ucsr0c = dataBitsVal.ucsrcVal | parityVal | stopBitsVal | modeVal; hal.setControlRegB(); hal.setControlRegC(); } static void txByte(data_t byte) FORCE_INLINE { detail::HardwareAbstraction hal; hal.txByte(byte); } static data_t rxByte() {} static data_t peek() {} private: static constexpr auto BAUD_RATE = cfg::BAUD_RATE; static constexpr auto PARITY = cfg::PARITY; static constexpr auto STOP_BITS = cfg::STOP_BITS; }; } // namespace uart #undef FORCE_INLINE