2019-07-27 18:55:17 +02:00
|
|
|
#pragma once
|
2019-07-30 21:43:52 +02:00
|
|
|
|
2019-08-02 20:23:54 +02:00
|
|
|
#include <stdint.h>
|
|
|
|
|
2019-08-05 17:59:33 +02:00
|
|
|
#include <avr/interrupt.h>
|
2019-08-02 20:23:54 +02:00
|
|
|
#include <avr/io.h>
|
|
|
|
|
2019-08-02 09:31:02 +02:00
|
|
|
#include "config.hpp"
|
2019-08-02 09:21:47 +02:00
|
|
|
#include "hardware.hpp"
|
2019-07-30 21:43:52 +02:00
|
|
|
|
|
|
|
#define FORCE_INLINE __attribute__((always_inline))
|
|
|
|
|
|
|
|
namespace uart {
|
|
|
|
|
|
|
|
namespace detail {
|
|
|
|
|
|
|
|
#if defined(__AVR_ATmega1284P__)
|
|
|
|
|
|
|
|
struct Registers1 {
|
|
|
|
static constexpr volatile auto *IO_REG = &UDR1;
|
|
|
|
static constexpr volatile auto *CTRL_STAT_REG_A = &UCSR1A;
|
|
|
|
static constexpr volatile auto *CTRL_STAT_REG_B = &UCSR1B;
|
|
|
|
static constexpr volatile auto *CTRL_STAT_REG_C = &UCSR1C;
|
|
|
|
static constexpr volatile auto *BAUD_REG_L = &UBRR1L;
|
|
|
|
static constexpr volatile auto *BAUD_REG_H = &UBRR1H;
|
|
|
|
};
|
|
|
|
|
|
|
|
enum class ControlFlagsA1 {
|
|
|
|
MULTI_PROC_COMM_MODE = MPCM1,
|
|
|
|
SPEED_2X = U2X1,
|
|
|
|
PARITY_ERROR = UPE1,
|
|
|
|
DATA_OVER_RUN = DOR1,
|
|
|
|
FRAME_ERROR = FE1,
|
|
|
|
DATA_REG_EMPTY = UDRE1,
|
|
|
|
TRANSMIT_COMPLETE = TXC1,
|
|
|
|
RECEIVE_COMPLETE = RXC1,
|
|
|
|
};
|
|
|
|
|
|
|
|
enum class ControlFlagsB1 {
|
|
|
|
TX_DATA_BIT_8 = TXB81,
|
|
|
|
RX_DATA_BIT_8 = RXB81,
|
|
|
|
CHAR_SIZE_2 = UCSZ12,
|
|
|
|
TX_ENABLE = TXEN1,
|
|
|
|
RX_ENABLE = RXEN1,
|
|
|
|
DATA_REG_EMPTY_INT_ENABLE = UDRIE1,
|
|
|
|
TX_INT_ENABLE = TXCIE1,
|
|
|
|
RX_INT_ENABLE = RXCIE1,
|
|
|
|
};
|
|
|
|
|
|
|
|
enum class ControlFlagsC1 {
|
|
|
|
CLK_POLARITY = UCPOL1,
|
|
|
|
CHAR_SIZE_0 = UCSZ10,
|
|
|
|
CHAR_SIZE_1 = UCSZ11,
|
|
|
|
STOP_BIT_SEL = USBS1,
|
|
|
|
PARITY_MODE_0 = UPM10,
|
|
|
|
PARITY_MODE_1 = UPM11,
|
|
|
|
MODE_SEL_0 = UMSEL10,
|
|
|
|
MODE_SEL_1 = UMSEL11,
|
|
|
|
};
|
|
|
|
|
2019-07-30 21:51:13 +02:00
|
|
|
// clang-format off
|
|
|
|
constexpr int operator<<(const int &lhs, const ControlFlagsA1 &rhs) { return lhs << static_cast<int>(rhs); }
|
|
|
|
constexpr int operator<<(const int &lhs, const ControlFlagsB1 &rhs) { return lhs << static_cast<int>(rhs); }
|
|
|
|
constexpr int operator<<(const int &lhs, const ControlFlagsC1 &rhs) { return lhs << static_cast<int>(rhs); }
|
|
|
|
// clang-format on
|
2019-07-30 21:43:52 +02:00
|
|
|
|
2019-08-02 20:29:04 +02:00
|
|
|
extern void (*fnRx1IntHandler)();
|
|
|
|
extern void (*fnDataReg1EmptyIntHandler)();
|
2019-08-02 15:46:07 +02:00
|
|
|
|
2019-07-30 21:43:52 +02:00
|
|
|
#define HAS_UART1
|
|
|
|
|
|
|
|
#else
|
|
|
|
#error "This chip is not supported"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
} // namespace detail
|
|
|
|
|
|
|
|
#ifdef HAS_UART1
|
|
|
|
|
|
|
|
template <Mode mode = Mode::ASYNCHRONOUS, class cfg = Config<>, Driven driven = Driven::INTERRUPT>
|
|
|
|
class Hardware1 {
|
|
|
|
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
|
2019-07-30 21:43:52 +02:00
|
|
|
{
|
|
|
|
HardwareImpl::init();
|
|
|
|
}
|
|
|
|
|
2019-08-02 16:41:53 +02:00
|
|
|
static void txByte(const data_t &byte) FORCE_INLINE
|
2019-07-30 21:43:52 +02:00
|
|
|
{
|
2019-08-02 12:08:16 +02:00
|
|
|
HardwareImpl::txByteBlocking(byte);
|
|
|
|
}
|
|
|
|
|
2019-08-02 17:36:07 +02:00
|
|
|
static bool rxByte(data_t &byte) FORCE_INLINE
|
|
|
|
{
|
2019-08-02 17:54:34 +02:00
|
|
|
return HardwareImpl::rxByteBlocking(byte);
|
2019-08-02 17:36:07 +02:00
|
|
|
}
|
2019-08-02 12:08:16 +02:00
|
|
|
|
2019-08-02 18:20:06 +02:00
|
|
|
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-02 12:08:16 +02:00
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2019-08-02 12:08:16 +02:00
|
|
|
private:
|
|
|
|
using HardwareImpl = detail::Hardware<detail::Registers1, detail::ControlFlagsA1, detail::ControlFlagsB1,
|
|
|
|
detail::ControlFlagsC1, cfg, mode, driven>;
|
|
|
|
};
|
|
|
|
|
|
|
|
template <Mode mode, class cfg>
|
|
|
|
class Hardware1<mode, cfg, Driven::INTERRUPT> {
|
|
|
|
public:
|
|
|
|
using data_t = typename cfg::data_t;
|
|
|
|
static constexpr auto DATA_BITS = cfg::DATA_BITS;
|
|
|
|
|
|
|
|
static void init() FORCE_INLINE
|
|
|
|
{
|
|
|
|
detail::fnRx1IntHandler = rxIntHandler;
|
2019-08-02 15:46:07 +02:00
|
|
|
detail::fnDataReg1EmptyIntHandler = dataRegEmptyIntHandler;
|
2019-08-02 12:08:16 +02:00
|
|
|
|
|
|
|
HardwareImpl::init();
|
2019-08-05 17:59:33 +02:00
|
|
|
sei();
|
2019-08-02 12:08:16 +02:00
|
|
|
}
|
|
|
|
|
2019-08-02 16:41:53 +02:00
|
|
|
static void txByte(const data_t &byte) FORCE_INLINE
|
2019-08-02 12:08:16 +02:00
|
|
|
{
|
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();
|
2019-07-30 21:43:52 +02:00
|
|
|
}
|
|
|
|
|
2019-08-02 17:36:07 +02:00
|
|
|
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;
|
|
|
|
}
|
2019-07-30 21:43:52 +02:00
|
|
|
|
2019-08-02 18:20:06 +02:00
|
|
|
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-07-30 21:43:52 +02:00
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2019-07-30 21:43:52 +02:00
|
|
|
private:
|
|
|
|
using HardwareImpl = detail::Hardware<detail::Registers1, detail::ControlFlagsA1, detail::ControlFlagsB1,
|
2019-08-02 12:08:16 +02:00
|
|
|
detail::ControlFlagsC1, cfg, mode, Driven::INTERRUPT>;
|
|
|
|
|
2019-08-02 16:41:53 +02:00
|
|
|
static constexpr auto TX_BUFFER_SIZE = 16;
|
2019-08-02 17:36:07 +02:00
|
|
|
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;
|
2019-08-02 17:36:07 +02:00
|
|
|
static volatile detail::RingBuffer<data_t, RX_BUFFER_SIZE> sm_rxBuf;
|
2019-08-02 16:41:53 +02:00
|
|
|
|
2019-08-02 15:46:07 +02:00
|
|
|
static void rxIntHandler()
|
2019-08-02 12:08:16 +02:00
|
|
|
{
|
2019-08-02 17:36:07 +02:00
|
|
|
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 12:08:16 +02:00
|
|
|
}
|
|
|
|
|
2019-08-02 16:41:53 +02:00
|
|
|
static void dataRegEmptyIntHandler() FORCE_INLINE
|
2019-08-02 12:08:16 +02:00
|
|
|
{
|
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-02 12:08:16 +02:00
|
|
|
}
|
2019-07-30 21:43:52 +02:00
|
|
|
};
|
|
|
|
|
2019-08-02 16:41:53 +02:00
|
|
|
template <Mode mode, class cfg>
|
2019-08-02 17:13:53 +02:00
|
|
|
volatile detail::RingBuffer<typename Hardware1<mode, cfg, Driven::INTERRUPT>::data_t,
|
|
|
|
Hardware1<mode, cfg, Driven::INTERRUPT>::TX_BUFFER_SIZE>
|
|
|
|
Hardware1<mode, cfg, Driven::INTERRUPT>::sm_txBuf = {0, 0, {0}};
|
2019-08-02 16:41:53 +02:00
|
|
|
|
2019-08-02 17:36:07 +02:00
|
|
|
template <Mode mode, class cfg>
|
|
|
|
volatile detail::RingBuffer<typename Hardware1<mode, cfg, Driven::INTERRUPT>::data_t,
|
|
|
|
Hardware1<mode, cfg, Driven::INTERRUPT>::RX_BUFFER_SIZE>
|
|
|
|
Hardware1<mode, cfg, Driven::INTERRUPT>::sm_rxBuf = {0, 0, {0}};
|
|
|
|
|
2019-07-30 21:43:52 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
} // namespace uart
|
|
|
|
|
|
|
|
#undef FORCE_INLINE
|