uart/hardware0.hpp

248 lines
6.2 KiB
C++
Raw Normal View History

#pragma once
2019-08-02 20:23:54 +02:00
#include <stdint.h>
#include <avr/interrupt.h>
2019-08-02 20:23:54 +02:00
#include <avr/io.h>
#include <avr/sfr_defs.h>
2019-08-02 20:23:54 +02:00
2019-07-28 12:15:19 +02:00
#include "config.hpp"
#include "hardware.hpp"
#define FORCE_INLINE __attribute__((always_inline))
namespace uart {
namespace detail {
2019-07-30 20:29:38 +02:00
#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
2019-07-30 20:29:38 +02:00
struct Registers0 {
static constexpr uintptr_t IO_REG_ADDR = UDR0;
static constexpr uintptr_t CTRL_STAT_REG_A_ADDR = UCSR0A;
static constexpr uintptr_t CTRL_STAT_REG_B_ADDR = UCSR0B;
static constexpr uintptr_t CTRL_STAT_REG_C_ADDR = UCSR0C;
static constexpr uintptr_t BAUD_REG_L_ADDR = UBRR0L;
static constexpr uintptr_t BAUD_REG_H_ADDR = UBRR0H;
};
#undef _SFR_MEM8
#define _SFR_MEM8(mem_addr) _MMIO_BYTE(mem_addr)
enum class ControlFlagsA0 {
2019-07-30 20:56:16 +02:00
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 {
2019-07-30 20:56:16 +02:00
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 {
2019-07-30 20:56:16 +02:00
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,
};
2019-07-30 21:51:13 +02:00
// clang-format off
constexpr int operator<<(const int &lhs, const ControlFlagsA0 &rhs) { return lhs << static_cast<int>(rhs); }
constexpr int operator<<(const int &lhs, const ControlFlagsB0 &rhs) { return lhs << static_cast<int>(rhs); }
constexpr int operator<<(const int &lhs, const ControlFlagsC0 &rhs) { return lhs << static_cast<int>(rhs); }
// clang-format on
2019-07-30 20:29:38 +02:00
extern void (*fnRx0IntHandler)();
extern void (*fnDataReg0EmptyIntHandler)();
2019-07-30 20:29:38 +02:00
#else
#error "This chip is not supported"
#endif
} // namespace detail
2019-08-05 20:05:59 +02:00
template <class cfg = Config<>, Driven driven = Driven::INTERRUPT, Mode mode = Mode::ASYNCHRONOUS>
class Hardware0 {
public:
using data_t = typename cfg::data_t;
static constexpr auto DATA_BITS = cfg::DATA_BITS;
2019-07-30 21:48:00 +02:00
static void init() FORCE_INLINE
{
HardwareImpl::init();
}
static void txByte(data_t byte) FORCE_INLINE
{
HardwareImpl::txByteBlocking(byte);
}
static bool rxByte(data_t &byte) FORCE_INLINE
{
2019-08-02 17:54:34 +02:00
return HardwareImpl::rxByteBlocking(byte);
}
static bool peek(data_t &byte) FORCE_INLINE
{
static_cast<void>(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();
}
2019-08-03 17:52:28 +02:00
static void flushTx() FORCE_INLINE
{
2019-08-03 18:45:51 +02:00
while (!HardwareImpl::txEmpty())
2019-08-03 17:52:28 +02:00
;
2019-08-03 18:45:51 +02:00
while (!HardwareImpl::txComplete())
;
HardwareImpl::clearTxComplete();
2019-08-03 17:52:28 +02:00
}
private:
using HardwareImpl = detail::Hardware<detail::Registers0, detail::ControlFlagsA0, detail::ControlFlagsB0,
2019-08-05 20:05:59 +02:00
detail::ControlFlagsC0, cfg, driven, mode>;
};
2019-08-05 20:05:59 +02:00
template <class cfg, Mode mode>
class Hardware0<cfg, Driven::INTERRUPT, mode> {
public:
using data_t = typename cfg::data_t;
static constexpr auto DATA_BITS = cfg::DATA_BITS;
static void init() FORCE_INLINE
{
detail::fnRx0IntHandler = rxIntHandler;
detail::fnDataReg0EmptyIntHandler = dataRegEmptyIntHandler;
HardwareImpl::init();
sei();
}
2019-08-02 16:41:53 +02:00
static void txByte(const data_t &byte) FORCE_INLINE
{
2019-08-02 17:13:53 +02:00
uint8_t tmpHead = (sm_txBuf.head + 1) % TX_BUFFER_SIZE;
while (tmpHead == sm_txBuf.tail)
2019-08-02 16:41:53 +02:00
;
2019-08-02 17:13:53 +02:00
sm_txBuf.buf[tmpHead] = byte;
sm_txBuf.head = tmpHead;
2019-08-02 16:41:53 +02:00
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);
}
2019-08-03 17:52:28 +02:00
static void flushTx() FORCE_INLINE
{
while (sm_txBuf.head != sm_txBuf.tail)
;
2019-08-03 18:45:51 +02:00
while (!HardwareImpl::txEmpty())
;
while (!HardwareImpl::txComplete())
2019-08-03 17:52:28 +02:00
;
2019-08-03 18:45:51 +02:00
HardwareImpl::clearTxComplete();
2019-08-03 17:52:28 +02:00
}
private:
using HardwareImpl = detail::Hardware<detail::Registers0, detail::ControlFlagsA0, detail::ControlFlagsB0,
2019-08-05 20:05:59 +02:00
detail::ControlFlagsC0, cfg, Driven::INTERRUPT, mode>;
2019-08-02 16:41:53 +02:00
static constexpr auto TX_BUFFER_SIZE = 16;
static constexpr auto RX_BUFFER_SIZE = 16;
2019-08-02 16:41:53 +02:00
2019-08-02 17:13:53 +02:00
static volatile detail::RingBuffer<data_t, TX_BUFFER_SIZE> sm_txBuf;
static volatile detail::RingBuffer<data_t, RX_BUFFER_SIZE> sm_rxBuf;
2019-08-02 16:41:53 +02:00
static void rxIntHandler()
{
uint8_t tmpHead = (sm_rxBuf.head + 1) % RX_BUFFER_SIZE;
if (tmpHead != sm_rxBuf.tail) {
sm_rxBuf.head = tmpHead;
sm_rxBuf.buf[tmpHead] = HardwareImpl::rxByteInterrupt();
}
}
2019-08-02 16:41:53 +02:00
static void dataRegEmptyIntHandler() FORCE_INLINE
{
2019-08-02 17:13:53 +02:00
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]);
2019-08-02 16:41:53 +02:00
} else
HardwareImpl::disableDataRegEmptyInt();
}
};
2019-08-05 20:05:59 +02:00
template <class cfg, Mode mode>
volatile detail::RingBuffer<typename Hardware0<cfg, Driven::INTERRUPT, mode>::data_t,
Hardware0<cfg, Driven::INTERRUPT, mode>::TX_BUFFER_SIZE>
Hardware0<cfg, Driven::INTERRUPT, mode>::sm_txBuf = {0, 0, {0}};
2019-08-02 16:41:53 +02:00
2019-08-05 20:05:59 +02:00
template <class cfg, Mode mode>
volatile detail::RingBuffer<typename Hardware0<cfg, Driven::INTERRUPT, mode>::data_t,
Hardware0<cfg, Driven::INTERRUPT, mode>::RX_BUFFER_SIZE>
Hardware0<cfg, Driven::INTERRUPT, mode>::sm_rxBuf = {0, 0, {0}};
} // namespace uart
#undef FORCE_INLINE