uart/hardware0.hpp

230 lines
4.9 KiB
C++

#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 ControlFlagsA {
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 ControlFlagsB {
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 ControlFlagsC {
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,
};
static constexpr auto getLastRxError() {}
static constexpr void set2xSpeed() {}
template <uint16_t BaudVal>
static inline void setBaudRate()
{
*Registers0::BAUD_REG_H = static_cast<uint8_t>(BaudVal >> 8);
*Registers0::BAUD_REG_L = static_cast<uint8_t>(BaudVal);
}
template <uint8_t RegVal>
static inline void setCtrlStatRegC()
{
*Registers0::CTRL_STAT_REG_C = RegVal;
}
#else
#error "This chip is not supported"
#endif
} // namespace detail
template <Mode mode = Mode::ASYNCHRONOUS, class cfg = Config<>, 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::setBaudRate<calcBaud()>();
constexpr auto dataBitsVal = calcDataBits();
constexpr auto parityVal = calcParity();
constexpr auto stopBitsVal = calcStopBits();
constexpr auto modeVal = calcMode();
constexpr auto enableRx = calcRxState<true>();
constexpr auto enableTx = calcTxState<true>();
constexpr uint8_t controlRegB = dataBitsVal.regBVal | enableRx | enableTx;
constexpr uint8_t controlRegC = dataBitsVal.regCVal | parityVal | stopBitsVal | modeVal;
*detail::Registers0::CTRL_STAT_REG_B = controlRegB;
detail::setCtrlStatRegC<controlRegC>();
}
static void txByte(data_t byte) FORCE_INLINE
{
while (!(*detail::Registers0::CTRL_STAT_REG_A & (1 << UDRE0)))
;
*detail::Registers0::IO_REG = 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;
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 * BAUD_RATE) / (16 * BAUD_RATE) - 1;
return baudVal;
}
struct DataBitsVal {
uint8_t regCVal = 0;
uint8_t regBVal = 0;
};
static constexpr auto calcDataBits()
{
DataBitsVal dataBitsVal;
switch (DATA_BITS) {
case DataBits::FIVE:
dataBitsVal.regCVal = 0;
break;
case DataBits::SIX:
dataBitsVal.regCVal = (1 << detail::CHAR_SIZE_0);
break;
case DataBits::SEVEN:
dataBitsVal.regCVal = (1 << detail::CHAR_SIZE_1);
break;
case DataBits::EIGHT:
dataBitsVal.regCVal = (1 << detail::CHAR_SIZE_1) | (1 << detail::CHAR_SIZE_0);
break;
case DataBits::NINE:
dataBitsVal.regCVal = (1 << detail::CHAR_SIZE_1) | (1 << detail::CHAR_SIZE_0);
dataBitsVal.regBVal = (1 << detail::CHAR_SIZE_2);
break;
}
return dataBitsVal;
}
static constexpr auto calcParity()
{
uint8_t parityVal = 0;
if (PARITY == Parity::EVEN)
parityVal = (1 << detail::PARITY_MODE_1);
else if (PARITY == Parity::ODD)
parityVal = (1 << detail::PARITY_MODE_1) | (1 << detail::PARITY_MODE_0);
return parityVal;
}
static constexpr auto calcStopBits()
{
uint8_t stopBitsVal = 0;
if (STOP_BITS == StopBits::TWO)
stopBitsVal = (1 << detail::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 << detail::MODE_SEL_0);
}
return modeVal;
}
template <bool enable>
static constexpr auto calcRxState()
{
uint8_t enableVal = 0;
if (enable)
enableVal = (1 << detail::RX_ENABLE);
return enableVal;
}
template <bool enable>
static constexpr auto calcTxState()
{
uint8_t enableVal = 0;
if (enable)
enableVal = (1 << detail::TX_ENABLE);
return enableVal;
}
};
} // namespace uart
#undef FORCE_INLINE