#pragma once #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 HARDWARE_TOGGLE \ defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega168A__) || \ defined(__AVR_ATmega328P__) || 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)) ////////////////////////////////////////////////////////////////////////// // Library implementation 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 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 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 PORT_D_AVAILABLE D0 = 0x30, D1 = 0x31, D2 = 0x32, D3 = 0x33, D4 = 0x34, D5 = 0x35, D6 = 0x36, D7 = 0x37, #endif }; enum class Bus { #if PORT_A_AVAILABLE A = 0x00, #endif #if PORT_B_AVAILABLE B = 0x01, #endif #if PORT_C_AVAILABLE C = 0x02, #endif #if PORT_D_AVAILABLE D = 0x03, #endif }; ////////////////////////////////////////////////////////////////////////// // Implementation details 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); } static constexpr auto getPin(const P pin) { // Lower 4 bits of pin encode which pin bit it is uint8_t pinBit = static_cast(pin) & 0x0F; return pinBit; } static constexpr auto getDDR(const Bus bus) { 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; } } static constexpr auto getPORT(const Bus bus) { 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; } } static constexpr auto getPIN(const Bus bus) { 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 ////////////////////////////////////////////////////////////////////////// // Zero overhead Pin object for pretty code without losing performance 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 { #if HARDWARE_TOGGLE constexpr auto bus = detail::getBus(pin); constexpr auto pinReg = detail::getPIN(bus); constexpr auto pinBit = detail::getPin(pin); *pinReg |= (1 << pinBit); #else constexpr auto bus = detail::getBus(pin); constexpr auto portReg = detail::getPORT(bus); constexpr auto pinBit = detail::getPin(pin); *portReg ^= (1 << pinBit); #endif } 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(); } }; ////////////////////////////////////////////////////////////////////////// // 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 { #if HARDWARE_TOGGLE constexpr auto pinReg = detail::getPIN(port); *pinReg = 0xFF; #else constexpr auto portReg = detail::getPORT(port); *portReg = ~(*portReg); #endif } 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 ////////////////////////////////////////////////////////////////////////// #undef GPIO_32 #undef GPIO_23 #undef GPIO_6 #undef PORT_A_AVAILABLE #undef PORT_B_AVAILABLE #undef PORT_C_AVAILABLE #undef PORT_D_AVAILABLE #undef PIN_B6_AVAILABLE #undef PIN_B7_AVAILABLE #undef PIN_C7_AVAILABLE #undef FORCE_INLINE