/* Arduino DigitalIO Library * Copyright (C) 2013 by William Greiman * * This file is part of the Arduino DigitalIO Library * * This Library is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This Library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with the Arduino DigitalIO Library. If not, see * . */ /** * @file * @brief Fast Digital Pin functions * * @defgroup digitalPin Fast Pin I/O * @details Fast Digital I/O functions and template class. * @{ */ #ifndef DigitalPin_h #define DigitalPin_h #if defined(__AVR__) || defined(DOXYGEN) #include /** GpioPinMap type */ struct GpioPinMap_t { volatile uint8_t* pin; /**< address of PIN for this pin */ volatile uint8_t* ddr; /**< address of DDR for this pin */ volatile uint8_t* port; /**< address of PORT for this pin */ uint8_t mask; /**< bit mask for this pin */ }; /** Initializer macro. */ #define GPIO_PIN(reg, bit) {&PIN##reg, &DDR##reg, &PORT##reg, 1 << bit} // Include pin map for current board. #include "boards/GpioPinMap.h" //------------------------------------------------------------------------------ /** generate bad pin number error */ void badPinNumber(void) __attribute__((error("Pin number is too large or not a constant"))); //------------------------------------------------------------------------------ /** Check for valid pin number * @param[in] pin Number of pin to be checked. */ static inline __attribute__((always_inline)) void badPinCheck(uint8_t pin) { if (!__builtin_constant_p(pin) || pin >= NUM_DIGITAL_PINS) { badPinNumber(); } } //------------------------------------------------------------------------------ /** DDR register address * @param[in] pin Arduino pin number * @return register address */ static inline __attribute__((always_inline)) volatile uint8_t* ddrReg(uint8_t pin) { badPinCheck(pin); return GpioPinMap[pin].ddr; } //------------------------------------------------------------------------------ /** Bit mask for pin * @param[in] pin Arduino pin number * @return mask */ static inline __attribute__((always_inline)) uint8_t pinMask(uint8_t pin) { badPinCheck(pin); return GpioPinMap[pin].mask; } //------------------------------------------------------------------------------ /** PIN register address * @param[in] pin Arduino pin number * @return register address */ static inline __attribute__((always_inline)) volatile uint8_t* pinReg(uint8_t pin) { badPinCheck(pin); return GpioPinMap[pin].pin; } //------------------------------------------------------------------------------ /** PORT register address * @param[in] pin Arduino pin number * @return register address */ static inline __attribute__((always_inline)) volatile uint8_t* portReg(uint8_t pin) { badPinCheck(pin); return GpioPinMap[pin].port; } //------------------------------------------------------------------------------ /** Fast write helper. * @param[in] address I/O register address * @param[in] mask bit mask for pin * @param[in] level value for bit */ static inline __attribute__((always_inline)) void fastBitWriteSafe(volatile uint8_t* address, uint8_t mask, bool level) { uint8_t s; if (address > reinterpret_cast(0X3F)) { s = SREG; cli(); } if (level) { *address |= mask; } else { *address &= ~mask; } if (address > reinterpret_cast(0X3F)) { SREG = s; } } //------------------------------------------------------------------------------ /** Read pin value. * @param[in] pin Arduino pin number * @return value read */ static inline __attribute__((always_inline)) bool fastDigitalRead(uint8_t pin) { return *pinReg(pin) & pinMask(pin); } //------------------------------------------------------------------------------ /** Toggle a pin. * @param[in] pin Arduino pin number * * If the pin is in output mode toggle the pin level. * If the pin is in input mode toggle the state of the 20K pullup. */ static inline __attribute__((always_inline)) void fastDigitalToggle(uint8_t pin) { if (pinReg(pin) > reinterpret_cast(0X3F)) { // must write bit to high address port *pinReg(pin) = pinMask(pin); } else { // will compile to sbi and PIN register will not be read. *pinReg(pin) |= pinMask(pin); } } //------------------------------------------------------------------------------ /** Set pin value. * @param[in] pin Arduino pin number * @param[in] level value to write */ static inline __attribute__((always_inline)) void fastDigitalWrite(uint8_t pin, bool level) { fastBitWriteSafe(portReg(pin), pinMask(pin), level); } //------------------------------------------------------------------------------ /** Write the DDR register. * @param[in] pin Arduino pin number * @param[in] level value to write */ static inline __attribute__((always_inline)) void fastDdrWrite(uint8_t pin, bool level) { fastBitWriteSafe(ddrReg(pin), pinMask(pin), level); } //------------------------------------------------------------------------------ /** Set pin mode. * @param[in] pin Arduino pin number * @param[in] mode INPUT, OUTPUT, or INPUT_PULLUP. * * The internal pullup resistors will be enabled if mode is INPUT_PULLUP * and disabled if the mode is INPUT. */ static inline __attribute__((always_inline)) void fastPinMode(uint8_t pin, uint8_t mode) { fastDdrWrite(pin, mode == OUTPUT); if (mode != OUTPUT) { fastDigitalWrite(pin, mode == INPUT_PULLUP); } } #else // defined(__AVR__) #if defined(CORE_TEENSY) //------------------------------------------------------------------------------ /** read pin value * @param[in] pin Arduino pin number * @return value read */ static inline __attribute__((always_inline)) bool fastDigitalRead(uint8_t pin) { return *portInputRegister(pin); } //------------------------------------------------------------------------------ /** Set pin value * @param[in] pin Arduino pin number * @param[in] level value to write */ static inline __attribute__((always_inline)) void fastDigitalWrite(uint8_t pin, bool value) { if (value) { *portSetRegister(pin) = 1; } else { *portClearRegister(pin) = 1; } } #elif defined(__SAM3X8E__) || defined(__SAM3X8H__) //------------------------------------------------------------------------------ /** read pin value * @param[in] pin Arduino pin number * @return value read */ static inline __attribute__((always_inline)) bool fastDigitalRead(uint8_t pin) { return g_APinDescription[pin].pPort->PIO_PDSR & g_APinDescription[pin].ulPin; } //------------------------------------------------------------------------------ /** Set pin value * @param[in] pin Arduino pin number * @param[in] level value to write */ static inline __attribute__((always_inline)) void fastDigitalWrite(uint8_t pin, bool value) { if (value) { g_APinDescription[pin].pPort->PIO_SODR = g_APinDescription[pin].ulPin; } else { g_APinDescription[pin].pPort->PIO_CODR = g_APinDescription[pin].ulPin; } } #elif defined(ESP8266) //------------------------------------------------------------------------------ /** Set pin value * @param[in] pin Arduino pin number * @param[in] val value to write */ static inline __attribute__((always_inline)) void fastDigitalWrite(uint8_t pin, uint8_t val) { if (pin < 16) { if (val) { GPOS = (1 << pin); } else { GPOC = (1 << pin); } } else if (pin == 16) { if (val) { GP16O |= 1; } else { GP16O &= ~1; } } } //------------------------------------------------------------------------------ /** Read pin value * @param[in] pin Arduino pin number * @return value read */ static inline __attribute__((always_inline)) bool fastDigitalRead(uint8_t pin) { if (pin < 16) { return GPIP(pin); } else if (pin == 16) { return GP16I & 0x01; } return 0; } #else // CORE_TEENSY //------------------------------------------------------------------------------ inline void fastDigitalWrite(uint8_t pin, bool value) { digitalWrite(pin, value); } //------------------------------------------------------------------------------ inline bool fastDigitalRead(uint8_t pin) { return digitalRead(pin); } #endif // CORE_TEENSY //------------------------------------------------------------------------------ inline void fastDigitalToggle(uint8_t pin) { fastDigitalWrite(pin, !fastDigitalRead(pin)); } //------------------------------------------------------------------------------ inline void fastPinMode(uint8_t pin, uint8_t mode) { pinMode(pin, mode); } #endif // __AVR__ //------------------------------------------------------------------------------ /** set pin configuration * @param[in] pin Arduino pin number * @param[in] mode mode INPUT or OUTPUT. * @param[in] level If mode is output, set level high/low. * If mode is input, enable or disable the pin's 20K pullup. */ #define fastPinConfig(pin, mode, level)\ {fastPinMode(pin, mode); fastDigitalWrite(pin, level);} //============================================================================== /** * @class DigitalPin * @brief Fast digital port I/O */ template class DigitalPin { public: //---------------------------------------------------------------------------- /** Constructor */ DigitalPin() {} //---------------------------------------------------------------------------- /** Asignment operator. * @param[in] value If true set the pin's level high else set the * pin's level low. * * @return This DigitalPin instance. */ inline DigitalPin & operator = (bool value) __attribute__((always_inline)) { write(value); return *this; } //---------------------------------------------------------------------------- /** Parenthesis operator. * @return Pin's level */ inline operator bool () const __attribute__((always_inline)) { return read(); } //---------------------------------------------------------------------------- /** Set pin configuration. * @param[in] mode: INPUT or OUTPUT. * @param[in] level If mode is OUTPUT, set level high/low. * If mode is INPUT, enable or disable the pin's 20K pullup. */ inline __attribute__((always_inline)) void config(uint8_t mode, bool level) { fastPinConfig(PinNumber, mode, level); } //---------------------------------------------------------------------------- /** * Set pin level high if output mode or enable 20K pullup if input mode. */ inline __attribute__((always_inline)) void high() {write(true);} //---------------------------------------------------------------------------- /** * Set pin level low if output mode or disable 20K pullup if input mode. */ inline __attribute__((always_inline)) void low() {write(false);} //---------------------------------------------------------------------------- /** * Set pin mode. * @param[in] mode: INPUT, OUTPUT, or INPUT_PULLUP. * * The internal pullup resistors will be enabled if mode is INPUT_PULLUP * and disabled if the mode is INPUT. */ inline __attribute__((always_inline)) void mode(uint8_t mode) { fastPinMode(PinNumber, mode); } //---------------------------------------------------------------------------- /** @return Pin's level. */ inline __attribute__((always_inline)) bool read() const { return fastDigitalRead(PinNumber); } //---------------------------------------------------------------------------- /** Toggle a pin. * * If the pin is in output mode toggle the pin's level. * If the pin is in input mode toggle the state of the 20K pullup. */ inline __attribute__((always_inline)) void toggle() { fastDigitalToggle(PinNumber); } //---------------------------------------------------------------------------- /** Write the pin's level. * @param[in] value If true set the pin's level high else set the * pin's level low. */ inline __attribute__((always_inline)) void write(bool value) { fastDigitalWrite(PinNumber, value); } }; #endif // DigitalPin_h /** @} */