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); +}