diff --git a/Adafruit_SPITFT.cpp b/Adafruit_SPITFT.cpp index 559c431..d48750d 100644 --- a/Adafruit_SPITFT.cpp +++ b/Adafruit_SPITFT.cpp @@ -1,205 +1,438 @@ /*! -* @file Adafruit_SPITFT.cpp -* -* @mainpage Adafruit SPI TFT Displays -* -* @section intro_sec Introduction -* This is our library for SPI TFT Displays with address windows and 16-bit -* color (e.g. ILI9341, HX8357D, ST7735...) -* -* These displays use SPI to communicate, 4 or 5 pins are required to -* interface (RST is optional) -* Adafruit invests time and resources providing this open source code, -* please support Adafruit and open-source hardware by purchasing -* products from Adafruit! + * @file Adafruit_SPITFT.cpp + * + * @mainpage Adafruit SPI TFT Displays (and some others) + * + * @section intro_sec Introduction + * + * Part of Adafruit's GFX graphics library. Originally this class was + * written to handle a range of color TFT displays connected via SPI, + * but over time this library and some display-specific subclasses have + * mutated to include some color OLEDs as well as parallel-interfaced + * displays. The name's been kept for the sake of older code. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! -* Written by Limor Fried/Ladyada for Adafruit Industries. -* MIT license, all text above must be included in any redistribution -* @section dependencies Dependencies -* -* This library depends on -* Adafruit_GFX being present on your system. Please make sure you have -* installed the latest version before using this library. -* -* @section author Author -* -* Written by Limor "ladyada" Fried for Adafruit Industries. -* -* @section license License -* -* BSD license, all text here must be included in any redistribution. -* -*/ + * @section dependencies Dependencies + * + * This library depends on + * Adafruit_GFX being present on your system. Please make sure you have + * installed the latest version before using this library. + * + * @section author Author + * + * Written by Limor "ladyada" Fried for Adafruit Industries, + * with contributions from the open source community. + * + * @section license License + * + * BSD license, all text here must be included in any redistribution. + */ -#if !defined(__AVR_ATtiny85__) // NOT A CHANCE of this stuff working on ATtiny +#if !defined(__AVR_ATtiny85__) // Not for ATtiny, at all #include "Adafruit_SPITFT.h" -#if !defined(ARDUINO_STM32_FEATHER) - #include "pins_arduino.h" -#endif -#if !defined(ARDUINO_STM32_FEATHER) && !defined(RASPI) - #include "wiring_private.h" -#endif -#include -#ifdef PORT_IOBUS +#if defined(PORT_IOBUS) // On SAMD21, redefine digitalPinToPort() to use the slightly-faster // PORT_IOBUS rather than PORT (not needed on SAMD51). #undef digitalPinToPort #define digitalPinToPort(P) (&(PORT_IOBUS->Group[g_APinDescription[P].ulPort])) -#endif +#endif // end PORT_IOBUS -#include "Adafruit_SPITFT_Macros.h" - -#ifdef USE_SPI_DMA +#if defined(USE_SPI_DMA) #include #include // memalign() function // DMA transfer-in-progress indicator and callback -static volatile boolean dma_busy = false; +static volatile bool dma_busy = false; static void dma_callback(Adafruit_ZeroDMA *dma) { dma_busy = false; } +#endif // end USE_SPI_DMA -#endif // USE_SPI_DMA +// Possible values for Adafruit_SPITFT.connection: +#define TFT_HARD_SPI 0 ///< Display interface = hardware SPI +#define TFT_SOFT_SPI 1 ///< Display interface = software SPI +#define TFT_PARALLEL 2 ///< Display interface = 8- or 16-bit parallel + + +// CONSTRUCTORS ------------------------------------------------------------ -/**************************************************************************/ /*! - @brief Pass 8-bit (each) R,G,B, get back 16-bit packed color - This function converts 8-8-8 RGB data to 16-bit 5-6-5 - @param red Red 8 bit color - @param green Green 8 bit color - @param blue Blue 8 bit color - @return Unsigned 16-bit down-sampled color in 5-6-5 format + @brief Adafruit_SPITFT constructor for software (bitbang) SPI. + @param w Display width in pixels at default rotation setting (0). + @param h Display height in pixels at default rotation setting (0). + @param cs Arduino pin # for chip-select (-1 if unused, tie CS low). + @param dc Arduino pin # for data/command select (required). + @param mosi Arduino pin # for bitbang SPI MOSI signal (required). + @param sck Arduino pin # for bitbang SPI SCK signal (required). + @param rst Arduino pin # for display reset (optional, display reset + can be tied to MCU reset, default of -1 means unused). + @param miso Arduino pin # for bitbang SPI MISO signal (optional, + -1 default, many displays don't support SPI read). + @return Adafruit_SPITFT object. + @note Output pins are not initialized; application typically will need + to call subclass' begin() function, which in turn calls this + library's initSPI() function to initialize pins. */ -/**************************************************************************/ -uint16_t Adafruit_SPITFT::color565(uint8_t red, uint8_t green, uint8_t blue) { - return ((red & 0xF8) << 8) | ((green & 0xFC) << 3) | ((blue & 0xF8) >> 3); -} - - -/**************************************************************************/ -/*! - @brief Instantiate Adafruit SPI display driver with software SPI - @param w Display width in pixels - @param h Display height in pixels - @param cs Chip select pin # - @param dc Data/Command pin # - @param mosi SPI MOSI pin # - @param sclk SPI Clock pin # - @param rst Reset pin # (optional, pass -1 if unused) - @param miso SPI MISO pin # (optional, pass -1 if unused) -*/ -/**************************************************************************/ Adafruit_SPITFT::Adafruit_SPITFT(uint16_t w, uint16_t h, - int8_t cs, int8_t dc, int8_t mosi, - int8_t sclk, int8_t rst, int8_t miso) - : Adafruit_GFX(w, h) { - _cs = cs; - _dc = dc; - _rst = rst; - _sclk = sclk; - _mosi = mosi; - _miso = miso; - _freq = 0; -#ifdef USE_FAST_PINIO - dcport = (RwReg *)portOutputRegister(digitalPinToPort(dc)); - dcpinmask = digitalPinToBitMask(dc); - clkport = (RwReg *)portOutputRegister(digitalPinToPort(sclk)); - clkpinmask = digitalPinToBitMask(sclk); - mosiport = (RwReg *)portOutputRegister(digitalPinToPort(mosi)); - mosipinmask = digitalPinToBitMask(mosi); - if(miso >= 0){ - misoport = (RwReg *)portInputRegister(digitalPinToPort(miso)); - misopinmask = digitalPinToBitMask(miso); - } else { - misoport = 0; - misopinmask = 0; - } + int8_t cs, int8_t dc, int8_t mosi, int8_t sck, int8_t rst, int8_t miso) : + Adafruit_GFX(w, h), connection(TFT_SOFT_SPI), _rst(rst), _cs(cs), _dc(dc) { + swspi._sck = sck; + swspi._mosi = mosi; + swspi._miso = miso; +#if defined(USE_FAST_PINIO) + #if defined(HAS_PORT_SET_CLR) + #if defined(CORE_TEENSY) + dcPortSet = portSetRegister(dc); + dcPortClr = portClearRegister(dc); + swspi.sckPortSet = portSetRegister(sck); + swspi.sckPortClr = portClearRegister(sck); + swspi.mosiPortSet = portSetRegister(mosi); + swspi.mosiPortClr = portClearRegister(mosi); if(cs >= 0) { - csport = (RwReg *)portOutputRegister(digitalPinToPort(cs)); - cspinmask = digitalPinToBitMask(cs); + csPortSet = portSetRegister(cs); + csPortClr = portClearRegister(cs); + } else { + csPortSet = dcPortSet; + csPortClr = dcPortClr; + } + if(miso >= 0) { + swspi.misoPort = portInputRegister(miso); + } else { + swspi.misoPort = portInputRegister(dc); + } + #else // !CORE_TEENSY + dcPinMask =digitalPinToBitMask(dc); + swspi.sckPinMask =digitalPinToBitMask(sck); + swspi.mosiPinMask=digitalPinToBitMask(mosi); + dcPortSet =&(PORT->Group[g_APinDescription[dc].ulPort].OUTSET.reg); + dcPortClr =&(PORT->Group[g_APinDescription[dc].ulPort].OUTCLR.reg); + swspi.sckPortSet =&(PORT->Group[g_APinDescription[sck].ulPort].OUTSET.reg); + swspi.sckPortClr =&(PORT->Group[g_APinDescription[sck].ulPort].OUTCLR.reg); + swspi.mosiPortSet=&(PORT->Group[g_APinDescription[mosi].ulPort].OUTSET.reg); + swspi.mosiPortClr=&(PORT->Group[g_APinDescription[mosi].ulPort].OUTCLR.reg); + if(cs >= 0) { + csPinMask = digitalPinToBitMask(cs); + csPortSet = &(PORT->Group[g_APinDescription[cs].ulPort].OUTSET.reg); + csPortClr = &(PORT->Group[g_APinDescription[cs].ulPort].OUTCLR.reg); } else { // No chip-select line defined; might be permanently tied to GND. // Assign a valid GPIO register (though not used for CS), and an // empty pin bitmask...the nonsense bit-twiddling might be faster // than checking _cs and possibly branching. - csport = dcport; - cspinmask = 0; + csPortSet = dcPortSet; + csPortClr = dcPortClr; + csPinMask = 0; } -#endif -} - -/**************************************************************************/ -/*! - @brief Instantiate Adafruit SPI display driver with hardware SPI - @param w Display width in pixels - @param h Display height in pixels - @param cs Chip select pin # - @param dc Data/Command pin # - @param rst Reset pin # (optional, pass -1 if unused) -*/ -/**************************************************************************/ -Adafruit_SPITFT::Adafruit_SPITFT(uint16_t w, uint16_t h, - int8_t cs, int8_t dc, int8_t rst) - : Adafruit_SPITFT(w, h, &SPI, cs, dc, rst) -{ - // We just call the hardware SPI instantiator with the default SPI device (&SPI) -} - -/**************************************************************************/ -/*! - @brief Instantiate Adafruit SPI display driver with hardware SPI - @param w Display width in pixels - @param h Display height in pixels - @param spiClass A pointer to an SPI hardware interface, e.g. &SPI1 - @param cs Chip select pin # - @param dc Data/Command pin # - @param rst Reset pin # (optional, pass -1 if unused) -*/ -/**************************************************************************/ -Adafruit_SPITFT::Adafruit_SPITFT(uint16_t w, uint16_t h, SPIClass *spiClass, - int8_t cs, int8_t dc, int8_t rst) - : Adafruit_GFX(w, h) { - _cs = cs; - _dc = dc; - _rst = rst; - _spi = spiClass; - _sclk = -1; - _mosi = -1; - _miso = -1; - _freq = 0; -#ifdef USE_FAST_PINIO - clkport = 0; - clkpinmask = 0; - mosiport = 0; - mosipinmask = 0; - misoport = 0; - misopinmask = 0; - dcport = (RwReg *)portOutputRegister(digitalPinToPort(dc)); - dcpinmask = digitalPinToBitMask(dc); - if(cs >= 0) { - csport = (RwReg *)portOutputRegister(digitalPinToPort(cs)); - cspinmask = digitalPinToBitMask(cs); + if(miso >= 0) { + swspi.misoPinMask=digitalPinToBitMask(miso); + swspi.misoPort =(PORTreg_t)portInputRegister(digitalPinToPort(miso)); } else { - // See notes in prior constructor. - csport = dcport; - cspinmask = 0; + swspi.misoPinMask=0; + swspi.misoPort =(PORTreg_t)portInputRegister(digitalPinToPort(dc)); } -#endif + #endif // end !CORE_TEENSY + #else // !HAS_PORT_SET_CLR + dcPort =(PORTreg_t)portOutputRegister(digitalPinToPort(dc)); + dcPinMaskSet =digitalPinToBitMask(dc); + swspi.sckPort =(PORTreg_t)portOutputRegister(digitalPinToPort(sck)); + swspi.sckPinMaskSet =digitalPinToBitMask(sck); + swspi.mosiPort =(PORTreg_t)portOutputRegister(digitalPinToPort(mosi)); + swspi.mosiPinMaskSet=digitalPinToBitMask(mosi); + if(cs >= 0) { + csPort = (PORTreg_t)portOutputRegister(digitalPinToPort(cs)); + csPinMaskSet = digitalPinToBitMask(cs); + } else { + // No chip-select line defined; might be permanently tied to GND. + // Assign a valid GPIO register (though not used for CS), and an + // empty pin bitmask...the nonsense bit-twiddling might be faster + // than checking _cs and possibly branching. + csPort = dcPort; + csPinMaskSet = 0; + } + if(miso >= 0) { + swspi.misoPort =(PORTreg_t)portInputRegister(digitalPinToPort(miso)); + swspi.misoPinMask=digitalPinToBitMask(cs); + } else { + swspi.misoPort =(PORTreg_t)portInputRegister(digitalPinToPort(dc)); + swspi.misoPinMask=0; + } + csPinMaskClr = ~csPinMaskSet; + dcPinMaskClr = ~dcPinMaskSet; + swspi.sckPinMaskClr = ~swspi.sckPinMaskSet; + swspi.mosiPinMaskClr = ~swspi.mosiPinMaskSet; + #endif // !end HAS_PORT_SET_CLR +#endif // end USE_FAST_PINIO } - -/**************************************************************************/ /*! - @brief Initialiaze the SPI interface (hardware or software) - @param freq The desired maximum SPI hardware clock frequency + @brief Adafruit_SPITFT constructor for hardware SPI using the board's + default SPI peripheral. + @param w Display width in pixels at default rotation setting (0). + @param h Display height in pixels at default rotation setting (0). + @param cs Arduino pin # for chip-select (-1 if unused, tie CS low). + @param dc Arduino pin # for data/command select (required). + @param rst Arduino pin # for display reset (optional, display reset + can be tied to MCU reset, default of -1 means unused). + @return Adafruit_SPITFT object. + @note Output pins are not initialized; application typically will need + to call subclass' begin() function, which in turn calls this + library's initSPI() function to initialize pins. */ -/**************************************************************************/ -void Adafruit_SPITFT::initSPI(uint32_t freq) { - _freq = freq; +Adafruit_SPITFT::Adafruit_SPITFT(uint16_t w, uint16_t h, int8_t cs, + int8_t dc, int8_t rst) : Adafruit_SPITFT(w, h, &SPI, cs, dc, rst) { + // This just invokes the hardware SPI constructor below, + // passing the default SPI device (&SPI). +} - // Control Pins +/*! + @brief Adafruit_SPITFT constructor for hardware SPI using a specific + SPI peripheral. + @param w Display width in pixels at default rotation (0). + @param h Display height in pixels at default rotation (0). + @param spiClass Pointer to SPIClass type (e.g. &SPI1). + @param cs Arduino pin # for chip-select (-1 if unused, tie CS low). + @param dc Arduino pin # for data/command select (required). + @param rst Arduino pin # for display reset (optional, display reset + can be tied to MCU reset, default of -1 means unused). + @return Adafruit_SPITFT object. + @note Output pins are not initialized; application typically will need + to call subclass' begin() function, which in turn calls this + library's initSPI() function to initialize pins. +*/ +Adafruit_SPITFT::Adafruit_SPITFT(uint16_t w, uint16_t h, SPIClass *spiClass, + int8_t cs, int8_t dc, int8_t rst) : Adafruit_GFX(w, h), + connection(TFT_HARD_SPI), _rst(rst), _cs(cs), _dc(dc) { + hwspi._spi = spiClass; +#if defined(USE_FAST_PINIO) + #if defined(HAS_PORT_SET_CLR) + #if defined(CORE_TEENSY) + dcPortSet =portSetRegister(dc); + dcPortClr =portClearRegister(dc); + if(cs >= 0) { + csPinMask=digitalPinToBitMask(cs); + csPortSet=portSetRegister(cs); + csPortClr=portClearRegister(dc); + } else { // see comments below + csPortSet = dcPortSet; + csPortClr = dcPortClr; + csPinMask = 0; + } + #else // !CORE_TEENSY + dcPinMask =digitalPinToBitMask(dc); + dcPortSet =&(PORT->Group[g_APinDescription[dc].ulPort].OUTSET.reg); + dcPortClr =&(PORT->Group[g_APinDescription[dc].ulPort].OUTCLR.reg); + if(cs >= 0) { + csPinMask=digitalPinToBitMask(cs); + csPortSet=&(PORT->Group[g_APinDescription[cs].ulPort].OUTSET.reg); + csPortClr=&(PORT->Group[g_APinDescription[cs].ulPort].OUTCLR.reg); + } else { + // No chip-select line defined; might be permanently tied to GND. + // Assign a valid GPIO register (though not used for CS), and an + // empty pin bitmask...the nonsense bit-twiddling might be faster + // than checking _cs and possibly branching. + csPortSet = dcPortSet; + csPortClr = dcPortClr; + csPinMask = 0; + } + #endif // end !CORE_TEENSY + #else // !HAS_PORT_SET_CLR + dcPort = (PORTreg_t)portOutputRegister(digitalPinToPort(dc)); + dcPinMaskSet = digitalPinToBitMask(dc); + if(cs >= 0) { + csPort = (PORTreg_t)portOutputRegister(digitalPinToPort(cs)); + csPinMaskSet = digitalPinToBitMask(cs); + } else { + // No chip-select line defined; might be permanently tied to GND. + // Assign a valid GPIO register (though not used for CS), and an + // empty pin bitmask...the nonsense bit-twiddling might be faster + // than checking _cs and possibly branching. + csPort = dcPort; + csPinMaskSet = 0; + } + csPinMaskClr = ~csPinMaskSet; + dcPinMaskClr = ~dcPinMaskSet; + #endif // end !HAS_PORT_SET_CLR +#endif // end USE_FAST_PINIO +} + +/*! + @brief Adafruit_SPITFT constructor for parallel display connection. + @param w Display width in pixels at default rotation (0). + @param h Display height in pixels at default rotation (0). + + @param wide If true, is a 16-bit parallel connection, else 8-bit. + 16-bit isn't fully implemented or tested yet so + applications should pass "false" for now...needed to + stick a required boolean argument in there to + disambiguate this constructor from the soft-SPI case. + Argument is ignored on 8-bit architectures (no 'wide' + support there since PORTs are 8 bits anyway). + @param d0 Arduino pin # for data bit 0 (1+ are extrapolated). + The 8 (or 16) data bits MUST be contiguous and byte- + aligned (or word-aligned for wide interface) within the + same PORT register (might not correspond to Arduino pin + sequence). + @param wr Arduino pin # for write strobe (required). + @param dc Arduino pin # for data/command select (required). + @param cs Arduino pin # for chip-select (optional, -1 if unused, + tie CS low). + @param rst Arduino pin # for display reset (optional, display reset + can be tied to MCU reset, default of -1 means unused). + @param wr Arduino pin # for read strobe (optional, -1 if unused). + @return Adafruit_SPITFT object. + @note Output pins are not initialized; application typically will need + to call subclass' begin() function, which in turn calls this + library's initSPI() function to initialize pins. + Yes, the name is a misnomer...this library originally handled + only SPI displays, parallel being a recent addition (but not + wanting to break existing code). +*/ +Adafruit_SPITFT::Adafruit_SPITFT(uint16_t w, uint16_t h, bool wide, + int8_t d0, int8_t wr, int8_t dc, int8_t cs, int8_t rst, int8_t rd) : + Adafruit_GFX(w, h), connection(TFT_PARALLEL), _rst(rst), _cs(cs), _dc(dc) { + tft8._d0 = d0; + tft8._wr = wr; + tft8._rd = rd; + tft8.wide = wide; +#if defined(USE_FAST_PINIO) + #if defined(HAS_PORT_SET_CLR) + #if defined(CORE_TEENSY) + tft8.wrPortSet =portSetRegister(wr); + tft8.wrPortClr =portClearRegister(wr); + dcPortSet =portSetRegister(dc); + dcPortClr =portClearRegister(dc); + if(cs >= 0) { + csPinMask=digitalPinToBitMask(cs); + csPortSet=portSetRegister(cs); + csPortClr=portClearRegister(cs); + } else { // see comments below + csPortSet = dcPortSet; + csPortClr = dcPortClr; + csPinMask = 0; + } + if(rd >= 0) { // if read-strobe pin specified... + #if defined(KINETISK) + tft8.rdPinMask = 1; + #else // !KINETISK + tft8.rdPinMask = digitalPinToBitMask(rd); + #endif + tft8.rdPortSet = portSetRegister(rd); + tft8.rdPortClr = portClearRegister(rd); + } else { + tft8.rdPinMask = 0; + tft8.rdPortSet = dcPortSet; + tft8.rdPortClr = dcPortClr; + } + // These are all uint8_t* pointers -- elsewhere they're recast + // as necessary if a 'wide' 16-bit interface is in use. + tft8.writePort = portOutputRegister(d0); + tft8.readPort = portInputRegister(d0); + tft8.dirSet = portModeRegister(d0); + tft8.dirClr = portModeRegister(d0); + #else // !CORE_TEENSY + tft8.wrPinMask =digitalPinToBitMask(wr); + tft8.wrPortSet =&(PORT->Group[g_APinDescription[wr].ulPort].OUTSET.reg); + tft8.wrPortClr =&(PORT->Group[g_APinDescription[wr].ulPort].OUTCLR.reg); + dcPinMask =digitalPinToBitMask(dc); + dcPortSet =&(PORT->Group[g_APinDescription[dc].ulPort].OUTSET.reg); + dcPortClr =&(PORT->Group[g_APinDescription[dc].ulPort].OUTCLR.reg); + if(cs >= 0) { + csPinMask=digitalPinToBitMask(cs); + csPortSet=&(PORT->Group[g_APinDescription[cs].ulPort].OUTSET.reg); + csPortClr=&(PORT->Group[g_APinDescription[cs].ulPort].OUTCLR.reg); + } else { + // No chip-select line defined; might be permanently tied to GND. + // Assign a valid GPIO register (though not used for CS), and an + // empty pin bitmask...the nonsense bit-twiddling might be faster + // than checking _cs and possibly branching. + csPortSet = dcPortSet; + csPortClr = dcPortClr; + csPinMask = 0; + } + if(rd >= 0) { // if read-strobe pin specified... + tft8.rdPinMask =digitalPinToBitMask(rd); + tft8.rdPortSet =&(PORT->Group[g_APinDescription[rd].ulPort].OUTSET.reg); + tft8.rdPortClr =&(PORT->Group[g_APinDescription[rd].ulPort].OUTCLR.reg); + } else { + tft8.rdPinMask = 0; + tft8.rdPortSet = dcPortSet; + tft8.rdPortClr = dcPortClr; + } + // Get pointers to PORT write/read/dir bytes within 32-bit PORT + uint8_t dBit = g_APinDescription[d0].ulPin; // d0 bit # in PORT + PortGroup *p = (&(PORT->Group[g_APinDescription[d0].ulPort])); + uint8_t offset = dBit / 8; // d[7:0] byte # within PORT + if(wide) offset &= ~1; // d[15:8] byte # within PORT + // These are all uint8_t* pointers -- elsewhere they're recast + // as necessary if a 'wide' 16-bit interface is in use. + tft8.writePort = (volatile uint8_t *)&(p->OUT.reg) + offset; + tft8.readPort = (volatile uint8_t *)&(p->IN.reg) + offset; + tft8.dirSet = (volatile uint8_t *)&(p->DIRSET.reg) + offset; + tft8.dirClr = (volatile uint8_t *)&(p->DIRCLR.reg) + offset; + #endif // end !CORE_TEENSY + #else // !HAS_PORT_SET_CLR + tft8.wrPort =(PORTreg_t)portOutputRegister(digitalPinToPort(wr)); + tft8.wrPinMaskSet =digitalPinToBitMask(wr); + dcPort =(PORTreg_t)portOutputRegister(digitalPinToPort(dc)); + dcPinMaskSet =digitalPinToBitMask(dc); + if(cs >= 0) { + csPort = (PORTreg_t)portOutputRegister(digitalPinToPort(cs)); + csPinMaskSet = digitalPinToBitMask(cs); + } else { + // No chip-select line defined; might be permanently tied to GND. + // Assign a valid GPIO register (though not used for CS), and an + // empty pin bitmask...the nonsense bit-twiddling might be faster + // than checking _cs and possibly branching. + csPort = dcPort; + csPinMaskSet = 0; + } + if(rd >= 0) { // if read-strobe pin specified... + tft8.rdPort =(PORTreg_t)portOutputRegister(digitalPinToPort(rd)); + tft8.rdPinMaskSet =digitalPinToBitMask(rd); + } else { + tft8.rdPort = dcPort; + tft8.rdPinMaskSet = 0; + } + csPinMaskClr = ~csPinMaskSet; + dcPinMaskClr = ~dcPinMaskSet; + tft8.wrPinMaskClr = ~tft8.wrPinMaskSet; + tft8.rdPinMaskClr = ~tft8.rdPinMaskSet; + tft8.writePort = (PORTreg_t)portOutputRegister(digitalPinToPort(d0)); + tft8.readPort = (PORTreg_t)portInputRegister(digitalPinToPort(d0)); + tft8.portDir = (PORTreg_t)portModeRegister(digitalPinToPort(d0)); + #endif // end !HAS_PORT_SET_CLR +#endif // end USE_FAST_PINIO +} + +// end constructors ------- + + +// CLASS MEMBER FUNCTIONS -------------------------------------------------- + +// begin() and setAddrWindow() MUST be declared by any subclass. + +/*! + @brief Configure microcontroller pins for TFT interfacing. Typically + called by a subclass' begin() function. + @param freq SPI frequency when using hardware SPI. If default (0) + is passed, will fall back on a device-specific value. + Value is ignored when using software SPI or parallel + connection. + @note Another anachronistically-named function; this is called even + when the display connection is parallel (not SPI). Also, this + could probably be made private...quite a few class functions + were generously put in the public section. +*/ +void Adafruit_SPITFT::initSPI(uint32_t freq) { + + if(!freq) freq = DEFAULT_SPI_FREQ; // If no freq specified, use default + + // Init basic control pins common to all connection types if(_cs >= 0) { pinMode(_cs, OUTPUT); digitalWrite(_cs, HIGH); // Deselect @@ -207,22 +440,73 @@ void Adafruit_SPITFT::initSPI(uint32_t freq) { pinMode(_dc, OUTPUT); digitalWrite(_dc, LOW); - // Software SPI - if(_sclk >= 0){ - pinMode(_mosi, OUTPUT); - digitalWrite(_mosi, LOW); - pinMode(_sclk, OUTPUT); - digitalWrite(_sclk, HIGH); - if(_miso >= 0){ - pinMode(_miso, INPUT); + switch(connection) { + case TFT_HARD_SPI: +#if defined(SPI_HAS_TRANSACTION) + hwspi._spi->begin(); + hwspi.settings = SPISettings(freq, MSBFIRST, SPI_MODE0); +#else + hwspi._freq = freq; // Save freq value for later + hwspi._spi->begin(); +#endif + break; + case TFT_SOFT_SPI: + pinMode(swspi._mosi, OUTPUT); + digitalWrite(swspi._mosi, LOW); + pinMode(swspi._sck, OUTPUT); + digitalWrite(swspi._sck, LOW); + if(swspi._miso >= 0) { + pinMode(swspi._miso, INPUT); } + break; + case TFT_PARALLEL: + // Initialize data pins. We were only passed d0, so scan + // the pin description list looking for the other pins. + // They'll be on the same PORT, and within the next 7 (or 15) bits + // (because we need to write to a contiguous PORT byte or word). +#if defined(__AVR__) + // PORT registers are 8 bits wide, so just need a register match... + for(uint8_t i=0; i= dBit ) && + (g_APinDescription[i].ulPin <= (uint32_t)lastBit)) { + pinMode(i, OUTPUT); + digitalWrite(i, LOW); + } + } + #endif // end !CORE_TEENSY +#endif + pinMode(tft8._wr, OUTPUT); + digitalWrite(tft8._wr, HIGH); + if(tft8._rd >= 0) { + pinMode(tft8._rd, OUTPUT); + digitalWrite(tft8._rd, HIGH); + } + break; } - // Hardware SPI - SPI_BEGIN(); - - // toggle RST low to reset - if (_rst >= 0) { + if(_rst >= 0) { + // Toggle _rst low to reset pinMode(_rst, OUTPUT); digitalWrite(_rst, HIGH); delay(100); @@ -232,7 +516,7 @@ void Adafruit_SPITFT::initSPI(uint32_t freq) { delay(200); } -#ifdef USE_SPI_DMA +#if defined(USE_SPI_DMA) // INITIALIZE DMA @@ -282,31 +566,31 @@ void Adafruit_SPITFT::initSPI(uint32_t freq) { // on that structure not changing nor the compiler // rearranging things. Oh the humanity! - if(*(SERCOM **)_spi == &sercom0) { + if(*(SERCOM **)hwspi._spi == &sercom0) { dmac_id = SERCOM0_DMAC_ID_TX; data_reg = &SERCOM0->SPI.DATA.reg; #if defined SERCOM1 - } else if(*(SERCOM **)_spi == &sercom1) { + } else if(*(SERCOM **)hwspi._spi == &sercom1) { dmac_id = SERCOM1_DMAC_ID_TX; data_reg = &SERCOM1->SPI.DATA.reg; #endif #if defined SERCOM2 - } else if(*(SERCOM **)_spi == &sercom2) { + } else if(*(SERCOM **)hwspi._spi == &sercom2) { dmac_id = SERCOM2_DMAC_ID_TX; data_reg = &SERCOM2->SPI.DATA.reg; #endif #if defined SERCOM3 - } else if(*(SERCOM **)_spi == &sercom3) { + } else if(*(SERCOM **)hwspi._spi == &sercom3) { dmac_id = SERCOM3_DMAC_ID_TX; data_reg = &SERCOM3->SPI.DATA.reg; #endif #if defined SERCOM4 - } else if(*(SERCOM **)_spi == &sercom4) { + } else if(*(SERCOM **)hwspi._spi == &sercom4) { dmac_id = SERCOM4_DMAC_ID_TX; data_reg = &SERCOM4->SPI.DATA.reg; #endif #if defined SERCOM5 - } else if(*(SERCOM **)_spi == &sercom5) { + } else if(*(SERCOM **)hwspi._spi == &sercom5) { dmac_id = SERCOM5_DMAC_ID_TX; data_reg = &SERCOM5->SPI.DATA.reg; #endif @@ -349,112 +633,69 @@ void Adafruit_SPITFT::initSPI(uint32_t freq) { #endif // end DMA init } -/**************************************************************************/ /*! - @brief Read one byte from SPI interface (hardware or software) - @returns One byte, MSB order + @brief Call before issuing command(s) or data to display. Performs + chip-select (if required) and starts an SPI transaction (if + using hardware SPI and transactions are supported). Required + for all display types; not an SPI-specific function. */ -/**************************************************************************/ -uint8_t Adafruit_SPITFT::spiRead() { - if(_sclk < 0){ - return HSPI_READ(); - } - if(_miso < 0){ - return 0; - } - uint8_t r = 0; - for (uint8_t i=0; i<8; i++) { - SSPI_SCK_LOW(); - SSPI_SCK_HIGH(); - r <<= 1; - if (SSPI_MISO_READ()){ - r |= 0x1; - } - } - return r; +void Adafruit_SPITFT::startWrite(void) { + if(_cs >= 0) SPI_CS_LOW(); + SPI_BEGIN_TRANSACTION(); } -/**************************************************************************/ /*! - @brief Write one byte to SPI interface (hardware or software) - @param b One byte to send, MSB order + @brief Call after issuing command(s) or data to display. Performs + chip-deselect (if required) and ends an SPI transaction (if + using hardware SPI and transactions are supported). Required + for all display types; not an SPI-specific function. */ -/**************************************************************************/ -void Adafruit_SPITFT::spiWrite(uint8_t b) { - if(_sclk < 0){ - HSPI_WRITE(b); +void Adafruit_SPITFT::endWrite(void) { + SPI_END_TRANSACTION(); + if(_cs >= 0) SPI_CS_HIGH(); +} + + +// ------------------------------------------------------------------------- +// Lower-level graphics operations. These functions require a chip-select +// and/or SPI transaction around them (via startWrite(), endWrite() above). +// Higher-level graphics primitives might start a single transaction and +// then make multiple calls to these functions (e.g. circle or text +// rendering might make repeated lines or rects) before ending the +// transaction. It's more efficient than starting a transaction every time. + +/*! + @brief Draw a single pixel to the display at requested coordinates. + Not self-contained; should follow a startWrite() call. + @param x Horizontal position (0 = left). + @param y Vertical position (0 = top). + @param color 16-bit pixel color in '565' RGB format. +*/ +void Adafruit_SPITFT::writePixel(int16_t x, int16_t y, uint16_t color) { + if((x >= 0) && (x < _width) && (y >= 0) && (y < _height)) { + setAddrWindow(x, y, 1, 1); + SPI_WRITE16(color); + } +} + +/*! + @brief Issue a series of pixels from memory to the display. Not self- + contained; should follow startWrite() and setAddrWindow() calls. + @param colors Pointer to array of 16-bit pixel values in '565' RGB + format. + @param len Number of elements in 'colors' array. +*/ +void Adafruit_SPITFT::writePixels(uint16_t *colors, uint32_t len) { + + if(!len) return; // Avoid 0-byte transfers + +#if defined(ESP32) // ESP32 has a special SPI pixel-writing function... + if(connection == TFT_HARD_SPI) { + hwspi._spi->writePixels(colors, len * 2); return; } - for(uint8_t bit = 0x80; bit; bit >>= 1){ - if((b) & bit){ - SSPI_MOSI_HIGH(); - } else { - SSPI_MOSI_LOW(); - } - SSPI_SCK_LOW(); - SSPI_SCK_HIGH(); - } -} - - -/* - * Transaction API - * */ - -/**************************************************************************/ -/*! - @brief Begin an SPI transaction & set CS low. -*/ -/**************************************************************************/ -void inline Adafruit_SPITFT::startWrite(void){ - SPI_BEGIN_TRANSACTION(); - SPI_CS_LOW(); -} - -/**************************************************************************/ -/*! - @brief Begin an SPI transaction & set CS high. -*/ -/**************************************************************************/ -void inline Adafruit_SPITFT::endWrite(void){ - SPI_CS_HIGH(); - SPI_END_TRANSACTION(); -} - -/**************************************************************************/ -/*! - @brief Write a command byte (must have a transaction in progress) - @param cmd The 8-bit command to send -*/ -/**************************************************************************/ -void Adafruit_SPITFT::writeCommand(uint8_t cmd){ - SPI_DC_LOW(); - spiWrite(cmd); - SPI_DC_HIGH(); -} - -/**************************************************************************/ -/*! - @brief Push a 2-byte color to the framebuffer RAM, will start transaction - @param color 16-bit 5-6-5 Color to draw -*/ -/**************************************************************************/ -void Adafruit_SPITFT::pushColor(uint16_t color) { - startWrite(); - SPI_WRITE16(color); - endWrite(); -} - -/**************************************************************************/ -/*! - @brief Blit multiple 2-byte colors (must have a transaction in progress) - @param colors Array of 16-bit 5-6-5 Colors to draw - @param len How many pixels to draw - 2 bytes per pixel! -*/ -/**************************************************************************/ -void Adafruit_SPITFT::writePixels(uint16_t *colors, uint32_t len) { -#ifdef USE_SPI_DMA - if(_sclk < 0) { // using hardware SPI? +#elif defined(USE_SPI_DMA) + if(connection == TFT_HARD_SPI) { int maxSpan = maxFillLen / 2; // One scanline max uint8_t pixelBufIdx = 0; // Active pixel buffer number while(len) { @@ -489,33 +730,57 @@ void Adafruit_SPITFT::writePixels(uint16_t *colors, uint32_t len) { lastFillColor = 0x0000; // pixelBuf has been sullied lastFillLen = 0; while(dma_busy); // Wait for last line to complete -#ifdef __SAMD51__ - _spi->setDataMode(SPI_MODE0); // See note in writeColor() +#if defined(__SAMD51__) + hwspi._spi->setDataMode(SPI_MODE0); // See SAMD51 note in writeColor() #endif return; } -#else - SPI_WRITE_PIXELS((uint8_t*)colors , len * 2); -#endif +#endif // end USE_SPI_DMA + + // All other cases (non-DMA hard SPI, bitbang SPI, parallel), + // use a loop with the normal 16-bit data write function: + while(len--) { + SPI_WRITE16(*colors++); + } } -/**************************************************************************/ /*! - @brief Blit a 2-byte color many times (must have a transaction in progress) - @param color The 16-bit 5-6-5 Color to draw - @param len How many pixels to draw + @brief Issue a series of pixels, all the same color. Not self- + contained; should follow startWrite() and setAddrWindow() calls. + @param color 16-bit pixel color in '565' RGB format. + @param len Number of pixels to draw. */ -/**************************************************************************/ void Adafruit_SPITFT::writeColor(uint16_t color, uint32_t len) { if(!len) return; // Avoid 0-byte transfers uint8_t hi = color >> 8, lo = color; - if(_sclk < 0) { // Using hardware SPI - -#ifdef USE_SPI_DMA - +#if defined(ESP32) // ESP32 has a special SPI pixel-writing function... + if(connection == TFT_HARD_SPI) { + #define SPI_MAX_PIXELS_AT_ONCE 32 + #define TMPBUF_LONGWORDS (SPI_MAX_PIXELS_AT_ONCE + 1) / 2 + #define TMPBUF_PIXELS (TMPBUF_LONGWORDS * 2) + static uint32_t temp[TMPBUF_LONGWORDS]; + uint32_t c32 = color * 0x00010001; + uint16_t bufLen = (len < TMPBUF_PIXELS) ? len : TMPBUF_PIXELS, + xferLen, fillLen; + // Fill temp buffer 32 bits at a time + fillLen = (bufLen + 1) / 2; // Round up to next 32-bit boundary + for(uint32_t t=0; tsetDataMode(SPI_MODE0); -#endif - // Unfortunately blocking is necessary. An earlier version returned // immediately and checked dma_busy on startWrite() instead, but it // turns out to be MUCH slower on many graphics operations (as when // drawing lines, pixel-by-pixel), perhaps because it's a volatile // type and doesn't cache. Working on this. - -#else // Non-DMA - - #ifdef SPI_HAS_WRITE_PIXELS - #define TMPBUF_LONGWORDS (SPI_MAX_PIXELS_AT_ONCE + 1) / 2 - #define TMPBUF_PIXELS (TMPBUF_LONGWORDS * 2) - static uint32_t temp[TMPBUF_LONGWORDS]; - uint32_t c32 = color * 0x00010001; - uint16_t bufLen = (len < TMPBUF_PIXELS) ? len : TMPBUF_PIXELS, - xferLen, fillLen; - - // Fill temp buffer 32 bits at a time - fillLen = (bufLen + 1) / 2; // Round up to next 32-bit boundary - for(uint32_t t=0; tsetDataMode(SPI_MODE0); #endif + return; + } + #endif // end USE_SPI_DMA +#endif // end !ESP32 -#endif // end non-DMA + // All other cases (non-DMA hard SPI, bitbang SPI, parallel), + // use a loop with the normal write function: + while(len--) { + spiWrite(hi); + spiWrite(lo); + } +} - } else { // Bitbang SPI - while(len--) { - spiWrite(hi); - spiWrite(lo); +/*! + @brief Draw a filled rectangle to the display. Not self-contained; + should follow startWrite(). Typically used by higher-level + graphics primitives; user code shouldn't need to call this and + is likely to use the self-contained fillRect() instead. + writeFillRect() performs its own edge clipping and rejection; + see writeFillRectPreclipped() for a more 'raw' implementation. + @param x Horizontal position of first corner. + @param y Vertical position of first corner. + @param w Rectangle width in pixels (positive = right of first + corner, negative = left of first corner). + @param h Rectangle height in pixels (positive = below first + corner, negative = above first corner). + @param color 16-bit fill color in '565' RGB format. + @note Written in this deep-nested way because C by definition will + optimize for the 'if' case, not the 'else' -- avoids branches + and rejects clipped rectangles at the least-work possibility. +*/ +void Adafruit_SPITFT::writeFillRect(int16_t x, int16_t y, + int16_t w, int16_t h, uint16_t color) { + if(w && h) { // Nonzero width and height? + if(w < 0) { // If negative width... + x += w + 1; // Move X to left edge + w = -w; // Use positive width + } + if(x < _width) { // Not off right + if(h < 0) { // If negative height... + y += h + 1; // Move Y to top edge + h = -h; // Use positive height + } + if(y < _height) { // Not off bottom + int16_t x2 = x + w - 1; + if(x2 >= 0) { // Not off left + int16_t y2 = y + h - 1; + if(y2 >= 0) { // Not off top + // Rectangle partly or fully overlaps screen + if(x < 0) { x = 0; w = x2 + 1; } // Clip left + if(y < 0) { y = 0; h = y2 + 1; } // Clip top + if(x2 >= _width) { w = _width - x; } // Clip right + if(y2 >= _height) { h = _height - y; } // Clip bottom + writeFillRectPreclipped(x, y, w, h, color); + } + } + } } } } -/**************************************************************************/ /*! - @brief Write a pixel (must have a transaction in progress) - @param x x coordinate - @param y y coordinate - @param color 16-bit 5-6-5 Color to draw with + @brief Draw a horizontal line on the display. Performs edge clipping + and rejection. Not self-contained; should follow startWrite(). + Typically used by higher-level graphics primitives; user code + shouldn't need to call this and is likely to use the self- + contained drawFastHLine() instead. + @param x Horizontal position of first point. + @param y Vertical position of first point. + @param w Line width in pixels (positive = right of first point, + negative = point of first corner). + @param color 16-bit line color in '565' RGB format. */ -/**************************************************************************/ -void Adafruit_SPITFT::writePixel(int16_t x, int16_t y, uint16_t color) { - if((x < 0) ||(x >= _width) || (y < 0) || (y >= _height)) return; - setAddrWindow(x,y,1,1); - writePixel(color); +void inline Adafruit_SPITFT::writeFastHLine(int16_t x, int16_t y, int16_t w, + uint16_t color) { + if((y >= 0) && (y < _height) && w) { // Y on screen, nonzero width + if(w < 0) { // If negative width... + x += w + 1; // Move X to left edge + w = -w; // Use positive width + } + if(x < _width) { // Not off right + int16_t x2 = x + w - 1; + if(x2 >= 0) { // Not off left + // Line partly or fully overlaps screen + if(x < 0) { x = 0; w = x2 + 1; } // Clip left + if(x2 >= _width) { w = _width - x; } // Clip right + writeFillRectPreclipped(x, y, w, 1, color); + } + } + } } -/**************************************************************************/ /*! - @brief Write a filled rectangle (must have a transaction in progress) - @param x Top left corner x coordinate - @param y Top left corner y coordinate - @param w Width in pixels - @param h Height in pixels - @param color 16-bit 5-6-5 Color to fill with + @brief Draw a vertical line on the display. Performs edge clipping and + rejection. Not self-contained; should follow startWrite(). + Typically used by higher-level graphics primitives; user code + shouldn't need to call this and is likely to use the self- + contained drawFastVLine() instead. + @param x Horizontal position of first point. + @param y Vertical position of first point. + @param w Line height in pixels (positive = below first point, + negative = above first point). + @param color 16-bit line color in '565' RGB format. */ -/**************************************************************************/ -void Adafruit_SPITFT::writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color){ - if((x >= _width) || (y >= _height)) return; - int16_t x2 = x + w - 1, y2 = y + h - 1; - if((x2 < 0) || (y2 < 0)) return; - - // Clip left/top - if(x < 0) { - x = 0; - w = x2 + 1; - } - if(y < 0) { - y = 0; - h = y2 + 1; +void inline Adafruit_SPITFT::writeFastVLine(int16_t x, int16_t y, int16_t h, + uint16_t color) { + if((x >= 0) && (x < _width) && h) { // X on screen, nonzero height + if(h < 0) { // If negative height... + y += h + 1; // Move Y to top edge + h = -h; // Use positive height + } + if(y < _height) { // Not off bottom + int16_t y2 = y + h - 1; + if(y2 >= 0) { // Not off top + // Line partly or fully overlaps screen + if(y < 0) { y = 0; h = y2 + 1; } // Clip top + if(y2 >= _height) { h = _height - y; } // Clip bottom + writeFillRectPreclipped(x, y, 1, h, color); + } + } } +} - // Clip right/bottom - if(x2 >= _width) w = _width - x; - if(y2 >= _height) h = _height - y; - +/*! + @brief A lower-level version of writeFillRect(). This version requires + all inputs are in-bounds, that width and height are positive, + and no part extends offscreen. NO EDGE CLIPPING OR REJECTION IS + PERFORMED. If higher-level graphics primitives are written to + handle their own clipping earlier in the drawing process, this + can avoid unnecessary function calls and repeated clipping + operations in the lower-level functions. + @param x Horizontal position of first corner. MUST BE WITHIN + SCREEN BOUNDS. + @param y Vertical position of first corner. MUST BE WITHIN SCREEN + BOUNDS. + @param w Rectangle width in pixels. MUST BE POSITIVE AND NOT + EXTEND OFF SCREEN. + @param h Rectangle height in pixels. MUST BE POSITIVE AND NOT + EXTEND OFF SCREEN. + @param color 16-bit fill color in '565' RGB format. + @note This is a new function, no graphics primitives besides rects + and horizontal/vertical lines are written to best use this yet. +*/ +inline void Adafruit_SPITFT::writeFillRectPreclipped(int16_t x, int16_t y, + int16_t w, int16_t h, uint16_t color) { setAddrWindow(x, y, w, h); - writeColor(color, (int32_t)w * h); + writeColor(color, (uint32_t)w * h); } -/**************************************************************************/ /*! - @brief Write a perfectly vertical line (must have a transaction in progress) - @param x Top-most x coordinate - @param y Top-most y coordinate - @param h Height in pixels - @param color 16-bit 5-6-5 Color to fill with + @brief Writes a single color value to the display. Not self-contained; + should follow startWrite() and setAddrWindow() calls. + Since this just calls SPI_WRITE16(), one could just call that + directly to get the same effect. + @param color 16-bit pixel color in '565' RGB format. */ -/**************************************************************************/ -void inline Adafruit_SPITFT::writeFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color){ - writeFillRect(x, y, 1, h, color); +inline void Adafruit_SPITFT::writePixel(uint16_t color) { + SPI_WRITE16(color); } -/**************************************************************************/ -/*! - @brief Write a perfectly horizontal line (must have a transaction in progress) - @param x Left-most x coordinate - @param y Left-most y coordinate - @param w Width in pixels - @param color 16-bit 5-6-5 Color to fill with -*/ -/**************************************************************************/ -void inline Adafruit_SPITFT::writeFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color){ - writeFillRect(x, y, w, 1, color); -} -/**************************************************************************/ +// ------------------------------------------------------------------------- +// Ever-so-slightly higher-level graphics operations. Similar to the 'write' +// functions above, but these contain their own chip-select and SPI +// transactions as needed (via startWrite(), endWrite()). They're typically +// used solo -- as graphics primitives in themselves, not invoked by higher- +// level primitives (which should use the functions above for better +// performance). + /*! - @brief Draw a pixel - sets up transaction - @param x x coordinate - @param y y coordinate - @param color 16-bit 5-6-5 Color to draw with + @brief Draw a single pixel to the display at requested coordinates. + Self-contained and provides its own transaction as needed + (see writePixel(x,y,color) for a lower-level variant). + Edge clipping is performed here. + @param x Horizontal position (0 = left). + @param y Vertical position (0 = top). + @param color 16-bit pixel color in '565' RGB format. */ -/**************************************************************************/ -void Adafruit_SPITFT::drawPixel(int16_t x, int16_t y, uint16_t color){ +void Adafruit_SPITFT::drawPixel(int16_t x, int16_t y, uint16_t color) { // Clip first... if((x >= 0) && (x < _width) && (y >= 0) && (y < _height)) { // THEN set up transaction (if needed) and draw... startWrite(); setAddrWindow(x, y, 1, 1); - writePixel(color); + SPI_WRITE16(color); endWrite(); } } -/**************************************************************************/ /*! - @brief Write a perfectly vertical line - sets up transaction - @param x Top-most x coordinate - @param y Top-most y coordinate - @param h Height in pixels - @param color 16-bit 5-6-5 Color to fill with + @brief Draw a filled rectangle to the display. Self-contained and + provides its own transaction as needed (see writeFillRect() or + writeFillRectPreclipped() for lower-level variants). Edge + clipping and rejection is performed here. + @param x Horizontal position of first corner. + @param y Vertical position of first corner. + @param w Rectangle width in pixels (positive = right of first + corner, negative = left of first corner). + @param h Rectangle height in pixels (positive = below first + corner, negative = above first corner). + @param color 16-bit fill color in '565' RGB format. + @note This repeats the writeFillRect() function almost in its entirety, + with the addition of a transaction start/end. It's done this way + (rather than starting the transaction and calling writeFillRect() + to handle clipping and so forth) so that the transaction isn't + performed at all if the rectangle is rejected. It's really not + that much code. */ -/**************************************************************************/ -void Adafruit_SPITFT::drawFastVLine(int16_t x, int16_t y, - int16_t h, uint16_t color) { - startWrite(); - writeFastVLine(x, y, h, color); - endWrite(); -} - -/**************************************************************************/ -/*! - @brief Write a perfectly horizontal line - sets up transaction - @param x Left-most x coordinate - @param y Left-most y coordinate - @param w Width in pixels - @param color 16-bit 5-6-5 Color to fill with -*/ -/**************************************************************************/ -void Adafruit_SPITFT::drawFastHLine(int16_t x, int16_t y, - int16_t w, uint16_t color) { - startWrite(); - writeFastHLine(x, y, w, color); - endWrite(); -} - -/**************************************************************************/ -/*! - @brief Fill a rectangle completely with one color. - @param x Top left corner x coordinate - @param y Top left corner y coordinate - @param w Width in pixels - @param h Height in pixels - @param color 16-bit 5-6-5 Color to fill with -*/ -/**************************************************************************/ void Adafruit_SPITFT::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, - uint16_t color) { + uint16_t color) { + if(w && h) { // Nonzero width and height? + if(w < 0) { // If negative width... + x += w + 1; // Move X to left edge + w = -w; // Use positive width + } + if(x < _width) { // Not off right + if(h < 0) { // If negative height... + y += h + 1; // Move Y to top edge + h = -h; // Use positive height + } + if(y < _height) { // Not off bottom + int16_t x2 = x + w - 1; + if(x2 >= 0) { // Not off left + int16_t y2 = y + h - 1; + if(y2 >= 0) { // Not off top + // Rectangle partly or fully overlaps screen + if(x < 0) { x = 0; w = x2 + 1; } // Clip left + if(y < 0) { y = 0; h = y2 + 1; } // Clip top + if(x2 >= _width) { w = _width - x; } // Clip right + if(y2 >= _height) { h = _height - y; } // Clip bottom + startWrite(); + writeFillRectPreclipped(x, y, w, h, color); + endWrite(); + } + } + } + } + } +} + +/*! + @brief Draw a horizontal line on the display. Self-contained and + provides its own transaction as needed (see writeFastHLine() for + a lower-level variant). Edge clipping and rejection is performed + here. + @param x Horizontal position of first point. + @param y Vertical position of first point. + @param w Line width in pixels (positive = right of first point, + negative = point of first corner). + @param color 16-bit line color in '565' RGB format. + @note This repeats the writeFastHLine() function almost in its + entirety, with the addition of a transaction start/end. It's + done this way (rather than starting the transaction and calling + writeFastHLine() to handle clipping and so forth) so that the + transaction isn't performed at all if the line is rejected. +*/ +void Adafruit_SPITFT::drawFastHLine(int16_t x, int16_t y, int16_t w, + uint16_t color) { + if((y >= 0) && (y < _height) && w) { // Y on screen, nonzero width + if(w < 0) { // If negative width... + x += w + 1; // Move X to left edge + w = -w; // Use positive width + } + if(x < _width) { // Not off right + int16_t x2 = x + w - 1; + if(x2 >= 0) { // Not off left + // Line partly or fully overlaps screen + if(x < 0) { x = 0; w = x2 + 1; } // Clip left + if(x2 >= _width) { w = _width - x; } // Clip right + startWrite(); + writeFillRectPreclipped(x, y, w, 1, color); + endWrite(); + } + } + } +} + +/*! + @brief Draw a vertical line on the display. Self-contained and provides + its own transaction as needed (see writeFastHLine() for a lower- + level variant). Edge clipping and rejection is performed here. + @param x Horizontal position of first point. + @param y Vertical position of first point. + @param w Line height in pixels (positive = below first point, + negative = above first point). + @param color 16-bit line color in '565' RGB format. + @note This repeats the writeFastVLine() function almost in its + entirety, with the addition of a transaction start/end. It's + done this way (rather than starting the transaction and calling + writeFastVLine() to handle clipping and so forth) so that the + transaction isn't performed at all if the line is rejected. +*/ +void Adafruit_SPITFT::drawFastVLine(int16_t x, int16_t y, int16_t h, + uint16_t color) { + if((x >= 0) && (x < _width) && h) { // X on screen, nonzero height + if(h < 0) { // If negative height... + y += h + 1; // Move Y to top edge + h = -h; // Use positive height + } + if(y < _height) { // Not off bottom + int16_t y2 = y + h - 1; + if(y2 >= 0) { // Not off top + // Line partly or fully overlaps screen + if(y < 0) { y = 0; h = y2 + 1; } // Clip top + if(y2 >= _height) { h = _height - y; } // Clip bottom + startWrite(); + writeFillRectPreclipped(x, y, 1, h, color); + endWrite(); + } + } + } +} + +/*! + @brief Essentially writePixel() with a transaction around it. I don't + think this is in use by any of our code anymore (believe it was + for some older BMP-reading examples), but is kept here in case + any user code relies on it. Consider it DEPRECATED. + @param color 16-bit pixel color in '565' RGB format. +*/ +void Adafruit_SPITFT::pushColor(uint16_t color) { startWrite(); - writeFillRect(x,y,w,h,color); + SPI_WRITE16(color); endWrite(); } - -/**************************************************************************/ /*! - @brief Invert the display using built-in hardware command - @param i True if you want to invert, false to make 'normal' + @brief Draw a 16-bit image (565 RGB) at the specified (x,y) position. + For 16-bit display devices; no color reduction performed. + Adapted from https://github.com/PaulStoffregen/ILI9341_t3 + by Marc MERLIN. See examples/pictureEmbed to use this. + 5/6/2017: function name and arguments have changed for + compatibility with current GFX library and to avoid naming + problems in prior implementation. Formerly drawBitmap() with + arguments in different order. Handles its own transaction and + edge clipping/rejection. + @param x Top left corner horizontal coordinate. + @param y Top left corner vertical coordinate. + @param pcolors Pointer to 16-bit array of pixel values. + @param w Width of bitmap in pixels. + @param h Height of bitmap in pixels. */ -/**************************************************************************/ -void Adafruit_SPITFT::invertDisplay(boolean i) { - startWrite(); - writeCommand(i ? invertOnCommand : invertOffCommand); - endWrite(); -} - - -/**************************************************************************/ -/*! - @brief Draw a 16-bit image (RGB 5/6/5) at the specified (x,y) position. - For 16-bit display devices; no color reduction performed. - Adapted from https://github.com/PaulStoffregen/ILI9341_t3 - by Marc MERLIN. See examples/pictureEmbed to use this. - 5/6/2017: function name and arguments have changed for compatibility - with current GFX library and to avoid naming problems in prior - implementation. Formerly drawBitmap() with arguments in different order. - - @param x Top left corner x coordinate - @param y Top left corner y coordinate - @param pcolors 16-bit array with 16-bit color bitmap - @param w Width of bitmap in pixels - @param h Height of bitmap in pixels -*/ -/**************************************************************************/ void Adafruit_SPITFT::drawRGBBitmap(int16_t x, int16_t y, uint16_t *pcolors, int16_t w, int16_t h) { @@ -835,5 +1229,469 @@ void Adafruit_SPITFT::drawRGBBitmap(int16_t x, int16_t y, endWrite(); } -#endif // !__AVR_ATtiny85__ +// ------------------------------------------------------------------------- +// Miscellaneous class member functions that don't draw anything. + +/*! + @brief Invert the colors of the display (if supported by hardware). + Self-contained, no transaction setup required. + @param i true = inverted display, false = normal display. +*/ +void Adafruit_SPITFT::invertDisplay(bool i) { + startWrite(); + writeCommand(i ? invertOnCommand : invertOffCommand); + endWrite(); +} + +/*! + @brief Given 8-bit red, green and blue values, return a 'packed' + 16-bit color value in '565' RGB format (5 bits red, 6 bits + green, 5 bits blue). This is just a mathematical operation, + no hardware is touched. + @param red 8-bit red brightnesss (0 = off, 255 = max). + @param green 8-bit green brightnesss (0 = off, 255 = max). + @param blue 8-bit blue brightnesss (0 = off, 255 = max). +*/ +uint16_t Adafruit_SPITFT::color565(uint8_t red, uint8_t green, uint8_t blue) { + return ((red & 0xF8) << 8) | ((green & 0xFC) << 3) | (blue >> 3); +} + + +// ------------------------------------------------------------------------- +// Lowest-level hardware-interfacing functions. Many of these are inline and +// compile to different things based on #defines -- typically just a few +// instructions. Others, not so much, those are not inlined. + +/*! + @brief Start an SPI transaction if using the hardware SPI interface to + the display. If using an earlier version of the Arduino platform + (before the addition of SPI transactions), this instead attempts + to set up the SPI clock and mode. No action is taken if the + connection is not hardware SPI-based. This does NOT include a + chip-select operation -- see startWrite() for a function that + encapsulated both actions. +*/ +inline void Adafruit_SPITFT::SPI_BEGIN_TRANSACTION(void) { + if(connection == TFT_HARD_SPI) { +#if defined(SPI_HAS_TRANSACTION) + hwspi._spi->beginTransaction(hwspi.settings); +#else // No transactions, configure SPI manually... + #if defined(__AVR__) || defined(TEENSYDUINO) || defined(ARDUINO_ARCH_STM32F1) + hwspi._spi->setClockDivider(SPI_CLOCK_DIV2); + #elif defined(__arm__) + hwspi._spi->setClockDivider(11); + #elif defined(ESP8266) || defined(ESP32) + hwspi._spi->setFrequency(hwspi._freq); + #elif defined(RASPI) || defined(ARDUINO_ARCH_STM32F1) + hwspi._spi->setClock(hwspi._freq); + #endif + hwspi._spi->setBitOrder(MSBFIRST); + hwspi._spi->setDataMode(SPI_MODE0); +#endif // end !SPI_HAS_TRANSACTION + } +} + +/*! + @brief End an SPI transaction if using the hardware SPI interface to + the display. No action is taken if the connection is not + hardware SPI-based or if using an earlier version of the Arduino + platform (before the addition of SPI transactions). This does + NOT include a chip-deselect operation -- see endWrite() for a + function that encapsulated both actions. +*/ +inline void Adafruit_SPITFT::SPI_END_TRANSACTION(void) { +#if defined(SPI_HAS_TRANSACTION) + if(connection == TFT_HARD_SPI) { + hwspi._spi->endTransaction(); + } +#endif +} + +/*! + @brief Issue a single 8-bit value to the display. Chip-select, + transaction and data/command selection must have been + previously set -- this ONLY issues the byte. This is another of + those functions in the library with a now-not-accurate name + that's being maintained for compatibility with outside code. + This function is used even if display connection is parallel. + @param b 8-bit value to write. +*/ +void Adafruit_SPITFT::spiWrite(uint8_t b) { + switch(connection) { + case TFT_HARD_SPI: +#if defined(__AVR__) + for(SPDR = b; !(SPSR & _BV(SPIF)); ); +#elif defined(ESP8266) || defined(ESP32) + hwspi._spi->write(b); +#else + hwspi._spi->transfer(b); +#endif + break; + case TFT_SOFT_SPI: + for(uint8_t bit=0; bit<8; bit++) { + if(b & 0x80) SPI_MOSI_HIGH(); + else SPI_MOSI_LOW(); + SPI_SCK_HIGH(); + SPI_SCK_LOW(); + b <<= 1; + } + break; + case TFT_PARALLEL: +#if defined(__AVR__) + *tft8.writePort = b; +#elif defined(USE_FAST_PINIO) + if(!tft8.wide) *tft8.writePort = b; + else *(volatile uint16_t *)tft8.writePort = b; +#endif + TFT_WR_STROBE(); + break; + } +} + +/*! + @brief Write a single command byte to the display. Chip-select and + transaction must have been previously set -- this ONLY sets + the device to COMMAND mode, issues the byte and then restores + DATA mode. There is no corresponding explicit writeData() + function -- just use spiWrite(). + @param b 8-bit command to write. +*/ +void Adafruit_SPITFT::writeCommand(uint8_t cmd) { + SPI_DC_LOW(); + spiWrite(cmd); + SPI_DC_HIGH(); +} + +/*! + @brief Read a single 8-bit value from the display. Chip-select and + transaction must have been previously set -- this ONLY reads + the byte. This is another of those functions in the library + with a now-not-accurate name that's being maintained for + compatibility with outside code. This function is used even if + display connection is parallel. + @return Unsigned 8-bit value read (always zero if USE_FAST_PINIO is + not supported by the MCU architecture). +*/ +uint8_t Adafruit_SPITFT::spiRead(void) { + uint8_t b = 0; + uint16_t w = 0; + switch(connection) { + case TFT_HARD_SPI: + return hwspi._spi->transfer((uint8_t)0); + case TFT_SOFT_SPI: + if(swspi._miso >= 0) { + for(uint8_t i=0; i<8; i++) { + SPI_SCK_HIGH(); + SPI_SCK_LOW(); + b <<= 1; + if(SPI_MISO_READ()) b |= 1; + } + } + return b; + // case TFT_PARALLEL: + default: // Avoids compiler warning about no return value + if(tft8._rd >= 0) { +#if defined(USE_FAST_PINIO) + TFT_RD_LOW(); // Read strobe LOW + #if defined(__AVR__) + *tft8.portDir = 0x00; // Set port to input state + w = *tft8.readPort; // Read value from port + *tft8.portDir = 0xFF; // Restore port to output + #else // !__AVR__ + if(!tft8.wide) { // 8-bit TFT connection + #if defined(HAS_PORT_SET_CLR) + *tft8.dirClr = 0xFF; // Set port to input state + w = *tft8.readPort; // Read value from port + *tft8.dirSet = 0xFF; // Restore port to output + #else // !HAS_PORT_SET_CLR + *tft8.portDir = 0x00; // Set port to input state + w = *tft8.readPort; // Read value from port + *tft8.dirSet = 0xFF; // Restore port to output + #endif // end HAS_PORT_SET_CLR + } else { // 16-bit TFT connection + #if defined(HAS_PORT_SET_CLR) + *(volatile uint16_t *)tft8.dirClr = 0xFFFF; // Input state + w = *(volatile uint16_t *)tft8.readPort; // 16-bit read + *(volatile uint16_t *)tft8.dirSet = 0xFFFF; // Output state + #else // !HAS_PORT_SET_CLR + *(volatile uint16_t *)tft8.portDir = 0x0000; // Input state + w = *(volatile uint16_t *)tft8.readPort; // 16-bit read + *(volatile uint16_t *)tft8.portDir = 0xFFFF; // Output state + #endif // end !HAS_PORT_SET_CLR + } + #endif // end !__AVR__ + TFT_RD_HIGH(); // Read strobe HIGH +#else // !USE_FAST_PINIO + w = 0; // Parallel TFT is NOT SUPPORTED without USE_FAST_PINIO +#endif // end !USE_FAST_PINIO + } + return w; + } +} + +/*! + @brief Set the software (bitbang) SPI MOSI line HIGH. +*/ +inline void Adafruit_SPITFT::SPI_MOSI_HIGH(void) { +#if defined(USE_FAST_PINIO) + #if defined(HAS_PORT_SET_CLR) + #if defined(KINETISK) + *swspi.mosiPortSet = 1; + #else // !KINETISK + *swspi.mosiPortSet = swspi.mosiPinMask; + #endif + #else // !HAS_PORT_SET_CLR + *swspi.mosiPort |= swspi.mosiPinMaskSet; + #endif // end !HAS_PORT_SET_CLR +#else // !USE_FAST_PINIO + digitalWrite(swspi._mosi, HIGH); +#endif // end !USE_FAST_PINIO +} + +/*! + @brief Set the software (bitbang) SPI MOSI line LOW. +*/ +inline void Adafruit_SPITFT::SPI_MOSI_LOW(void) { +#if defined(USE_FAST_PINIO) + #if defined(HAS_PORT_SET_CLR) + #if defined(KINETISK) + *swspi.mosiPortClr = 1; + #else // !KINETISK + *swspi.mosiPortClr = swspi.mosiPinMask; + #endif + #else // !HAS_PORT_SET_CLR + *swspi.mosiPort &= swspi.mosiPinMaskClr; + #endif // end !HAS_PORT_SET_CLR +#else // !USE_FAST_PINIO + digitalWrite(swspi._mosi, LOW); +#endif // end !USE_FAST_PINIO +} + +/*! + @brief Set the software (bitbang) SPI SCK line HIGH. +*/ +inline void Adafruit_SPITFT::SPI_SCK_HIGH(void) { +#if defined(USE_FAST_PINIO) + #if defined(HAS_PORT_SET_CLR) + #if defined(KINETISK) + *swspi.sckPortSet = 1; + #else // !KINETISK + *swspi.sckPortSet = swspi.sckPinMask; + #endif + #else // !HAS_PORT_SET_CLR + *swspi.sckPort |= swspi.sckPinMaskSet; + #endif // end !HAS_PORT_SET_CLR +#else // !USE_FAST_PINIO + digitalWrite(swspi._sck, HIGH); +#endif // end !USE_FAST_PINIO +} + +/*! + @brief Set the software (bitbang) SPI SCK line LOW. +*/ +inline void Adafruit_SPITFT::SPI_SCK_LOW(void) { +#if defined(USE_FAST_PINIO) + #if defined(HAS_PORT_SET_CLR) + #if defined(KINETISK) + *swspi.sckPortClr = 1; + #else // !KINETISK + *swspi.sckPortClr = swspi.sckPinMask; + #endif + #else // !HAS_PORT_SET_CLR + *swspi.sckPort &= swspi.sckPinMaskClr; + #endif // end !HAS_PORT_SET_CLR +#else // !USE_FAST_PINIO + digitalWrite(swspi._sck, LOW); +#endif // end !USE_FAST_PINIO +} + +/*! + @brief Read the state of the software (bitbang) SPI MISO line. + @return true if HIGH, false if LOW. +*/ +inline bool Adafruit_SPITFT::SPI_MISO_READ(void) { +#if defined(USE_FAST_PINIO) + #if defined(KINETISK) + return *swspi.misoPort; + #else // !KINETISK + return *swspi.misoPort & swspi.misoPinMask; + #endif // end !KINETISK +#else // !USE_FAST_PINIO + return digitalRead(swspi._miso); +#endif // end !USE_FAST_PINIO +} + +/*! + @brief Issue a single 16-bit value to the display. Chip-select, + transaction and data/command selection must have been + previously set -- this ONLY issues the word. Despite the name, + this function is used even if display connection is parallel; + name was maintaned for backward compatibility. Naming is also + not consistent with the 8-bit version, spiWrite(). Sorry about + that. Again, staying compatible with outside code. + @param w 16-bit value to write. +*/ +void Adafruit_SPITFT::SPI_WRITE16(uint16_t w) { + switch(connection) { + case TFT_HARD_SPI: +#if defined(__AVR__) + for(SPDR = (w >> 8); !(SPSR & _BV(SPIF)); ); + for(SPDR = w ; !(SPSR & _BV(SPIF)); ); +#elif defined(ESP8266) || defined(ESP32) + hwspi._spi->write16(w); +#else + hwspi._spi->transfer(w >> 8); + hwspi._spi->transfer(w); +#endif + break; + case TFT_SOFT_SPI: + for(uint8_t bit=0; bit<16; bit++) { + if(w & 0x8000) SPI_MOSI_HIGH(); + else SPI_MOSI_LOW(); + SPI_SCK_HIGH(); + SPI_SCK_LOW(); + w <<= 1; + } + break; + case TFT_PARALLEL: +#if defined(__AVR__) + *tft8.writePort = w >> 8; + TFT_WR_STROBE(); + *tft8.writePort = w; +#elif defined(USE_FAST_PINIO) + if(!tft8.wide) { + *tft8.writePort = w >> 8; + TFT_WR_STROBE(); + *tft8.writePort = w; + } else { + *(volatile uint16_t *)tft8.writePort = w; + } +#endif + TFT_WR_STROBE(); + break; + } +} + +/*! + @brief Issue a single 32-bit value to the display. Chip-select, + transaction and data/command selection must have been + previously set -- this ONLY issues the longword. Despite the + name, this function is used even if display connection is + parallel; name was maintaned for backward compatibility. Naming + is also not consistent with the 8-bit version, spiWrite(). + Sorry about that. Again, staying compatible with outside code. + @param w 16-bit value to write. +*/ +void Adafruit_SPITFT::SPI_WRITE32(uint32_t l) { + switch(connection) { + case TFT_HARD_SPI: +#if defined(__AVR__) + for(SPDR = (l >> 24); !(SPSR & _BV(SPIF)); ); + for(SPDR = (l >> 16); !(SPSR & _BV(SPIF)); ); + for(SPDR = (l >> 8); !(SPSR & _BV(SPIF)); ); + for(SPDR = l ; !(SPSR & _BV(SPIF)); ); +#elif defined(ESP8266) || defined(ESP32) + hwspi._spi->write32(l); +#else + hwspi._spi->transfer(l >> 24); + hwspi._spi->transfer(l >> 16); + hwspi._spi->transfer(l >> 8); + hwspi._spi->transfer(l); +#endif + break; + case TFT_SOFT_SPI: + for(uint8_t bit=0; bit<32; bit++) { + if(l & 0x80000000) SPI_MOSI_HIGH(); + else SPI_MOSI_LOW(); + SPI_SCK_HIGH(); + SPI_SCK_LOW(); + l <<= 1; + } + break; + case TFT_PARALLEL: +#if defined(__AVR__) + *tft8.writePort = l >> 24; + TFT_WR_STROBE(); + *tft8.writePort = l >> 16; + TFT_WR_STROBE(); + *tft8.writePort = l >> 8; + TFT_WR_STROBE(); + *tft8.writePort = l; +#elif defined(USE_FAST_PINIO) + if(!tft8.wide) { + *tft8.writePort = l >> 24; + TFT_WR_STROBE(); + *tft8.writePort = l >> 16; + TFT_WR_STROBE(); + *tft8.writePort = l >> 8; + TFT_WR_STROBE(); + *tft8.writePort = l; + } else { + *(volatile uint16_t *)tft8.writePort = l >> 16; + TFT_WR_STROBE(); + *(volatile uint16_t *)tft8.writePort = l; + } +#endif + TFT_WR_STROBE(); + break; + } +} + +/*! + @brief Set the RD line LOW, then HIGH. Used for parallel-connected + interfaces when writing data. +*/ +inline void Adafruit_SPITFT::TFT_WR_STROBE(void) { +#if defined(USE_FAST_PINIO) + #if defined(HAS_PORT_SET_CLR) + #if defined(KINETISK) + *tft8.wrPortClr = 1; + *tft8.wrPortSet = 1; + #else // !KINETISK + *tft8.wrPortClr = tft8.wrPinMask; + *tft8.wrPortSet = tft8.wrPinMask; + #endif // end !KINETISK + #else // !HAS_PORT_SET_CLR + *tft8.wrPort &= tft8.wrPinMaskClr; + *tft8.wrPort |= tft8.wrPinMaskSet; + #endif // end !HAS_PORT_SET_CLR +#else // !USE_FAST_PINIO + digitalWrite(tft8._wr, LOW); + digitalWrite(tft8._wr, HIGH); +#endif // end !USE_FAST_PINIO +} + +/*! + @brief Set the RD line HIGH. Used for parallel-connected interfaces + when reading data. +*/ +inline void Adafruit_SPITFT::TFT_RD_HIGH(void) { +#if defined(USE_FAST_PINIO) + #if defined(HAS_PORT_SET_CLR) + *tft8.rdPortSet = tft8.rdPinMask; + #else // !HAS_PORT_SET_CLR + *tft8.rdPort |= tft8.rdPinMaskSet; + #endif // end !HAS_PORT_SET_CLR +#else // !USE_FAST_PINIO + digitalWrite(tft8._rd, HIGH); +#endif // end !USE_FAST_PINIO +} + +/*! + @brief Set the RD line LOW. Used for parallel-connected interfaces + when reading data. +*/ +inline void Adafruit_SPITFT::TFT_RD_LOW(void) { +#if defined(USE_FAST_PINIO) + #if defined(HAS_PORT_SET_CLR) + *tft8.rdPortClr = tft8.rdPinMask; + #else // !HAS_PORT_SET_CLR + *tft8.rdPort &= tft8.rdPinMaskClr; + #endif // end !HAS_PORT_SET_CLR +#else // !USE_FAST_PINIO + digitalWrite(tft8._rd, LOW); +#endif // end !USE_FAST_PINIO +} + +#endif // end __AVR_ATtiny85__ diff --git a/Adafruit_SPITFT.h b/Adafruit_SPITFT.h index d0707de..8bea32b 100644 --- a/Adafruit_SPITFT.h +++ b/Adafruit_SPITFT.h @@ -1,154 +1,465 @@ -#ifndef _ADAFRUIT_SPITFT_ -#define _ADAFRUIT_SPITFT_ +/*! + * @file Adafruit_SPITFT.h + * + * Part of Adafruit's GFX graphics library. Originally this class was + * written to handle a range of color TFT displays connected via SPI, + * but over time this library and some display-specific subclasses have + * mutated to include some color OLEDs as well as parallel-interfaced + * displays. The name's been kept for the sake of older code. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * @section author Author + * + * Written by Limor "ladyada" Fried for Adafruit Industries, + * with contributions from the open source community. + * + * @section license License + * + * BSD license, all text here must be included in any redistribution. + */ -#if !defined(__AVR_ATtiny85__) // NOT A CHANCE of this stuff working on ATtiny +#ifndef _ADAFRUIT_SPITFT_H_ +#define _ADAFRUIT_SPITFT_H_ + +#if !defined(__AVR_ATtiny85__) // Not for ATtiny, at all -#if ARDUINO >= 100 - #include "Arduino.h" - #include "Print.h" -#else - #include "WProgram.h" -#endif #include #include "Adafruit_GFX.h" -#define USE_FAST_PINIO ///< If set, use PORT access instead of digitalWrite() -//#define USE_SPI_DMA ///< If set, use SPI DMA if available +// HARDWARE CONFIG --------------------------------------------------------- + +#if defined(__AVR__) + typedef uint8_t PORT_t; ///< PORT values are 8-bit + #define USE_FAST_PINIO ///< Use direct PORT register access +#elif defined(__arm__) + #if defined(SAM) + // Arduino Due + typedef uint32_t PORT_t; ///< PORT values are 32-bit + // USE_FAST_PINIO not available here (yet)...Due has a totally different + // GPIO register set and will require some changes elsewhere (e.g. in + // constructors especially). + #elif defined(CORE_TEENSY) + // PJRC Teensy 3.x + typedef uint8_t PORT_t; ///< PORT values are 8-bit + #define USE_FAST_PINIO ///< Use direct PORT register access + #define HAS_PORT_SET_CLR ///< PORTs have set & clear registers + #else + // Adafruit M0, M4 + typedef uint32_t PORT_t; ///< PORT values are 32-bit + #define USE_FAST_PINIO ///< Use direct PORT register access + #define HAS_PORT_SET_CLR ///< PORTs have set & clear registers + #endif +#else // !ARM + // Probably ESP8266 or ESP32. USE_FAST_PINIO is not available here (yet) + // but don't worry about it too much...the digitalWrite() implementation + // on these platforms is reasonably efficient and already RAM-resident, + // only gotcha then is no parallel connection support for now. + typedef uint32_t PORT_t; ///< PORT values are 32-bit +#endif // end !ARM +typedef volatile PORT_t* PORTreg_t; ///< PORT register type + +#if defined(__AVR__) + #define DEFAULT_SPI_FREQ 8000000L ///< Hardware SPI default speed +#else + #define DEFAULT_SPI_FREQ 16000000L ///< Hardware SPI default speed +#endif + +//#define USE_SPI_DMA ///< If set, use SPI DMA if available +// Another "oops" name -- in the future parallel DMA will also be handled. // If DMA is enabled, Arduino sketch MUST #include // Estimated RAM usage: // 4 bytes/pixel on display major axis + 8 bytes/pixel on minor axis, // e.g. 320x240 pixels = 320 * 4 + 240 * 8 = 3,200 bytes. #if !defined(ARDUINO_ARCH_SAMD) - #undef USE_SPI_DMA ///< Only for SAMD chips + #undef USE_SPI_DMA ///< DMA currently for SAMD chips only #endif -#ifdef USE_SPI_DMA - #pragma message ("SPI DMA IS ENABLED. HIGHLY EXPERIMENTAL.") +#if defined(USE_SPI_DMA) + #pragma message ("GFX DMA IS ENABLED. HIGHLY EXPERIMENTAL.") #include #endif -#if defined(__AVR__) - typedef volatile uint8_t RwReg; -#elif defined(ARDUINO_STM32_FEATHER) - typedef volatile uint32_t RwReg; - #undef USE_FAST_PINIO - typedef class HardwareSPI SPIClass; -#elif defined(__OPENCR__) || defined (__OPENCM904__) - #undef USE_FAST_PINIO -#elif defined(ARDUINO_FEATHER52) || defined(__arm__) - typedef volatile uint32_t RwReg; -#elif defined(ESP32) || defined(ESP8266) - typedef volatile uint32_t RwReg; - #undef USE_FAST_PINIO -#else - #undef USE_FAST_PINIO -#endif +// CLASS DEFINITION -------------------------------------------------------- -#include "Adafruit_SPITFT_Macros.h" - -/// A heavily optimized SPI display subclass of GFX. Manages SPI bitbanging, transactions, DMA, etc! Despite being called SPITFT, the classic SPI data/command interface is also used by OLEDs. +/*! + @brief Adafruit_SPITFT is an intermediary class between Adafruit_GFX + and various hardware-specific subclasses for different displays. + It handles certain operations that are common to a range of + displays (address window, area fills, etc.). Originally these were + all color TFT displays interfaced via SPI, but it's since expanded + to include color OLEDs and parallel-interfaced TFTs. THE NAME HAS + BEEN KEPT TO AVOID BREAKING A LOT OF SUBCLASSES AND EXAMPLE CODE. + Many of the class member functions similarly live on with names + that don't necessarily accurately describe what they're doing, + again to avoid breaking a lot of other code. If in doubt, read + the comments. +*/ class Adafruit_SPITFT : public Adafruit_GFX { - public: - Adafruit_SPITFT(uint16_t w, uint16_t h, int8_t _CS, int8_t _DC, int8_t _MOSI, int8_t _SCLK, int8_t _RST = -1, int8_t _MISO = -1); - Adafruit_SPITFT(uint16_t w, uint16_t h, int8_t _CS, int8_t _DC, int8_t _RST = -1); - Adafruit_SPITFT(uint16_t w, uint16_t h, SPIClass *spiClass, int8_t _CS, int8_t _DC, int8_t _RST = -1); - virtual void begin(uint32_t freq) = 0; ///< Virtual begin() function to set SPI frequency, must be overridden in subclass. @param freq Maximum SPI hardware clock speed + public: - void initSPI(uint32_t freq); + // CONSTRUCTORS -------------------------------------------------------- - // Required Non-Transaction - void drawPixel(int16_t x, int16_t y, uint16_t color); + // Software SPI constructor: expects width & height (at default rotation + // setting 0), 4 signal pins (cs, dc, mosi, sclk), 2 optional pins + // (reset, miso). cs argument is required but can be -1 if unused -- + // rather than moving it to the optional arguments, it was done this way + // to avoid breaking existing code (-1 option was a later addition). + Adafruit_SPITFT(uint16_t w, uint16_t h, + int8_t cs, int8_t dc, int8_t mosi, int8_t sck, + int8_t rst = -1, int8_t miso = -1); - // Transaction API - void startWrite(void); - void endWrite(void); + // Hardware SPI constructor using the default SPI port: expects width & + // height (at default rotation setting 0), 2 signal pins (cs, dc), + // optional reset pin. cs is required but can be -1 if unused -- rather + // than moving it to the optional arguments, it was done this way to + // avoid breaking existing code (-1 option was a later addition). + Adafruit_SPITFT(uint16_t w, uint16_t h, + int8_t cs, int8_t dc, int8_t rst = -1); - void writePixel(int16_t x, int16_t y, uint16_t color); - void writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color); - void writeFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color); - void writeFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color); + // Hardware SPI constructor using an arbitrary SPI peripheral: expects + // width & height (rotation 0), SPIClass pointer, 2 signal pins (cs, dc) + // and optional reset pin. cs is required but can be -1 if unused. + Adafruit_SPITFT(uint16_t w, uint16_t h, SPIClass *spiClass, + int8_t cs, int8_t dc, int8_t rst = -1); - // Transaction API not used by GFX + // Parallel constructor: expects width & height (rotation 0), flag + // indicating whether 16-bit (true) or 8-bit (false) interface, 3 signal + // pins (d0, wr, dc), 3 optional pins (cs, rst, rd). 16-bit parallel + // isn't even fully implemented but the 'wide' flag was added as a + // required argument to avoid ambiguity with other constructors. + Adafruit_SPITFT(uint16_t w, uint16_t h, bool wide, + int8_t d0, int8_t wr, int8_t dc, + int8_t cs = -1, int8_t rst = -1, int8_t rd = -1); - /*! - @brief SPI displays set an address window rectangle for blitting pixels - @param x Top left corner x coordinate - @param y Top left corner x coordinate - @param w Width of window - @param h Height of window - */ - virtual void setAddrWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h) = 0; + // CLASS MEMBER FUNCTIONS ---------------------------------------------- - /*! - @brief Write a 2-byte color (must have a transaction in progress) - @param color 16-bit 5-6-5 Color to draw - */ - void inline writePixel(uint16_t color) { SPI_WRITE16(color); } - void writePixels(uint16_t * colors, uint32_t len); - void writeColor(uint16_t color, uint32_t len); - void pushColor(uint16_t color); + // These first two functions MUST be declared by subclasses: - // Recommended Non-Transaction - void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color); - void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color); - void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color); + /*! + @brief Display-specific initialization function. + @param freq SPI frequency, in hz (or 0 for default or unused). + */ + virtual void begin(uint32_t freq) = 0; - using Adafruit_GFX::drawRGBBitmap; // Check base class first - void drawRGBBitmap(int16_t x, int16_t y, - uint16_t *pcolors, int16_t w, int16_t h); - void invertDisplay(boolean i); + /*! + @brief Set up the specific display hardware's "address window" + for subsequent pixel-pushing operations. + @param x Leftmost pixel of area to be drawn (MUST be within + display bounds at current rotation setting). + @param y Topmost pixel of area to be drawn (MUST be within + display bounds at current rotation setting). + @param w Width of area to be drawn, in pixels (MUST be >0 and, + added to x, within display bounds at current rotation). + @param h Height of area to be drawn, in pixels (MUST be >0 and, + added to x, within display bounds at current rotation). + */ + virtual void setAddrWindow( + uint16_t x, uint16_t y, uint16_t w, uint16_t h) = 0; - uint16_t color565(uint8_t r, uint8_t g, uint8_t b); - void writeCommand(uint8_t cmd); - void spiWrite(uint8_t v); - uint8_t spiRead(void); + // Remaining functions do not need to be declared in subclasses + // unless they wish to provide hardware-specific optimizations. + // Brief comments here...documented more thoroughly in .cpp file. - protected: - SPIClass *_spi; ///< The SPI device we want to use (set in constructor) - uint32_t _freq; ///< SPI clock frequency (for hardware SPI) -#if defined (__AVR__) || defined(TEENSYDUINO) || defined (ESP8266) || defined (ESP32) - int8_t _cs, _dc, _rst, _sclk, _mosi, _miso; -#else - int32_t _cs, ///< Arduino pin # for chip-select pin - _dc, ///< Arduino pin # for data-command pin - _rst, ///< Arduino pin # for reset pin - _sclk, ///< Arduino pin # for SPI clock pin - _mosi, ///< Arduino pin # for SPI MOSI pin - _miso; ///< Arduino pin # for SPI MISO pin + // Subclass' begin() function invokes this to initialize hardware. + // Name is outdated (interface may be parallel) but for compatibility: + void initSPI(uint32_t freq = 0); // 0 = use default SPI speed + // Chip select and/or hardware SPI transaction start as needed: + void startWrite(void); + // Chip deselect and/or hardware SPI transaction end as needed: + void endWrite(void); + + // These functions require a chip-select and/or SPI transaction + // around them. Higher-level graphics primitives might start a + // single transaction and then make multiple calls to these functions + // (e.g. circle or text rendering might make repeated lines or rects) + // before ending the transaction. It's more efficient than starting a + // transaction every time. + void writePixel(int16_t x, int16_t y, uint16_t color); + void writePixels(uint16_t *colors, uint32_t len); + void writeColor(uint16_t color, uint32_t len); + void writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, + uint16_t color); + void writeFastHLine(int16_t x, int16_t y, int16_t w, + uint16_t color); + void writeFastVLine(int16_t x, int16_t y, int16_t h, + uint16_t color); + // This is a new function, similar to writeFillRect() except that + // all arguments MUST be onscreen, sorted and clipped. If higher-level + // primitives can handle their own sorting/clipping, it avoids repeating + // such operations in the low-level code, making it potentially faster. + // CALLING THIS WITH UNCLIPPED OR NEGATIVE VALUES COULD BE DISASTROUS. + inline void writeFillRectPreclipped(int16_t x, int16_t y, + int16_t w, int16_t h, uint16_t color); + // This variant of writePixel() just calls SPI_WRITE16()...new code + // should probably just do the latter, consider this deprecated: + inline void writePixel(uint16_t color); + + // These functions are similar to the 'write' functions above, but with + // a chip-select and/or SPI transaction built-in. They're typically used + // solo -- that is, as graphics primitives in themselves, not invoked by + // higher-level primitives (which should use the functions above). + void drawPixel(int16_t x, int16_t y, uint16_t color); + void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, + uint16_t color); + void drawFastHLine(int16_t x, int16_t y, int16_t w, + uint16_t color); + void drawFastVLine(int16_t x, int16_t y, int16_t h, + uint16_t color); + // A single-pixel push encapsulated in a transaction. I don't think + // this is used anymore (BMP demos might've used it?) but is provided + // for backward compatibility, consider it deprecated: + void pushColor(uint16_t color); + + using Adafruit_GFX::drawRGBBitmap; // Check base class first + void drawRGBBitmap(int16_t x, int16_t y, + uint16_t *pcolors, int16_t w, int16_t h); + + void invertDisplay(bool i); + uint16_t color565(uint8_t r, uint8_t g, uint8_t b); + + // Despite parallel additions, function names kept for compatibility: + void spiWrite(uint8_t b); // Write single byte as DATA + void writeCommand(uint8_t cmd); // Write single byte as COMMAND + uint8_t spiRead(void); // Read single byte of data + + // Most of these low-level functions were formerly macros in + // Adafruit_SPITFT_Macros.h. Some have been made into inline functions + // to avoid macro mishaps. Despite the addition of code for a parallel + // display interface, the names have been kept for backward + // compatibility (some subclasses may be invoking these): + void SPI_WRITE16(uint16_t w); // Not inline + void SPI_WRITE32(uint32_t l); // Not inline + void SPI_WRITE_PIXELS(uint16_t *data, uint32_t bytes); // ditto + // Old code had both a spiWrite16() function and SPI_WRITE16 macro + // in addition to the SPI_WRITE32 macro. The latter two have been + // made into functions here, and spiWrite16() removed (use SPI_WRITE16() + // instead). It looks like most subclasses had gotten comfortable with + // SPI_WRITE16 and SPI_WRITE32 anyway so those names were kept rather + // than the less-obnoxious camelcase variants, oh well. + + // Placing these functions entirely in the class definition inlines + // them implicitly them while allowing their use in other code: + + /*! + @brief Set the chip-select line HIGH. Does NOT check whether CS pin + is set (>=0), that should be handled in calling function. + Despite function name, this is used even if the display + connection is parallel. + */ + void SPI_CS_HIGH(void) { + #if defined(USE_FAST_PINIO) + #if defined(HAS_PORT_SET_CLR) + *csPortSet = csPinMask; + #else // !HAS_PORT_SET_CLR + *csPort |= csPinMaskSet; + #endif // end !HAS_PORT_SET_CLR + #else // !USE_FAST_PINIO + digitalWrite(_cs, HIGH); + #endif // end !USE_FAST_PINIO + } + + /*! + @brief Set the chip-select line LOW. Does NOT check whether CS pin + is set (>=0), that should be handled in calling function. + Despite function name, this is used even if the display + connection is parallel. + */ + void SPI_CS_LOW(void) { + #if defined(USE_FAST_PINIO) + #if defined(HAS_PORT_SET_CLR) + *csPortClr = csPinMask; + #else // !HAS_PORT_SET_CLR + *csPort &= csPinMaskClr; + #endif // end !HAS_PORT_SET_CLR + #else // !USE_FAST_PINIO + digitalWrite(_cs, LOW); + #endif // end !USE_FAST_PINIO + } + + /*! + @brief Set the data/command line HIGH (data mode). + */ + void SPI_DC_HIGH(void) { + #if defined(USE_FAST_PINIO) + #if defined(HAS_PORT_SET_CLR) + #if defined(KINETISK) + *dcPortSet = 1; + #else // !KINETISK + *dcPortSet = dcPinMask; + #endif // end !KINETISK + #else // !HAS_PORT_SET_CLR + *dcPort |= dcPinMaskSet; + #endif // end !HAS_PORT_SET_CLR + #else // !USE_FAST_PINIO + digitalWrite(_dc, HIGH); + #endif // end !USE_FAST_PINIO + } + + /*! + @brief Set the data/command line LOW (command mode). + */ + void SPI_DC_LOW(void) { + #if defined(USE_FAST_PINIO) + #if defined(HAS_PORT_SET_CLR) + #if defined(KINETISK) + *dcPortClr = 1; + #else // !KINETISK + *dcPortClr = dcPinMask; + #endif // end !KINETISK + #else // !HAS_PORT_SET_CLR + *dcPort &= dcPinMaskClr; + #endif // end !HAS_PORT_SET_CLR + #else // !USE_FAST_PINIO + digitalWrite(_dc, LOW); + #endif // end !USE_FAST_PINIO + } + + protected: + + // A few more low-level member functions -- some may have previously + // been macros. Shouldn't have a need to access these externally, so + // they've been moved to the protected section. Additionally, they're + // declared inline here and the code is in the .cpp file, since outside + // code doesn't need to see these. + inline void SPI_MOSI_HIGH(void); + inline void SPI_MOSI_LOW(void); + inline void SPI_SCK_HIGH(void); + inline void SPI_SCK_LOW(void); + inline bool SPI_MISO_READ(void); + inline void SPI_BEGIN_TRANSACTION(void); + inline void SPI_END_TRANSACTION(void); + inline void TFT_WR_STROBE(void); // Parallel interface write strobe + inline void TFT_RD_HIGH(void); // Parallel interface read high + inline void TFT_RD_LOW(void); // Parallel interface read low + + // CLASS INSTANCE VARIABLES -------------------------------------------- + + // Here be dragons! There's a big union of three structures here -- + // one each for hardware SPI, software (bitbang) SPI, and parallel + // interfaces. This is to save some memory, since a display's connection + // will be only one of these. The order of some things is a little weird + // in an attempt to get values to align and pack better in RAM. + +#if defined(USE_FAST_PINIO) +#if defined(HAS_PORT_SET_CLR) + PORTreg_t csPortSet; ///< PORT register for chip select SET + PORTreg_t csPortClr; ///< PORT register for chip select CLEAR + PORTreg_t dcPortSet; ///< PORT register for data/command SET + PORTreg_t dcPortClr; ///< PORT register for data/command CLEAR +#else // !HAS_PORT_SET_CLR + PORTreg_t csPort; ///< PORT register for chip select + PORTreg_t dcPort; ///< PORT register for data/command +#endif // end HAS_PORT_SET_CLR +#endif // end USE_FAST_PINIO + union { + struct { // Values specific to HARDWARE SPI: + SPIClass *_spi; ///< SPI class pointer + uint32_t _freq; + SPISettings settings; + } hwspi; + struct { // Values specific to SOFTWARE SPI: +#if defined(USE_FAST_PINIO) + PORTreg_t misoPort; ///< PORT (PIN) register for MISO +#if defined(HAS_PORT_SET_CLR) + PORTreg_t mosiPortSet; ///< PORT register for MOSI SET + PORTreg_t mosiPortClr; ///< PORT register for MOSI CLEAR + PORTreg_t sckPortSet; ///< PORT register for SCK SET + PORTreg_t sckPortClr; ///< PORT register for SCK CLEAR + #if !defined(KINETISK) + PORT_t mosiPinMask; ///< Bitmask for MOSI + PORT_t sckPinMask; ///< Bitmask for SCK + #endif // end !KINETISK +#else // !HAS_PORT_SET_CLR + PORTreg_t mosiPort; ///< PORT register for MOSI + PORTreg_t sckPort; ///< PORT register for SCK + PORT_t mosiPinMaskSet; ///< Bitmask for MOSI SET (OR) + PORT_t mosiPinMaskClr; ///< Bitmask for MOSI CLEAR (AND) + PORT_t sckPinMaskSet; ///< Bitmask for SCK SET (OR bitmask) + PORT_t sckPinMaskClr; ///< Bitmask for SCK CLEAR (AND) +#endif // end HAS_PORT_SET_CLR + #if !defined(KINETISK) + PORT_t misoPinMask; ///< Bitmask for MISO + #endif // end !KINETISK +#endif // end USE_FAST_PINIO + int8_t _mosi; ///< MOSI pin # + int8_t _miso; ///< MISO pin # + int8_t _sck; ///< SCK pin # + } swspi; + struct { // Values specific to 8-bit parallel: +#if defined(USE_FAST_PINIO) + volatile uint8_t *writePort; ///< PORT register for DATA WRITE + volatile uint8_t *readPort; ///< PORT (PIN) register for DATA READ +#if defined(HAS_PORT_SET_CLR) + // Port direction register pointers are always 8-bit regardless of + // PORTreg_t -- even if 32-bit port, we modify a byte-aligned 8 bits. + volatile uint8_t *dirSet; ///< PORT byte data direction SET + volatile uint8_t *dirClr; ///< PORT byte data direction CLEAR + PORTreg_t wrPortSet; ///< PORT register for write strobe SET + PORTreg_t wrPortClr; ///< PORT register for write strobe CLEAR + PORTreg_t rdPortSet; ///< PORT register for read strobe SET + PORTreg_t rdPortClr; ///< PORT register for read strobe CLEAR + #if !defined(KINETISK) + PORT_t wrPinMask; ///< Bitmask for write strobe + #endif // end !KINETISK + PORT_t rdPinMask; ///< Bitmask for read strobe +#else // !HAS_PORT_SET_CLR + // Port direction register pointer is always 8-bit regardless of + // PORTreg_t -- even if 32-bit port, we modify a byte-aligned 8 bits. + volatile uint8_t *portDir; ///< PORT direction register + PORTreg_t wrPort; ///< PORT register for write strobe + PORTreg_t rdPort; ///< PORT register for read strobe + PORT_t wrPinMaskSet; ///< Bitmask for write strobe SET (OR) + PORT_t wrPinMaskClr; ///< Bitmask for write strobe CLEAR (AND) + PORT_t rdPinMaskSet; ///< Bitmask for read strobe SET (OR) + PORT_t rdPinMaskClr; ///< Bitmask for read strobe CLEAR (AND) +#endif // end HAS_PORT_SET_CLR +#endif // end USE_FAST_PINIO + int8_t _d0; ///< Data pin 0 # + int8_t _wr; ///< Write strobe pin # + int8_t _rd; ///< Read strobe pin # (or -1) + bool wide = 0; ///< If true, is 16-bit interface + } tft8; + }; +#if defined(USE_SPI_DMA) // Used by hardware SPI and tft8 + Adafruit_ZeroDMA dma; ///< DMA instance + DmacDescriptor *dptr = NULL; ///< 1st descriptor + DmacDescriptor *descriptor = NULL; ///< Allocated descriptor list + uint16_t *pixelBuf[2]; ///< Working buffers + uint16_t maxFillLen; ///< Max pixels per DMA xfer + uint16_t lastFillColor = 0; ///< Last color used w/fill + uint32_t lastFillLen = 0; ///< # of pixels w/last fill + uint8_t onePixelBuf; ///< For hi==lo fill #endif +#if defined(USE_FAST_PINIO) +#if defined(HAS_PORT_SET_CLR) + PORT_t csPinMask; ///< Bitmask for chip select + #if !defined(KINETISK) + PORT_t dcPinMask; ///< Bitmask for data/command + #endif // end !KINETISK +#else // !HAS_PORT_SET_CLR + PORT_t csPinMaskSet; ///< Bitmask for chip select SET (OR) + PORT_t csPinMaskClr; ///< Bitmask for chip select CLEAR (AND) + PORT_t dcPinMaskSet; ///< Bitmask for data/command SET (OR) + PORT_t dcPinMaskClr; ///< Bitmask for data/command CLEAR (AND) +#endif // end HAS_PORT_SET_CLR +#endif // end USE_FAST_PINIO + uint8_t connection; ///< TFT_HARD_SPI, TFT_SOFT_SPI, etc. + int8_t _rst; ///< Reset pin # (or -1) + int8_t _cs; ///< Chip select pin # (or -1) + int8_t _dc; ///< Data/command pin # -#ifdef USE_FAST_PINIO - volatile RwReg *mosiport, ///< Direct chip register for toggling MOSI with fast bitbang IO - *misoport, ///< Direct chip register for toggling MISO with fast bitbang IO - *clkport, ///< Direct chip register for toggling CLK with fast bitbang IO - *dcport, ///< Direct chip register for toggling DC with fast bitbang IO - *csport; ///< Direct chip register for toggling CS with fast bitbang IO - RwReg mosipinmask, ///< bitmask for turning on/off MOSI with fast register bitbang IO - misopinmask, ///< bitmask for turning on/off MISO with fast register bitbang IO - clkpinmask, ///< bitmask for turning on/off CLK with fast register bitbang IO - cspinmask, ///< bitmask for turning on/off CS with fast register bitbang IO - dcpinmask; ///< bitmask for turning on/off DC with fast register bitbang IO -#endif + int16_t _xstart = 0; ///< Internal framebuffer X offset + int16_t _ystart = 0; ///< Internal framebuffer Y offset + uint8_t invertOnCommand = 0; ///< Command to enable invert mode + uint8_t invertOffCommand = 0; ///< Command to disable invert mode - uint8_t invertOnCommand = 0, ///< SPI command byte to turn on invert - invertOffCommand = 0; ///< SPI command byte to turn off invert - int16_t _xstart = 0; ///< Many displays don't have pixels starting at (0,0) of the internal framebuffer, this is the x offset from 0 to align - int16_t _ystart = 0; ///< Many displays don't have pixels starting at (0,0) of the internal framebuffer, this is the y offset from 0 to align - -#ifdef USE_SPI_DMA - Adafruit_ZeroDMA dma; ///< DMA instance - DmacDescriptor *dptr = NULL; ///< 1st descriptor - DmacDescriptor *descriptor = NULL; ///< Allocated descriptor list - uint16_t *pixelBuf[2]; ///< Working buffers - uint16_t maxFillLen; ///< Max pixels per DMA xfer - uint16_t lastFillColor = 0; ///< Last color used w/fill - uint32_t lastFillLen = 0; ///< # of pixels w/last fill - uint8_t onePixelBuf; ///< For hi==lo fill -#endif + uint32_t _freq = 0; ///< Dummy var to keep subclasses happy }; -#endif // !__AVR_ATtiny85__ - -#endif // !_ADAFRUIT_SPITFT_ +#endif // end __AVR_ATtiny85__ +#endif // end _ADAFRUIT_SPITFT_H_ diff --git a/Adafruit_SPITFT_Macros.h b/Adafruit_SPITFT_Macros.h index 1b5bd47..fcd6253 100644 --- a/Adafruit_SPITFT_Macros.h +++ b/Adafruit_SPITFT_Macros.h @@ -1,116 +1,6 @@ -#ifndef _ADAFRUIT_SPITFT_MACROS -#define _ADAFRUIT_SPITFT_MACROS +// THIS FILE INTENTIONALLY LEFT BLANK. -/* - * Control Pins - * */ - -#ifdef USE_FAST_PINIO -#define SPI_DC_HIGH() *dcport |= dcpinmask -#define SPI_DC_LOW() *dcport &= ~dcpinmask -#define SPI_CS_HIGH() *csport |= cspinmask -#define SPI_CS_LOW() *csport &= ~cspinmask -#else -#define SPI_DC_HIGH() digitalWrite(_dc, HIGH) -#define SPI_DC_LOW() digitalWrite(_dc, LOW) -#define SPI_CS_HIGH() { if(_cs >= 0) digitalWrite(_cs, HIGH); } -#define SPI_CS_LOW() { if(_cs >= 0) digitalWrite(_cs, LOW); } -#endif - -/* - * Software SPI Macros - * */ - -#ifdef USE_FAST_PINIO -#define SSPI_MOSI_HIGH() *mosiport |= mosipinmask -#define SSPI_MOSI_LOW() *mosiport &= ~mosipinmask -#define SSPI_SCK_HIGH() *clkport |= clkpinmask -#define SSPI_SCK_LOW() *clkport &= ~clkpinmask -#define SSPI_MISO_READ() ((*misoport & misopinmask) != 0) -#else -#define SSPI_MOSI_HIGH() digitalWrite(_mosi, HIGH) -#define SSPI_MOSI_LOW() digitalWrite(_mosi, LOW) -#define SSPI_SCK_HIGH() digitalWrite(_sclk, HIGH) -#define SSPI_SCK_LOW() digitalWrite(_sclk, LOW) -#define SSPI_MISO_READ() digitalRead(_miso) -#endif - -#define SSPI_BEGIN_TRANSACTION() -#define SSPI_END_TRANSACTION() -#define SSPI_WRITE(v) spiWrite(v) -#define SSPI_WRITE16(s) SSPI_WRITE((s) >> 8); SSPI_WRITE(s) -#define SSPI_WRITE32(l) SSPI_WRITE((l) >> 24); SSPI_WRITE((l) >> 16); SSPI_WRITE((l) >> 8); SSPI_WRITE(l) -#define SSPI_WRITE_PIXELS(c,l) for(uint32_t i=0; i<(l); i+=2){ SSPI_WRITE(((uint8_t*)(c))[i+1]); SSPI_WRITE(((uint8_t*)(c))[i]); } - -/* - * Hardware SPI Macros - * */ - -#if defined (__AVR__) || defined(TEENSYDUINO) || defined(ARDUINO_ARCH_STM32F1) - #define HSPI_SET_CLOCK() _spi->setClockDivider(SPI_CLOCK_DIV2); -#elif defined (__arm__) - #define HSPI_SET_CLOCK() _spi->setClockDivider(11); -#elif defined(ESP8266) || defined(ESP32) - #define HSPI_SET_CLOCK() _spi->setFrequency(_freq); -#elif defined(RASPI) - #define HSPI_SET_CLOCK() _spi->setClock(_freq); -#elif defined(ARDUINO_ARCH_STM32F1) - #define HSPI_SET_CLOCK() _spi->setClock(_freq); -#else - #define HSPI_SET_CLOCK() -#endif - -#ifdef SPI_HAS_TRANSACTION - #define HSPI_BEGIN_TRANSACTION() _spi->beginTransaction(SPISettings(_freq, MSBFIRST, SPI_MODE0)) - #define HSPI_END_TRANSACTION() _spi->endTransaction() -#else - #define HSPI_BEGIN_TRANSACTION() HSPI_SET_CLOCK(); _spi->setBitOrder(MSBFIRST); _spi->setDataMode(SPI_MODE0) - #define HSPI_END_TRANSACTION() -#endif - -#ifdef ESP32 - #define SPI_HAS_WRITE_PIXELS -#endif -#if defined(ESP8266) || defined(ESP32) - // Optimized SPI (ESP8266 and ESP32) - #define HSPI_READ() _spi->transfer(0) - #define HSPI_WRITE(b) _spi->write(b) - #define HSPI_WRITE16(s) _spi->write16(s) - #define HSPI_WRITE32(l) _spi->write32(l) - #ifdef SPI_HAS_WRITE_PIXELS - #define SPI_MAX_PIXELS_AT_ONCE 32 - #define HSPI_WRITE_PIXELS(c,l) _spi->writePixels(c,l) - #else - #define HSPI_WRITE_PIXELS(c,l) for(uint32_t i=0; i<((l)/2); i++){ SPI_WRITE16(((uint16_t*)(c))[i]); } - #endif -#else - // Standard Byte-by-Byte SPI - - #if defined (__AVR__) || defined(TEENSYDUINO) -static inline uint8_t _avr_spi_read(void) __attribute__((always_inline)); -static inline uint8_t _avr_spi_read(void) { - uint8_t r = 0; - SPDR = r; - while(!(SPSR & _BV(SPIF))); - r = SPDR; - return r; -} - #define HSPI_WRITE(b) {SPDR = (b); while(!(SPSR & _BV(SPIF)));} - #define HSPI_READ() _avr_spi_read() - #else - #define HSPI_WRITE(b) _spi->transfer((uint8_t)(b)) - #define HSPI_READ() HSPI_WRITE(0) - #endif - #define HSPI_WRITE16(s) HSPI_WRITE((s) >> 8); HSPI_WRITE(s) - #define HSPI_WRITE32(l) HSPI_WRITE((l) >> 24); HSPI_WRITE((l) >> 16); HSPI_WRITE((l) >> 8); HSPI_WRITE(l) - #define HSPI_WRITE_PIXELS(c,l) for(uint32_t i=0; i<(l); i+=2){ HSPI_WRITE(((uint8_t*)(c))[i+1]); HSPI_WRITE(((uint8_t*)(c))[i]); } -#endif - - #define SPI_BEGIN() if(_sclk < 0){_spi->begin();} - #define SPI_BEGIN_TRANSACTION() if(_sclk < 0){HSPI_BEGIN_TRANSACTION();} - #define SPI_END_TRANSACTION() if(_sclk < 0){HSPI_END_TRANSACTION();} - #define SPI_WRITE16(s) if(_sclk < 0){HSPI_WRITE16(s);}else{SSPI_WRITE16(s);} - #define SPI_WRITE32(l) if(_sclk < 0){HSPI_WRITE32(l);}else{SSPI_WRITE32(l);} - #define SPI_WRITE_PIXELS(c,l) if(_sclk < 0){HSPI_WRITE_PIXELS(c,l);}else{SSPI_WRITE_PIXELS(c,l);} - -#endif // _ADAFRUIT_SPITFT_MACROS +// Macros previously #defined here have been made into (mostly) inline +// functions in the Adafruit_SPITFT class. Other libraries might still +// contain code trying to #include this header file, so until everything's +// updated this file still exists (but doing nothing) to avoid trouble. diff --git a/Adafruit_TFT8.cpp b/Adafruit_TFT8.cpp deleted file mode 100644 index 67f9937..0000000 --- a/Adafruit_TFT8.cpp +++ /dev/null @@ -1,608 +0,0 @@ -/*! -* @file Adafruit_TFT8.cpp -* -* @mainpage Adafruit 8-bit Parallel TFT Displays -* -* @section intro_sec Introduction -* This is our library for TFT Displays using an 8-bit parallel interface -* with address windows and 16 bit color. -* -* These displays use ip to 13 pins to communicate: -* - 8 data lines (required) -* - Write strobe (required) -* - Command/data (required) -* - Chip select (optional, can be tied LOW) -* - Read strobe (optional) -* - Reset (optional, can connect to MCU reset) -* -* Adafruit invests time and resources providing this open source code, -* please support Adafruit and open-source hardware by purchasing -* products from Adafruit! -* -* Written by Limor Fried/Ladyada for Adafruit Industries. -* MIT license, all text above must be included in any redistribution -* @section dependencies Dependencies -* -* This library depends on -* Adafruit_GFX being present on your system. Please make sure you have -* installed the latest version before using this library. -* -* @section author Author -* -* Written by Limor "ladyada" Fried for Adafruit Industries. -* -* @section license License -* -* BSD license, all text here must be included in any redistribution. -* -*/ - -#if !defined(__AVR_ATtiny85__) // Not for ATtiny, at all - -#include "Adafruit_TFT8.h" - -#ifdef PORT_IOBUS -// On SAMD21, redefine digitalPinToPort() to use the slightly-faster -// PORT_IOBUS rather than PORT (not needed on SAMD51). -#undef digitalPinToPort -#define digitalPinToPort(P) (&(PORT_IOBUS->Group[g_APinDescription[P].ulPort])) -#endif - -#ifdef USE_PORT_DMA - #include - #include // memalign() function - - // DMA transfer-in-progress indicator and callback - static volatile boolean dma_busy = false; - static void dma_callback(Adafruit_ZeroDMA *dma) { - dma_busy = false; - } -#endif // USE_PORT_DMA - -#if defined(__AVR__) - #define WR_LOW() *wrPort &= wrPinMaskClr; - #define WR_HIGH() *wrPort |= wrPinMaskSet; - #define DC_LOW() *dcPort &= dcPinMaskClr; - #define DC_HIGH() *dcPort |= dcPinMaskSet; - #define CS_LOW() if(_cs >= 0) *csPort &= csPinMaskClr; - #define CS_HIGH() if(_cs >= 0) *csPort |= csPinMaskSet; - #define RD_LOW() *rdPort &= rdPinMaskClr; - #define RD_HIGH() *rdPort |= rdPinMaskSet; - #define PORT_OUTPUT() *portDir = 0xFF; - #define PORT_INPUT() *portDir = 0x00; -#else - #define WR_LOW() *wrPortClr = wrPinMask; - #define WR_HIGH() *wrPortSet = wrPinMask; - #define DC_LOW() *dcPortClr = dcPinMask; - #define DC_HIGH() *dcPortSet = dcPinMask; - #define CS_LOW() if(_cs >= 0) *csPortClr = csPinMask; - #define CS_HIGH() if(_cs >= 0) *csPortSet = csPinMask; - #define RD_LOW() *rdPortClr = rdPinMask; - #define RD_HIGH() *rdPortSet = rdPinMask; - #define PORT_OUTPUT() *dirSet = 0xFF; - #define PORT_INPUT() *dirClr = 0xFF; - #define PORT_OUTPUT16() *(volatile uint16_t *)dirSet = 0xFFFF; - #define PORT_INPUT16() *(volatile uint16_t *)dirClr = 0xFFFF; -#endif -#define WR_STROBE() { WR_LOW(); WR_HIGH(); } - -/*! - @brief Instantiate Adafruit TFT8 display driver. - @param w Display width in pixels. - @param h Display height in pixels. - @param D0 Arduino pin # for data bit 0 (1+ are extrapolated). - The 8 data bits MUST be contiguous and byte-aligned - (word-aligned for 'wide' interface) within the same - PORT register (may not correspond to Arduino pin sequence). - @param WR Arduino pin # for write strobe. - @param DC Arduino pin # for data/command. - @param CS Arduino pin # for chip select (-1 if unused, tie CS low). - @param RST Arduino pin # for reset (-1 if unused, tie to MCU reset). - @param RD Arduino pin # for read strobe (-1 if unused). - @param wide If true, use 16-bit wide interface (not on AVR). -*/ -Adafruit_TFT8::Adafruit_TFT8(uint16_t w, uint16_t h, - int8_t D0, int8_t WR, int8_t DC, int8_t CS, int8_t RST, int8_t RD, - bool wide) : Adafruit_GFX(w, h), - _d0(D0), _wr(WR), _dc(DC), _cs(CS), _rst(RST), _rd(RD) -{ -#if defined(__AVR__) - if(digitalPinToBitMask(D0) != 1) return; // D0 MUST be port bit 0 - _d0 = D0; // Save D0 pin to indicate valid alignment - wrPort = (PORTreg_t)portOutputRegister(digitalPinToPort(WR)); - wrPinMaskSet = digitalPinToBitMask(WR); - dcPort = (PORTreg_t)portOutputRegister(digitalPinToPort(DC)); - dcPinMaskSet = digitalPinToBitMask(DC); - if(CS >= 0) { - csPort = (PORTreg_t)portOutputRegister(digitalPinToPort(CS)); - csPinMaskSet = digitalPinToBitMask(CS); - } else { - // No chip-select line defined; might be permanently tied to GND. - // Assign a valid GPIO register (though not used for CS), and an - // empty pin bitmask...the nonsense bit-twiddling might be faster - // than checking _cs and possibly branching. - csPort = dcPort; - csPinMaskSet = 0; - } - if(RD >= 0) { - rdPort = (PORTreg_t)portOutputRegister(digitalPinToPort(RD)); - rdPinMaskSet = digitalPinToBitMask(RD); - } else { - // No read-strobe line defined; similar to CS case above - rdPort = dcPort; - rdPinMaskSet = 0; - } - wrPinMaskClr = ~wrPinMaskSet; - dcPinMaskClr = ~dcPinMaskSet; - csPinMaskClr = ~csPinMaskSet; - rdPinMaskClr = ~rdPinMaskSet; - writePort = (PORTreg_t)portOutputRegister(digitalPinToPort(D0)); - readPort = (PORTreg_t)portInputRegister(digitalPinToPort(D0)); - portDir = (PORTreg_t)portModeRegister(digitalPinToPort(D0)); -#else - // Confirm D0 bit is byte- or word-aligned in PORT... - if(g_APinDescription[D0].ulPin & (wide ? 15 : 7)) return; - _d0 = D0; // Save D0 pin to indicate valid alignment - _wide = wide; - wrPinMask = digitalPinToBitMask(WR); - wrPortSet = &(PORT->Group[g_APinDescription[WR].ulPort].OUTSET.reg); - wrPortClr = &(PORT->Group[g_APinDescription[WR].ulPort].OUTCLR.reg); - dcPinMask = digitalPinToBitMask(DC); - dcPortSet = &(PORT->Group[g_APinDescription[DC].ulPort].OUTSET.reg); - dcPortClr = &(PORT->Group[g_APinDescription[DC].ulPort].OUTCLR.reg); - if(CS >= 0) { // If chip-select pin is specified... - csPinMask = digitalPinToBitMask(CS); - csPortSet = &(PORT->Group[g_APinDescription[CS].ulPort].OUTSET.reg); - csPortClr = &(PORT->Group[g_APinDescription[CS].ulPort].OUTCLR.reg); - } else { - // No chip-select line defined; might be permanently tied to GND. - // Assign a valid GPIO register (though not used for CS), and an - // empty pin bitmask...the nonsense bit-twiddling might be faster - // than checking _cs and possibly branching. - csPinMask = 0; - csPortSet = dcPortSet; - csPortClr = dcPortClr; - } - if(RD >= 0) { // If read-strobe pin is specified... - rdPinMask = digitalPinToBitMask(RD); - rdPortSet = &(PORT->Group[g_APinDescription[RD].ulPort].OUTSET.reg); - rdPortClr = &(PORT->Group[g_APinDescription[RD].ulPort].OUTCLR.reg); - } else { - rdPinMask = 0; - rdPortSet = dcPortSet; - rdPortClr = dcPortClr; - } - - // Get pointers to PORT write/read/dir bytes within 32-bit PORT - uint8_t dBit = g_APinDescription[_d0].ulPin; // d0 bit # in PORT - PortGroup *p = (&(PORT->Group[g_APinDescription[_d0].ulPort])); - uint8_t offset = dBit / 8; // d[7:0] byte # within PORT - if(wide) offset &= ~1; // d[15:8] byte # within PORT - // These are all uint8_t* pointers -- elsewhere they're recast - // as necessary if a 'wide' 16-bit interface is in use. - writePort = (volatile uint8_t *)&(p->OUT.reg) + offset; - readPort = (volatile uint8_t *)&(p->IN.reg) + offset; - dirSet = (volatile uint8_t *)&(p->DIRSET.reg) + offset; - dirClr = (volatile uint8_t *)&(p->DIRCLR.reg) + offset; -#endif -} - -/*! - @brief Initialiaze hardware interface. -*/ -bool Adafruit_TFT8::init(void) { - - if(_d0 < 0) return false; // Bad alignment in constructor - - // Initialize data pins. We were only passed d0, so scan - // the pin description list looking for the other pins. - // They'll be on the same PORT, and within the next 7 (or 15) bits - // (because we need to write to a contiguous PORT byte or word). -#if defined(__AVR__) - // PORT registers are 8 bits wide, so just need a register match... - for(uint8_t i=0; i= dBit ) && - (g_APinDescription[i].ulPin <= (uint32_t)lastBit)) { - pinMode(i, OUTPUT); - digitalWrite(i, LOW); - } - } -#endif - - // Initialize control signal pins, all active LOW - pinMode(_wr, OUTPUT); - digitalWrite(_wr, HIGH); - pinMode(_dc, OUTPUT); - digitalWrite(_dc, HIGH); - if(_cs >= 0) { - pinMode(_cs, OUTPUT); - digitalWrite(_cs, HIGH); // Deselect - } - if(_rd >= 0) { - pinMode(_rd, OUTPUT); - digitalWrite(_rd, HIGH); - } - if(_rst >= 0) { - pinMode(_rst, OUTPUT); - digitalWrite(_rst, HIGH); - delay(100); - digitalWrite(_rst, LOW); // Toggle RST low to reset - delay(100); - digitalWrite(_rst, HIGH); - delay(200); - } - - return true; -} - -/*! - @brief Initiate write (or read!) operation. -*/ -inline void Adafruit_TFT8::startWrite(void) { - CS_LOW(); // Chip select LOW -} - -/*! - @brief End write (or read!) operation. -*/ -inline void Adafruit_TFT8::endWrite(void) { - CS_HIGH(); // Chip select HIGH -} - -/*! - @brief Write one byte to hardware interface. - @param b One byte to send, MSB order -*/ -void Adafruit_TFT8::write8(uint8_t b) { -#if defined(__AVR__) - *writePort = b; -#else - if(!_wide) { - *writePort = b; - } else { - *(volatile uint16_t *)writePort = b; - } -#endif - WR_STROBE(); // Write strobe LOW, HIGH -} - -/*! - @brief Write one word to hardware interface. - @param w One word to send, MSB order -*/ -void Adafruit_TFT8::write16(uint16_t w) { -#if defined(__AVR__) - *writePort = w >> 8; // MSB - WR_STROBE(); // Write strobe LOW, HIGH - *writePort = w; // LSB - WR_STROBE(); // Write strobe LOW, HIGH -#else - if(!_wide) { - *writePort = w >> 8; // MSB - WR_STROBE(); // Write strobe LOW, HIGH - *writePort = w; // LSB - } else { - *(volatile uint16_t *)writePort = w; - } - WR_STROBE(); // Write strobe LOW, HIGH -#endif -} - -/*! - @brief Write a command byte. - @param cmd The 8-bit command to send. -*/ -void Adafruit_TFT8::writeCommand(uint8_t cmd) { - DC_LOW(); // Data/Command LOW (command mode) - write8(cmd); // Issue value - DC_HIGH(); // Data/Command HIGH (data mode) -} - -/*! - @brief Read one byte or word from TFT interface. - @returns One byte or word, native order. -*/ -uint16_t Adafruit_TFT8::read(void) { - uint16_t r = 0; - if(_rd >= 0) { -#if defined(__AVR__) - PORT_INPUT(); // Set port to INPUT - RD_LOW(); // Read strobe LOW - r = *readPort; // Read value from port - RD_HIGH(); // Read strobe HIGH - PORT_OUTPUT(); // Set port back to OUTPUT -#else - if(!_wide) { - PORT_INPUT(); // Set port to INPUT - RD_LOW(); // Read strobe LOW - r = *readPort; // Read value from port - RD_HIGH(); // Read strobe HIGH - PORT_OUTPUT(); // Set port back to OUTPUT - } else { - PORT_INPUT16(); // Set port to INPUT (16-bit) - RD_LOW(); // Read strobe LOW - r = *(volatile uint16_t *)readPort; // Read value from port - RD_HIGH(); // Read strobe HIGH - PORT_OUTPUT16(); // Set port back to OUTPUT (16-bit) - } -#endif - } - return r; -} - -/*! - @brief Draw a single pixel. - @param x X coordinate. - @param y Y coordinate. - @param color 16-bit 5-6-5 pixel color. -*/ -void Adafruit_TFT8::drawPixel(int16_t x, int16_t y, uint16_t color) { - // Clip first... - if((x >= 0) && (x < _width) && (y >= 0) && (y < _height)) { - // THEN device-select and draw... - startWrite(); - setAddrWindow(x, y, 1, 1); - writePixel(color); - endWrite(); - } -} - - - - -/*! - @brief Converts 8-bit (each) R,G,B color to 16-bit packed 5-6-5 value. - @param red Red level, 0 to 255 - @param green Green level, 0 to 255 - @param blue Blue level, 0 to 255 - @return Unsigned 16-bit decimated color in "5-6-5" format -*/ -uint16_t Adafruit_TFT8::color565(uint8_t red, uint8_t green, uint8_t blue) { - return ((red & 0xF8) << 8) | ((green & 0xFC) << 3) | (blue >> 3); -} - - -#if 0 - - - - - - -/*! - @brief Issue multiple 2-byte colors. - @param colors Array of 16-bit 5-6-5 Colors to draw. - @param len How many pixels to draw. -*/ -void Adafruit_TFT8::writePixels(uint16_t *colors, uint32_t len) { - SPI_WRITE_PIXELS((uint8_t*)colors , len * 2); -} - -/*! - @brief Issue a 2-byte color many times. - @param color The 16-bit 5-6-5 Color to draw. - @param len How many pixels to draw. -*/ -void Adafruit_TFT8::writeColor(uint16_t color, uint32_t len) { - - if(!len) return; // Avoid 0-byte transfers - - uint8_t hi = color >> 8, lo = color; - - if(hi != lo) { - } else { - len *= 2; -// Issue as bytes - } -} - -/**************************************************************************/ -/*! - @brief Write a pixel (must have a transaction in progress) - @param x x coordinate - @param y y coordinate - @param color 16-bit 5-6-5 Color to draw with -*/ -/**************************************************************************/ -void Adafruit_SPITFT::writePixel(int16_t x, int16_t y, uint16_t color) { - if((x < 0) ||(x >= _width) || (y < 0) || (y >= _height)) return; - setAddrWindow(x,y,1,1); - writePixel(color); -} - -/**************************************************************************/ -/*! - @brief Write a filled rectangle (must have a transaction in progress) - @param x Top left corner x coordinate - @param y Top left corner y coordinate - @param w Width in pixels - @param h Height in pixels - @param color 16-bit 5-6-5 Color to fill with -*/ -/**************************************************************************/ -void Adafruit_SPITFT::writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color){ - if((x >= _width) || (y >= _height)) return; - int16_t x2 = x + w - 1, y2 = y + h - 1; - if((x2 < 0) || (y2 < 0)) return; - - // Clip left/top - if(x < 0) { - x = 0; - w = x2 + 1; - } - if(y < 0) { - y = 0; - h = y2 + 1; - } - - // Clip right/bottom - if(x2 >= _width) w = _width - x; - if(y2 >= _height) h = _height - y; - - setAddrWindow(x, y, w, h); - writeColor(color, (int32_t)w * h); -} - -/**************************************************************************/ -/*! - @brief Write a perfectly vertical line (must have a transaction in progress) - @param x Top-most x coordinate - @param y Top-most y coordinate - @param h Height in pixels - @param color 16-bit 5-6-5 Color to fill with -*/ -/**************************************************************************/ -void inline Adafruit_SPITFT::writeFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color){ - writeFillRect(x, y, 1, h, color); -} - -/**************************************************************************/ -/*! - @brief Write a perfectly horizontal line (must have a transaction in progress) - @param x Left-most x coordinate - @param y Left-most y coordinate - @param w Width in pixels - @param color 16-bit 5-6-5 Color to fill with -*/ -/**************************************************************************/ -void inline Adafruit_SPITFT::writeFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color){ - writeFillRect(x, y, w, 1, color); -} - -/**************************************************************************/ -/*! - @brief Write a perfectly vertical line - sets up transaction - @param x Top-most x coordinate - @param y Top-most y coordinate - @param h Height in pixels - @param color 16-bit 5-6-5 Color to fill with -*/ -/**************************************************************************/ -void Adafruit_SPITFT::drawFastVLine(int16_t x, int16_t y, - int16_t h, uint16_t color) { - startWrite(); - writeFastVLine(x, y, h, color); - endWrite(); -} - -/**************************************************************************/ -/*! - @brief Write a perfectly horizontal line - sets up transaction - @param x Left-most x coordinate - @param y Left-most y coordinate - @param w Width in pixels - @param color 16-bit 5-6-5 Color to fill with -*/ -/**************************************************************************/ -void Adafruit_SPITFT::drawFastHLine(int16_t x, int16_t y, - int16_t w, uint16_t color) { - startWrite(); - writeFastHLine(x, y, w, color); - endWrite(); -} - -/**************************************************************************/ -/*! - @brief Fill a rectangle completely with one color. - @param x Top left corner x coordinate - @param y Top left corner y coordinate - @param w Width in pixels - @param h Height in pixels - @param color 16-bit 5-6-5 Color to fill with -*/ -/**************************************************************************/ -void Adafruit_SPITFT::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, - uint16_t color) { - startWrite(); - writeFillRect(x,y,w,h,color); - endWrite(); -} - - -/**************************************************************************/ -/*! - @brief Invert the display using built-in hardware command - @param i True if you want to invert, false to make 'normal' -*/ -/**************************************************************************/ -void Adafruit_SPITFT::invertDisplay(boolean i) { - startWrite(); - writeCommand(i ? invertOnCommand : invertOffCommand); - endWrite(); -} - - -/**************************************************************************/ -/*! - @brief Draw a 16-bit image (RGB 5/6/5) at the specified (x,y) position. - For 16-bit display devices; no color reduction performed. - Adapted from https://github.com/PaulStoffregen/ILI9341_t3 - by Marc MERLIN. See examples/pictureEmbed to use this. - 5/6/2017: function name and arguments have changed for compatibility - with current GFX library and to avoid naming problems in prior - implementation. Formerly drawBitmap() with arguments in different order. - - @param x Top left corner x coordinate - @param y Top left corner y coordinate - @param pcolors 16-bit array with 16-bit color bitmap - @param w Width of bitmap in pixels - @param h Height of bitmap in pixels -*/ -/**************************************************************************/ -void Adafruit_SPITFT::drawRGBBitmap(int16_t x, int16_t y, - uint16_t *pcolors, int16_t w, int16_t h) { - - int16_t x2, y2; // Lower-right coord - if(( x >= _width ) || // Off-edge right - ( y >= _height) || // " top - ((x2 = (x+w-1)) < 0 ) || // " left - ((y2 = (y+h-1)) < 0) ) return; // " bottom - - int16_t bx1=0, by1=0, // Clipped top-left within bitmap - saveW=w; // Save original bitmap width value - if(x < 0) { // Clip left - w += x; - bx1 = -x; - x = 0; - } - if(y < 0) { // Clip top - h += y; - by1 = -y; - y = 0; - } - if(x2 >= _width ) w = _width - x; // Clip right - if(y2 >= _height) h = _height - y; // Clip bottom - - pcolors += by1 * saveW + bx1; // Offset bitmap ptr to clipped top-left - startWrite(); - setAddrWindow(x, y, w, h); // Clipped area - while(h--) { // For each (clipped) scanline... - writePixels(pcolors, w); // Push one (clipped) row - pcolors += saveW; // Advance pointer by one full (unclipped) line - } - endWrite(); -} - - - - - - -#endif // 0 - - - - -#endif // !__AVR_ATtiny85__ - diff --git a/Adafruit_TFT8.h b/olde/Adafruit_TFT8.h similarity index 100% rename from Adafruit_TFT8.h rename to olde/Adafruit_TFT8.h diff --git a/olde/ttt.ttt b/olde/ttt.ttt new file mode 100644 index 0000000..e52072c --- /dev/null +++ b/olde/ttt.ttt @@ -0,0 +1,60 @@ +#define TFT_HARD_SPI 0 +#define TFT_SOFT_SPI 1 +#define TFT_PARALLEL 2 + +#if defined(USE_FAST_PINIO) +#if defined(HAS_PORT_SET_CLR) + #define TFT_CS_HIGH() { *csPortSet = csPinMask; } + #define TFT_CS_LOW() { *csPortClr = csPinMask; } + #define TFT_DC_HIGH() { *dcPortSet = dcPinMask; } + #define TFT_DC_LOW() { *dcPortClr = dcPinMask; } + #define TFT_MOSI_HIGH() { *mosiPortSet = mosiPinMask; } + #define TFT_MOSI_LOW() { *mosiPortClr = mosiPinMask; } + #define TFT_SCK_HIGH() { *sckPortSet = sckPinMask; } + #define TFT_SCK_LOW() { *sckPortClr = sckPinMask; } + #define TFT_WR_HIGH() { *wrPortSet = wrPinMask; } + #define TFT_WR_LOW() { *wrPortClr = wrPinMask; } + #define TFT_RD_HIGH() { *rdPortSet = rdPinMask; } + #define TFT_RD_LOW() { *rdPortClr = rdPinMask; } + #define TFT_PORT_OUTPUT() { *dirSet = 0xFF; } + #define TFT_PORT_INPUT() { *dirClr = 0xFF; } + #define TFT_PORT_OUTPUT16() { *(volatile uint16_t *)dirSet = 0xFFFF; } + #define TFT_PORT_INPUT16() { *(volatile uint16_t *)dirClr = 0xFFFF; } +#else // !HAS_PORT_SET_CLR + #define TFT_CS_HIGH() { *csPort |= csPinMaskSet; } + #define TFT_CS_LOW() { *csPort &= csPinMaskClr; } + #define TFT_DC_HIGH() { *dcPort |= dcPinMaskSet; } + #define TFT_DC_LOW() { *dcPort &= dcPinMaskClr; } + #define TFT_MOSI_HIGH() { *mosiPort |= mosiPinMaskSet; } + #define TFT_MOSI_LOW() { *mosiPort &= mosiPinMaskClr; } + #define TFT_SCK_HIGH() { *sckPort |= sckPinMaskSet; } + #define TFT_SCK_LOW() { *sckPort &= sckPinMaskClr; } + #define TFT_WR_HIGH() { *wrPort |= wrPinMaskSet; } + #define TFT_WR_LOW() { *wrPort &= wrPinMaskClr; } + #define TFT_RD_HIGH() { *rdPort |= rdPinMaskSet; } + #define TFT_RD_LOW() { *rdPort &= rdPinMaskClr; } + #define TFT_PORT_OUTPUT() { *portDir = 0xFF; } + #define TFT_PORT_INPUT() { *portDir = 0x00; } + #define TFT_PORT_OUTPUT16() { *(volatile uint16_t *)portDir = 0xFFFF; } + #define TFT_PORT_INPUT16() { *(volatile uint16_t *)portDir = 0x0000; } +#endif // end HAS_PORT_SET_CLR + #define TFT_SPI_READ() (*misoPort & misoPinMask) +#else // !USE_FAST_PINIO + #define TFT_CS_HIGH() digitalWrite(_cs , HIGH) + #define TFT_CS_LOW() digitalWrite(_cs , LOW ) + #define TFT_DC_HIGH() digitalWrite(_dc , HIGH) + #define TFT_DC_LOW() digitalWrite(_dc , LOW ) + #define TFT_MOSI_HIGH() digitalWrite(_mosi, HIGH) + #define TFT_MOSI_LOW() digitalWrite(_mosi, LOW ) + #define TFT_SCK_HIGH() digitalWrite(_sck , HIGH) + #define TFT_SCK_LOW() digitalWrite(_sck , LOW ) + #define TFT_WR_HIGH() digitalWrite(_wr , HIGH) + #define TFT_WR_LOW() digitalWrite(_wr , LOW ) + #define TFT_RD_HIGH() digitalWrite(_rd , HIGH) + #define TFT_RD_LOW() digitalWrite(_rd , LOW ) + #define TFT_PORT_OUTPUT() { } // 8-bit parallel is not supported + #define TFT_PORT_INPUT() { } // if USE_FAST_PINIO is unavailable. + #define TFT_PORT_OUTPUT16() { } // No plans to implement this. If no + #define TFT_PORT_INPUT16() { } // PORT access, use an SPI display! + #define TFT_SPI_READ() digitalRead(_miso) +#endif // end USE_FAST_PINIO