From 0835b336bd3de5c89a25b328d48da811b4db9ebf Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Tue, 27 Jun 2017 17:41:12 +0200 Subject: [PATCH 01/16] Very first step, talking successfully to ST7787 --- TFT_Drivers/ST7787_Defines.h | 116 ++++++++++++++++++++++++ TFT_Drivers/ST7787_Init.h | 37 ++++++++ TFT_Drivers/ST7787_Rotation.h | 28 ++++++ TFT_eSPI.cpp | 161 +++++++++++++++++++++++++++++++--- TFT_eSPI.h | 15 ++++ User_Setup.h | 11 ++- User_Setup_Select.h | 2 + 7 files changed, 354 insertions(+), 16 deletions(-) create mode 100644 TFT_Drivers/ST7787_Defines.h create mode 100644 TFT_Drivers/ST7787_Init.h create mode 100644 TFT_Drivers/ST7787_Rotation.h diff --git a/TFT_Drivers/ST7787_Defines.h b/TFT_Drivers/ST7787_Defines.h new file mode 100644 index 0000000..715173b --- /dev/null +++ b/TFT_Drivers/ST7787_Defines.h @@ -0,0 +1,116 @@ +// Change the width and height if required (defined in portrait mode) +// or use the constructor to over-ride defaults +#ifndef TFT_WIDTH + #define TFT_WIDTH 240 +#endif +#ifndef TFT_HEIGHT + #define TFT_HEIGHT 320 +#endif + + +// Color definitions for backwards compatibility with old sketches +// use colour definitions like TFT_BLACK to make sketches more portable +#define ST7735_BLACK 0x0000 /* 0, 0, 0 */ +#define ST7735_NAVY 0x000F /* 0, 0, 128 */ +#define ST7735_DARKGREEN 0x03E0 /* 0, 128, 0 */ +#define ST7735_DARKCYAN 0x03EF /* 0, 128, 128 */ +#define ST7735_MAROON 0x7800 /* 128, 0, 0 */ +#define ST7735_PURPLE 0x780F /* 128, 0, 128 */ +#define ST7735_OLIVE 0x7BE0 /* 128, 128, 0 */ +#define ST7735_LIGHTGREY 0xC618 /* 192, 192, 192 */ +#define ST7735_DARKGREY 0x7BEF /* 128, 128, 128 */ +#define ST7735_BLUE 0x001F /* 0, 0, 255 */ +#define ST7735_GREEN 0x07E0 /* 0, 255, 0 */ +#define ST7735_CYAN 0x07FF /* 0, 255, 255 */ +#define ST7735_RED 0xF800 /* 255, 0, 0 */ +#define ST7735_MAGENTA 0xF81F /* 255, 0, 255 */ +#define ST7735_YELLOW 0xFFE0 /* 255, 255, 0 */ +#define ST7735_WHITE 0xFFFF /* 255, 255, 255 */ +#define ST7735_ORANGE 0xFD20 /* 255, 165, 0 */ +#define ST7735_GREENYELLOW 0xAFE5 /* 173, 255, 47 */ +#define ST7735_PINK 0xF81F + +#define ILI9341_BLACK ST7735_BLACK + + +// Delay between some initialisation commands +#define TFT_INIT_DELAY 0x80 + + +// Generic commands used by TFT_eSPI.cpp +#define TFT_NOP 0x00 +#define TFT_SWRST 0x01 + +#define TFT_CASET 0x2A +#define TFT_PASET 0x2B +#define TFT_RAMWR 0x2C + +#define TFT_RAMRD 0x2E +#define TFT_IDXRD 0x00 //0xDD // ILI9341 only, indexed control register read + +#define TFT_MADCTL 0x36 +#define TFT_MAD_MY 0x80 +#define TFT_MAD_MX 0x40 +#define TFT_MAD_MV 0x20 +#define TFT_MAD_ML 0x10 +#define TFT_MAD_BGR 0x08 +#define TFT_MAD_MH 0x04 +#define TFT_MAD_RGB 0x00 + +#define TFT_INVOFF 0x20 +#define TFT_INVON 0x21 + +// ST7787 specific commands used in init +#define ST7787_NOP 0x00 +#define ST7787_SWRESET 0x01 +#define ST7787_RDDID 0x04 +#define ST7787_RDDST 0x09 + +#define ST7787_SLPIN 0x10 +#define ST7787_SLPOUT 0x11 +#define ST7787_PTLON 0x12 +#define ST7787_NORON 0x13 + +#define ST7787_INVOFF 0x20 +#define ST7787_INVON 0x21 +#define ST7787_DISPOFF 0x28 +#define ST7787_DISPON 0x29 +#define ST7787_CASET 0x2A +#define ST7787_RASET 0x2B // PASET +#define ST7787_RAMWR 0x2C +#define ST7787_RAMRD 0x2E + +#define ST7787_PTLAR 0x30 +#define ST7787_MADCTL 0x36 +#define ST7787_COLMOD 0x3A + +#define ST7787_FRMCTR1 0xB1 +#define ST7787_FRMCTR2 0xB2 +#define ST7787_FRMCTR3 0xB3 +#define ST7787_INVCTR 0xB4 +#define ST7787_DISSET5 0xB6 +#define ST7787_VSYNCOUT 0xBC +#define ST7787_VSYNCIN 0xBD + +#define ST7787_PWCTR1 0xC0 +#define ST7787_PWCTR2 0xC1 +#define ST7787_PWCTR3 0xC2 +#define ST7787_PWCTR4 0xC3 +#define ST7787_PWCTR5 0xC4 +#define ST7787_VMCTR1 0xC5 + +#define ST7787_RDID1 0xDA +#define ST7787_RDID2 0xDB +#define ST7787_RDID3 0xDC + +#define ST7787_GMCTRP1 0xE0 +#define ST7787_GMCTRN1 0xE1 + +/* Timing delays from ST7787 datasheet (in nanoseconds). */ +#define ST7787_T_CSS 60 +#define ST7787_T_SCC 20 +#define ST7787_T_CHW 40 +#define ST7787_T_SHW 20 +#define ST7787_T_SLW 20 +#define ST7787_T_SHR 60 +#define ST7787_T_SLR 60 diff --git a/TFT_Drivers/ST7787_Init.h b/TFT_Drivers/ST7787_Init.h new file mode 100644 index 0000000..3ef08ce --- /dev/null +++ b/TFT_Drivers/ST7787_Init.h @@ -0,0 +1,37 @@ + +// This is the command sequence that initialises the ST7787 driver +// +// This setup information uses simple 8 bit SPI writecommand() and writedata() functions +// +// See ST7735_Setup.h file for an alternative format + +{ + /* Take the display out of sleep mode. */ + writecommand(ST7787_SLPOUT); + /* + Wait for sleep-out command to complete. + Datasheet says 5 msec is enough before next command, but 120 msec is + needed before the display is fully out of sleep mode. + */ + delay(120); + /* + Select 16-bit 565 RGB pixel format (mode 5). + Same for RGB mode (but we don't use it). + */ + writecommand(ST7787_COLMOD); + writedata((13 << 4) | 5); + /* Disable external vsync. */ + writecommand(ST7787_VSYNCOUT); + /* Turn on the display */ + writecommand(ST7787_DISPON); + + { + /* Debug: Read ID and status, to see if the basics are working. */ + uint8_t buf[4]; + docommand(ST7787_RDDID, NULL, 0, buf, 3); + Serial.print("RDDID: "); Serial.print(buf[0], HEX); Serial.print(" "); Serial.print(buf[1], HEX); Serial.print(" "); Serial.print(buf[2], HEX); Serial.print("\r\n"); + docommand(ST7787_RDDST, NULL, 0, buf, 4); + Serial.print("RDDST: "); Serial.print(buf[0], HEX); Serial.print(" "); Serial.print(buf[1], HEX); Serial.print(" "); Serial.print(buf[2], HEX); Serial.print(" "); Serial.print(buf[3], HEX); Serial.print("\r\n"); + for (;;) { } + } +} diff --git a/TFT_Drivers/ST7787_Rotation.h b/TFT_Drivers/ST7787_Rotation.h new file mode 100644 index 0000000..13a0ebe --- /dev/null +++ b/TFT_Drivers/ST7787_Rotation.h @@ -0,0 +1,28 @@ + +// This is the command sequence that rotates the ST7735 driver coordinate frame + + rotation = m % 4; // Limit the range of values to 0-3 + + writecommand(TFT_MADCTL); + switch (rotation) { + case 0: + writedata(TFT_MAD_RGB); + _width = TFT_WIDTH; + _height = TFT_HEIGHT; + break; + case 1: + writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_RGB); + _width = TFT_HEIGHT; + _height = TFT_WIDTH; + break; + case 2: + writedata(TFT_MAD_MX | TFT_MAD_MY | TFT_MAD_RGB); + _width = TFT_WIDTH; + _height = TFT_HEIGHT; + break; + case 3: + writedata(TFT_MAD_MY | TFT_MAD_MV | TFT_MAD_RGB); + _width = TFT_HEIGHT; + _height = TFT_WIDTH; + break; + } diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 0189854..5a2d940 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -24,7 +24,9 @@ //#include //#include "pins_arduino.h" //#include "wiring_private.h" +#ifndef ST7787_DRIVER #include +#endif // If it is a 16bit serial display we must transfer 16 bits every time #ifdef RPI_ILI9486_DRIVER @@ -90,6 +92,16 @@ TFT_eSPI::TFT_eSPI(int16_t w, int16_t h) } #endif +#ifdef TFT_DAT + digitalWrite(TFT_DAT, HIGH); + pinMode(TFT_DAT, OUTPUT); +#endif + +#ifdef TFT_SCK + digitalWrite(TFT_SCK, LOW); + pinMode(TFT_SCK, OUTPUT); +#endif + _width = w; // Set by specific xxxxx_Defines.h file or by users sketch _height = h; // Set by specific xxxxx_Defines.h file or by users sketch rotation = 0; @@ -172,7 +184,17 @@ void TFT_eSPI::init(void) SPI.pins(6, 7, 8, 0); #endif + #ifdef TFT_DAT + cspinmask = (uint32_t) digitalPinToBitMask(TFT_CS); + #endif + + #ifdef TFT_SCK + cspinmask = (uint32_t) digitalPinToBitMask(TFT_CS); + #endif + +#ifndef ST7787_DRIVER SPI.begin(); // This will set HMISO to input +#endif #else #if defined (TFT_MOSI) && !defined (TFT_SPI_OVERLAP) SPI.begin(TFT_SCLK, TFT_MISO, TFT_MOSI, -1); @@ -185,6 +207,7 @@ void TFT_eSPI::init(void) inTransaction = false; locked = true; +#ifdef ST7787_DRIVER #ifndef SUPPORT_TRANSACTIONS SPI.setBitOrder(MSBFIRST); @@ -197,6 +220,7 @@ void TFT_eSPI::init(void) locked = false; // Flag to stop repeat beginTransaction calls #endif +#endif #endif // Set to output once again in case D6 (MISO) is used for CS @@ -249,6 +273,9 @@ void TFT_eSPI::init(void) #elif defined (RPI_ILI9486_DRIVER) #include "TFT_Drivers/RPI_ILI9486_Init.h" +#elif defined(ST7787_DRIVER) + #include "TFT_Drivers/ST7787_Init.h" + #endif spi_end(); @@ -281,6 +308,9 @@ void TFT_eSPI::setRotation(uint8_t m) #elif defined (RPI_ILI9486_DRIVER) #include "TFT_Drivers/RPI_ILI9486_Rotation.h" +#elif defined (ST7787_DRIVER) + #include "TFT_Drivers/ST7787_Rotation.h" + #endif delayMicroseconds(10); @@ -338,20 +368,101 @@ void TFT_eSPI::spiwrite(uint8_t c) } +void TFT_eSPI::docommand(uint8_t c, uint8_t *in, uint32_t in_len, uint8_t *out, uint32_t out_len) +{ + uint32_t i; + uint32_t val; + + DAT_O; + CS_L; + delay_ns(ST7787_T_CSS); + + /* Write the command byte. */ + /* First shift out the data/command bit ('0' for command). */ + SCK_L; + DAT_L; + delay_ns(ST7787_T_SLW); + SCK_H; + delay_ns(ST7787_T_SHW); + /* Then shift out the bits, MSB-to-LSB. */ + val = c; + for (i = 8; i; --i) { + SCK_L; + if ((val>>7) & 1) + DAT_H; + else + DAT_L; + val <<= 1; + delay_ns(ST7787_T_SLW); + SCK_H; + delay_ns(ST7787_T_SHW); + } + + /* Write any command data. */ + if (in_len) { + do { + val = *in++; + + /* Data/command bit ('1' for data). */ + SCK_L; + DAT_H; + delay_ns(ST7787_T_SLW); + SCK_H; + delay_ns(ST7787_T_SHW); + + for (i = 8; i; --i) { + SCK_L; + if ((val>>7) & 1) + DAT_H; + else + DAT_L; + val <<= 1; + delay_ns(ST7787_T_SLW); + SCK_H; + delay_ns(ST7787_T_SHW); + } + } while (--in_len > 0); + } + + DAT_I; + + /* Read reply. */ + if (out_len) { + /* Dummy bit (according to data sheet, data/command placeholder?). */ + SCK_L; + delay_ns(ST7787_T_SLR); + SCK_H; + delay_ns(ST7787_T_SHR); + + do { + val = 0; + for (i = 8; i; --i) { + SCK_L; + delay_ns(ST7787_T_SLR); + val = (val << 1); + if (DAT_V) + val = val | 1; + SCK_H; + delay_ns(ST7787_T_SHR); + } + *out++ = val; + } while (--out_len > 0); + } + + SCK_L; + delay_ns(ST7787_T_SCC); + CS_H; + delay_ns(ST7787_T_CHW); +} + + /*************************************************************************************** ** Function name: writecommand ** Description: Send an 8 bit command to the TFT ***************************************************************************************/ void TFT_eSPI::writecommand(uint8_t c) { - DC_C; - CS_L; - #ifdef SEND_16_BITS - SPI.transfer(0); - #endif - SPI.transfer(c); - CS_H; - DC_D; + docommand(c, NULL, 0, NULL, 0); } @@ -361,12 +472,38 @@ void TFT_eSPI::writecommand(uint8_t c) ***************************************************************************************/ void TFT_eSPI::writedata(uint8_t c) { + uint32_t i; + uint32_t val; + + DAT_O; CS_L; - #ifdef SEND_16_BITS - SPI.transfer(0); - #endif - SPI.transfer(c); + delay_ns(ST7787_T_CSS); + + /* Data/command bit ('1' for data). */ + val = c; + SCK_L; + DAT_H; + delay_ns(ST7787_T_SLW); + SCK_H; + delay_ns(ST7787_T_SHW); + + for (i = 8; i; --i) { + SCK_L; + if ((val>>7) & 1) + DAT_H; + else + DAT_L; + val <<= 1; + delay_ns(ST7787_T_SLW); + SCK_H; + delay_ns(ST7787_T_SHW); + } + + DAT_I; + + delay_ns(ST7787_T_SCC); CS_H; + delay_ns(ST7787_T_CHW); } diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 25c86b6..e6d7719 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -120,6 +120,19 @@ #endif #endif +#ifdef TFT_DAT +#define DAT_I pinMode(TFT_DAT, INPUT) +#define DAT_O pinMode(TFT_DAT, OUTPUT) +#define DAT_L digitalWrite(TFT_DAT, LOW) +#define DAT_H digitalWrite(TFT_DAT, HIGH) +#define DAT_V digitalRead(TFT_DAT) +#endif + +#ifdef TFT_SCK +#define SCK_L digitalWrite(TFT_SCK, LOW) +#define SCK_H digitalWrite(TFT_SCK, HIGH) +#endif + #ifdef LOAD_GFXFF // We can include all the free fonts and they will only be built into // the sketch if they are used @@ -361,6 +374,7 @@ class TFT_eSPI : public Print { setTextFont(uint8_t font), #endif spiwrite(uint8_t), + docommand(uint8_t c, uint8_t *in, uint32_t in_len, uint8_t *out, uint32_t out_len), writecommand(uint8_t c), writedata(uint8_t d), commandList(const uint8_t *addr); @@ -458,6 +472,7 @@ inline void spi_end() __attribute__((always_inline)); *gfxFont; #endif + void delay_ns(uint32_t d) { delayMicroseconds(1); /* ToDo */ } }; #endif diff --git a/User_Setup.h b/User_Setup.h index a106fd9..67d178b 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -14,15 +14,16 @@ // ################################################################################## // Only define one driver, the other ones must be commented out -#define ILI9341_DRIVER +//#define ILI9341_DRIVER //#define ST7735_DRIVER //#define ILI9163_DRIVER //#define S6D02A1_DRIVER //#define RPI_ILI9486_DRIVER // 20MHz maximum SPI +#define ST7787_DRIVER // For ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation -//#define TFT_WIDTH 128 -//#define TFT_HEIGHT 160 +#define TFT_WIDTH 240 +#define TFT_HEIGHT 320 //#define TFT_HEIGHT 128 // For ST7735 ONLY, define the type of display, originally this was based on the @@ -81,9 +82,11 @@ // For ModeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation #define TFT_CS PIN_D8 // Chip select control pin D8 -#define TFT_DC PIN_D3 // Data Command control pin +//#define TFT_DC PIN_D3 // Data Command control pin #define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) //#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V +#define TFT_SCK PIN_D5 +#define TFT_DAT PIN_D7 //#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only diff --git a/User_Setup_Select.h b/User_Setup_Select.h index 3d8c600..16a9478 100644 --- a/User_Setup_Select.h +++ b/User_Setup_Select.h @@ -51,6 +51,8 @@ #include #elif defined (RPI_ILI9486_DRIVER) #include +#elif defined (ST7787_DRIVER) + #include #endif // These are the pins for all ESP8266 boards From c52327324fc5f7b0bced2e9825270b99eec52d28 Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Tue, 27 Jun 2017 23:06:37 +0200 Subject: [PATCH 02/16] Mandelbrot example works now. --- TFT_Drivers/ST7787_Init.h | 10 --- TFT_eSPI.cpp | 139 +++++++++++++++++++++++++++++++++++++- TFT_eSPI.h | 11 ++- 3 files changed, 146 insertions(+), 14 deletions(-) diff --git a/TFT_Drivers/ST7787_Init.h b/TFT_Drivers/ST7787_Init.h index 3ef08ce..306e9c1 100644 --- a/TFT_Drivers/ST7787_Init.h +++ b/TFT_Drivers/ST7787_Init.h @@ -24,14 +24,4 @@ writecommand(ST7787_VSYNCOUT); /* Turn on the display */ writecommand(ST7787_DISPON); - - { - /* Debug: Read ID and status, to see if the basics are working. */ - uint8_t buf[4]; - docommand(ST7787_RDDID, NULL, 0, buf, 3); - Serial.print("RDDID: "); Serial.print(buf[0], HEX); Serial.print(" "); Serial.print(buf[1], HEX); Serial.print(" "); Serial.print(buf[2], HEX); Serial.print("\r\n"); - docommand(ST7787_RDDST, NULL, 0, buf, 4); - Serial.print("RDDST: "); Serial.print(buf[0], HEX); Serial.print(" "); Serial.print(buf[1], HEX); Serial.print(" "); Serial.print(buf[2], HEX); Serial.print(" "); Serial.print(buf[3], HEX); Serial.print("\r\n"); - for (;;) { } - } } diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 5a2d940..a90f9fd 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -1710,7 +1710,41 @@ void TFT_eSPI::setWindow(int16_t x0, int16_t y0, int16_t x1, int16_t y1) ***************************************************************************************/ // Chip select stays low, use setWindow() from sketches -#if defined (ESP8266) && !defined (RPI_WRITE_STROBE) && !defined (RPI_ILI9486_DRIVER) +#if defined (ESP8266) && defined(ST7787_DRIVER) +inline void TFT_eSPI::setAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) +{ + uint8_t buf[4]; + + addr_col = 0xFFFF; + addr_row = 0xFFFF; + +#ifdef CGRAM_OFFSET + xs+=colstart; + xe+=colstart; + ys+=rowstart; + ye+=rowstart; +#endif + + // Column addr set + buf[0] = xs >> 8; + buf[1] = xs & 0xff; + buf[2] = xe >> 8; + buf[3] = xe & 0xff; + docommand(TFT_CASET, buf, 4, NULL, 0); + // Row addr set + buf[0] = ys >> 8; + buf[1] = ys & 0xff; + buf[2] = ye >> 8; + buf[3] = ye & 0xff; + docommand(TFT_PASET, buf, 4, NULL, 0); + + // write to RAM + docommand(TFT_RAMWR, NULL, 0, NULL, 0); + CS_L; + /* ToDo: This could be done smarter, for now just made simple to match other code. */ +} + +#elif defined (ESP8266) && !defined (RPI_WRITE_STROBE) && !defined (RPI_ILI9486_DRIVER) inline void TFT_eSPI::setAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) { //spi_begin(); @@ -2106,7 +2140,51 @@ void TFT_eSPI::readAddrWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) ** Function name: drawPixel ** Description: push a single pixel at an arbitrary position ***************************************************************************************/ -#if defined (ESP8266) && !defined (RPI_WRITE_STROBE) +#ifdef ST7787_DRIVER +void TFT_eSPI::drawPixel(uint32_t x, uint32_t y, uint32_t color) +{ + uint8_t buf[4]; + + // Faster range checking, possible because x and y are unsigned + if ((x >= _width) || (y >= _height)) return; + +#ifdef CGRAM_OFFSET + x+=colstart; + y+=rowstart; +#endif + + spi_begin(); + + CS_L; + + // No need to send x if it has not changed (speeds things up) + if (addr_col != x) { + buf[0] = x >> 8; + buf[1] = x & 0xff; + buf[2] = x >> 8; + buf[3] = x & 0xff; + docommand(TFT_CASET, buf, 4, NULL, 0); + addr_col = x; + } + + // No need to send y if it has not changed (speeds things up) + if (addr_row != y) { + buf[0] = y >> 8; + buf[1] = y & 0xff; + buf[2] = y >> 8; + buf[3] = y & 0xff; + docommand(TFT_PASET, buf, 4, NULL, 0); + addr_row = y; + } + + buf[0] = color >> 8; + buf[1] = color & 0xff; + docommand(TFT_RAMWR, buf, 2, NULL, 0); + + spi_end(); +} + +#elif defined (ESP8266) && !defined (RPI_WRITE_STROBE) void TFT_eSPI::drawPixel(uint32_t x, uint32_t y, uint32_t color) { // Faster range checking, possible because x and y are unsigned @@ -3675,7 +3753,62 @@ void TFT_eSPI::setTextFont(uint8_t f) ** Function name: spiBlockWrite ** Description: Write a block of pixels of the same colour ***************************************************************************************/ -#if defined (ESP8266) && (SPI_FREQUENCY != 80000000) +#if defined(ESP8266) && defined(ST7787_DRIVER) +void spiWriteBlock(uint16_t color, uint32_t repeat) +{ + uint32_t i; + uint32_t val; + + DAT_O; + delay_ns(ST7787_T_CSS); + + while (repeat-- > 0) + { + val = color >> 8; + /* Data/command bit ('1' for data). */ + SCK_L; + DAT_H; + delay_ns(ST7787_T_SLW); + SCK_H; + delay_ns(ST7787_T_SHW); + + for (i = 8; i; --i) { + SCK_L; + if ((val>>7) & 1) + DAT_H; + else + DAT_L; + val <<= 1; + delay_ns(ST7787_T_SLW); + SCK_H; + delay_ns(ST7787_T_SHW); + } + + val = color & 0xff; + /* Data/command bit ('1' for data). */ + SCK_L; + DAT_H; + delay_ns(ST7787_T_SLW); + SCK_H; + delay_ns(ST7787_T_SHW); + + for (i = 8; i; --i) { + SCK_L; + if ((val>>7) & 1) + DAT_H; + else + DAT_L; + val <<= 1; + delay_ns(ST7787_T_SLW); + SCK_H; + delay_ns(ST7787_T_SHW); + } + } + + DAT_I; +} + +#elif defined (ESP8266) && (SPI_FREQUENCY != 80000000) void spiWriteBlock(uint16_t color, uint32_t repeat) { uint16_t color16 = (color >> 8) | (color << 8); diff --git a/TFT_eSPI.h b/TFT_eSPI.h index e6d7719..a2b6834 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -472,9 +472,18 @@ inline void spi_end() __attribute__((always_inline)); *gfxFont; #endif - void delay_ns(uint32_t d) { delayMicroseconds(1); /* ToDo */ } }; +static inline void delay_ns(int32_t d) +{ + while (d > 0) + { + asm volatile ("nop ; nop ; nop ; nop" : : : "memory"); + d -= 25; + } + /* ToDo */ +} + #endif /*************************************************** From aa9352a7b24b9ea8787ae3ff79e6963c3200dbaf Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Thu, 29 Jun 2017 00:00:48 +0200 Subject: [PATCH 03/16] Use hardware 9-bit SPI to talk to ST7787 controller. --- .gitignore | 3 + TFT_eSPI.cpp | 220 ++++++++++++++++++++++++++++++++++++++++++++++++--- TFT_eSPI.h | 9 +++ User_Setup.h | 4 +- 4 files changed, 223 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index cd2946a..fb5289c 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,9 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk +# Backup files +*~ + # ========================= # Operating System Files # ========================= diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index a90f9fd..f39464a 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -24,9 +24,7 @@ //#include //#include "pins_arduino.h" //#include "wiring_private.h" -#ifndef ST7787_DRIVER #include -#endif // If it is a 16bit serial display we must transfer 16 bits every time #ifdef RPI_ILI9486_DRIVER @@ -46,7 +44,13 @@ void spiWriteBlock(uint16_t color, uint32_t repeat); inline void TFT_eSPI::spi_begin(void){ #ifdef SPI_HAS_TRANSACTION #ifdef SUPPORT_TRANSACTIONS - if (locked) {locked = false; SPI.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, SPI_MODE0));} + if (locked) { + locked = false; + SPI.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, SPI_MODE0)); + #ifdef IFACE_3WIRE_ESP8266 + SPI1U |= (uint32_t)(SPIUWRBYO|SPIURDBYO); + #endif + } #endif #endif } @@ -54,7 +58,15 @@ inline void TFT_eSPI::spi_begin(void){ inline void TFT_eSPI::spi_end(void){ #ifdef SPI_HAS_TRANSACTION #ifdef SUPPORT_TRANSACTIONS - if(!inTransaction) {if (!locked) {locked = true; SPI.endTransaction();}} + if(!inTransaction) { + if (!locked) { + locked = true; + SPI.endTransaction(); + #ifdef IFACE_3WIRE_ESP8266 + SPI1U &= ~(uint32_t)(SPIUWRBYO|SPIURDBYO); + #endif + } + } #endif #endif } @@ -192,11 +204,12 @@ void TFT_eSPI::init(void) cspinmask = (uint32_t) digitalPinToBitMask(TFT_CS); #endif -#ifndef ST7787_DRIVER - SPI.begin(); // This will set HMISO to input -#endif + #if !defined(IFACE_3WIRE) || defined(IFACE_3WIRE_ESP8266) + SPI.begin(); // This will set HMISO to input + #endif #else #if defined (TFT_MOSI) && !defined (TFT_SPI_OVERLAP) + // ToDo: handle 3-wire interface on ESP32. SPI.begin(TFT_SCLK, TFT_MISO, TFT_MOSI, -1); #else SPI.begin(); @@ -207,20 +220,21 @@ void TFT_eSPI::init(void) inTransaction = false; locked = true; -#ifdef ST7787_DRIVER #ifndef SUPPORT_TRANSACTIONS SPI.setBitOrder(MSBFIRST); SPI.setDataMode(SPI_MODE0); SPI.setFrequency(SPI_FREQUENCY); - + #ifdef IFACE_3WIRE_ESP8266 + SPI1U |= (uint32_t)(SPIUWRBYO|SPIURDBYO); + #endif + #ifdef ESP32 // Unlock the SPI hal mutex and set the lock management flags SPI.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, SPI_MODE0)); inTransaction = true; // Flag to stop intermediate spi_end calls locked = false; // Flag to stop repeat beginTransaction calls #endif -#endif #endif // Set to output once again in case D6 (MISO) is used for CS @@ -368,6 +382,62 @@ void TFT_eSPI::spiwrite(uint8_t c) } +#ifdef IFACE_3WIRE + #ifdef IFACE_3WIRE_ESP8266 +void TFT_eSPI::docommand(uint8_t c, uint8_t *in, uint32_t in_len, uint8_t *out, uint32_t out_len) +{ + uint32_t i; + uint32_t val; + + CS_L; + + uint32_t mask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); + mask = SPI1U1 & mask; + + // Send the command byte, with a '0' DC bit. + SPI1U1 = mask | ((CMD_BITS+1) << SPILMOSI) | ((CMD_BITS+1) << SPILMISO); + SPI1W0 = (uint32_t)c << 23; + SPI1CMD |= SPIBUSY; + while(SPI1CMD & SPIBUSY) {} + + if (in_len) { + do { + val = *in++; + + SPI1U1 = mask | ((CMD_BITS+1) << SPILMOSI) | ((CMD_BITS+1) << SPILMISO); + SPI1W0 = (uint32_t)0x80000000 | (val << 23); + SPI1CMD |= SPIBUSY; + val <<= 1; + --in_len; + while(SPI1CMD & SPIBUSY) {} + } while (in_len > 0); + } + + if (out_len) { + // temporarily disable the MOSI pin while we read into MISO. + pinMode(MOSI, INPUT); + + SPI1U1 = mask | ((CMD_BITS+1) << SPILMOSI) | ((CMD_BITS+1) << SPILMISO); + SPI1W0 = 0; + SPI1CMD |= SPIBUSY; + while(SPI1CMD & SPIBUSY) {} + *out++ = SPI1W0 >> 23; + + while (--out_len > 0) { + SPI1U1 = mask | (CMD_BITS << SPILMOSI) | (CMD_BITS << SPILMISO); + SPI1W0 = 0; + SPI1CMD |= SPIBUSY; + while(SPI1CMD & SPIBUSY) {} + *out++ = SPI1W0 >> 24; + } + + pinMode(MOSI, SPECIAL); + } + + CS_H; +} + + #else void TFT_eSPI::docommand(uint8_t c, uint8_t *in, uint32_t in_len, uint8_t *out, uint32_t out_len) { uint32_t i; @@ -455,21 +525,76 @@ void TFT_eSPI::docommand(uint8_t c, uint8_t *in, uint32_t in_len, uint8_t *out, delay_ns(ST7787_T_CHW); } + #endif +#endif /*************************************************************************************** ** Function name: writecommand ** Description: Send an 8 bit command to the TFT ***************************************************************************************/ +#ifdef IFACE_3WIRE +#ifdef IFACE_3WIRE_ESP8266 +void TFT_eSPI::writecommand(uint8_t c) +{ + CS_L; + + uint32_t mask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); + mask = SPI1U1 & mask; + + SPI1U1 = mask | ((CMD_BITS+1) << SPILMOSI) | ((CMD_BITS+1) << SPILMISO); + + SPI1W0 = (uint32_t)c << 23; + SPI1CMD |= SPIBUSY; + while(SPI1CMD & SPIBUSY) {} + + CS_H; +} + +#else void TFT_eSPI::writecommand(uint8_t c) { docommand(c, NULL, 0, NULL, 0); } +#endif +#else +void TFT_eSPI::writecommand(uint8_t c) +{ + DC_C; + CS_L; + #ifdef SEND_16_BITS + SPI.transfer(0); + #endif + SPI.transfer(c); + CS_H; + DC_D; +} + +#endif /* IFACE_3WIRE */ /*************************************************************************************** ** Function name: writedata ** Description: Send a 8 bit data value to the TFT ***************************************************************************************/ +#ifdef IFACE_3WIRE +#ifdef IFACE_3WIRE_ESP8266 +void TFT_eSPI::writedata(uint8_t c) +{ + CS_L; + + uint32_t mask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); + mask = SPI1U1 & mask; + + SPI1U1 = mask | ((CMD_BITS+1) << SPILMOSI) | ((CMD_BITS+1) << SPILMISO); + + SPI1W0 = (uint32_t)0x80000000 | ((uint32_t)c << 23); + SPI1CMD |= SPIBUSY; + while(SPI1CMD & SPIBUSY) {} + + CS_H; +} + +#else void TFT_eSPI::writedata(uint8_t c) { uint32_t i; @@ -505,7 +630,19 @@ void TFT_eSPI::writedata(uint8_t c) CS_H; delay_ns(ST7787_T_CHW); } +#endif +#else /* !IFACE_3WIRE */ + void TFT_eSPI::writedata(uint8_t c) + { + CS_L; + #ifdef SEND_16_BITS + SPI.transfer(0); + #endif + SPI.transfer(c); + CS_H; +} +#endif /*************************************************************************************** ** Function name: readcommand8 (for ILI9341 Interface II i.e. IM [3:0] = "1101") @@ -3749,11 +3886,71 @@ void TFT_eSPI::setTextFont(uint8_t f) #endif +#define STARTBITS(cur_, pos_, spireg_) { cur_ = 0; pos_ = 32; spireg_ = &SPI1W0; } +#define ADDBITS(val_, bits_, cur_, pos_, spireg_) { \ + if (pos_ < bits_) \ + { \ + cur_ |= val_ >> (bits_ - pos_); \ + *spireg_++ = cur_; \ + pos_ = 32 - (bits_ - pos_); \ + cur_ = ((uint32_t)val_ << pos_); \ + } else { \ + pos_ -= bits_; \ + cur_ |= ((uint32_t)val_ << pos_); \ + } \ +} +#define FLUSHBITS(cur_, spireg_) { *spireg_ = cur_; } + /*************************************************************************************** ** Function name: spiBlockWrite ** Description: Write a block of pixels of the same colour ***************************************************************************************/ -#if defined(ESP8266) && defined(ST7787_DRIVER) +#ifdef IFACE_3WIRE +#ifdef IFACE_3WIRE_ESP8266 +void spiWriteBlock(uint16_t color, uint32_t repeat) +{ + uint32_t mask = ~(SPIMMOSI << SPILMOSI); + mask = SPI1U1 & mask; + + // Stuff two color bytes with two '1' DC bits. + uint32_t color18 = (uint32_t)0x20100 | (((uint32_t)color & 0xff00) << 1) | (color & 0xff); + uint32_t cur, pos; + volatile uint32_t *spireg; + + STARTBITS(cur, pos, spireg); + SPI1U = SPIUMOSI | SPIUSSE | SPIUWRBYO | SPIURDBYO; + + uint32_t fillcount = repeat; + if (fillcount > 28) + fillcount = 28; + while (fillcount-- > 0) + ADDBITS(color18, 18, cur, pos, spireg); + FLUSHBITS(cur, spireg); + + if (repeat >= 28) + { + SPI1U1 = mask | ((18*28-1) << SPILMOSI); + while(repeat>=28) + { + while(SPI1CMD & SPIBUSY) {} + SPI1CMD |= SPIBUSY; + repeat -= 28; + } + while(SPI1CMD & SPIBUSY) {} + } + + if (repeat) + { + repeat = (repeat * 18) - 1; + SPI1U1 = mask | (repeat << SPILMOSI); + SPI1CMD |= SPIBUSY; + while(SPI1CMD & SPIBUSY) {} + } + + SPI1U = SPIUMOSI | SPIUDUPLEX | SPIUSSE | SPIUWRBYO | SPIURDBYO; +} + +#else void spiWriteBlock(uint16_t color, uint32_t repeat) { uint32_t i; @@ -3808,6 +4005,7 @@ void spiWriteBlock(uint16_t color, uint32_t repeat) DAT_I; } +#endif /* IFACE_3WIRE */ #elif defined (ESP8266) && (SPI_FREQUENCY != 80000000) void spiWriteBlock(uint16_t color, uint32_t repeat) { diff --git a/TFT_eSPI.h b/TFT_eSPI.h index a2b6834..d187acb 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -30,6 +30,15 @@ #define SPI_FREQUENCY 20000000 #endif +#ifdef ST7787_DRIVER + // ST7787 only supports a 3-wire serial interface, where the data/command bit + // is sent as a 9th bit in the SPI protocol, not on a separate wire. + #define IFACE_3WIRE 1 + #ifdef ESP8266 + #define IFACE_3WIRE_ESP8266 1 + #endif +#endif + // Only load the fonts defined in User_Setup.h (to save space) // Set flag so RLE rendering code is optionally compiled #ifdef LOAD_GLCD diff --git a/User_Setup.h b/User_Setup.h index 67d178b..c94c59d 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -160,8 +160,8 @@ // #define SPI_FREQUENCY 1000000 // #define SPI_FREQUENCY 5000000 // #define SPI_FREQUENCY 10000000 -// #define SPI_FREQUENCY 20000000 - #define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 +#define SPI_FREQUENCY 20000000 +// #define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 // #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS // #define SPI_FREQUENCY 80000000 From 81b97a25b9a4a6cf9b796c2ac28c79cefa9ece50 Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Thu, 29 Jun 2017 01:46:00 +0200 Subject: [PATCH 04/16] Use 9-bit hardware SPI for ST7787 SetAddrWindow() --- TFT_eSPI.cpp | 77 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 61 insertions(+), 16 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index f39464a..f74119b 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -37,6 +37,24 @@ // Fast SPI block write prototype void spiWriteBlock(uint16_t color, uint32_t repeat); +// Bit-stuffing for fast ESP8266 9-bit SPI. +#ifdef IFACE_3WIRE_ESP8266 +#define STARTBITS(cur_, pos_, spireg_) { cur_ = 0; pos_ = 32; spireg_ = &SPI1W0; } +#define ADDBITS(val_, bits_, cur_, pos_, spireg_) { \ + if ((pos_) < (bits_)) \ + { \ + cur_ |= (val_) >> ((bits_) - (pos_)); \ + *spireg_++ = cur_; \ + pos_ = 32 - ((bits_) - (pos_)); \ + cur_ = ((uint32_t)(val_) << (pos_)); \ + } else { \ + pos_ -= (bits_); \ + cur_ |= ((uint32_t)(val_) << (pos_)); \ + } \ +} +#define FLUSHBITS(cur_, spireg_) { *spireg_ = cur_; } +#endif + // If the SPI library has transaction support, these functions // establish settings and protect from interference from other // libraries. Otherwise, they simply do nothing. @@ -1847,7 +1865,48 @@ void TFT_eSPI::setWindow(int16_t x0, int16_t y0, int16_t x1, int16_t y1) ***************************************************************************************/ // Chip select stays low, use setWindow() from sketches -#if defined (ESP8266) && defined(ST7787_DRIVER) +#ifdef IFACE_3WIRE +#ifdef IFACE_3WIRE_ESP8266 +inline void TFT_eSPI::setAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) +{ + //spi_begin(); + addr_col = 0xFFFF; + addr_row = 0xFFFF; + +#ifdef CGRAM_OFFSET + xs+=colstart; + xe+=colstart; + ys+=rowstart; + ye+=rowstart; +#endif + + // Column addr set + CS_L; + + uint32_t mask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); + mask = SPI1U1 & mask; + + uint32_t cur, pos; + volatile uint32_t *spireg; + uint32_t numbits = 9+36+9+36+9-1; + STARTBITS(cur, pos, spireg); + ADDBITS(((uint32_t)TFT_CASET << 18) | (uint32_t)0x20100 | ((xs & 0xff00) << 1) | (xs & 0xff), + 27, cur, pos, spireg); + ADDBITS((uint32_t)0x20100 | ((xe & 0xff00) << 1) | (xe & 0xff), 18, cur, pos, spireg); + ADDBITS(((uint32_t)TFT_PASET << 18) | (uint32_t)0x20100 | ((ys & 0xff00) << 1) | (ys & 0xff), + 27, cur, pos, spireg); + ADDBITS((uint32_t)0x20100 | ((ye & 0xff00) << 1) | (ye & 0xff), 18, cur, pos, spireg); + ADDBITS(TFT_RAMWR, 9, cur, pos, spireg); + FLUSHBITS(cur, spireg); + SPI1U1 = mask | (numbits << SPILMOSI) | (numbits << SPILMISO); + + SPI1CMD |= SPIBUSY; + while(SPI1CMD & SPIBUSY) {} + + //spi_end(); +} + +#else /* !IFACE_3WIRE_ESP8266 */ inline void TFT_eSPI::setAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) { uint8_t buf[4]; @@ -1881,6 +1940,7 @@ inline void TFT_eSPI::setAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t /* ToDo: This could be done smarter, for now just made simple to match other code. */ } +#endif /* IFACE_3WIRE_ESP8266 */ #elif defined (ESP8266) && !defined (RPI_WRITE_STROBE) && !defined (RPI_ILI9486_DRIVER) inline void TFT_eSPI::setAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) { @@ -3886,21 +3946,6 @@ void TFT_eSPI::setTextFont(uint8_t f) #endif -#define STARTBITS(cur_, pos_, spireg_) { cur_ = 0; pos_ = 32; spireg_ = &SPI1W0; } -#define ADDBITS(val_, bits_, cur_, pos_, spireg_) { \ - if (pos_ < bits_) \ - { \ - cur_ |= val_ >> (bits_ - pos_); \ - *spireg_++ = cur_; \ - pos_ = 32 - (bits_ - pos_); \ - cur_ = ((uint32_t)val_ << pos_); \ - } else { \ - pos_ -= bits_; \ - cur_ |= ((uint32_t)val_ << pos_); \ - } \ -} -#define FLUSHBITS(cur_, spireg_) { *spireg_ = cur_; } - /*************************************************************************************** ** Function name: spiBlockWrite ** Description: Write a block of pixels of the same colour From cf0040be4f852eec18801c65d9a2b72c5d62636f Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Thu, 29 Jun 2017 03:08:01 +0200 Subject: [PATCH 05/16] Use 9-bit hardware SPI for ST7787 drawPixel() --- TFT_eSPI.cpp | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index f74119b..41c4250 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -2337,7 +2337,62 @@ void TFT_eSPI::readAddrWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) ** Function name: drawPixel ** Description: push a single pixel at an arbitrary position ***************************************************************************************/ -#ifdef ST7787_DRIVER +#ifdef IFACE_3WIRE +#ifdef IFACE_3WIRE_ESP8266 +void TFT_eSPI::drawPixel(uint32_t x, uint32_t y, uint32_t color) +{ + // Faster range checking, possible because x and y are unsigned + if ((x >= _width) || (y >= _height)) return; + +#ifdef CGRAM_OFFSET + x+=colstart; + y+=rowstart; +#endif + + spi_begin(); + + CS_L; + + uint32_t mask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); + mask = SPI1U1 & mask; + + uint32_t cur, pos; + volatile uint32_t *spireg; + uint32_t numbits = 27; + STARTBITS(cur, pos, spireg); + + // No need to send x if it has not changed (speeds things up) + if (addr_col != x) { + ADDBITS(((uint32_t)TFT_CASET << 18) | ((uint32_t)0x20100 | ((x & 0xff00) << 1) | (x & 0xff)), + 27, cur, pos, spireg); + ADDBITS((uint32_t)0x20100 | ((x & 0xff00) << 1) | (x & 0xff), 18, cur, pos, spireg); + numbits += 45; + addr_col = x; + } + + // No need to send y if it has not changed (speeds things up) + if (addr_row != y) { + ADDBITS(((uint32_t)TFT_PASET << 18) | ((uint32_t)0x20100 | ((y & 0xff00) << 1) | (y & 0xff)), + 27, cur, pos, spireg); + ADDBITS((uint32_t)0x20100 | ((y & 0xff00) << 1) | (y & 0xff), 18, cur, pos, spireg); + numbits += 45; + addr_row = y; + } + + ADDBITS(((uint32_t)TFT_RAMWR << 18) | (uint32_t)0x20100 | ((color & 0xff00) << 1) | (color & 0xff), + 27, cur, pos, spireg); + FLUSHBITS(cur, spireg); + + SPI1U1 = mask | (numbits << SPILMOSI) | (numbits << SPILMISO); + SPI1CMD |= SPIBUSY; + while(SPI1CMD & SPIBUSY) {} + + CS_H; + + spi_end(); +} + +#else void TFT_eSPI::drawPixel(uint32_t x, uint32_t y, uint32_t color) { uint8_t buf[4]; @@ -2380,6 +2435,7 @@ void TFT_eSPI::drawPixel(uint32_t x, uint32_t y, uint32_t color) spi_end(); } +#endif #elif defined (ESP8266) && !defined (RPI_WRITE_STROBE) void TFT_eSPI::drawPixel(uint32_t x, uint32_t y, uint32_t color) From 4cf14bd50787fd328e3c1f064920bf10ae80c00b Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Fri, 30 Jun 2017 00:29:44 +0200 Subject: [PATCH 06/16] Implement readPixel(), readRect(), and pushRect() for ST7787. --- TFT_eSPI.cpp | 239 +++++++++++++++++- .../320 x 240/TFT_ReadPixel/TFT_ReadPixel.ino | 61 +++++ 2 files changed, 298 insertions(+), 2 deletions(-) create mode 100644 examples/320 x 240/TFT_ReadPixel/TFT_ReadPixel.ino diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 41c4250..7f93cba 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -37,7 +37,8 @@ // Fast SPI block write prototype void spiWriteBlock(uint16_t color, uint32_t repeat); -// Bit-stuffing for fast ESP8266 9-bit SPI. +// Bit-stuffing for fast ESP8266 9-bit SPI (for 3-wire protocol used +// by ST7787_DRIVER). #ifdef IFACE_3WIRE_ESP8266 #define STARTBITS(cur_, pos_, spireg_) { cur_ = 0; pos_ = 32; spireg_ = &SPI1W0; } #define ADDBITS(val_, bits_, cur_, pos_, spireg_) { \ @@ -394,10 +395,14 @@ void TFT_eSPI::commandList (const uint8_t *addr) ** Function name: spiwrite ** Description: Write 8 bits to SPI port (legacy support only) ***************************************************************************************/ +// Does not make sense in 3-wire mode, which requires an extra data/command bit +// with every byte. +#ifndef IFACE_3WIRE void TFT_eSPI::spiwrite(uint8_t c) { SPI.transfer(c); } +#endif #ifdef IFACE_3WIRE @@ -666,6 +671,25 @@ void TFT_eSPI::writedata(uint8_t c) ** Function name: readcommand8 (for ILI9341 Interface II i.e. IM [3:0] = "1101") ** Description: Read a 8 bit data value from an indexed command register ***************************************************************************************/ +#ifdef IFACE_3WIRE +// The 3-wire readcommand8() function is currently untested. The ST7787 is the +// only driver using this protocol, and it does not have the 0xD9 command for +// reading partial registers. Instead, the docommand() function can be used +// to do a full read with the various read commands available (RDDID, RDDST, ...) + uint8_t TFT_eSPI::readcommand8(uint8_t cmd_function, uint8_t index) +{ + spi_begin(); + index = 0x10 + (index & 0x0F); + + docommand(0xD9, &index, 1, NULL, 0); + uint8_t reg; + docommand(cmd_function, NULL, 0, ®, 1); + + spi_end(); + return reg; +} + +#else uint8_t TFT_eSPI::readcommand8(uint8_t cmd_function, uint8_t index) { spi_begin(); @@ -688,6 +712,7 @@ void TFT_eSPI::writedata(uint8_t c) spi_end(); return reg; } +#endif /*************************************************************************************** @@ -725,6 +750,40 @@ void TFT_eSPI::writedata(uint8_t c) ** Function name: read pixel (for SPI Interface II i.e. IM [3:0] = "1101") ** Description: Read 565 pixel colours from a pixel ***************************************************************************************/ +#ifdef IFACE_3WIRE + #ifdef IFACE_3WIRE_ESP8266 +uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) +{ + spi_begin(); + + readAddrWindow(x0, y0, x0, y0); // Sets CS low and MOSI in input + + // Read 4 bytes - one dummy value and the three RGB values. + // Apparently there is also a dummy bit before, just like other reads. + uint32_t mask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); + mask = SPI1U1 & mask; + + SPI1U1 = mask | ((33-1) << SPILMOSI) | ((33-1) << SPILMISO); + SPI1W0 = 0; + SPI1CMD |= SPIBUSY; + while(SPI1CMD & SPIBUSY) {} + uint32_t result = (SPI1W0 << 1) | (SPI1W1 >> 31); + uint8_t r = result >> 16; + uint8_t g = result >> 8; + uint8_t b = result; + + pinMode(MOSI, SPECIAL); + CS_H; + + spi_end(); + + return color565(r, g, b); +} + + #else + // ToDo: ESP32 3-wire version of readPixel(). + #endif /* IFACE_3WIRE_ESP8266 */ +#else uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) { spi_begin(); @@ -745,12 +804,92 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) return color565(r, g, b); } +#endif /*************************************************************************************** ** Function name: read rectangle (for SPI Interface II i.e. IM [3:0] = "1101") ** Description: Read 565 pixel colours from a defined area ***************************************************************************************/ +#ifdef IFACE_3WIRE + #ifdef IFACE_3WIRE_ESP8266 + void TFT_eSPI::readRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t *data) +{ + if ((x > _width) || (y > _height) || (w == 0) || (h == 0)) return; + + addr_col = 0xFFFF; + addr_row = 0xFFFF; + +#ifdef CGRAM_OFFSET + x+=colstart; + y+=rowstart; +#endif + + spi_begin(); + CS_L; + + uint32_t mask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); + mask = SPI1U1 & mask; + + // Apparently, the 3-wire serial interface in the ST7787 does not support + // reading out multiple pixels. The data pin goes high-Z after one pixel no + // matter the size of the read window, and the datasheet seems to hint at + // this as well. + // So we have to go the slow way, with one RAMRD command per pixel :-/ + uint32_t i, j; + for (j = 0; j < h; ++j) { + for (i = 0; i < w; ++i) { + + uint32_t cur, pos; + volatile uint32_t *spireg; + uint32_t numbits = 9-1; + STARTBITS(cur, pos, spireg); + if (w != 1 || (j == 0 && i == 0)) { + uint32_t tmp = (uint32_t)0x20100 | (((x+i) & 0xff00) << 1) | ((x+i) & 0xff); + ADDBITS(((uint32_t)TFT_CASET << 18) | tmp, 27, cur, pos, spireg); + ADDBITS(tmp, 18, cur, pos, spireg); + numbits += 27+18; + } + if (i == 0) { + uint32_t tmp = (uint32_t)0x20100 | (((y+j) & 0xff00) << 1) | ((y+j) & 0xff); + ADDBITS(((uint32_t)TFT_PASET << 18) | tmp, 27, cur, pos, spireg); + ADDBITS(tmp, 18, cur, pos, spireg); + numbits += 27+18; + } + ADDBITS(TFT_RAMRD, 9, cur, pos, spireg); + FLUSHBITS(cur, spireg); + SPI1U1 = mask | (numbits << SPILMOSI) | (numbits << SPILMISO); + + SPI1CMD |= SPIBUSY; + while(SPI1CMD & SPIBUSY) {} + // temporarily disable the MOSI pin while we read into MISO. + pinMode(MOSI, INPUT); + + // There is one dummy bit and one dummy byte before the real data. + SPI1U1 = mask | ((9+24-1) << SPILMOSI) | ((9+24-1) << SPILMISO); + SPI1CMD |= SPIBUSY; + while(SPI1CMD & SPIBUSY) {} + + uint32_t val1 = SPI1W0; + uint32_t val2 = SPI1W1; + uint8_t r = val1 >> 15; + uint8_t g = val1 >> 7; + uint8_t b = (val1 << 1) | (val2 >> 31); + // Swapped colour byte order for compatibility with pushRect() + *data++ = (r & 0xF8) | (g & 0xE0) >> 5 | (b & 0xF8) << 5 | (g & 0x1C) << 11; + + pinMode(MOSI, SPECIAL); + } + } + + CS_H; + spi_end(); +} + + #else + // ToDo: ESP32 3-wire version of readRect(). + #endif /* IFACE_3WIRE_ESP8266 */ +#else void TFT_eSPI::readRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t *data) { if ((x > _width) || (y > _height) || (w == 0) || (h == 0)) return; @@ -783,12 +922,60 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) spi_end(); } +#endif /*************************************************************************************** ** Function name: push rectangle (for SPI Interface II i.e. IM [3:0] = "1101") ** Description: push 565 pixel colours into a defined area ***************************************************************************************/ +#ifdef IFACE_3WIRE + #ifdef IFACE_3WIRE_ESP8266 + void TFT_eSPI::pushRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t *data) +{ + if ((x > _width) || (y > _height) || (w == 0) || (h == 0)) return; + + spi_begin(); + + setAddrWindow(x, y, x + w - 1, y + h - 1); // Sets CS low and sent RAMWR + + uint32_t mask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); + mask = SPI1U1 & mask; + + uint32_t len = w * h; + while (len > 0) { + uint32_t cur, pos; + volatile uint32_t *spireg; + STARTBITS(cur, pos, spireg); + + uint32_t chunk = len; + if (chunk > 28) + chunk = 28; + + uint32_t count = chunk; + while (count-- > 0) { + uint32_t val = *data++; + uint32_t color18 = (uint32_t)0x20100 | ((val & 0xff) << 9) | ((val >> 8) & 0xff); + ADDBITS(color18, 18, cur, pos, spireg); + } + FLUSHBITS(cur, spireg); + + SPI1U1 = mask | ((chunk*18-1) << SPILMOSI) | ((chunk*18-1) << SPILMISO); + SPI1CMD |= SPIBUSY; + while(SPI1CMD & SPIBUSY) {} + + len -= chunk; + } + + CS_H; + + spi_end(); +} + + #else + // ToDo: ESP32 3-wire version of pushRect(). + #endif /* IFACE_3WIRE_ESP8266 */ +#else void TFT_eSPI::pushRect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t *data) { if ((x > _width) || (y > _height) || (w == 0) || (h == 0)) return; @@ -806,6 +993,7 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) spi_end(); } +#endif /*************************************************************************************** @@ -2221,7 +2409,54 @@ inline void TFT_eSPI::setAddrWindow(int32_t x0, int32_t y0, int32_t x1, int32_t ** Description: define an area to read a stream of pixels ***************************************************************************************/ // Chip select stays low -#if defined (ESP8266) && !defined (RPI_WRITE_STROBE) +#ifdef IFACE_3WIRE + #ifdef IFACE_3WIRE_ESP8266 +void TFT_eSPI::readAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) +{ + //spi_begin(); + + addr_col = 0xFFFF; + addr_row = 0xFFFF; + +#ifdef CGRAM_OFFSET + xs+=colstart; + xe+=colstart; + ys+=rowstart; + ye+=rowstart; +#endif + + CS_L; + + uint32_t mask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); + mask = SPI1U1 & mask; + + uint32_t cur, pos; + volatile uint32_t *spireg; + uint32_t numbits = 9+36+9+36+9-1; + STARTBITS(cur, pos, spireg); + ADDBITS(((uint32_t)TFT_CASET << 18) | (uint32_t)0x20100 | ((xs & 0xff00) << 1) | (xs & 0xff), + 27, cur, pos, spireg); + ADDBITS((uint32_t)0x20100 | ((xe & 0xff00) << 1) | (xe & 0xff), 18, cur, pos, spireg); + ADDBITS(((uint32_t)TFT_PASET << 18) | (uint32_t)0x20100 | ((ys & 0xff00) << 1) | (ys & 0xff), + 27, cur, pos, spireg); + ADDBITS((uint32_t)0x20100 | ((ye & 0xff00) << 1) | (ye & 0xff), 18, cur, pos, spireg); + ADDBITS(TFT_RAMRD, 9, cur, pos, spireg); + FLUSHBITS(cur, spireg); + SPI1U1 = mask | (numbits << SPILMOSI) | (numbits << SPILMISO); + + SPI1CMD |= SPIBUSY; + while(SPI1CMD & SPIBUSY) {} + + // temporarily disable the MOSI pin while we read into MISO. + pinMode(MOSI, INPUT); + + //spi_end(); +} + + #else + // ToDo: ESP32 3-wire version of readAddrWindow(). + #endif /* IFACE_3WIRE_ESP8266 */ +#elif defined (ESP8266) && !defined (RPI_WRITE_STROBE) void TFT_eSPI::readAddrWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye) { //spi_begin(); diff --git a/examples/320 x 240/TFT_ReadPixel/TFT_ReadPixel.ino b/examples/320 x 240/TFT_ReadPixel/TFT_ReadPixel.ino new file mode 100644 index 0000000..5fd03ab --- /dev/null +++ b/examples/320 x 240/TFT_ReadPixel/TFT_ReadPixel.ino @@ -0,0 +1,61 @@ +// Test readPixel() and readRect() + +#include // Hardware-specific library +#include + +TFT_eSPI tft = TFT_eSPI(); // Invoke custom library + +unsigned long runTime = 0; + + +void setup() +{ + //randomSeed(analogRead(A0)); + Serial.begin(115200); + + // Setup the LCD + tft.init(); +} + +uint16_t pixel_buf[16*16]; + +void loop() +{ + runTime = millis(); + + tft.fillScreen(ILI9341_BLACK); + tft.drawPixel(1, 1, tft.color565(255, 0, 0)); + tft.drawPixel(10, 10, tft.color565(0, 255, 0)); + tft.drawPixel(3, 15, tft.color565(0, 0, 255)); + tft.drawPixel(13, 4, tft.color565(30<<3, 20<<2, 10<<3)); + Serial.println("readPixel() test:"); + printRGB(tft.readPixel(0, 0)); + printRGB(tft.readPixel(1, 1)); + printRGB(tft.readPixel(10, 10)); + printRGB(tft.readPixel(3, 15)); + printRGB(tft.readPixel(13, 4)); + + tft.fillRect(20, 5, 18, 18, tft.color565(0x22, 0x22, 0x22)); + tft.drawPixel(11, 10, tft.color565(3<<3, 5<<2, 9<<3)); + tft.drawPixel(10, 11, tft.color565(0<<3, 1<<2, 2<<3)); + Serial.println("readRect() test:"); + tft.readRect(10, 10, 2, 2, pixel_buf); + int i; + for (i = 0; i < 4; ++i) + printRGB((pixel_buf[i]<<8)|(pixel_buf[i]>>8)); + + tft.readRect(0, 0, 16, 16, pixel_buf); + tft.pushRect(20+1, 5+1, 16, 16, pixel_buf); + + while(1) yield(); +} + +void printRGB(uint16_t pixel) +{ + Serial.print("RGB: "); + Serial.print(pixel >> 11); + Serial.print(" "); + Serial.print((pixel >> 5) & 0x3f); + Serial.print(" "); + Serial.println(pixel & 0x1f); +} From f9ceca205221e5de0b5ef2fce7fb38dc9046be84 Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Fri, 30 Jun 2017 13:13:16 +0200 Subject: [PATCH 07/16] Implement pushColor() and pushColors() for ST7787 --- TFT_eSPI.cpp | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index 7f93cba..eaac40d 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -2942,6 +2942,33 @@ void TFT_eSPI::drawPixel(uint32_t x, uint32_t y, uint32_t color) ** Function name: pushColor ** Description: push a single pixel ***************************************************************************************/ +#ifdef IFACE_3WIRE + #ifdef IFACE_3WIRE_ESP8266 +void TFT_eSPI::pushColor(uint16_t color) +{ + spi_begin(); + + CS_L; + + uint32_t mask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); + mask = SPI1U1 & mask; + + SPI1U1 = mask | ((18-1) << SPILMOSI) | ((18-1) << SPILMISO); + SPI1W0 = (uint32_t)0x80400000 | + (((uint32_t)color & 0xff00) << 15) | + (((uint32_t)color & 0xff) << 14); + SPI1CMD |= SPIBUSY; + while(SPI1CMD & SPIBUSY) {} + + CS_H; + + spi_end(); +} + + #else + // ToDo: ESP32 3-wire version of pushColor(). + #endif /* IFACE_3WIRE_ESP8266 */ +#else void TFT_eSPI::pushColor(uint16_t color) { spi_begin(); @@ -2954,6 +2981,7 @@ void TFT_eSPI::pushColor(uint16_t color) spi_end(); } +#endif /*************************************************************************************** @@ -2999,6 +3027,35 @@ void TFT_eSPI::pushColors(uint16_t *data, uint8_t len) while (len--) SPI.write16(*(data++)); +#elif defined(IFACE_3WIRE_ESP8266) + + uint32_t mask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); + mask = SPI1U1 & mask; + + while (len > 0) { + uint32_t cur, pos; + volatile uint32_t *spireg; + STARTBITS(cur, pos, spireg); + + uint32_t chunk = len; + if (chunk > 28) + chunk = 28; + + uint32_t count = chunk; + while (count-- > 0) { + uint32_t val = *data++; + uint32_t color18 = (uint32_t)0x20100 | ((val & 0xff00) << 1) | (val & 0xff); + ADDBITS(color18, 18, cur, pos, spireg); + } + FLUSHBITS(cur, spireg); + + SPI1U1 = mask | ((chunk*18-1) << SPILMOSI) | ((chunk*18-1) << SPILMISO); + SPI1CMD |= SPIBUSY; + while(SPI1CMD & SPIBUSY) {} + + len -= chunk; + } + #else uint32_t mask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); @@ -3059,6 +3116,37 @@ void TFT_eSPI::pushColors(uint8_t *data, uint32_t len) //while ( len ) {SPI.writePattern(data, 2, 1); data += 2; len -= 2; } while ( len >=64 ) {SPI.writePattern(data, 64, 1); data += 64; len -= 64; } if (len) SPI.writePattern(data, len, 1); +#elif defined(IFACE_3WIRE_ESP8266) + + uint32_t mask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); + mask = SPI1U1 & mask; + + len /= 2; + while (len > 0) { + uint32_t cur, pos; + volatile uint32_t *spireg; + STARTBITS(cur, pos, spireg); + + uint32_t chunk = len; + if (chunk > 28) + chunk = 28; + + uint32_t count = chunk; + while (count-- > 0) { + uint32_t val1 = *data++; + uint32_t val2 = *data++; + uint32_t color18 = (uint32_t)0x20100 | (val1 << 9) | val2; + ADDBITS(color18, 18, cur, pos, spireg); + } + FLUSHBITS(cur, spireg); + + SPI1U1 = mask | ((chunk*18-1) << SPILMOSI) | ((chunk*18-1) << SPILMISO); + SPI1CMD |= SPIBUSY; + while(SPI1CMD & SPIBUSY) {} + + len -= chunk; + } + #else #if (SPI_FREQUENCY == 80000000) while ( len >=64 ) {SPI.writePattern(data, 64, 1); data += 64; len -= 64; } From 32703fd922ce3bd3a309419d3e0cbe2547dead8c Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Fri, 30 Jun 2017 13:14:01 +0200 Subject: [PATCH 08/16] Fix #include file name for case-sensitive file systems --- examples/160 x 128/Flash_Bitmap2/Flash_Bitmap2.ino | 2 +- examples/320 x 240/TFT_Flash_Bitmap/TFT_Flash_Bitmap.ino | 2 +- examples/480 x 320/Flash_Bitmap/Flash_Bitmap.ino | 2 +- examples/480 x 320/TFT_ring_meter/TFT_ring_meter.ino | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/160 x 128/Flash_Bitmap2/Flash_Bitmap2.ino b/examples/160 x 128/Flash_Bitmap2/Flash_Bitmap2.ino index 3cf517a..8051cf3 100644 --- a/examples/160 x 128/Flash_Bitmap2/Flash_Bitmap2.ino +++ b/examples/160 x 128/Flash_Bitmap2/Flash_Bitmap2.ino @@ -28,7 +28,7 @@ TFT_eSPI tft = TFT_eSPI(); // Invoke library, pins defined in User_Setup.h // Include the header files that contain the icons -#include "alert.h" +#include "Alert.h" #include "Close.h" #include "Info.h" diff --git a/examples/320 x 240/TFT_Flash_Bitmap/TFT_Flash_Bitmap.ino b/examples/320 x 240/TFT_Flash_Bitmap/TFT_Flash_Bitmap.ino index 081087c..6faa709 100644 --- a/examples/320 x 240/TFT_Flash_Bitmap/TFT_Flash_Bitmap.ino +++ b/examples/320 x 240/TFT_Flash_Bitmap/TFT_Flash_Bitmap.ino @@ -24,7 +24,7 @@ TFT_eSPI tft = TFT_eSPI(); // Invoke custom library // Include the header files that contain the icons -#include "alert.h" +#include "Alert.h" #include "Close.h" #include "Info.h" diff --git a/examples/480 x 320/Flash_Bitmap/Flash_Bitmap.ino b/examples/480 x 320/Flash_Bitmap/Flash_Bitmap.ino index 0329e5f..f2d06b1 100644 --- a/examples/480 x 320/Flash_Bitmap/Flash_Bitmap.ino +++ b/examples/480 x 320/Flash_Bitmap/Flash_Bitmap.ino @@ -31,7 +31,7 @@ TFT_eSPI tft = TFT_eSPI(); // Invoke custom library with default width and height // Include the header files that contain the icons -#include "alert.h" +#include "Alert.h" #include "Close.h" #include "Info.h" diff --git a/examples/480 x 320/TFT_ring_meter/TFT_ring_meter.ino b/examples/480 x 320/TFT_ring_meter/TFT_ring_meter.ino index ebed42b..6f20389 100644 --- a/examples/480 x 320/TFT_ring_meter/TFT_ring_meter.ino +++ b/examples/480 x 320/TFT_ring_meter/TFT_ring_meter.ino @@ -15,7 +15,7 @@ #define TFT_GREY 0x2104 // Dark grey 16 bit colour -#include "alert.h" // Out of range alert icon +#include "Alert.h" // Out of range alert icon #include // Hardware-specific library #include From e734af8950e4c7a983518365fbb511224ec6d510 Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Sat, 1 Jul 2017 09:28:08 +0200 Subject: [PATCH 09/16] Complete ST7787 implementation (lines and fonts) --- TFT_eSPI.cpp | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index eaac40d..e267c62 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -1813,10 +1813,17 @@ void TFT_eSPI::drawChar(int32_t x, int32_t y, unsigned char c, uint32_t color, u column[5] = 0; #if defined (ESP8266) +#ifdef IFACE_3WIRE_ESP8266 + color = (uint32_t)0x80400000 | ((color & 0xff00) << 15) | ((color & 0xff) << 14); + bg = (uint32_t)0x80400000 | ((bg & 0xff00) << 15) | ((bg & 0xff) << 14); + uint32_t spimask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); + SPI1U1 = (SPI1U1 & spimask) | (17 << SPILMOSI) | (17 << SPILMISO); +#else color = (color >> 8) | (color << 8); bg = (bg >> 8) | (bg << 8); uint32_t spimask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); SPI1U1 = (SPI1U1 & spimask) | (15 << SPILMOSI) | (15 << SPILMISO); +#endif for (int8_t j = 0; j < 8; j++) { for (int8_t k = 0; k < 5; k++ ) { if (column[k] & mask) { @@ -3169,7 +3176,7 @@ void TFT_eSPI::pushColors(uint8_t *data, uint32_t len) // Bresenham's algorithm - thx wikipedia - speed enhanced by Bodmer to use // an eficient FastH/V Line draw routine for line segments of 2 pixels or more -#if defined (RPI_ILI9486_DRIVER) || defined (ESP32) || defined (RPI_WRITE_STROBE) +#if defined (RPI_ILI9486_DRIVER) || defined (ESP32) || defined (RPI_WRITE_STROBE) || defined(IFACE_3WIRE) void TFT_eSPI::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color) { @@ -3772,6 +3779,16 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) spi_begin(); setAddrWindow(x, y, (x + w * 8) - 1, y + height - 1); +#ifdef IFACE_3WIRE_ESP8266 + uint32_t textcolor18 = (uint32_t)0x80400000 | + ((textcolor & 0xff00) << 15) | ((textcolor & 0xff) << 14); + uint32_t textbgcolor18 = (uint32_t)0x80400000 | + ((textbgcolor & 0xff00) << 15) | ((textbgcolor & 0xff) << 14); + uint32_t spi_mask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); + spi_mask = SPI1U1 & spi_mask; + SPI1U1 = spi_mask | ((18-1) << SPILMOSI) | ((18-1) << SPILMISO); +#endif + byte mask; for (int i = 0; i < height; i++) { @@ -3782,10 +3799,22 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) mask = 0x80; while (mask) { if (line & mask) { +#ifdef IFACE_3WIRE_ESP8266 + SPI1W0 = textcolor18; + SPI1CMD |= SPIBUSY; + while(SPI1CMD & SPIBUSY) {} +#else SPI.write16(textcolor); +#endif } else { +#ifdef IFACE_3WIRE_ESP8266 + SPI1W0 = textbgcolor18; + SPI1CMD |= SPIBUSY; + while(SPI1CMD & SPIBUSY) {} +#else SPI.write16(textbgcolor); +#endif } mask = mask >> 1; } @@ -3815,6 +3844,13 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) int pc = 0; // Pixel count byte np = textsize * textsize; // Number of pixels in a drawn pixel +#ifdef IFACE_3WIRE_ESP8266 + uint32_t textcolor18 = (uint32_t)0x80400000 | + ((textcolor & 0xff00) << 15) | ((textcolor & 0xff) << 14); + uint32_t spi_mask = ~((SPIMMOSI << SPILMOSI) | (SPIMMISO << SPILMISO)); + spi_mask = SPI1U1 & spi_mask; +#endif + byte tnp = 0; // Temporary copy of np for while loop byte ts = textsize - 1; // Temporary copy of textsize // 16 bit pixel count so maximum font size is equivalent to 180x180 pixels in area @@ -3838,14 +3874,29 @@ int16_t TFT_eSPI::drawChar(unsigned int uniCode, int x, int y, int font) pc++; // This is faster than putting pc+=line before while()? setAddrWindow(px, py, px + ts, py + ts); +#ifdef IFACE_3WIRE_ESP8266 + SPI1U1 = spi_mask | (((18-1) << SPILMOSI) | ((18-1) << SPILMISO)); +#endif if (ts) { tnp = np; while (tnp--) { +#ifdef IFACE_3WIRE_ESP8266 + SPI1W0 = textcolor18; + SPI1CMD |= SPIBUSY; + while(SPI1CMD & SPIBUSY) {} +#else SPI.write16(textcolor); +#endif } } else { +#ifdef IFACE_3WIRE_ESP8266 + SPI1W0 = textcolor18; + SPI1CMD |= SPIBUSY; + while(SPI1CMD & SPIBUSY) {} +#else SPI.write16(textcolor); +#endif } px += textsize; From ce66629b707dd5e221476d9a3c2b684739659a4c Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Sat, 1 Jul 2017 11:04:07 +0200 Subject: [PATCH 10/16] Add a couple definitions to make demos work unmodified --- TFT_Drivers/ST7787_Defines.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/TFT_Drivers/ST7787_Defines.h b/TFT_Drivers/ST7787_Defines.h index 715173b..ec8440e 100644 --- a/TFT_Drivers/ST7787_Defines.h +++ b/TFT_Drivers/ST7787_Defines.h @@ -31,7 +31,10 @@ #define ST7735_PINK 0xF81F #define ILI9341_BLACK ST7735_BLACK +#define ILI9341_WHITE ST7735_WHITE +#define ILI9341_VSCRDEF ST7787_SCRLAR +#define ILI9341_VSCRSADD ST7787_VSCSAD // Delay between some initialisation commands #define TFT_INIT_DELAY 0x80 @@ -81,7 +84,9 @@ #define ST7787_RAMRD 0x2E #define ST7787_PTLAR 0x30 +#define ST7787_SCRLAR 0x33 #define ST7787_MADCTL 0x36 +#define ST7787_VSCSAD 0x37 #define ST7787_COLMOD 0x3A #define ST7787_FRMCTR1 0xB1 From 4b546e1811870b16409b8a97d9ae7cd4f83bcd64 Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Thu, 6 Jul 2017 00:30:14 +0200 Subject: [PATCH 11/16] Add TEOFF and TEON command definitions --- TFT_Drivers/ST7787_Defines.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TFT_Drivers/ST7787_Defines.h b/TFT_Drivers/ST7787_Defines.h index ec8440e..6a20a5e 100644 --- a/TFT_Drivers/ST7787_Defines.h +++ b/TFT_Drivers/ST7787_Defines.h @@ -85,6 +85,8 @@ #define ST7787_PTLAR 0x30 #define ST7787_SCRLAR 0x33 +#define ST7787_TEOFF 0x34 +#define ST7787_TEON 0x35 #define ST7787_MADCTL 0x36 #define ST7787_VSCSAD 0x37 #define ST7787_COLMOD 0x3A From e99e5a856bedeac12083e78dd7c1995300bfde2f Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Mon, 31 Jul 2017 12:34:51 +0200 Subject: [PATCH 12/16] Remove incomplete bit-banging implementation This was never completed and is not used in ESP8266 code. To implement 3-wire mode for ESP32, it will in any case be better to use a similar implementation as for ESP8266 with 9-bit hardware SPI. --- TFT_eSPI.cpp | 206 +++------------------------------------------------ TFT_eSPI.h | 23 ------ User_Setup.h | 2 - 3 files changed, 9 insertions(+), 222 deletions(-) diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index e267c62..e5e9e3d 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -123,16 +123,6 @@ TFT_eSPI::TFT_eSPI(int16_t w, int16_t h) } #endif -#ifdef TFT_DAT - digitalWrite(TFT_DAT, HIGH); - pinMode(TFT_DAT, OUTPUT); -#endif - -#ifdef TFT_SCK - digitalWrite(TFT_SCK, LOW); - pinMode(TFT_SCK, OUTPUT); -#endif - _width = w; // Set by specific xxxxx_Defines.h file or by users sketch _height = h; // Set by specific xxxxx_Defines.h file or by users sketch rotation = 0; @@ -215,17 +205,7 @@ void TFT_eSPI::init(void) SPI.pins(6, 7, 8, 0); #endif - #ifdef TFT_DAT - cspinmask = (uint32_t) digitalPinToBitMask(TFT_CS); - #endif - - #ifdef TFT_SCK - cspinmask = (uint32_t) digitalPinToBitMask(TFT_CS); - #endif - - #if !defined(IFACE_3WIRE) || defined(IFACE_3WIRE_ESP8266) - SPI.begin(); // This will set HMISO to input - #endif + SPI.begin(); // This will set HMISO to input #else #if defined (TFT_MOSI) && !defined (TFT_SPI_OVERLAP) // ToDo: handle 3-wire interface on ESP32. @@ -405,6 +385,11 @@ void TFT_eSPI::spiwrite(uint8_t c) #endif +/*************************************************************************************** +** Function name: docommand +** Description: Send command, with optional input data in IN buffer and +** optional read out result to OUT buffer +***************************************************************************************/ #ifdef IFACE_3WIRE #ifdef IFACE_3WIRE_ESP8266 void TFT_eSPI::docommand(uint8_t c, uint8_t *in, uint32_t in_len, uint8_t *out, uint32_t out_len) @@ -461,93 +446,7 @@ void TFT_eSPI::docommand(uint8_t c, uint8_t *in, uint32_t in_len, uint8_t *out, } #else -void TFT_eSPI::docommand(uint8_t c, uint8_t *in, uint32_t in_len, uint8_t *out, uint32_t out_len) -{ - uint32_t i; - uint32_t val; - - DAT_O; - CS_L; - delay_ns(ST7787_T_CSS); - - /* Write the command byte. */ - /* First shift out the data/command bit ('0' for command). */ - SCK_L; - DAT_L; - delay_ns(ST7787_T_SLW); - SCK_H; - delay_ns(ST7787_T_SHW); - /* Then shift out the bits, MSB-to-LSB. */ - val = c; - for (i = 8; i; --i) { - SCK_L; - if ((val>>7) & 1) - DAT_H; - else - DAT_L; - val <<= 1; - delay_ns(ST7787_T_SLW); - SCK_H; - delay_ns(ST7787_T_SHW); - } - - /* Write any command data. */ - if (in_len) { - do { - val = *in++; - - /* Data/command bit ('1' for data). */ - SCK_L; - DAT_H; - delay_ns(ST7787_T_SLW); - SCK_H; - delay_ns(ST7787_T_SHW); - - for (i = 8; i; --i) { - SCK_L; - if ((val>>7) & 1) - DAT_H; - else - DAT_L; - val <<= 1; - delay_ns(ST7787_T_SLW); - SCK_H; - delay_ns(ST7787_T_SHW); - } - } while (--in_len > 0); - } - - DAT_I; - - /* Read reply. */ - if (out_len) { - /* Dummy bit (according to data sheet, data/command placeholder?). */ - SCK_L; - delay_ns(ST7787_T_SLR); - SCK_H; - delay_ns(ST7787_T_SHR); - - do { - val = 0; - for (i = 8; i; --i) { - SCK_L; - delay_ns(ST7787_T_SLR); - val = (val << 1); - if (DAT_V) - val = val | 1; - SCK_H; - delay_ns(ST7787_T_SHR); - } - *out++ = val; - } while (--out_len > 0); - } - - SCK_L; - delay_ns(ST7787_T_SCC); - CS_H; - delay_ns(ST7787_T_CHW); -} - + #error 3WIRE protocol currently only implemented for ESP8266 #endif #endif @@ -618,41 +517,7 @@ void TFT_eSPI::writedata(uint8_t c) } #else -void TFT_eSPI::writedata(uint8_t c) -{ - uint32_t i; - uint32_t val; - - DAT_O; - CS_L; - delay_ns(ST7787_T_CSS); - - /* Data/command bit ('1' for data). */ - val = c; - SCK_L; - DAT_H; - delay_ns(ST7787_T_SLW); - SCK_H; - delay_ns(ST7787_T_SHW); - - for (i = 8; i; --i) { - SCK_L; - if ((val>>7) & 1) - DAT_H; - else - DAT_L; - val <<= 1; - delay_ns(ST7787_T_SLW); - SCK_H; - delay_ns(ST7787_T_SHW); - } - - DAT_I; - - delay_ns(ST7787_T_SCC); - CS_H; - delay_ns(ST7787_T_CHW); -} +#error 3WIRE protocol currently only implemented for ESP8266 #endif #else /* !IFACE_3WIRE */ void TFT_eSPI::writedata(uint8_t c) @@ -4426,60 +4291,7 @@ void spiWriteBlock(uint16_t color, uint32_t repeat) } #else -void spiWriteBlock(uint16_t color, uint32_t repeat) -{ - uint32_t i; - uint32_t val; - - DAT_O; - delay_ns(ST7787_T_CSS); - - while (repeat-- > 0) - { - val = color >> 8; - /* Data/command bit ('1' for data). */ - SCK_L; - DAT_H; - delay_ns(ST7787_T_SLW); - SCK_H; - delay_ns(ST7787_T_SHW); - - for (i = 8; i; --i) { - SCK_L; - if ((val>>7) & 1) - DAT_H; - else - DAT_L; - val <<= 1; - delay_ns(ST7787_T_SLW); - SCK_H; - delay_ns(ST7787_T_SHW); - } - - val = color & 0xff; - /* Data/command bit ('1' for data). */ - SCK_L; - DAT_H; - delay_ns(ST7787_T_SLW); - SCK_H; - delay_ns(ST7787_T_SHW); - - for (i = 8; i; --i) { - SCK_L; - if ((val>>7) & 1) - DAT_H; - else - DAT_L; - val <<= 1; - delay_ns(ST7787_T_SLW); - SCK_H; - delay_ns(ST7787_T_SHW); - } - } - - DAT_I; -} - +#error 3WIRE protocol currently only implemented for ESP8266 #endif /* IFACE_3WIRE */ #elif defined (ESP8266) && (SPI_FREQUENCY != 80000000) void spiWriteBlock(uint16_t color, uint32_t repeat) diff --git a/TFT_eSPI.h b/TFT_eSPI.h index d187acb..4521a0a 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -129,19 +129,6 @@ #endif #endif -#ifdef TFT_DAT -#define DAT_I pinMode(TFT_DAT, INPUT) -#define DAT_O pinMode(TFT_DAT, OUTPUT) -#define DAT_L digitalWrite(TFT_DAT, LOW) -#define DAT_H digitalWrite(TFT_DAT, HIGH) -#define DAT_V digitalRead(TFT_DAT) -#endif - -#ifdef TFT_SCK -#define SCK_L digitalWrite(TFT_SCK, LOW) -#define SCK_H digitalWrite(TFT_SCK, HIGH) -#endif - #ifdef LOAD_GFXFF // We can include all the free fonts and they will only be built into // the sketch if they are used @@ -483,16 +470,6 @@ inline void spi_end() __attribute__((always_inline)); }; -static inline void delay_ns(int32_t d) -{ - while (d > 0) - { - asm volatile ("nop ; nop ; nop ; nop" : : : "memory"); - d -= 25; - } - /* ToDo */ -} - #endif /*************************************************** diff --git a/User_Setup.h b/User_Setup.h index c94c59d..7cf940f 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -85,8 +85,6 @@ //#define TFT_DC PIN_D3 // Data Command control pin #define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) //#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V -#define TFT_SCK PIN_D5 -#define TFT_DAT PIN_D7 //#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only From b33b88c648aa1012daaf0478011dc3f33a5fa014 Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Tue, 1 Aug 2017 11:43:57 +0200 Subject: [PATCH 13/16] Fix ST7787 display rotation With this change, both the hardware-scrolling examples (TFT_Terminal and TFT_Matrix) work correctly. Rotations 0, 1, 2, and 3 are defined as successive clockwise 90 degree rotations. Vertical refresh is defined to be in direction from low to high coordinate ("top to bottom", or "left to right" in landscape orientations). --- TFT_Drivers/ST7787_Rotation.h | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/TFT_Drivers/ST7787_Rotation.h b/TFT_Drivers/ST7787_Rotation.h index 13a0ebe..3b9f8f6 100644 --- a/TFT_Drivers/ST7787_Rotation.h +++ b/TFT_Drivers/ST7787_Rotation.h @@ -1,17 +1,25 @@ -// This is the command sequence that rotates the ST7735 driver coordinate frame +// This is the command sequence that rotates the ST7787 driver coordinate frame +// +// Rotation 0 is portrait mode. Rotations 1, 2, and 3 are rotated clockwise +// by 90, 180, and 270 degrees. +// +// Vertical refresh direction is set to be the same as data write order. +// So coordinate 0 is refresh first, and coordinate 319 is refreshed last. +// For some reason, counter to datasheet information, MADCTL must be set +// with the ML bit as the inverse of the MY bit to achieve this. rotation = m % 4; // Limit the range of values to 0-3 writecommand(TFT_MADCTL); switch (rotation) { case 0: - writedata(TFT_MAD_RGB); + writedata(TFT_MAD_ML | TFT_MAD_RGB); _width = TFT_WIDTH; _height = TFT_HEIGHT; break; case 1: - writedata(TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_RGB); + writedata(TFT_MAD_ML | TFT_MAD_MX | TFT_MAD_MV | TFT_MAD_RGB); _width = TFT_HEIGHT; _height = TFT_WIDTH; break; From da867d585c95aba575aa4a53a184cd473eee50c6 Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Tue, 1 Aug 2017 13:01:23 +0200 Subject: [PATCH 14/16] Couple small fixes in preparation for submission upstream --- TFT_Drivers/ST7787_Init.h | 3 +++ TFT_eSPI.cpp | 10 +++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/TFT_Drivers/ST7787_Init.h b/TFT_Drivers/ST7787_Init.h index 306e9c1..07fdf3f 100644 --- a/TFT_Drivers/ST7787_Init.h +++ b/TFT_Drivers/ST7787_Init.h @@ -20,6 +20,9 @@ */ writecommand(ST7787_COLMOD); writedata((13 << 4) | 5); + /* Initialise to Rotation 0. */ + writecommand(TFT_MADCTL); + writedata(TFT_MAD_ML | TFT_MAD_RGB); /* Disable external vsync. */ writecommand(ST7787_VSYNCOUT); /* Turn on the display */ diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index e5e9e3d..17d970e 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -80,10 +80,10 @@ inline void TFT_eSPI::spi_end(void){ if(!inTransaction) { if (!locked) { locked = true; - SPI.endTransaction(); - #ifdef IFACE_3WIRE_ESP8266 + #ifdef IFACE_3WIRE_ESP8266 SPI1U &= ~(uint32_t)(SPIUWRBYO|SPIURDBYO); - #endif + #endif + SPI.endTransaction(); } } #endif @@ -208,7 +208,6 @@ void TFT_eSPI::init(void) SPI.begin(); // This will set HMISO to input #else #if defined (TFT_MOSI) && !defined (TFT_SPI_OVERLAP) - // ToDo: handle 3-wire interface on ESP32. SPI.begin(TFT_SCLK, TFT_MISO, TFT_MOSI, -1); #else SPI.begin(); @@ -224,10 +223,11 @@ void TFT_eSPI::init(void) SPI.setBitOrder(MSBFIRST); SPI.setDataMode(SPI_MODE0); SPI.setFrequency(SPI_FREQUENCY); + #ifdef IFACE_3WIRE_ESP8266 SPI1U |= (uint32_t)(SPIUWRBYO|SPIURDBYO); #endif - + #ifdef ESP32 // Unlock the SPI hal mutex and set the lock management flags SPI.beginTransaction(SPISettings(SPI_FREQUENCY, MSBFIRST, SPI_MODE0)); inTransaction = true; // Flag to stop intermediate spi_end calls From 1ba67f485df43e888ee47dd4fcc6f3794f4dfd74 Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Tue, 1 Aug 2017 13:10:40 +0200 Subject: [PATCH 15/16] Revert to having ILI9341_DRIVER the default, like upstream --- User_Setup.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/User_Setup.h b/User_Setup.h index 7cf940f..53ccfee 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -14,16 +14,16 @@ // ################################################################################## // Only define one driver, the other ones must be commented out -//#define ILI9341_DRIVER +#define ILI9341_DRIVER //#define ST7735_DRIVER //#define ILI9163_DRIVER //#define S6D02A1_DRIVER //#define RPI_ILI9486_DRIVER // 20MHz maximum SPI -#define ST7787_DRIVER +//#define ST7787_DRIVER // For ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation -#define TFT_WIDTH 240 -#define TFT_HEIGHT 320 +//#define TFT_WIDTH 128 +//#define TFT_HEIGHT 160 //#define TFT_HEIGHT 128 // For ST7735 ONLY, define the type of display, originally this was based on the @@ -82,7 +82,7 @@ // For ModeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation #define TFT_CS PIN_D8 // Chip select control pin D8 -//#define TFT_DC PIN_D3 // Data Command control pin +#define TFT_DC PIN_D3 // Data Command control pin #define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) //#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V @@ -158,8 +158,8 @@ // #define SPI_FREQUENCY 1000000 // #define SPI_FREQUENCY 5000000 // #define SPI_FREQUENCY 10000000 -#define SPI_FREQUENCY 20000000 -// #define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 +// #define SPI_FREQUENCY 20000000 + #define SPI_FREQUENCY 27000000 // Actually sets it to 26.67MHz = 80/3 // #define SPI_FREQUENCY 40000000 // Maximum to use SPIFFS // #define SPI_FREQUENCY 80000000 From e24559fe363aa43936e5d4ba008cfbe98c9e9e5e Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Tue, 1 Aug 2017 13:35:12 +0200 Subject: [PATCH 16/16] Add some documentation/notes about ST7787 --- README.md | 5 ++++- User_Setup.h | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 606516d..b611fad 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,14 @@ # TFT_eSPI -An Arduino IDE compatible graphics and fonts library for ESP8266 and ESP32 processors with a driver for ILI9341, ILI9163, ST7735 and S6D02A1 based TFT displays that support SPI. +An Arduino IDE compatible graphics and fonts library for ESP8266 and ESP32 processors with a driver for ILI9341, ILI9163, ST7735, ST7787 and S6D02A1 based TFT displays that support SPI. The library also supports TFT displays designed for the Raspberry Pi that are based on a ILI9486 driver chip with a 480 x 320 pixel screen. This display must be of the Waveshare design and use a 16 bit serial interface based on the 74HC04, 74HC4040 and 2 x 74HC4094 logic chips. A modification to these displays is possible (see mod image in Tools folder) to make many graphics functions much faster (e.g. 23ms to clear the screen, 1.2ms to draw a 72 pixel high numeral). The library supports SPI overlap so the TFT screen can share MOSI, MISO and SCLK pins with the program FLASH. +The library supports the 3-wire mode of some display controllers (currently tested with ST7787 only, and currently only on ESP8266). This mode reduces pin count by not using the DC and MISO pins. + + The library contains proportional fonts, different sizes can be enabled/disabled at compile time to optimise the use of FLASH memory. The library has been tested with the NodeMCU (ESP8266 based) and an ESP32 demo board. The library is based on the Adafruit GFX and Adafruit driver libraries and the aim is to retain compatibility. Significant additions have been made to the library to boost the speed for ESP8266/ESP32 processors (it is typically 3 to 10 times faster) and to add new features. The new graphics functions include different size proportional fonts and formatting features. There are a significant number of example sketches to demonstrate the different features. diff --git a/User_Setup.h b/User_Setup.h index 53ccfee..2afcbf8 100644 --- a/User_Setup.h +++ b/User_Setup.h @@ -73,6 +73,11 @@ // // See Section 2. below if DC or CS is connected to D0 // +// On ST7787, using 3-wire mode, the DC pin is not needed and TFT_DC can be +// left undefined. MOSI should be connected to D0 on the display. MISO should +// also be connected to D0 to be able to read from the display, or can be left +// unconnected if reading is not needed. +// // Note: only some versions of the NodeMCU provide the USB 5V on the VIN pin // If 5V is not available at a pin you can use 3.3V but backlight brightness // will be lower.