uart/hardware.hpp

205 lines
4.6 KiB
C++

#pragma once
#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 {
template <class Registers, typename CtrlFlagsA, typename CtrlFlagsB, typename CtrlFlagsC, class cfg, Mode mode,
Driven driven>
class Hardware {
public:
static void init() FORCE_INLINE
{
constexpr auto baudVal = calcBaud();
*Registers::BAUD_REG_H = static_cast<uint8_t>(baudVal >> 8);
*Registers::BAUD_REG_L = static_cast<uint8_t>(baudVal);
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 auto interruptVal = calcInterrupt();
constexpr uint8_t controlRegB = dataBitsVal.regBVal | enableRx | enableTx | interruptVal;
constexpr uint8_t controlRegC = dataBitsVal.regCVal | parityVal | stopBitsVal | modeVal;
*Registers::CTRL_STAT_REG_B = controlRegB;
*Registers::CTRL_STAT_REG_C = controlRegC;
}
static bool rxByteBlocking(typename cfg::data_t &byte) FORCE_INLINE
{
if (*Registers::CTRL_STAT_REG_A & (1 << CtrlFlagsA::RECEIVE_COMPLETE)) {
byte = *Registers::IO_REG;
return true;
}
return false;
}
static typename cfg::data_t rxByteInterrupt() FORCE_INLINE
{
return *Registers::IO_REG;
}
static void txByteBlocking(const typename cfg::data_t &byte) FORCE_INLINE
{
while (!(*Registers::CTRL_STAT_REG_A & (1 << CtrlFlagsA::DATA_REG_EMPTY)))
;
*Registers::IO_REG = byte;
}
static void txByteInterrupt(volatile const typename cfg::data_t &byte) FORCE_INLINE
{
*Registers::IO_REG = byte;
}
static void enableDataRegEmptyInt() FORCE_INLINE
{
*Registers::CTRL_STAT_REG_B |= (1 << CtrlFlagsB::DATA_REG_EMPTY_INT_ENABLE);
}
static void disableDataRegEmptyInt() FORCE_INLINE
{
*Registers::CTRL_STAT_REG_B &= ~(1 << CtrlFlagsB::DATA_REG_EMPTY_INT_ENABLE);
}
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 <bool enable>
static constexpr auto calcRxState()
{
uint8_t enableVal = 0;
if (enable)
enableVal = (1 << CtrlFlagsB::RX_ENABLE);
return enableVal;
}
template <bool enable>
static constexpr auto calcTxState()
{
uint8_t enableVal = 0;
if (enable)
enableVal = (1 << CtrlFlagsB::TX_ENABLE);
return enableVal;
}
static constexpr auto calcInterrupt()
{
uint8_t interruptVal = 0;
if (driven == Driven::INTERRUPT)
interruptVal |= (1 << CtrlFlagsB::DATA_REG_EMPTY_INT_ENABLE) | (1 << CtrlFlagsB::RX_INT_ENABLE);
return interruptVal;
}
};
template <typename data_t, uint8_t Size>
struct RingBuffer {
uint8_t head;
uint8_t tail;
data_t buf[Size];
};
} // namespace detail
} // namespace uart
#undef FORCE_INLINE