#pragma once #include #include #include ////////////////////////////////////////////////////////////////////////// // Preprocessor defines #if defined(__AVR_ATmega32__) || defined(__AVR_ATmega32A__) || defined(__AVR_ATmega644P__) || \ defined(__AVR_ATmega1284P__) #define GPIO_32 #endif #if defined(__AVR_ATmega8__) || defined(__AVR_ATmega8A__) || defined(__AVR_ATmega168A__) || defined(__AVR_ATmega328P__) #define GPIO_23 #endif #if defined(__AVR_ATtiny13A__) || defined(__AVR_ATtiny85__) #define GPIO_6 #endif #if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega168A__) || \ defined(__AVR_ATmega328P__) || defined(__AVR_ATtiny13A__) || defined(__AVR_ATtiny85__) #define HARDWARE_TOGGLE #endif #ifdef GPIO_32 #define PORT_A_AVAILABLE #endif #if defined(GPIO_32) || defined(GPIO_23) || defined(GPIO_6) #define PORT_B_AVAILABLE #endif #if defined(GPIO_32) || defined(GPIO_23) #define PORT_C_AVAILABLE #define PORT_D_AVAILABLE #define PIN_B6_AVAILABLE #define PIN_B7_AVAILABLE #endif #if defined(GPIO_32) #define PIN_C7_AVAILABLE #endif #define FORCE_INLINE __attribute__((always_inline)) ////////////////////////////////////////////////////////////////////////// // Library implementation namespace io { enum class Dir { IN, OUT }; enum class P { #ifdef PORT_A_AVAILABLE A0 = 0x00, A1 = 0x01, A2 = 0x02, A3 = 0x03, A4 = 0x04, A5 = 0x05, A6 = 0x06, A7 = 0x07, #endif #ifdef PORT_B_AVAILABLE B0 = 0x10, B1 = 0x11, B2 = 0x12, B3 = 0x13, B4 = 0x14, B5 = 0x15, #ifdef PIN_B6_AVAILABLE B6 = 0x16, #endif #ifdef PIN_B7_AVAILABLE B7 = 0x17, #endif #endif #ifdef PORT_C_AVAILABLE C0 = 0x20, C1 = 0x21, C2 = 0x22, C3 = 0x23, C4 = 0x24, C5 = 0x25, C6 = 0x26, #ifdef PIN_C7_AVAILABLE C7 = 0x27, #endif #endif #ifdef PORT_D_AVAILABLE D0 = 0x30, D1 = 0x31, D2 = 0x32, D3 = 0x33, D4 = 0x34, D5 = 0x35, D6 = 0x36, D7 = 0x37, #endif }; enum class Bus { #ifdef PORT_A_AVAILABLE A = 0x00, #endif #ifdef PORT_B_AVAILABLE B = 0x01, #endif #ifdef PORT_C_AVAILABLE C = 0x02, #endif #ifdef PORT_D_AVAILABLE D = 0x03, #endif }; ////////////////////////////////////////////////////////////////////////// // Implementation details namespace detail { /* 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 */ #ifdef PORT_A_AVAILABLE static constexpr uintptr_t PORT_A_DIR_REG_ADDR = _SFR_ADDR(DDRA); static constexpr uintptr_t PORT_A_OUTPUT_REG_ADDR = _SFR_ADDR(PORTA); static constexpr uintptr_t PORT_A_INPUT_REG_ADDR = _SFR_ADDR(PINA); #else static constexpr uintptr_t PORT_A_DIR_REG_ADDR = 0; static constexpr uintptr_t PORT_A_OUTPUT_REG_ADDR = 0; static constexpr uintptr_t PORT_A_INPUT_REG_ADDR = 0; #endif #ifdef PORT_B_AVAILABLE static constexpr uintptr_t PORT_B_DIR_REG_ADDR = _SFR_ADDR(DDRB); static constexpr uintptr_t PORT_B_OUTPUT_REG_ADDR = _SFR_ADDR(PORTB); static constexpr uintptr_t PORT_B_INPUT_REG_ADDR = _SFR_ADDR(PINB); #else static constexpr uintptr_t PORT_B_DIR_REG_ADDR = 0; static constexpr uintptr_t PORT_B_OUTPUT_REG_ADDR = 0; static constexpr uintptr_t PORT_B_INPUT_REG_ADDR = 0; #endif #ifdef PORT_C_AVAILABLE static constexpr uintptr_t PORT_C_DIR_REG_ADDR = _SFR_ADDR(DDRC); static constexpr uintptr_t PORT_C_OUTPUT_REG_ADDR = _SFR_ADDR(PORTC); static constexpr uintptr_t PORT_C_INPUT_REG_ADDR = _SFR_ADDR(PINC); #else static constexpr uintptr_t PORT_C_DIR_REG_ADDR = 0; static constexpr uintptr_t PORT_C_OUTPUT_REG_ADDR = 0; static constexpr uintptr_t PORT_C_INPUT_REG_ADDR = 0; #endif #ifdef PORT_D_AVAILABLE static constexpr uintptr_t PORT_D_DIR_REG_ADDR = _SFR_ADDR(DDRD); static constexpr uintptr_t PORT_D_OUTPUT_REG_ADDR = _SFR_ADDR(PORTD); static constexpr uintptr_t PORT_D_INPUT_REG_ADDR = _SFR_ADDR(PIND); #else static constexpr uintptr_t PORT_D_DIR_REG_ADDR = 0; static constexpr uintptr_t PORT_D_OUTPUT_REG_ADDR = 0; static constexpr uintptr_t PORT_D_INPUT_REG_ADDR = 0; #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 getPinBit(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_ADDR; case 1: // Bus::B return PORT_B_DIR_REG_ADDR; case 2: // Bus::C return PORT_C_DIR_REG_ADDR; case 3: // Bus::D return PORT_D_DIR_REG_ADDR; } } static constexpr auto getPORT(const Bus bus) { switch (static_cast(bus)) { case 0: // Bus::A return PORT_A_OUTPUT_REG_ADDR; case 1: // Bus::B return PORT_B_OUTPUT_REG_ADDR; case 2: // Bus::C return PORT_C_OUTPUT_REG_ADDR; case 3: // Bus::D return PORT_D_OUTPUT_REG_ADDR; } } static constexpr auto getPIN(const Bus bus) { switch (static_cast(bus)) { case 0: // Bus::A return PORT_A_INPUT_REG_ADDR; case 1: // Bus::B return PORT_B_INPUT_REG_ADDR; case 2: // Bus::C return PORT_C_INPUT_REG_ADDR; case 3: // Bus::D return PORT_D_INPUT_REG_ADDR; } } using reg_ptr_t = volatile uint8_t *; template static inline reg_ptr_t getRegPtr() { return reinterpret_cast(Address); } } // 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 pinBit = detail::getPinBit(pin); detail::reg_ptr_t dirRegPtr = detail::getRegPtr(); if (dir == Dir::IN) *dirRegPtr &= ~(1 << pinBit); else if (dir == Dir::OUT) *dirRegPtr |= (1 << pinBit); } static inline void pullup(const bool enable) FORCE_INLINE { constexpr auto bus = detail::getBus(pin); constexpr auto pinBit = detail::getPinBit(pin); detail::reg_ptr_t portRegPtr = detail::getRegPtr(); if (enable) *portRegPtr |= (1 << pinBit); else *portRegPtr &= ~(1 << pinBit); } static inline void write(const bool value) FORCE_INLINE { constexpr auto bus = detail::getBus(pin); constexpr auto pinBit = detail::getPinBit(pin); detail::reg_ptr_t portRegPtr = detail::getRegPtr(); if (value) *portRegPtr |= (1 << pinBit); else *portRegPtr &= ~(1 << pinBit); } static inline void toggle() FORCE_INLINE { constexpr auto bus = detail::getBus(pin); constexpr auto pinBit = detail::getPinBit(pin); #ifdef HARDWARE_TOGGLE detail::reg_ptr_t pinRegPtr = detail::getRegPtr(); *pinRegPtr |= (1 << pinBit); #else detail::reg_ptr_t portRegPtr = detail::getRegPtr(); *portRegPtr ^= (1 << pinBit); #endif } static inline bool read() FORCE_INLINE { constexpr auto bus = detail::getBus(pin); constexpr auto pinBit = detail::getPinBit(pin); detail::reg_ptr_t pinRegPtr = detail::getRegPtr(); if (*pinRegPtr >> 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 { detail::reg_ptr_t dirRegPtr = detail::getRegPtr(); if (dir == Dir::IN) *dirRegPtr = 0x00; else if (dir == Dir::OUT) *dirRegPtr = 0xFF; } static inline void pullup(const bool enable) FORCE_INLINE { detail::reg_ptr_t portRegPtr = detail::getRegPtr(); if (enable) *portRegPtr = 0xFF; else *portRegPtr = 0x00; } static inline void write(const uint8_t value) FORCE_INLINE { detail::reg_ptr_t portRegPtr = detail::getRegPtr(); *portRegPtr = value; } static inline void invert() FORCE_INLINE { #ifdef HARDWARE_TOGGLE detail::reg_ptr_t pinRegPtr = detail::getRegPtr(); *pinRegPtr = 0xFF; #else detail::reg_ptr_t portRegPtr = detail::getRegPtr(); *portRegPtr = ~(*portRegPtr); #endif } static inline uint8_t read() FORCE_INLINE { detail::reg_ptr_t pinRegPtr = detail::getRegPtr(); return *pinRegPtr; } 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