diff --git a/lib/lib_display/Adafruit_SSD1331-1.2.0/Adafruit_SPITFT_Renderer.cpp b/lib/lib_display/Adafruit_SSD1331-1.2.0/Adafruit_SPITFT_Renderer.cpp
new file mode 100644
index 000000000..d49141a28
--- /dev/null
+++ b/lib/lib_display/Adafruit_SSD1331-1.2.0/Adafruit_SPITFT_Renderer.cpp
@@ -0,0 +1,2217 @@
+/*!
+ * @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!
+
+ * @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 for ATtiny, at all
+
+#include "Adafruit_SPITFT_Renderer.h"
+
+#if defined(__AVR__)
+#if defined(__AVR_XMEGA__) //only tested with __AVR_ATmega4809__
+#define AVR_WRITESPI(x) for(SPI0_DATA = (x); (!(SPI0_INTFLAGS & _BV(SPI_IF_bp))); )
+#else
+#define AVR_WRITESPI(x) for(SPDR = (x); (!(SPSR & _BV(SPIF))); )
+#endif
+#endif
+
+#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 // end PORT_IOBUS
+
+#if defined(USE_SPI_DMA)
+ #include
+ #include "wiring_private.h" // pinPeripheral() function
+ #include // memalign() function
+ #define tcNum 2 // Timer/Counter for parallel write strobe PWM
+ #define wrPeripheral PIO_CCL // Use CCL to invert write strobe
+
+ // DMA transfer-in-progress indicator and callback
+ static volatile bool dma_busy = false;
+ static void dma_callback(Adafruit_ZeroDMA *dma) {
+ dma_busy = false;
+ }
+
+ #if defined(__SAMD51__)
+ // Timer/counter info by index #
+ static const struct {
+ Tc *tc; // -> Timer/Counter base address
+ int gclk; // GCLK ID
+ int evu; // EVSYS user ID
+ } tcList[] = {
+ { TC0, TC0_GCLK_ID, EVSYS_ID_USER_TC0_EVU },
+ { TC1, TC1_GCLK_ID, EVSYS_ID_USER_TC1_EVU },
+ { TC2, TC2_GCLK_ID, EVSYS_ID_USER_TC2_EVU },
+ { TC3, TC3_GCLK_ID, EVSYS_ID_USER_TC3_EVU },
+ #if defined(TC4)
+ { TC4, TC4_GCLK_ID, EVSYS_ID_USER_TC4_EVU },
+ #endif
+ #if defined(TC5)
+ { TC5, TC5_GCLK_ID, EVSYS_ID_USER_TC5_EVU },
+ #endif
+ #if defined(TC6)
+ { TC6, TC6_GCLK_ID, EVSYS_ID_USER_TC6_EVU },
+ #endif
+ #if defined(TC7)
+ { TC7, TC7_GCLK_ID, EVSYS_ID_USER_TC7_EVU }
+ #endif
+ };
+ #define NUM_TIMERS (sizeof tcList / sizeof tcList[0]) ///< # timer/counters
+ #endif // end __SAMD51__
+
+#endif // end 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 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.
+*/
+Adafruit_SPITFT::Adafruit_SPITFT(uint16_t w, uint16_t h,
+ int8_t cs, int8_t dc, int8_t mosi, int8_t sck, int8_t rst, int8_t miso) :
+ Renderer(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)
+ #if !defined(KINETISK)
+ dcPinMask = digitalPinToBitMask(dc);
+ swspi.sckPinMask = digitalPinToBitMask(sck);
+ swspi.mosiPinMask = digitalPinToBitMask(mosi);
+ #endif
+ 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) {
+ #if !defined(KINETISK)
+ csPinMask = digitalPinToBitMask(cs);
+ #endif
+ csPortSet = portSetRegister(cs);
+ csPortClr = portClearRegister(cs);
+ } else {
+ #if !defined(KINETISK)
+ csPinMask = 0;
+ #endif
+ csPortSet = dcPortSet;
+ csPortClr = dcPortClr;
+ }
+ if(miso >= 0) {
+ swspi.misoPort = portInputRegister(miso);
+ #if !defined(KINETISK)
+ swspi.misoPinMask = digitalPinToBitMask(miso);
+ #endif
+ } 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.
+ csPortSet = dcPortSet;
+ csPortClr = dcPortClr;
+ csPinMask = 0;
+ }
+ if(miso >= 0) {
+ swspi.misoPinMask=digitalPinToBitMask(miso);
+ swspi.misoPort =(PORTreg_t)portInputRegister(digitalPinToPort(miso));
+ } else {
+ swspi.misoPinMask=0;
+ swspi.misoPort =(PORTreg_t)portInputRegister(digitalPinToPort(dc));
+ }
+ #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(miso);
+ } 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 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.
+*/
+#if defined(ESP8266) // See notes below
+Adafruit_SPITFT::Adafruit_SPITFT(uint16_t w, uint16_t h, int8_t cs,
+ int8_t dc, int8_t rst) : Renderer(w, h),
+ connection(TFT_HARD_SPI), _rst(rst), _cs(cs), _dc(dc) {
+ hwspi._spi = &SPI;
+}
+#else // !ESP8266
+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).
+}
+#endif // end !ESP8266
+
+#if !defined(ESP8266)
+// ESP8266 compiler freaks out at this constructor -- it can't disambiguate
+// beteween the SPIClass pointer (argument #3) and a regular integer.
+// Solution here it to just not offer this variant on the ESP8266. You can
+// use the default hardware SPI peripheral, or you can use software SPI,
+// but if there's any library out there that creates a 'virtual' SPIClass
+// peripheral and drives it with software bitbanging, that's not supported.
+/*!
+ @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. &SPI or &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 in constructor; application
+ typically will need to call subclass' begin() function, which
+ in turn calls this library's initSPI() function to initialize
+ pins. EXCEPT...if you have built your own SERCOM SPI peripheral
+ (calling the SPIClass constructor) rather than one of the
+ built-in SPI devices (e.g. &SPI, &SPI1 and so forth), you will
+ need to call the begin() function for your object as well as
+ pinPeripheral() for the MOSI, MISO and SCK pins to configure
+ GPIO manually. Do this BEFORE calling the display-specific
+ begin or init function. Unfortunate but unavoidable.
+*/
+Adafruit_SPITFT::Adafruit_SPITFT(uint16_t w, uint16_t h, SPIClass *spiClass,
+ int8_t cs, int8_t dc, int8_t rst) : Renderer(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)
+ #if !defined(KINETISK)
+ dcPinMask = digitalPinToBitMask(dc);
+ #endif
+ dcPortSet = portSetRegister(dc);
+ dcPortClr = portClearRegister(dc);
+ if(cs >= 0) {
+ #if !defined(KINETISK)
+ csPinMask = digitalPinToBitMask(cs);
+ #endif
+ csPortSet = portSetRegister(cs);
+ csPortClr = portClearRegister(cs);
+ } else { // see comments below
+ #if !defined(KINETISK)
+ csPinMask = 0;
+ #endif
+ csPortSet = dcPortSet;
+ csPortClr = dcPortClr;
+ }
+ #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
+}
+#endif // end !ESP8266
+
+/*!
+ @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 busWidth If tft16 (enumeration in header file), is a 16-bit
+ parallel connection, else 8-bit.
+ 16-bit isn't fully implemented or tested yet so
+ applications should pass "tft8bitbus" for now...needed to
+ stick a required enum 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 rd 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, tftBusWidth busWidth,
+ int8_t d0, int8_t wr, int8_t dc, int8_t cs, int8_t rst, int8_t rd) :
+ Renderer(w, h), connection(TFT_PARALLEL), _rst(rst), _cs(cs), _dc(dc) {
+ tft8._d0 = d0;
+ tft8._wr = wr;
+ tft8._rd = rd;
+ tft8.wide = (busWidth == tft16bitbus);
+#if defined(USE_FAST_PINIO)
+ #if defined(HAS_PORT_SET_CLR)
+ #if defined(CORE_TEENSY)
+ tft8.wrPortSet = portSetRegister(wr);
+ tft8.wrPortClr = portClearRegister(wr);
+ #if !defined(KINETISK)
+ dcPinMask = digitalPinToBitMask(dc);
+ #endif
+ dcPortSet = portSetRegister(dc);
+ dcPortClr = portClearRegister(dc);
+ if(cs >= 0) {
+ #if !defined(KINETISK)
+ csPinMask = digitalPinToBitMask(cs);
+ #endif
+ csPortSet = portSetRegister(cs);
+ csPortClr = portClearRegister(cs);
+ } else { // see comments below
+ #if !defined(KINETISK)
+ csPinMask = 0;
+ #endif
+ csPortSet = dcPortSet;
+ csPortClr = dcPortClr;
+ }
+ 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(tft8.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.
+ @param spiMode SPI mode when using hardware SPI. MUST be one of the
+ values SPI_MODE0, SPI_MODE1, SPI_MODE2 or SPI_MODE3
+ defined in SPI.h. Do NOT attempt to pass '0' for
+ SPI_MODE0 and so forth...the values are NOT the same!
+ Use ONLY the defines! (Pity it's not an enum.)
+ @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, uint8_t spiMode) {
+
+ 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
+ }
+ pinMode(_dc, OUTPUT);
+ digitalWrite(_dc, HIGH); // Data mode
+
+ if(connection == TFT_HARD_SPI) {
+
+#if defined(SPI_HAS_TRANSACTION)
+ hwspi.settings = SPISettings(freq, MSBFIRST, spiMode);
+#else
+ hwspi._freq = freq; // Save freq value for later
+#endif
+ hwspi._mode = spiMode; // Save spiMode value for later
+ // Call hwspi._spi->begin() ONLY if this is among the 'established'
+ // SPI interfaces in variant.h. For DIY roll-your-own SERCOM SPIs,
+ // begin() and pinPeripheral() calls MUST be made in one's calling
+ // code, BEFORE the screen-specific begin/init function is called.
+ // Reason for this is that SPI::begin() makes its own calls to
+ // pinPeripheral() based on g_APinDescription[n].ulPinType, which
+ // on non-established SPI interface pins will always be PIO_DIGITAL
+ // or similar, while we need PIO_SERCOM or PIO_SERCOM_ALT...it's
+ // highly unique between devices and variants for each pin or
+ // SERCOM so we can't make those calls ourselves here. And the SPI
+ // device needs to be set up before calling this because it's
+ // immediately followed with initialization commands. Blargh.
+ if(
+#if !defined(SPI_INTERFACES_COUNT)
+ 1
+#endif
+#if SPI_INTERFACES_COUNT > 0
+ (hwspi._spi == &SPI)
+#endif
+#if SPI_INTERFACES_COUNT > 1
+ || (hwspi._spi == &SPI1)
+#endif
+#if SPI_INTERFACES_COUNT > 2
+ || (hwspi._spi == &SPI2)
+#endif
+#if SPI_INTERFACES_COUNT > 3
+ || (hwspi._spi == &SPI3)
+#endif
+#if SPI_INTERFACES_COUNT > 4
+ || (hwspi._spi == &SPI4)
+#endif
+#if SPI_INTERFACES_COUNT > 5
+ || (hwspi._spi == &SPI5)
+#endif
+ ) {
+ hwspi._spi->begin();
+ }
+ } else if(connection == 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);
+ }
+
+ } else { // 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);
+ }
+ }
+
+ if(_rst >= 0) {
+ // Toggle _rst low to reset
+ pinMode(_rst, OUTPUT);
+ digitalWrite(_rst, HIGH);
+ delay(100);
+ digitalWrite(_rst, LOW);
+ delay(100);
+ digitalWrite(_rst, HIGH);
+ delay(200);
+ }
+
+#if defined(USE_SPI_DMA)
+ if(((connection == TFT_HARD_SPI) || (connection == TFT_PARALLEL)) &&
+ (dma.allocate() == DMA_STATUS_OK)) { // Allocate channel
+ // The DMA library needs to alloc at least one valid descriptor,
+ // so we do that here. It's not used in the usual sense though,
+ // just before a transfer we copy descriptor[0] to this address.
+ if(dptr = dma.addDescriptor(NULL, NULL, 42, DMA_BEAT_SIZE_BYTE,
+ false, false)) {
+ // Alloc 2 scanlines worth of pixels on display's major axis,
+ // whichever that is, rounding each up to 2-pixel boundary.
+ int major = (WIDTH > HEIGHT) ? WIDTH : HEIGHT;
+ major += (major & 1); // -> next 2-pixel bound, if needed.
+ maxFillLen = major * 2; // 2 scanlines
+ // Note to future self: if you decide to make the pixel buffer
+ // much larger, remember that DMA transfer descriptors can't
+ // exceed 65,535 bytes (not 65,536), meaning 32,767 pixels max.
+ // Not that we have that kind of RAM to throw around right now.
+ if((pixelBuf[0] =
+ (uint16_t *)malloc(maxFillLen * sizeof(uint16_t)))) {
+ // Alloc OK. Get pointer to start of second scanline.
+ pixelBuf[1] = &pixelBuf[0][major];
+ // Determine number of DMA descriptors needed to cover
+ // entire screen when entire 2-line pixelBuf is used
+ // (round up for fractional last descriptor).
+ int numDescriptors = (WIDTH * HEIGHT + (maxFillLen - 1)) /
+ maxFillLen;
+ // DMA descriptors MUST be 128-bit (16 byte) aligned.
+ // memalign() is considered obsolete but it's replacements
+ // (aligned_alloc() or posix_memalign()) are not currently
+ // available in the version of ARM GCC in use, but this
+ // is, so here we are.
+ if((descriptor = (DmacDescriptor *)memalign(16,
+ numDescriptors * sizeof(DmacDescriptor)))) {
+ int dmac_id;
+ volatile uint32_t *data_reg;
+
+ if(connection == TFT_HARD_SPI) {
+ // THIS IS AN AFFRONT TO NATURE, but I don't know
+ // any "clean" way to get the sercom number from the
+ // the SPIClass pointer (e.g. &SPI or &SPI1), which
+ // is all we have to work with. SPIClass does contain
+ // a SERCOM pointer but it is a PRIVATE member!
+ // Doing an UNSPEAKABLY HORRIBLE THING here, directly
+ // accessing the first 32-bit value in the SPIClass
+ // structure, knowing that's (currently) where the
+ // SERCOM pointer lives, but this ENTIRELY DEPENDS on
+ // that structure not changing nor the compiler
+ // rearranging things. Oh the humanity!
+
+ if(*(SERCOM **)hwspi._spi == &sercom0) {
+ dmac_id = SERCOM0_DMAC_ID_TX;
+ data_reg = &SERCOM0->SPI.DATA.reg;
+#if defined 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 **)hwspi._spi == &sercom2) {
+ dmac_id = SERCOM2_DMAC_ID_TX;
+ data_reg = &SERCOM2->SPI.DATA.reg;
+#endif
+#if defined 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 **)hwspi._spi == &sercom4) {
+ dmac_id = SERCOM4_DMAC_ID_TX;
+ data_reg = &SERCOM4->SPI.DATA.reg;
+#endif
+#if defined SERCOM5
+ } else if(*(SERCOM **)hwspi._spi == &sercom5) {
+ dmac_id = SERCOM5_DMAC_ID_TX;
+ data_reg = &SERCOM5->SPI.DATA.reg;
+#endif
+#if defined SERCOM6
+ } else if(*(SERCOM **)hwspi._spi == &sercom6) {
+ dmac_id = SERCOM6_DMAC_ID_TX;
+ data_reg = &SERCOM6->SPI.DATA.reg;
+#endif
+#if defined SERCOM7
+ } else if(*(SERCOM **)hwspi._spi == &sercom7) {
+ dmac_id = SERCOM7_DMAC_ID_TX;
+ data_reg = &SERCOM7->SPI.DATA.reg;
+#endif
+ }
+ dma.setPriority(DMA_PRIORITY_3);
+ dma.setTrigger(dmac_id);
+ dma.setAction(DMA_TRIGGER_ACTON_BEAT);
+
+ // Initialize descriptor list.
+ for(int d=0; dChannel[dmaChannel].CHEVCTRL.bit.EVOE = 1;
+ DMAC->Channel[dmaChannel].CHEVCTRL.bit.EVOMODE = 0;
+
+ // CONFIGURE TIMER/COUNTER (for write strobe)
+
+ Tc *timer = tcList[tcNum].tc; // -> Timer struct
+ int id = tcList[tcNum].gclk; // Timer GCLK ID
+ GCLK_PCHCTRL_Type pchctrl;
+
+ // Set up timer clock source from GCLK
+ GCLK->PCHCTRL[id].bit.CHEN = 0; // Stop timer
+ while(GCLK->PCHCTRL[id].bit.CHEN); // Wait for it
+ pchctrl.bit.GEN = GCLK_PCHCTRL_GEN_GCLK0_Val;
+ pchctrl.bit.CHEN = 1; // Enable
+ GCLK->PCHCTRL[id].reg = pchctrl.reg;
+ while(!GCLK->PCHCTRL[id].bit.CHEN); // Wait for it
+
+ // Disable timer/counter before configuring it
+ timer->COUNT8.CTRLA.bit.ENABLE = 0;
+ while(timer->COUNT8.SYNCBUSY.bit.STATUS);
+
+ timer->COUNT8.WAVE.bit.WAVEGEN = 2; // NPWM
+ timer->COUNT8.CTRLA.bit.MODE = 1; // 8-bit
+ timer->COUNT8.CTRLA.bit.PRESCALER = 0; // 1:1
+ while(timer->COUNT8.SYNCBUSY.bit.STATUS);
+
+ timer->COUNT8.CTRLBCLR.bit.DIR = 1; // Count UP
+ while(timer->COUNT8.SYNCBUSY.bit.CTRLB);
+ timer->COUNT8.CTRLBSET.bit.ONESHOT = 1; // One-shot
+ while(timer->COUNT8.SYNCBUSY.bit.CTRLB);
+ timer->COUNT8.PER.reg = 6; // PWM top
+ while(timer->COUNT8.SYNCBUSY.bit.PER);
+ timer->COUNT8.CC[0].reg = 2; // Compare
+ while(timer->COUNT8.SYNCBUSY.bit.CC0);
+ // Enable async input events,
+ // event action = restart.
+ timer->COUNT8.EVCTRL.bit.TCEI = 1;
+ timer->COUNT8.EVCTRL.bit.EVACT = 1;
+
+ // Enable timer
+ timer->COUNT8.CTRLA.reg |= TC_CTRLA_ENABLE;
+ while(timer->COUNT8.SYNCBUSY.bit.STATUS);
+
+#if(wrPeripheral == PIO_CCL)
+ // CONFIGURE CCL (inverts timer/counter output)
+
+ MCLK->APBCMASK.bit.CCL_ = 1; // Enable CCL clock
+ CCL->CTRL.bit.ENABLE = 0; // Disable to config
+ CCL->CTRL.bit.SWRST = 1; // Reset CCL registers
+ CCL->LUTCTRL[tcNum].bit.ENABLE = 0; // Disable LUT
+ CCL->LUTCTRL[tcNum].bit.FILTSEL = 0; // No filter
+ CCL->LUTCTRL[tcNum].bit.INSEL0 = 6; // TC input
+ CCL->LUTCTRL[tcNum].bit.INSEL1 = 0; // MASK
+ CCL->LUTCTRL[tcNum].bit.INSEL2 = 0; // MASK
+ CCL->LUTCTRL[tcNum].bit.TRUTH = 1; // Invert in 0
+ CCL->LUTCTRL[tcNum].bit.ENABLE = 1; // Enable LUT
+ CCL->CTRL.bit.ENABLE = 1; // Enable CCL
+#endif
+
+ // CONFIGURE EVENT SYSTEM
+
+ // Set up event system clock source from GCLK...
+ // Disable EVSYS, wait for disable
+ GCLK->PCHCTRL[EVSYS_GCLK_ID_0].bit.CHEN = 0;
+ while(GCLK->PCHCTRL[EVSYS_GCLK_ID_0].bit.CHEN);
+ pchctrl.bit.GEN = GCLK_PCHCTRL_GEN_GCLK0_Val;
+ pchctrl.bit.CHEN = 1; // Re-enable
+ GCLK->PCHCTRL[EVSYS_GCLK_ID_0].reg = pchctrl.reg;
+ // Wait for it, then enable EVSYS clock
+ while(!GCLK->PCHCTRL[EVSYS_GCLK_ID_0].bit.CHEN);
+ MCLK->APBBMASK.bit.EVSYS_ = 1;
+
+ // Connect Timer EVU to ch 0
+ EVSYS->USER[tcList[tcNum].evu].reg = 1;
+ // Datasheet recommends single write operation;
+ // reg instead of bit. Also datasheet: PATH bits
+ // must be zero when using async!
+ EVSYS_CHANNEL_Type ev;
+ ev.reg = 0;
+ ev.bit.PATH = 2; // Asynchronous
+ ev.bit.EVGEN = 0x22 + dmaChannel; // DMA channel 0+
+ EVSYS->Channel[0].CHANNEL.reg = ev.reg;
+
+ // Initialize descriptor list.
+ for(int d=0; d= 0) SPI_CS_LOW();
+}
+
+/*!
+ @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::endWrite(void) {
+ if(_cs >= 0) SPI_CS_HIGH();
+ SPI_END_TRANSACTION();
+}
+
+
+// -------------------------------------------------------------------------
+// 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.
+ @param block If true (default case if unspecified), function blocks
+ until DMA transfer is complete. This is simply IGNORED
+ if DMA is not enabled. If false, the function returns
+ immediately after the last DMA transfer is started,
+ and one should use the dmaWait() function before
+ doing ANY other display-related activities (or even
+ any SPI-related activities, if using an SPI display
+ that shares the bus with other devices).
+ @param bigEndian If using DMA, and if set true, bitmap in memory is in
+ big-endian order (most significant byte first). By
+ default this is false, as most microcontrollers seem
+ to be little-endian and 16-bit pixel values must be
+ byte-swapped before issuing to the display (which tend
+ to be big-endian when using SPI or 8-bit parallel).
+ If an application can optimize around this -- for
+ example, a bitmap in a uint16_t array having the byte
+ values already reordered big-endian, this can save
+ some processing time here, ESPECIALLY if using this
+ function's non-blocking DMA mode. Not all cases are
+ covered...this is really here only for SAMD DMA and
+ much forethought on the application side.
+*/
+void Adafruit_SPITFT::writePixels(uint16_t *colors, uint32_t len,
+ bool block, bool bigEndian) {
+
+ 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;
+ }
+#elif defined(USE_SPI_DMA)
+ if((connection == TFT_HARD_SPI) || (connection == TFT_PARALLEL)) {
+ int maxSpan = maxFillLen / 2; // One scanline max
+ uint8_t pixelBufIdx = 0; // Active pixel buffer number
+ #if defined(__SAMD51__)
+ if(connection == TFT_PARALLEL) {
+ // Switch WR pin to PWM or CCL
+ pinPeripheral(tft8._wr, wrPeripheral);
+ }
+ #endif // end __SAMD51__
+ if(!bigEndian) { // Normal little-endian situation...
+ while(len) {
+ int count = (len < maxSpan) ? len : maxSpan;
+
+ // Because TFT and SAMD endianisms are different, must swap
+ // bytes from the 'colors' array passed into a DMA working
+ // buffer. This can take place while the prior DMA transfer
+ // is in progress, hence the need for two pixelBufs.
+ for(int i=0; isetDataMode(hwspi._mode);
+ } else {
+ pinPeripheral(tft8._wr, PIO_OUTPUT); // Switch WR back to GPIO
+ }
+ #endif // end __SAMD51__ || _SAMD21_
+ }
+ return;
+ }
+#endif // end USE_SPI_DMA
+
+ // All other cases (bitbang SPI or non-DMA hard SPI or parallel),
+ // use a loop with the normal 16-bit data write function:
+ while(len--) {
+ SPI_WRITE16(*colors++);
+ }
+}
+
+/*!
+ @brief Wait for the last DMA transfer in a prior non-blocking
+ writePixels() call to complete. This does nothing if DMA
+ is not enabled, and is not needed if blocking writePixels()
+ was used (as is the default case).
+*/
+void Adafruit_SPITFT::dmaWait(void) {
+#if defined(USE_SPI_DMA)
+ while(dma_busy);
+ #if defined(__SAMD51__) || defined(_SAMD21_)
+ if(connection == TFT_HARD_SPI) {
+ // See SAMD51/21 note in writeColor()
+ hwspi._spi->setDataMode(hwspi._mode);
+ } else {
+ pinPeripheral(tft8._wr, PIO_OUTPUT); // Switch WR back to GPIO
+ }
+ #endif // end __SAMD51__ || _SAMD21_
+#endif
+}
+
+/*!
+ @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 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; t= 16)) { // Don't bother with DMA on short pixel runs
+ int i, d, numDescriptors;
+ if(hi == lo) { // If high & low bytes are same...
+ onePixelBuf = color;
+ // Can do this with a relatively short descriptor list,
+ // each transferring a max of 32,767 (not 32,768) pixels.
+ // This won't run off the end of the allocated descriptor list,
+ // since we're using much larger chunks per descriptor here.
+ numDescriptors = (len + 32766) / 32767;
+ for(d=0; d lastFillLen) {
+ int fillStart = lastFillLen / 2,
+ fillEnd = (((len < maxFillLen) ?
+ len : maxFillLen) + 1) / 2;
+ for(i=fillStart; isetDataMode(hwspi._mode);
+ } else {
+ pinPeripheral(tft8._wr, PIO_OUTPUT); // Switch WR back to GPIO
+ }
+ #endif // end __SAMD51__
+ return;
+ }
+ #endif // end USE_SPI_DMA
+#endif // end !ESP32
+
+ // All other cases (non-DMA hard SPI, bitbang SPI, parallel)...
+
+ if(connection == TFT_HARD_SPI) {
+#if defined(ESP8266)
+ do {
+ uint32_t pixelsThisPass = len;
+ if(pixelsThisPass > 50000) pixelsThisPass = 50000;
+ len -= pixelsThisPass;
+ yield(); // Periodic yield() on long fills
+ while(pixelsThisPass--) {
+ hwspi._spi->write(hi);
+ hwspi._spi->write(lo);
+ }
+ } while(len);
+#else // !ESP8266
+ while(len--) {
+ #if defined(__AVR__)
+ AVR_WRITESPI(hi);
+ AVR_WRITESPI(lo);
+ #elif defined(ESP32)
+ hwspi._spi->write(hi);
+ hwspi._spi->write(lo);
+ #else
+ hwspi._spi->transfer(hi);
+ hwspi._spi->transfer(lo);
+ #endif
+ }
+#endif // end !ESP8266
+ } else if(connection == TFT_SOFT_SPI) {
+#if defined(ESP8266)
+ do {
+ uint32_t pixelsThisPass = len;
+ if(pixelsThisPass > 20000) pixelsThisPass = 20000;
+ len -= pixelsThisPass;
+ yield(); // Periodic yield() on long fills
+ while(pixelsThisPass--) {
+ for(uint16_t bit=0, x=color; bit<16; bit++) {
+ if(x & 0x8000) SPI_MOSI_HIGH();
+ else SPI_MOSI_LOW();
+ SPI_SCK_HIGH();
+ SPI_SCK_LOW();
+ x <<= 1;
+ }
+ }
+ } while(len);
+#else // !ESP8266
+ while(len--) {
+ #if defined(__AVR__)
+ for(uint8_t bit=0, x=hi; bit<8; bit++) {
+ if(x & 0x80) SPI_MOSI_HIGH();
+ else SPI_MOSI_LOW();
+ SPI_SCK_HIGH();
+ SPI_SCK_LOW();
+ x <<= 1;
+ }
+ for(uint8_t bit=0, x=lo; bit<8; bit++) {
+ if(x & 0x80) SPI_MOSI_HIGH();
+ else SPI_MOSI_LOW();
+ SPI_SCK_HIGH();
+ SPI_SCK_LOW();
+ x <<= 1;
+ }
+ #else // !__AVR__
+ for(uint16_t bit=0, x=color; bit<16; bit++) {
+ if(x & 0x8000) SPI_MOSI_HIGH();
+ else SPI_MOSI_LOW();
+ SPI_SCK_HIGH();
+ x <<= 1;
+ SPI_SCK_LOW();
+ }
+ #endif // end !__AVR__
+ }
+#endif // end !ESP8266
+ } else { // PARALLEL
+ if(hi == lo) {
+#if defined(__AVR__)
+ len *= 2;
+ *tft8.writePort = hi;
+ while(len--) {
+ TFT_WR_STROBE();
+ }
+#elif defined(USE_FAST_PINIO)
+ if(!tft8.wide) {
+ len *= 2;
+ *tft8.writePort = hi;
+ } else {
+ *(volatile uint16_t *)tft8.writePort = color;
+ }
+ while(len--) {
+ TFT_WR_STROBE();
+ }
+#endif
+ } else {
+ while(len--) {
+#if defined(__AVR__)
+ *tft8.writePort = hi;
+ TFT_WR_STROBE();
+ *tft8.writePort = lo;
+#elif defined(USE_FAST_PINIO)
+ if(!tft8.wide) {
+ *tft8.writePort = hi;
+ TFT_WR_STROBE();
+ *tft8.writePort = lo;
+ } else {
+ *(volatile uint16_t *)tft8.writePort = color;
+ }
+#endif
+ TFT_WR_STROBE();
+ }
+ }
+ }
+}
+
+/*!
+ @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 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 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 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 h Line height in pixels (positive = below first point,
+ negative = above first point).
+ @param color 16-bit line color in '565' RGB format.
+*/
+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);
+ }
+ }
+ }
+}
+
+/*!
+ @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, (uint32_t)w * h);
+}
+
+
+// -------------------------------------------------------------------------
+// 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 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) {
+ // 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);
+ SPI_WRITE16(color);
+ endWrite();
+ }
+}
+
+/*!
+ @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::fillRect(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
+ 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 h 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();
+ SPI_WRITE16(color);
+ endWrite();
+}
+
+/*!
+ @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::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();
+}
+
+
+// -------------------------------------------------------------------------
+// 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).
+ @return 'Packed' 16-bit color value (565 format).
+*/
+uint16_t Adafruit_SPITFT::color565(uint8_t red, uint8_t green, uint8_t blue) {
+ return ((red & 0xF8) << 8) | ((green & 0xFC) << 3) | (blue >> 3);
+}
+
+/*!
+ @brief Adafruit_SPITFT Send Command handles complete sending of commands and data
+ @param commandByte The Command Byte
+ @param dataBytes A pointer to the Data bytes to send
+ @param numDataBytes The number of bytes we should send
+ */
+void Adafruit_SPITFT::sendCommand(uint8_t commandByte, uint8_t *dataBytes, uint8_t numDataBytes) {
+ SPI_BEGIN_TRANSACTION();
+ if(_cs >= 0) SPI_CS_LOW();
+
+ SPI_DC_LOW(); // Command mode
+ spiWrite(commandByte); // Send the command byte
+
+ SPI_DC_HIGH();
+ for (int i=0; i= 0) SPI_CS_HIGH();
+ SPI_END_TRANSACTION();
+}
+
+/*!
+ @brief Adafruit_SPITFT Send Command handles complete sending of commands and const data
+ @param commandByte The Command Byte
+ @param dataBytes A pointer to the Data bytes to send
+ @param numDataBytes The number of bytes we should send
+ */
+void Adafruit_SPITFT::sendCommand(uint8_t commandByte, const uint8_t *dataBytes, uint8_t numDataBytes) {
+ SPI_BEGIN_TRANSACTION();
+ if(_cs >= 0) SPI_CS_LOW();
+
+ SPI_DC_LOW(); // Command mode
+ spiWrite(commandByte); // Send the command byte
+
+ SPI_DC_HIGH();
+ for (int i=0; i= 0) SPI_CS_HIGH();
+ SPI_END_TRANSACTION();
+}
+
+/*!
+ @brief Read 8 bits of data from display configuration memory (not RAM).
+ This is highly undocumented/supported and should be avoided,
+ function is only included because some of the examples use it.
+ @param commandByte
+ The command register to read data from.
+ @param index
+ The byte index into the command to read from.
+ @return Unsigned 8-bit data read from display register.
+ */
+/**************************************************************************/
+uint8_t Adafruit_SPITFT::readcommand8(uint8_t commandByte, uint8_t index) {
+ uint8_t result;
+ startWrite();
+ SPI_DC_LOW(); // Command mode
+ spiWrite(commandByte);
+ SPI_DC_HIGH(); // Data mode
+ do {
+ result = spiRead();
+ } while(index--); // Discard bytes up to index'th
+ endWrite();
+ return result;
+}
+
+// -------------------------------------------------------------------------
+// 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(hwspi._mode);
+#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) {
+ if(connection == TFT_HARD_SPI) {
+#if defined(__AVR__)
+ AVR_WRITESPI(b);
+#elif defined(ESP8266) || defined(ESP32)
+ hwspi._spi->write(b);
+#else
+ hwspi._spi->transfer(b);
+#endif
+ } else if(connection == TFT_SOFT_SPI) {
+ for(uint8_t bit=0; bit<8; bit++) {
+ if(b & 0x80) SPI_MOSI_HIGH();
+ else SPI_MOSI_LOW();
+ SPI_SCK_HIGH();
+ b <<= 1;
+ SPI_SCK_LOW();
+ }
+ } else { // 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();
+ }
+}
+
+/*!
+ @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 cmd 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;
+ if(connection == TFT_HARD_SPI) {
+ return hwspi._spi->transfer((uint8_t)0);
+ } else if(connection == TFT_SOFT_SPI) {
+ if(swspi._miso >= 0) {
+ for(uint8_t i=0; i<8; i++) {
+ SPI_SCK_HIGH();
+ b <<= 1;
+ if(SPI_MISO_READ()) b++;
+ SPI_SCK_LOW();
+ }
+ }
+ return b;
+ } else { // TFT_PARALLEL
+ if(tft8._rd >= 0) {
+#if defined(USE_FAST_PINIO)
+ TFT_RD_LOW(); // Read line 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.portDir = 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
+ }
+ TFT_RD_HIGH(); // Read line HIGH
+ #endif // end !__AVR__
+#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);
+ #if defined(ESP32)
+ for(volatile uint8_t i=0; i<1; i++);
+ #endif // end ESP32
+#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);
+ #if defined(ESP32)
+ for(volatile uint8_t i=0; i<1; i++);
+ #endif // end ESP32
+#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;
+ #if defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x
+ for(volatile uint8_t i=0; i<1; i++);
+ #endif
+ #endif
+ #else // !HAS_PORT_SET_CLR
+ *swspi.sckPort |= swspi.sckPinMaskSet;
+ #endif // end !HAS_PORT_SET_CLR
+#else // !USE_FAST_PINIO
+ digitalWrite(swspi._sck, HIGH);
+ #if defined(ESP32)
+ for(volatile uint8_t i=0; i<1; i++);
+ #endif // end ESP32
+#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;
+ #if defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x
+ for(volatile uint8_t i=0; i<1; i++);
+ #endif
+ #endif
+ #else // !HAS_PORT_SET_CLR
+ *swspi.sckPort &= swspi.sckPinMaskClr;
+ #endif // end !HAS_PORT_SET_CLR
+#else // !USE_FAST_PINIO
+ digitalWrite(swspi._sck, LOW);
+ #if defined(ESP32)
+ for(volatile uint8_t i=0; i<1; i++);
+ #endif // end ESP32
+#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) {
+ if(connection == TFT_HARD_SPI) {
+#if defined(__AVR__)
+ AVR_WRITESPI(w >> 8);
+ AVR_WRITESPI(w);
+#elif defined(ESP8266) || defined(ESP32)
+ hwspi._spi->write16(w);
+#else
+ hwspi._spi->transfer(w >> 8);
+ hwspi._spi->transfer(w);
+#endif
+ } else if(connection == 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;
+ }
+ } else { // 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();
+ }
+}
+
+/*!
+ @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 l 32-bit value to write.
+*/
+void Adafruit_SPITFT::SPI_WRITE32(uint32_t l) {
+ if(connection == TFT_HARD_SPI) {
+#if defined(__AVR__)
+ AVR_WRITESPI(l >> 24);
+ AVR_WRITESPI(l >> 16);
+ AVR_WRITESPI(l >> 8);
+ AVR_WRITESPI(l );
+#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
+ } else if(connection == 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;
+ }
+ } else { // 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();
+ }
+}
+
+/*!
+ @brief Set the WR 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/lib/lib_display/Adafruit_SSD1331-1.2.0/Adafruit_SPITFT_Renderer.h b/lib/lib_display/Adafruit_SSD1331-1.2.0/Adafruit_SPITFT_Renderer.h
new file mode 100644
index 000000000..dcfc1646b
--- /dev/null
+++ b/lib/lib_display/Adafruit_SSD1331-1.2.0/Adafruit_SPITFT_Renderer.h
@@ -0,0 +1,520 @@
+/*!
+ * @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!
+ *
+ * Written by Limor "ladyada" Fried for Adafruit Industries,
+ * with contributions from the open source community.
+ *
+ * BSD license, all text here must be included in any redistribution.
+ */
+
+#ifndef _ADAFRUIT_SPITFT_H_
+#define _ADAFRUIT_SPITFT_H_
+
+#if !defined(__AVR_ATtiny85__) // Not for ATtiny, at all
+
+#include
+#include "Adafruit_GFX.h"
+#include "renderer.h"
+
+// HARDWARE CONFIG ---------------------------------------------------------
+
+#if defined(__AVR__)
+ typedef uint8_t ADAGFX_PORT_t; ///< PORT values are 8-bit
+ #define USE_FAST_PINIO ///< Use direct PORT register access
+#elif defined(ARDUINO_STM32_FEATHER) // WICED
+ typedef class HardwareSPI SPIClass; ///< SPI is a bit odd on WICED
+ typedef uint32_t ADAGFX_PORT_t; ///< PORT values are 32-bit
+#elif defined(__arm__)
+ #if defined(ARDUINO_ARCH_SAMD)
+ // Adafruit M0, M4
+ typedef uint32_t ADAGFX_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
+ #elif defined(CORE_TEENSY)
+ // PJRC Teensy 4.x
+ #if defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x
+ typedef uint32_t ADAGFX_PORT_t; ///< PORT values are 32-bit
+ // PJRC Teensy 3.x
+ #else
+ typedef uint8_t ADAGFX_PORT_t; ///< PORT values are 8-bit
+ #endif
+ #define USE_FAST_PINIO ///< Use direct PORT register access
+ #define HAS_PORT_SET_CLR ///< PORTs have set & clear registers
+ #else
+ // Arduino Due?
+ typedef uint32_t ADAGFX_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).
+ #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 ADAGFX_PORT_t; ///< PORT values are 32-bit
+#endif // end !ARM
+typedef volatile ADAGFX_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
+
+#if defined(ADAFRUIT_PYPORTAL) || defined(ADAFRUIT_PYBADGE_M4_EXPRESS) || defined(ADAFRUIT_PYGAMER_M4_EXPRESS)
+ #define USE_SPI_DMA ///< Auto DMA if using PyPortal
+#else
+ //#define USE_SPI_DMA ///< If set, use DMA if available
+#endif
+// Another "oops" name -- this now also handles parallel DMA.
+// 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 ///< DMA currently for SAMD chips only
+#endif
+
+#if defined(USE_SPI_DMA)
+ #pragma message ("GFX DMA IS ENABLED. HIGHLY EXPERIMENTAL.")
+ #include
+#endif
+
+// This is kind of a kludge. Needed a way to disambiguate the software SPI
+// and parallel constructors via their argument lists. Originally tried a
+// bool as the first argument to the parallel constructor (specifying 8-bit
+// vs 16-bit interface) but the compiler regards this as equivalent to an
+// integer and thus still ambiguous. SO...the parallel constructor requires
+// an enumerated type as the first argument: tft8 (for 8-bit parallel) or
+// tft16 (for 16-bit)...even though 16-bit isn't fully implemented or tested
+// and might never be, still needed that disambiguation from soft SPI.
+enum tftBusWidth { tft8bitbus, tft16bitbus }; ///< For first arg to parallel constructor
+
+// CLASS DEFINITION --------------------------------------------------------
+
+/*!
+ @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 Renderer {
+
+ public:
+
+ // CONSTRUCTORS --------------------------------------------------------
+
+ // 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);
+
+ // 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);
+
+#if !defined(ESP8266) // See notes in .cpp
+ // 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);
+#endif // end !ESP8266
+
+ // 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, tftBusWidth busWidth,
+ int8_t d0, int8_t wr, int8_t dc,
+ int8_t cs = -1, int8_t rst = -1, int8_t rd = -1);
+
+ // CLASS MEMBER FUNCTIONS ----------------------------------------------
+
+ // These first two functions MUST be declared by subclasses:
+
+ /*!
+ @brief Display-specific initialization function.
+ @param freq SPI frequency, in hz (or 0 for default or unused).
+ */
+ virtual void begin(uint32_t freq) = 0;
+
+ /*!
+ @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;
+
+ // 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.
+
+ // Subclass' begin() function invokes this to initialize hardware.
+ // freq=0 to use default SPI speed. spiMode must be one of the SPI_MODEn
+ // values defined in SPI.h, which are NOT the same as 0 for SPI_MODE0,
+ // 1 for SPI_MODE1, etc...use ONLY the SPI_MODEn defines! Only!
+ // Name is outdated (interface may be parallel) but for compatibility:
+ void initSPI(uint32_t freq = 0, uint8_t spiMode = SPI_MODE0);
+ // 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);
+ void sendCommand(uint8_t commandByte, uint8_t *dataBytes = NULL, uint8_t numDataBytes = 0);
+ void sendCommand(uint8_t commandByte, const uint8_t *dataBytes, uint8_t numDataBytes);
+ uint8_t readcommand8(uint8_t commandByte, uint8_t index = 0);
+
+ // 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,
+ bool block=true, bool bigEndian=false);
+ 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);
+ // Another new function, companion to the new non-blocking
+ // writePixels() variant.
+ void dmaWait(void);
+
+
+ // 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
+ // 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)
+ #if defined(KINETISK)
+ *csPortSet = 1;
+ #else // !KINETISK
+ *csPortSet = csPinMask;
+ #endif // end !KINETISK
+ #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)
+ #if defined(KINETISK)
+ *csPortClr = 1;
+ #else // !KINETISK
+ *csPortClr = csPinMask;
+ #endif // end !KINETISK
+ #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
+#if defined(__cplusplus) && (__cplusplus >= 201100)
+ union {
+#endif
+ struct { // Values specific to HARDWARE SPI:
+ SPIClass *_spi; ///< SPI class pointer
+#if defined(SPI_HAS_TRANSACTION)
+ SPISettings settings; ///< SPI transaction settings
+#else
+ uint32_t _freq; ///< SPI bitrate (if no SPI transactions)
+#endif
+ uint32_t _mode; ///< SPI data mode (transactions or no)
+ } hwspi; ///< Hardware SPI values
+ 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)
+ ADAGFX_PORT_t mosiPinMask; ///< Bitmask for MOSI
+ ADAGFX_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
+ ADAGFX_PORT_t mosiPinMaskSet; ///< Bitmask for MOSI SET (OR)
+ ADAGFX_PORT_t mosiPinMaskClr; ///< Bitmask for MOSI CLEAR (AND)
+ ADAGFX_PORT_t sckPinMaskSet; ///< Bitmask for SCK SET (OR bitmask)
+ ADAGFX_PORT_t sckPinMaskClr; ///< Bitmask for SCK CLEAR (AND)
+#endif // end HAS_PORT_SET_CLR
+ #if !defined(KINETISK)
+ ADAGFX_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; ///< Software SPI values
+ struct { // Values specific to 8-bit parallel:
+#if defined(USE_FAST_PINIO)
+
+ #if defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x
+ volatile uint32_t *writePort; ///< PORT register for DATA WRITE
+ volatile uint32_t *readPort; ///< PORT (PIN) register for DATA READ
+ #else
+ volatile uint8_t *writePort; ///< PORT register for DATA WRITE
+ volatile uint8_t *readPort; ///< PORT (PIN) register for DATA READ
+ #endif
+#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.
+ #if defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x
+ volatile uint32_t *dirSet; ///< PORT byte data direction SET
+ volatile uint32_t *dirClr; ///< PORT byte data direction CLEAR
+ #else
+ volatile uint8_t *dirSet; ///< PORT byte data direction SET
+ volatile uint8_t *dirClr; ///< PORT byte data direction CLEAR
+ #endif
+ 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)
+ ADAGFX_PORT_t wrPinMask; ///< Bitmask for write strobe
+ #endif // end !KINETISK
+ ADAGFX_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
+ ADAGFX_PORT_t wrPinMaskSet; ///< Bitmask for write strobe SET (OR)
+ ADAGFX_PORT_t wrPinMaskClr; ///< Bitmask for write strobe CLEAR (AND)
+ ADAGFX_PORT_t rdPinMaskSet; ///< Bitmask for read strobe SET (OR)
+ ADAGFX_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; ///< Parallel interface settings
+#if defined(__cplusplus) && (__cplusplus >= 201100)
+ }; ///< Only one interface is active
+#endif
+#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)
+ #if !defined(KINETISK)
+ ADAGFX_PORT_t csPinMask; ///< Bitmask for chip select
+ ADAGFX_PORT_t dcPinMask; ///< Bitmask for data/command
+ #endif // end !KINETISK
+#else // !HAS_PORT_SET_CLR
+ ADAGFX_PORT_t csPinMaskSet; ///< Bitmask for chip select SET (OR)
+ ADAGFX_PORT_t csPinMaskClr; ///< Bitmask for chip select CLEAR (AND)
+ ADAGFX_PORT_t dcPinMaskSet; ///< Bitmask for data/command SET (OR)
+ ADAGFX_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 #
+
+ 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
+
+ uint32_t _freq = 0; ///< Dummy var to keep subclasses happy
+};
+
+#endif // end __AVR_ATtiny85__
+#endif // end _ADAFRUIT_SPITFT_H_
diff --git a/lib/lib_display/Adafruit_SSD1331-1.2.0/Adafruit_SSD1331.cpp b/lib/lib_display/Adafruit_SSD1331-1.2.0/Adafruit_SSD1331.cpp
new file mode 100644
index 000000000..78d9901d6
--- /dev/null
+++ b/lib/lib_display/Adafruit_SSD1331-1.2.0/Adafruit_SSD1331.cpp
@@ -0,0 +1,190 @@
+/*!
+ * @file Adafruit_SSD1331.cpp
+ *
+ * @mainpage Adafruit SSD1331 Arduino Library
+ *
+ * @section intro_sec Introduction
+ *
+ * This is a library for the 0.96" 16-bit Color OLED with SSD1331 driver chip
+ *
+ * Pick one up today in the adafruit shop!
+ * ------> http://www.adafruit.com/products/684
+ *
+ * These displays use SPI to communicate, 4 or 5 pins are required to
+ * interface
+ * 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 Fried/Ladyada for Adafruit Industries.
+ *
+ * @section license License
+ *
+ * BSD license, all text above must be included in any redistribution
+ */
+
+#include "Adafruit_SSD1331.h"
+#include "pins_arduino.h"
+#include "wiring_private.h"
+
+/***********************************/
+
+/*!
+ @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
+*/
+void Adafruit_SSD1331::setAddrWindow(uint16_t x, uint16_t y, uint16_t w,
+ uint16_t h) {
+
+ uint8_t x1 = x;
+ uint8_t y1 = y;
+ if (x1 > 95)
+ x1 = 95;
+ if (y1 > 63)
+ y1 = 63;
+
+ uint8_t x2 = (x + w - 1);
+ uint8_t y2 = (y + h - 1);
+ if (x2 > 95)
+ x2 = 95;
+ if (y2 > 63)
+ y2 = 63;
+
+ if (x1 > x2) {
+ uint8_t t = x2;
+ x2 = x1;
+ x1 = t;
+ }
+ if (y1 > y2) {
+ uint8_t t = y2;
+ y2 = y1;
+ y1 = t;
+ }
+
+ sendCommand(0x15); // Column addr set
+ sendCommand(x1);
+ sendCommand(x2);
+
+ sendCommand(0x75); // Column addr set
+ sendCommand(y1);
+ sendCommand(y2);
+
+ startWrite();
+}
+
+/**************************************************************************/
+/*!
+ @brief Initialize SSD1331 chip
+ Connects to the SSD1331 over SPI and sends initialization procedure commands
+ @param freq Desired SPI clock frequency
+*/
+/**************************************************************************/
+void Adafruit_SSD1331::begin(uint32_t freq) {
+ initSPI(freq);
+
+ // Initialization Sequence
+ sendCommand(SSD1331_CMD_DISPLAYOFF); // 0xAE
+ sendCommand(SSD1331_CMD_SETREMAP); // 0xA0
+#if defined SSD1331_COLORORDER_RGB
+ sendCommand(0x72); // RGB Color
+#else
+ sendCommand(0x76); // BGR Color
+#endif
+ sendCommand(SSD1331_CMD_STARTLINE); // 0xA1
+ sendCommand(0x0);
+ sendCommand(SSD1331_CMD_DISPLAYOFFSET); // 0xA2
+ sendCommand(0x0);
+ sendCommand(SSD1331_CMD_NORMALDISPLAY); // 0xA4
+ sendCommand(SSD1331_CMD_SETMULTIPLEX); // 0xA8
+ sendCommand(0x3F); // 0x3F 1/64 duty
+ sendCommand(SSD1331_CMD_SETMASTER); // 0xAD
+ sendCommand(0x8E);
+ sendCommand(SSD1331_CMD_POWERMODE); // 0xB0
+ sendCommand(0x0B);
+ sendCommand(SSD1331_CMD_PRECHARGE); // 0xB1
+ sendCommand(0x31);
+ sendCommand(SSD1331_CMD_CLOCKDIV); // 0xB3
+ sendCommand(0xF0); // 7:4 = Oscillator Frequency, 3:0 = CLK Div Ratio
+ // (A[3:0]+1 = 1..16)
+ sendCommand(SSD1331_CMD_PRECHARGEA); // 0x8A
+ sendCommand(0x64);
+ sendCommand(SSD1331_CMD_PRECHARGEB); // 0x8B
+ sendCommand(0x78);
+ sendCommand(SSD1331_CMD_PRECHARGEC); // 0x8C
+ sendCommand(0x64);
+ sendCommand(SSD1331_CMD_PRECHARGELEVEL); // 0xBB
+ sendCommand(0x3A);
+ sendCommand(SSD1331_CMD_VCOMH); // 0xBE
+ sendCommand(0x3E);
+ sendCommand(SSD1331_CMD_MASTERCURRENT); // 0x87
+ sendCommand(0x06);
+ sendCommand(SSD1331_CMD_CONTRASTA); // 0x81
+ sendCommand(0x91);
+ sendCommand(SSD1331_CMD_CONTRASTB); // 0x82
+ sendCommand(0x50);
+ sendCommand(SSD1331_CMD_CONTRASTC); // 0x83
+ sendCommand(0x7D);
+ sendCommand(SSD1331_CMD_DISPLAYON); //--turn on oled panel
+ _width = TFTWIDTH;
+ _height = TFTHEIGHT;
+}
+
+/**************************************************************************/
+/*!
+ @brief Instantiate Adafruit SSD1331 driver with software SPI
+ @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)
+*/
+/**************************************************************************/
+Adafruit_SSD1331::Adafruit_SSD1331(int8_t cs, int8_t dc, int8_t mosi,
+ int8_t sclk, int8_t rst)
+ : Adafruit_SPITFT(TFTWIDTH, TFTHEIGHT, cs, dc, mosi, sclk, rst, -1) {}
+
+/**************************************************************************/
+/*!
+ @brief Instantiate Adafruit SSD1331 driver with hardware SPI
+ @param cs Chip select pin #
+ @param dc Data/Command pin #
+ @param rst Reset pin # (optional, pass -1 if unused)
+*/
+/**************************************************************************/
+Adafruit_SSD1331::Adafruit_SSD1331(int8_t cs, int8_t dc, int8_t rst)
+ : Adafruit_SPITFT(TFTWIDTH, TFTHEIGHT, cs, dc, rst) {}
+
+/**************************************************************************/
+/*!
+ @brief Instantiate Adafruit SSD1331 driver with hardware SPI
+ @param spi Pointer to an existing SPIClass instance (e.g. &SPI, the
+ microcontroller's primary SPI bus).
+ @param cs Chip select pin #
+ @param dc Data/Command pin #
+ @param rst Reset pin # (optional, pass -1 if unused)
+*/
+/**************************************************************************/
+Adafruit_SSD1331::Adafruit_SSD1331(SPIClass *spi, int8_t cs, int8_t dc,
+ int8_t rst)
+ :
+#if defined(ESP8266)
+ Adafruit_SPITFT(TFTWIDTH, TFTWIDTH, cs, dc, rst) {
+#else
+ Adafruit_SPITFT(TFTWIDTH, TFTWIDTH, spi, cs, dc, rst) {
+#endif
+}
+
+/**************************************************************************/
+/*!
+ @brief Change whether display is on or off
+ @param enable True if you want the display ON, false OFF
+*/
+/**************************************************************************/
+void Adafruit_SSD1331::enableDisplay(boolean enable) {
+ sendCommand(enable ? SSD1331_CMD_DISPLAYON : SSD1331_CMD_DISPLAYOFF);
+}
diff --git a/lib/lib_display/Adafruit_SSD1331-1.2.0/Adafruit_SSD1331.h b/lib/lib_display/Adafruit_SSD1331-1.2.0/Adafruit_SSD1331.h
new file mode 100644
index 000000000..7d9bc85a0
--- /dev/null
+++ b/lib/lib_display/Adafruit_SSD1331-1.2.0/Adafruit_SSD1331.h
@@ -0,0 +1,76 @@
+/*!
+ * @file Adafruit_SSD1331.h
+ */
+
+#include "Arduino.h"
+#include
+// Tasmota change: use custom version of Adafruit_SPITFT which extends Renderer instead of Adafruit_GFX
+#include
+#include
+#include
+
+/*!
+ * @brief Select one of these defines to set the pixel color order
+ */
+#define SSD1331_COLORORDER_RGB
+// #define SSD1331_COLORORDER_BGR
+
+#if defined SSD1331_COLORORDER_RGB && defined SSD1331_COLORORDER_BGR
+#error "RGB and BGR can not both be defined for SSD1331_COLORODER."
+#endif
+
+// Timing Delays
+#define SSD1331_DELAYS_HWFILL (3) //!< Fill delay
+#define SSD1331_DELAYS_HWLINE (1) //!< Line delay
+
+// SSD1331 Commands
+#define SSD1331_CMD_DRAWLINE 0x21 //!< Draw line
+#define SSD1331_CMD_DRAWRECT 0x22 //!< Draw rectangle
+#define SSD1331_CMD_FILL 0x26 //!< Fill enable/disable
+#define SSD1331_CMD_SETCOLUMN 0x15 //!< Set column address
+#define SSD1331_CMD_SETROW 0x75 //!< Set row adress
+#define SSD1331_CMD_CONTRASTA 0x81 //!< Set contrast for color A
+#define SSD1331_CMD_CONTRASTB 0x82 //!< Set contrast for color B
+#define SSD1331_CMD_CONTRASTC 0x83 //!< Set contrast for color C
+#define SSD1331_CMD_MASTERCURRENT 0x87 //!< Master current control
+#define SSD1331_CMD_SETREMAP 0xA0 //!< Set re-map & data format
+#define SSD1331_CMD_STARTLINE 0xA1 //!< Set display start line
+#define SSD1331_CMD_DISPLAYOFFSET 0xA2 //!< Set display offset
+#define SSD1331_CMD_NORMALDISPLAY 0xA4 //!< Set display to normal mode
+#define SSD1331_CMD_DISPLAYALLON 0xA5 //!< Set entire display ON
+#define SSD1331_CMD_DISPLAYALLOFF 0xA6 //!< Set entire display OFF
+#define SSD1331_CMD_INVERTDISPLAY 0xA7 //!< Invert display
+#define SSD1331_CMD_SETMULTIPLEX 0xA8 //!< Set multiplex ratio
+#define SSD1331_CMD_SETMASTER 0xAD //!< Set master configuration
+#define SSD1331_CMD_DISPLAYOFF 0xAE //!< Display OFF (sleep mode)
+#define SSD1331_CMD_DISPLAYON 0xAF //!< Normal Brightness Display ON
+#define SSD1331_CMD_POWERMODE 0xB0 //!< Power save mode
+#define SSD1331_CMD_PRECHARGE 0xB1 //!< Phase 1 and 2 period adjustment
+#define SSD1331_CMD_CLOCKDIV \
+ 0xB3 //!< Set display clock divide ratio/oscillator frequency
+#define SSD1331_CMD_PRECHARGEA 0x8A //!< Set second pre-charge speed for color A
+#define SSD1331_CMD_PRECHARGEB 0x8B //!< Set second pre-charge speed for color B
+#define SSD1331_CMD_PRECHARGEC 0x8C //!< Set second pre-charge speed for color C
+#define SSD1331_CMD_PRECHARGELEVEL 0xBB //!< Set pre-charge voltage
+#define SSD1331_CMD_VCOMH 0xBE //!< Set Vcomh voltge
+
+/// Class to manage hardware interface with SSD1331 chipset
+class Adafruit_SSD1331 : public Adafruit_SPITFT {
+public:
+ Adafruit_SSD1331(int8_t cs, int8_t dc, int8_t mosi, int8_t sclk, int8_t rst);
+ Adafruit_SSD1331(int8_t cs, int8_t dc, int8_t rst);
+ // 3-4 args using hardware SPI (must specify peripheral) (reset optional)
+ Adafruit_SSD1331(SPIClass *spi, int8_t cs, int8_t dc, int8_t rst = -1);
+
+ // commands
+ void begin(uint32_t begin = 8000000);
+
+ void setAddrWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h);
+
+ void enableDisplay(boolean enable);
+
+ static const int16_t TFTWIDTH = 96; ///< The width of the display
+ static const int16_t TFTHEIGHT = 64; ///< The height of the display
+
+private:
+};
diff --git a/lib/lib_display/Adafruit_SSD1331-1.2.0/README.md b/lib/lib_display/Adafruit_SSD1331-1.2.0/README.md
new file mode 100644
index 000000000..24c404c59
--- /dev/null
+++ b/lib/lib_display/Adafruit_SSD1331-1.2.0/README.md
@@ -0,0 +1,24 @@
+# Adafruit SSD1331 Arduino Library [![Build Status](https://github.com/adafruit/Adafruit-SSD1331-OLED-Driver-Library-for-Arduino/workflows/Arduino%20Library%20CI/badge.svg)](https://github.com/adafruit/Adafruit-SSD1331-OLED-Driver-Library-for-Arduino/actions)[![Documentation](https://github.com/adafruit/ci-arduino/blob/master/assets/doxygen_badge.svg)](http://adafruit.github.io/Adafruit-SSD1331-OLED-Driver-Library-for-Arduino/html/index.html)
+This is a library for the 0.96" 16-bit Color OLED with SSD1331 driver chip
+
+ Pick one up today in the adafruit shop!
+ ------> http://www.adafruit.com/products/684
+
+These displays use SPI to communicate, 4 or 5 pins are required to
+interface
+
+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.
+BSD license, check license.txt for more information
+All text above must be included in any redistribution
+
+To download. click the DOWNLOADS button in the top right corner, rename the uncompressed folder Adafruit_SSD1131. Check that the Adafruit_SSD1331 folder contains Adafruit_SSD1331.cpp and Adafruit_SSD1331.h
+
+Place the Adafruit_SSD1331 library folder your /libraries/ folder. You may need to create the libraries subfolder if its your first library. Restart the IDE.
+
+You will also have to download the Adafruit GFX Graphics core which does all the circles, text, rectangles, etc. You can get it from
+https://github.com/adafruit/Adafruit-GFX-Library
+and download/install that library as well
diff --git a/lib/lib_display/Adafruit_SSD1331-1.2.0/library.properties b/lib/lib_display/Adafruit_SSD1331-1.2.0/library.properties
new file mode 100644
index 000000000..931f1aa38
--- /dev/null
+++ b/lib/lib_display/Adafruit_SSD1331-1.2.0/library.properties
@@ -0,0 +1,10 @@
+name=Adafruit SSD1331 OLED Driver Library for Arduino
+version=1.2.0
+author=Adafruit
+maintainer=Adafruit
+sentence=For 0.96" OLEDs in the Adafruit shop
+paragraph=For 0.96" OLEDs in the Adafruit shop
+category=Display
+url=https://github.com/adafruit/Adafruit-SSD1331-OLED-Driver-Library-for-Arduino
+architectures=*
+depends=Adafruit GFX Library
diff --git a/lib/lib_display/Adafruit_SSD1331-1.2.0/license.txt b/lib/lib_display/Adafruit_SSD1331-1.2.0/license.txt
new file mode 100644
index 000000000..f6a0f22b8
--- /dev/null
+++ b/lib/lib_display/Adafruit_SSD1331-1.2.0/license.txt
@@ -0,0 +1,26 @@
+Software License Agreement (BSD License)
+
+Copyright (c) 2012, Adafruit Industries
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+1. Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+3. Neither the name of the copyright holders nor the
+names of its contributors may be used to endorse or promote products
+derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/tasmota/xdsp_14_SSD1331.ino b/tasmota/xdsp_14_SSD1331.ino
new file mode 100644
index 000000000..cae0c9aa2
--- /dev/null
+++ b/tasmota/xdsp_14_SSD1331.ino
@@ -0,0 +1,192 @@
+/*
+ xdsp_14_SSD1331.ino - Display SSD1331 support for Tasmota
+
+ Copyright (C) 2020 Jeroen Vermeulen, Gerhard Mutz and Theo Arends
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+#ifdef USE_SPI
+#ifdef USE_DISPLAY
+#ifdef USE_DISPLAY_SSD1331
+
+#define XDSP_14 14
+
+#define COLORED 1
+#define UNCOLORED 0
+#define USE_TINY_FONT
+
+#define SSD1331_BLACK 0x0000 // 0, 0, 0
+#define SSD1331_WHITE 0xFFFF // 255, 255, 255
+#define SSD1331_RED 0xF800 // 255, 0, 0
+#define SSD1331_BLUE 0x001F // 0, 0, 255
+
+#include
+#include
+
+extern uint8_t *buffer;
+extern uint8_t color_type;
+Adafruit_SSD1331 *ssd1331;
+
+/*********************************************************************************************/
+
+void SSD1331_InitDriver() {
+ if (!Settings.display_model) {
+ Settings.display_model = XDSP_14;
+ }
+
+ if (XDSP_14 == Settings.display_model) {
+
+ if (Settings.display_width != Adafruit_SSD1331::TFTWIDTH) {
+ Settings.display_width = Adafruit_SSD1331::TFTWIDTH;
+ }
+ if (Settings.display_height != Adafruit_SSD1331::TFTHEIGHT) {
+ Settings.display_height = Adafruit_SSD1331::TFTHEIGHT;
+ }
+
+ buffer=0;
+
+ // default colors
+ fg_color = SSD1331_WHITE;
+ bg_color = SSD1331_BLACK;
+
+ // init renderer
+ if (PinUsed(GPIO_SSPI_CS) && PinUsed(GPIO_SSPI_DC) && PinUsed(GPIO_SSPI_MOSI) && PinUsed(GPIO_SSPI_SCLK) && PinUsed(GPIO_OLED_RESET)) {
+ ssd1331 = new Adafruit_SSD1331(Pin(GPIO_SSPI_CS),Pin(GPIO_SSPI_DC),Pin(GPIO_SSPI_MOSI),Pin(GPIO_SSPI_SCLK),Pin(GPIO_OLED_RESET));
+ } else if (PinUsed(GPIO_SPI_CS) && PinUsed(GPIO_SPI_DC)) {
+ ssd1331 = new Adafruit_SSD1331(&SPI,Pin(GPIO_SPI_CS),Pin(GPIO_SPI_DC),Pin(GPIO_OLED_RESET));
+ } else {
+ return;
+ }
+
+ delay(100);
+ ssd1331->begin();
+ renderer = ssd1331;
+ // Rotation is currently broken, https://github.com/adafruit/Adafruit-SSD1331-OLED-Driver-Library-for-Arduino/issues/26
+ renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font);
+ renderer->dim(Settings.display_dimmer);
+
+#ifdef SHOW_SPLASH
+ // Welcome text
+ renderer->clearDisplay();
+ renderer->setTextFont(1);
+ renderer->DrawStringAt(24, 27, "SSD1331", SSD1331_RED, 0);
+ delay(1000);
+#endif
+
+ color_type = COLOR_COLOR;
+ }
+}
+
+#ifdef USE_DISPLAY_MODES1TO5
+
+void SSD1331PrintLog(bool withDateTime)
+{
+ disp_refresh--;
+ if (!disp_refresh) {
+ disp_refresh = Settings.display_refresh;
+ if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); }
+
+ char* txt = DisplayLogBuffer('\370');
+ if (txt != NULL) {
+ uint8_t last_row = Settings.display_rows -1;
+
+ renderer->clearDisplay();
+ renderer->setCursor(0,0);
+
+ if (withDateTime) {
+ char line[17];
+ snprintf_P(line, sizeof(line), PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d %02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%04d"), RtcTime.hour, RtcTime.minute, RtcTime.day_of_month, RtcTime.month, RtcTime.year); // [12:34 01-02-2018]
+ renderer->setTextColor(SSD1331_BLUE);
+ renderer->println(line);
+ renderer->setTextColor(fg_color);
+ last_row--;
+ }
+
+ for (byte i = 0; i < last_row; i++) {
+ strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols);
+ renderer->println(disp_screen_buffer[i]);
+ }
+ strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols);
+ DisplayFillScreen(last_row);
+
+ AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]);
+
+ renderer->println(disp_screen_buffer[last_row]);
+ renderer->Updateframe();
+ }
+ }
+}
+
+void SSD1331Time(void)
+{
+ char line[12];
+
+ renderer->clearDisplay();
+ renderer->setCursor(0, 0);
+ snprintf_P(line, sizeof(line), PSTR(" %02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); // [ 12:34:56 ]
+ renderer->println(line);
+ snprintf_P(line, sizeof(line), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%04d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year); // [01-02-2018]
+ renderer->println(line);
+ renderer->Updateframe();
+}
+
+void SSD1331Refresh(void) // Every second
+{
+ if (Settings.display_mode) { // Mode 0 is User text
+ switch (Settings.display_mode) {
+ case 1: // Time
+ SSD1331Time();
+ break;
+ case 2: // Local
+ case 4: // Mqtt
+ SSD1331PrintLog(false);
+ break;
+ case 3: // Local + Time
+ case 5: // Mqtt + Time
+ SSD1331PrintLog(true);
+ break;
+ }
+ }
+}
+
+#endif // USE_DISPLAY_MODES1TO5
+/*********************************************************************************************/
+/*********************************************************************************************\
+ * Interface
+\*********************************************************************************************/
+bool Xdsp14(uint8_t function)
+{
+ bool result = false;
+
+ if (FUNC_DISPLAY_INIT_DRIVER == function) {
+ SSD1331_InitDriver();
+ }
+ else if (XDSP_14 == Settings.display_model) {
+ switch (function) {
+ case FUNC_DISPLAY_MODEL:
+ result = true;
+ break;
+#ifdef USE_DISPLAY_MODES1TO5
+ case FUNC_DISPLAY_EVERY_SECOND:
+ SSD1331Refresh();
+ break;
+#endif // USE_DISPLAY_MODES1TO5
+ }
+ }
+ return result;
+}
+#endif // USE_DISPLAY_SSD1331
+#endif // USE_DISPLAY
+#endif // USE_SPI