diff --git a/io.hpp b/io.hpp index 74b17cf..c32341d 100644 --- a/io.hpp +++ b/io.hpp @@ -1,314 +1,374 @@ -/* -* Copyright (c) by BlackMark 2015-2019 -* Date 02/01/2019 -* Version 3.1 -*/ +#pragma once -#ifndef INOUT_H -#define INOUT_H - -////////////////////////////////////////////////////////////////////////// #include #include -/************************************************************************/ +////////////////////////////////////////////////////////////////////////// +// Preprocessor defines + +#define GPIO_32 \ + defined(__AVR_ATmega32__) || defined(__AVR_ATmega32A__) || defined(__AVR_ATmega644P__) || \ + defined(__AVR_ATmega1284P__) +#define GPIO_23 \ + defined(__AVR_ATmega8__) || defined(__AVR_ATmega8A__) || defined(__AVR_ATmega168A__) || defined(__AVR_ATmega328P__) +#define GPIO_6 defined(__AVR_ATtiny13A__) || defined(__AVR_ATtiny85__) + +#define PORT_A_AVAILABLE GPIO_32 +#define PORT_B_AVAILABLE GPIO_32 || GPIO_23 || GPIO_6 +#define PORT_C_AVAILABLE GPIO_32 || GPIO_23 +#define PORT_D_AVAILABLE GPIO_32 || GPIO_23 + +#define PIN_B6_AVAILABLE GPIO_32 || GPIO_23 +#define PIN_B7_AVAILABLE GPIO_32 || GPIO_23 +#define PIN_C7_AVAILABLE GPIO_32 + +#define FORCE_INLINE __attribute__((always_inline)) ////////////////////////////////////////////////////////////////////////// -namespace InOut -{ -#define AVR_DIP40 defined (__AVR_ATmega32A__) || defined (__AVR_ATmega644P__) || defined (__AVR_ATmega1284P__) -#define AVR_DIP28 defined (__AVR_ATmega8__) || defined (__AVR_ATmega8A__) || defined (__AVR_ATmega168A__) || defined (__AVR_ATmega328P__) -#define AVR_DIP8 defined (__AVR_ATtiny13A__) || defined (__AVR_ATtiny85__) +// Library implementation -#if AVR_DIP40 - static constexpr volatile uint8_t *IO_PINA = &PINA; - static constexpr volatile uint8_t *IO_DDRA = &DDRA; - static constexpr volatile uint8_t *IO_PORTA = &PORTA; -#else - static constexpr volatile uint8_t *IO_PINA = nullptr; - static constexpr volatile uint8_t *IO_DDRA = nullptr; - static constexpr volatile uint8_t *IO_PORTA = nullptr; +namespace io { + +enum class Dir { IN, OUT }; + +enum class P { +#if PORT_A_AVAILABLE + A0 = 0x00, + A1 = 0x01, + A2 = 0x02, + A3 = 0x03, + A4 = 0x04, + A5 = 0x05, + A6 = 0x06, + A7 = 0x07, #endif -#if AVR_DIP40 || AVR_DIP28 || AVR_DIP8 - static constexpr volatile uint8_t *IO_PINB = &PINB; - static constexpr volatile uint8_t *IO_DDRB = &DDRB; - static constexpr volatile uint8_t *IO_PORTB = &PORTB; -#else - static constexpr volatile uint8_t *IO_PINB = nullptr; - static constexpr volatile uint8_t *IO_DDRB = nullptr; - static constexpr volatile uint8_t *IO_PORTB = nullptr; +#if PORT_B_AVAILABLE + B0 = 0x10, + B1 = 0x11, + B2 = 0x12, + B3 = 0x13, + B4 = 0x14, + B5 = 0x15, +#if PIN_B6_AVAILABLE + B6 = 0x16, +#endif +#if PIN_B7_AVAILABLE + B7 = 0x17, +#endif #endif -#if AVR_DIP40 || AVR_DIP28 - static constexpr volatile uint8_t *IO_PINC = &PINC; - static constexpr volatile uint8_t *IO_DDRC = &DDRC; - static constexpr volatile uint8_t *IO_PORTC = &PORTC; -#else - static constexpr volatile uint8_t *IO_PINC = nullptr; - static constexpr volatile uint8_t *IO_DDRC = nullptr; - static constexpr volatile uint8_t *IO_PORTC = nullptr; +#if PORT_C_AVAILABLE + C0 = 0x20, + C1 = 0x21, + C2 = 0x22, + C3 = 0x23, + C4 = 0x24, + C5 = 0x25, + C6 = 0x26, +#if PIN_C7_AVAILABLE + C7 = 0x27, +#endif #endif -#if AVR_DIP40 || AVR_DIP28 - static constexpr volatile uint8_t *IO_PIND = &PIND; - static constexpr volatile uint8_t *IO_DDRD = &DDRD; - static constexpr volatile uint8_t *IO_PORTD = &PORTD; -#else - static constexpr volatile uint8_t *IO_PIND = nullptr; - static constexpr volatile uint8_t *IO_DDRD = nullptr; - static constexpr volatile uint8_t *IO_PORTD = nullptr; +#if PORT_D_AVAILABLE + D0 = 0x30, + D1 = 0x31, + D2 = 0x32, + D3 = 0x33, + D4 = 0x34, + D5 = 0x35, + D6 = 0x36, + D7 = 0x37, #endif +}; - enum class Pin - { -#if AVR_DIP40 - A0 = 0x00, - A1 = 0x01, - A2 = 0x02, - A3 = 0x03, - A4 = 0x04, - A5 = 0x05, - A6 = 0x06, - A7 = 0x07, +enum class Bus { +#if PORT_A_AVAILABLE + A = 0x00, #endif -#if AVR_DIP40 || AVR_DIP28 || AVR_DIP8 - B0 = 0x10, - B1 = 0x11, - B2 = 0x12, - B3 = 0x13, - B4 = 0x14, - B5 = 0x15, +#if PORT_B_AVAILABLE + B = 0x01, #endif -#if AVR_DIP40 || AVR_DIP28 - B6 = 0x16, - B7 = 0x17, - C0 = 0x20, - C1 = 0x21, - C2 = 0x22, - C3 = 0x23, - C4 = 0x24, - C5 = 0x25, - C6 = 0x26, +#if PORT_C_AVAILABLE + C = 0x02, #endif -#if AVR_DIP40 - C7 = 0x27, +#if PORT_D_AVAILABLE + D = 0x03, #endif -#if AVR_DIP40 || AVR_DIP28 - D0 = 0x30, - D1 = 0x31, - D2 = 0x32, - D3 = 0x33, - D4 = 0x34, - D5 = 0x35, - D6 = 0x36, - D7 = 0x37, -#endif - }; - - enum class Port - { -#if AVR_DIP40 - A = Pin::A0, -#endif -#if AVR_DIP40 || AVR_DIP28 || AVR_DIP8 - B = Pin::B0, -#endif -#if AVR_DIP40 || AVR_DIP28 - C = Pin::C0, - D = Pin::D0, -#endif - }; - - enum class Dir - { - IN, - OUT - }; - - enum class Type - { - DDR, - PIN, - PORT - }; - - constexpr volatile uint8_t* getPort( Pin enmPin, Type enmType ) - { - volatile uint8_t *vpui8Port = nullptr; - uint8_t ui8Port = static_cast( enmPin ) >> 4 & 0x0F; - - switch( ui8Port ) - { - case 0: - { - if( enmType == Type::DDR ) - vpui8Port = IO_DDRA; - else if( enmType == Type::PIN ) - vpui8Port = IO_PINA; - else if( enmType == Type::PORT ) - vpui8Port = IO_PORTA; - break; - } - - case 1: - { - if( enmType == Type::DDR ) - vpui8Port = IO_DDRB; - else if( enmType == Type::PIN ) - vpui8Port = IO_PINB; - else if( enmType == Type::PORT ) - vpui8Port = IO_PORTB; - break; - } - - case 2: - { - if( enmType == Type::DDR ) - vpui8Port = IO_DDRC; - else if( enmType == Type::PIN ) - vpui8Port = IO_PINC; - else if( enmType == Type::PORT ) - vpui8Port = IO_PORTC; - break; - } - - case 3: - { - if( enmType == Type::DDR ) - vpui8Port = IO_DDRD; - else if( enmType == Type::PIN ) - vpui8Port = IO_PIND; - else if( enmType == Type::PORT ) - vpui8Port = IO_PORTD; - break; - } - } - - return vpui8Port; - } - - constexpr uint8_t getPin( Pin enmPin ) - { - return static_cast( enmPin ) & 0x0F; - } -} - -/************************************************************************/ - -////////////////////////////////////////////////////////////////////////// -template -class InOutPin -{ -public: - static inline void direction( InOut::Dir enmDir ) __attribute__( (always_inline) ); - static inline void pullup( bool bPullup ) __attribute__( (always_inline) ); - - static inline bool read() __attribute__( (always_inline) ); - static inline void write( bool bValue ) __attribute__( (always_inline) ); - static inline void toggle() __attribute__( (always_inline) ); }; ////////////////////////////////////////////////////////////////////////// -template -inline void InOutPin::direction( InOut::Dir enmDir ) -{ - constexpr volatile uint8_t *vpui8DDR = InOut::getPort( enmPin, InOut::Type::DDR ); - constexpr uint8_t ui8Pin = InOut::getPin( enmPin ); +// Implementation details - if( enmDir == InOut::Dir::OUT ) - *vpui8DDR |= ( 1 << ui8Pin ); - else - *vpui8DDR &= ~( 1 << ui8Pin ); +namespace detail { + +#if PORT_A_AVAILABLE +static constexpr volatile uint8_t *PORT_A_DIR_REG = &DDRA; +static constexpr volatile uint8_t *PORT_A_OUTPUT_REG = &PORTA; +static constexpr volatile uint8_t *PORT_A_INPUT_REG = &PINA; +#else +static constexpr volatile uint8_t *PORT_A_DIR_REG = nullptr; +static constexpr volatile uint8_t *PORT_A_OUTPUT_REG = nullptr; +static constexpr volatile uint8_t *PORT_A_INPUT_REG = nullptr; +#endif + +#if PORT_B_AVAILABLE +static constexpr volatile uint8_t *PORT_B_DIR_REG = &DDRB; +static constexpr volatile uint8_t *PORT_B_OUTPUT_REG = &PORTB; +static constexpr volatile uint8_t *PORT_B_INPUT_REG = &PINB; +#else +static constexpr volatile uint8_t *PORT_B_DIR_REG = nullptr; +static constexpr volatile uint8_t *PORT_B_OUTPUT_REG = nullptr; +static constexpr volatile uint8_t *PORT_B_INPUT_REG = nullptr; +#endif + +#if PORT_C_AVAILABLE +static constexpr volatile uint8_t *PORT_C_DIR_REG = &DDRC; +static constexpr volatile uint8_t *PORT_C_OUTPUT_REG = &PORTC; +static constexpr volatile uint8_t *PORT_C_INPUT_REG = &PINC; +#else +static constexpr volatile uint8_t *PORT_C_DIR_REG = nullptr; +static constexpr volatile uint8_t *PORT_C_OUTPUT_REG = nullptr; +static constexpr volatile uint8_t *PORT_C_INPUT_REG = nullptr; +#endif + +#if PORT_D_AVAILABLE +static constexpr volatile uint8_t *PORT_D_DIR_REG = &DDRD; +static constexpr volatile uint8_t *PORT_D_OUTPUT_REG = &PORTD; +static constexpr volatile uint8_t *PORT_D_INPUT_REG = &PIND; +#else +static constexpr volatile uint8_t *PORT_D_DIR_REG = nullptr; +static constexpr volatile uint8_t *PORT_D_OUTPUT_REG = nullptr; +static constexpr volatile uint8_t *PORT_D_INPUT_REG = nullptr; +#endif + +static constexpr auto getBus(const P pin) +{ + // Upper 4 bits of pin encode which port this pin is on + uint8_t port = static_cast(pin) >> 4 & 0x0F; + return static_cast(port); } -////////////////////////////////////////////////////////////////////////// -template -inline void InOutPin::pullup( bool bPullup ) +static constexpr auto getPin(const P pin) { - write( bPullup ); + // Lower 4 bits of pin encode which pin bit it is + uint8_t pinBit = static_cast(pin) & 0x0F; + return pinBit; } -////////////////////////////////////////////////////////////////////////// -template -inline bool InOutPin::read() +static constexpr auto getDDR(const Bus bus) { - constexpr volatile uint8_t *vpui8PIN = InOut::getPort( enmPin, InOut::Type::PIN ); - constexpr uint8_t ui8Pin = InOut::getPin( enmPin ); - - if( *vpui8PIN >> ui8Pin & 1 ) - return true; - - return false; + switch (static_cast(bus)) { + case 0: // Bus::A + return PORT_A_DIR_REG; + case 1: // Bus::B + return PORT_B_DIR_REG; + case 2: // Bus::C + return PORT_C_DIR_REG; + case 3: // Bus::D + return PORT_D_DIR_REG; + } } -////////////////////////////////////////////////////////////////////////// -template -inline void InOutPin::write( bool bValue ) +static constexpr auto getPORT(const Bus bus) { - constexpr volatile uint8_t *vpui8PORT = InOut::getPort( enmPin, InOut::Type::PORT ); - constexpr uint8_t ui8Pin = InOut::getPin( enmPin ); - - if( bValue ) - *vpui8PORT |= ( 1 << ui8Pin ); - else - *vpui8PORT &= ~( 1 << ui8Pin ); + switch (static_cast(bus)) { + case 0: // Bus::A + return PORT_A_OUTPUT_REG; + case 1: // Bus::B + return PORT_B_OUTPUT_REG; + case 2: // Bus::C + return PORT_C_OUTPUT_REG; + case 3: // Bus::D + return PORT_D_OUTPUT_REG; + } } -////////////////////////////////////////////////////////////////////////// -template -inline void InOutPin::toggle() +static constexpr auto getPIN(const Bus bus) { - constexpr volatile uint8_t *vpui8PORT = InOut::getPort( enmPin, InOut::Type::PORT ); - constexpr uint8_t ui8Pin = InOut::getPin( enmPin ); - - *vpui8PORT ^= ( 1 << ui8Pin ); + switch (static_cast(bus)) { + case 0: // Bus::A + return PORT_A_INPUT_REG; + case 1: // Bus::B + return PORT_B_INPUT_REG; + case 2: // Bus::C + return PORT_C_INPUT_REG; + case 3: // Bus::D + return PORT_D_INPUT_REG; + } } -/************************************************************************/ +} // namespace detail ////////////////////////////////////////////////////////////////////////// -template -class InOutPort -{ -public: - static inline void direction( InOut::Dir enmDir ) __attribute__( (always_inline) ); - static inline void pullup( bool bPullup ) __attribute__( (always_inline) ); +// Zero overhead Pin object for pretty code without losing performance - static inline uint8_t read() __attribute__( (always_inline) ); - static inline void write( uint8_t ui8Value ) __attribute__( (always_inline) ); +template +class Pin { + public: + // Pin objects cannot be moved or copied + Pin(const Pin &) = delete; + Pin(Pin &&) = delete; + Pin &operator=(const Pin &) = delete; + Pin &operator=(Pin &&) = delete; + + // The only valid way to create a Pin object is with the default constructor + Pin() = default; + + static inline void dir(const Dir dir) FORCE_INLINE + { + constexpr auto bus = detail::getBus(pin); + constexpr auto dirReg = detail::getDDR(bus); + constexpr auto pinBit = detail::getPin(pin); + + if (dir == Dir::IN) + *dirReg &= ~(1 << pinBit); + else if (dir == Dir::OUT) + *dirReg |= (1 << pinBit); + } + + static inline void pullup(const bool enable) FORCE_INLINE + { + constexpr auto bus = detail::getBus(pin); + constexpr auto portReg = detail::getPORT(bus); + constexpr auto pinBit = detail::getPin(pin); + + if (enable) + *portReg |= (1 << pinBit); + else + *portReg &= ~(1 << pinBit); + } + + static inline void write(const bool value) FORCE_INLINE + { + constexpr auto bus = detail::getBus(pin); + constexpr auto portReg = detail::getPORT(bus); + constexpr auto pinBit = detail::getPin(pin); + + if (value) + *portReg |= (1 << pinBit); + else + *portReg &= ~(1 << pinBit); + } + + static inline void toggle() FORCE_INLINE + { + constexpr auto bus = detail::getBus(pin); + constexpr auto portReg = detail::getPORT(bus); + constexpr auto pinBit = detail::getPin(pin); + + *portReg ^= (1 << pinBit); + } + + static inline bool read() FORCE_INLINE + { + constexpr auto bus = detail::getBus(pin); + constexpr auto pinReg = detail::getPIN(bus); + constexpr auto pinBit = detail::getPin(pin); + + if (*pinReg >> pinBit & 1) + return true; + + return false; + } + + Pin &operator=(const bool value) FORCE_INLINE + { + write(value); + return *this; + } + + operator bool() const FORCE_INLINE + { + return read(); + } }; ////////////////////////////////////////////////////////////////////////// -template -inline void InOutPort::direction( InOut::Dir enmDir ) -{ - constexpr volatile uint8_t *vpui8DDR = InOut::getPort( static_cast( enmPort ), InOut::Type::DDR ); - *vpui8DDR = ( enmDir == InOut::Dir::OUT ) ? 0xFF : 0x00; -} +// Zero overhead Port object for pretty code without losing performance + +template +class Port { + public: + // Port objects cannot be moved or copied + Port(const Port &) = delete; + Port(Port &&) = delete; + Port &operator=(const Port &) = delete; + Port &operator=(Port &&) = delete; + + // The only valid way to create a Port object is with the default constructor + Port() = default; + + static inline void dir(const Dir dir) FORCE_INLINE + { + constexpr auto dirReg = detail::getDDR(port); + + if (dir == Dir::IN) + *dirReg = 0x00; + else if (dir == Dir::OUT) + *dirReg = 0xFF; + } + + static inline void pullup(const bool enable) FORCE_INLINE + { + constexpr auto portReg = detail::getPORT(port); + + if (enable) + *portReg = 0xFF; + else + *portReg = 0x00; + } + + static inline void write(const uint8_t value) FORCE_INLINE + { + constexpr auto portReg = detail::getPORT(port); + + *portReg = value; + } + + static inline void invert() FORCE_INLINE + { + constexpr auto portReg = detail::getPORT(port); + + *portReg = ~(*portReg); + } + + static inline uint8_t read() FORCE_INLINE + { + constexpr auto pinReg = detail::getPIN(port); + + return *pinReg; + } + + inline Port &operator=(const uint8_t value) FORCE_INLINE + { + write(value); + return *this; + } + + inline operator uint8_t() const FORCE_INLINE + { + return read(); + } +}; + +} // namespace io ////////////////////////////////////////////////////////////////////////// -template -inline void InOutPort::pullup( bool bPullup ) -{ - write( bPullup ? 0xFF : 0x00 ); -} -////////////////////////////////////////////////////////////////////////// -template -inline uint8_t InOutPort::read() -{ - constexpr volatile uint8_t *vpui8PIN = InOut::getPort( static_cast( enmPort ), InOut::Type::PIN ); - return *vpui8PIN; -} +#undef GPIO_32 +#undef GPIO_23 +#undef GPIO_6 -////////////////////////////////////////////////////////////////////////// -template -inline void InOutPort::write( uint8_t ui8Value ) -{ - constexpr volatile uint8_t *vpui8PORT = InOut::getPort( static_cast( enmPort ), InOut::Type::PORT ); - *vpui8PORT = ui8Value; -} +#undef PORT_A_AVAILABLE +#undef PORT_B_AVAILABLE +#undef PORT_C_AVAILABLE +#undef PORT_D_AVAILABLE -#endif \ No newline at end of file +#undef PIN_B6_AVAILABLE +#undef PIN_B7_AVAILABLE +#undef PIN_C7_AVAILABLE + +#undef FORCE_INLINE