uart/hardware1.hpp

157 lines
4.1 KiB
C++

#ifndef UART_HARDWARE_1_HPP
#define UART_HARDWARE_1_HPP
#include <stdint.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/sfr_defs.h>
#include "config.hpp"
#include "hardware.hpp"
#define FORCE_INLINE __attribute__((always_inline))
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
For this the sfr_defs.h provides a macro which returns the address of a register
*/
struct Registers1 {
static constexpr uintptr_t IO_REG_ADDR = _SFR_ADDR(UDR1);
static constexpr uintptr_t CTRL_STAT_REG_A_ADDR = _SFR_ADDR(UCSR1A);
static constexpr uintptr_t CTRL_STAT_REG_B_ADDR = _SFR_ADDR(UCSR1B);
static constexpr uintptr_t CTRL_STAT_REG_C_ADDR = _SFR_ADDR(UCSR1C);
static constexpr uintptr_t BAUD_REG_L_ADDR = _SFR_ADDR(UBRR1L);
static constexpr uintptr_t BAUD_REG_H_ADDR = _SFR_ADDR(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,
};
// 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
extern void (*fnRx1IntHandler)();
extern void (*fnDataReg1EmptyIntHandler)();
#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> {
using detail::InterruptHardware<detail::Registers1, detail::ControlFlagsA1, detail::ControlFlagsB1,
detail::ControlFlagsC1, cfg, mode>::rxIntHandler;
using detail::InterruptHardware<detail::Registers1, detail::ControlFlagsA1, detail::ControlFlagsB1,
detail::ControlFlagsC1, cfg, mode>::dataRegEmptyIntHandler;
using HardwareImpl = detail::Hardware<detail::Registers1, detail::ControlFlagsA1, detail::ControlFlagsB1,
detail::ControlFlagsC1, cfg, Driven::INTERRUPT, mode>;
public:
static void init() FORCE_INLINE
{
detail::fnRx1IntHandler = rxIntHandler;
detail::fnDataReg1EmptyIntHandler = dataRegEmptyIntHandler;
HardwareImpl::init();
sei();
}
};
#endif
} // namespace uart
#undef FORCE_INLINE
#endif
//////////////////////////////////////////////////////////////////////////
#ifndef UART1_NO_INT_VECTORS
#include <avr/interrupt.h>
namespace uart {
namespace detail {
#if defined(__AVR_ATmega1284P__)
void (*fnRx1IntHandler)() = nullptr;
void (*fnDataReg1EmptyIntHandler)() = nullptr;
ISR(USART1_RX_vect)
{
if (fnRx1IntHandler)
fnRx1IntHandler();
}
ISR(USART1_UDRE_vect)
{
if (fnDataReg1EmptyIntHandler)
fnDataReg1EmptyIntHandler();
}
#endif
} // namespace detail
} // namespace uart
#endif