158 lines
4.5 KiB
C++
158 lines
4.5 KiB
C++
#pragma once
|
|
|
|
#include <cstdint>
|
|
|
|
#include <avr/interrupt.h>
|
|
#include <avr/io.h>
|
|
#include <avr/sfr_defs.h>
|
|
|
|
#include "config.hpp"
|
|
#include "hardware.hpp"
|
|
|
|
namespace uart {
|
|
|
|
namespace detail {
|
|
|
|
#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.
|
|
The _SFR_ADDR macro in sfr_defs.h would give the address, but it does that by taking the address of the dereferenced
|
|
pointer and casts it to uint16_t, which is still not a legal constexpr.
|
|
The workaround therefore is to disable the pointer cast and dereference macro _MMIO_BYTE temporarily.
|
|
*/
|
|
|
|
#pragma push_macro("_MMIO_BYTE")
|
|
#undef _MMIO_BYTE
|
|
#define _MMIO_BYTE
|
|
|
|
struct Registers1 {
|
|
static constexpr std::uintptr_t IO_REG_ADDR = UDR1;
|
|
static constexpr std::uintptr_t CTRL_STAT_REG_A_ADDR = UCSR1A;
|
|
static constexpr std::uintptr_t CTRL_STAT_REG_B_ADDR = UCSR1B;
|
|
static constexpr std::uintptr_t CTRL_STAT_REG_C_ADDR = UCSR1C;
|
|
static constexpr std::uintptr_t BAUD_REG_L_ADDR = UBRR1L;
|
|
static constexpr std::uintptr_t BAUD_REG_H_ADDR = UBRR1H;
|
|
};
|
|
|
|
#pragma pop_macro("_MMIO_BYTE")
|
|
|
|
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,
|
|
};
|
|
|
|
// 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
|
|
|
|
#define HAS_UART1
|
|
|
|
#endif
|
|
|
|
} // namespace detail
|
|
|
|
#ifdef HAS_UART1
|
|
|
|
template <class cfg = Config<>, Driven driven = Driven::INTERRUPT, Mode mode = Mode::ASYNCHRONOUS>
|
|
class Hardware1 : public detail::BlockingHardware<detail::Registers1, detail::ControlFlagsA1, detail::ControlFlagsB1,
|
|
detail::ControlFlagsC1, cfg, mode> {
|
|
};
|
|
|
|
template <class cfg, Mode mode>
|
|
class Hardware1<cfg, Driven::INTERRUPT, mode>
|
|
: public detail::InterruptHardware<detail::Registers1, detail::ControlFlagsA1, detail::ControlFlagsB1,
|
|
detail::ControlFlagsC1, cfg, mode> {
|
|
public:
|
|
[[gnu::always_inline]] static void init()
|
|
{
|
|
HardwareImpl::init();
|
|
sei();
|
|
}
|
|
|
|
private:
|
|
using HardwareImpl = detail::Hardware<detail::Registers1, detail::ControlFlagsA1, detail::ControlFlagsB1,
|
|
detail::ControlFlagsC1, cfg, Driven::INTERRUPT, mode>;
|
|
|
|
using InterruptHardwareImpl = detail::InterruptHardware<detail::Registers1, detail::ControlFlagsA1,
|
|
detail::ControlFlagsB1, detail::ControlFlagsC1, cfg, mode>;
|
|
|
|
// Must be friends with Uart interface to call these private handlers
|
|
template <class Driver>
|
|
friend class Uart;
|
|
|
|
[[gnu::always_inline]] static void rxIntHandler()
|
|
{
|
|
InterruptHardwareImpl::rxIntHandler();
|
|
}
|
|
|
|
[[gnu::always_inline]] static void dataRegEmptyIntHandler()
|
|
{
|
|
InterruptHardwareImpl::dataRegEmptyIntHandler();
|
|
}
|
|
};
|
|
|
|
#endif
|
|
|
|
} // namespace uart
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifdef HAS_UART1
|
|
|
|
// Forward declare interrupt functions to allow adding them as friends
|
|
extern "C" {
|
|
void USART1_RX_vect() __attribute__((signal));
|
|
void USART1_UDRE_vect() __attribute__((signal));
|
|
}
|
|
|
|
// clang-format off
|
|
#define REGISTER_UART1_INT_VECTORS(uart_type) \
|
|
ISR(USART1_RX_vect) \
|
|
{ \
|
|
uart_type::rxIntHandler(); \
|
|
} \
|
|
ISR(USART1_UDRE_vect) \
|
|
{ \
|
|
uart_type::dataRegEmptyIntHandler(); \
|
|
} \
|
|
struct _##uart_type { \
|
|
}
|
|
// clang-format off
|
|
|
|
#endif
|
|
|