#pragma once #include #include #include #include #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(rhs); } constexpr int operator<<(const int &lhs, const ControlFlagsB1 &rhs) { return lhs << static_cast(rhs); } constexpr int operator<<(const int &lhs, const ControlFlagsC1 &rhs) { return lhs << static_cast(rhs); } // clang-format on #define HAS_UART1 #endif } // namespace detail #ifdef HAS_UART1 template , Driven driven = Driven::INTERRUPT, Mode mode = Mode::ASYNCHRONOUS> class Hardware1 : public detail::BlockingHardware { }; template class Hardware1 : public detail::InterruptHardware { public: [[gnu::always_inline]] static void init() { HardwareImpl::init(); sei(); } private: using HardwareImpl = detail::Hardware; using InterruptHardwareImpl = detail::InterruptHardware; // Must be friends with Uart interface to call these private handlers template 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