From cfb4689c85d55fe6bc037b7d0ead973a0d012d8f Mon Sep 17 00:00:00 2001 From: Bodmer Date: Sun, 24 Apr 2022 04:00:36 +0100 Subject: [PATCH] Patch for ESP32 C3 - may or may not work! I do NOT have and ESP32 C3 to test with! --- Processors/TFT_eSPI_ESP32_C3.c | 859 +++++++++++++++++++++++++++++++++ Processors/TFT_eSPI_ESP32_C3.h | 608 +++++++++++++++++++++++ TFT_eSPI.cpp | 2 + TFT_eSPI.h | 2 + 4 files changed, 1471 insertions(+) create mode 100644 Processors/TFT_eSPI_ESP32_C3.c create mode 100644 Processors/TFT_eSPI_ESP32_C3.h diff --git a/Processors/TFT_eSPI_ESP32_C3.c b/Processors/TFT_eSPI_ESP32_C3.c new file mode 100644 index 0000000..d438551 --- /dev/null +++ b/Processors/TFT_eSPI_ESP32_C3.c @@ -0,0 +1,859 @@ + //////////////////////////////////////////////////// + // TFT_eSPI driver functions for ESP32 processors // + //////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////// +// Global variables +//////////////////////////////////////////////////////////////////////////////////////// + +// Select the SPI port to use, ESP32 has 2 options +#if !defined (TFT_PARALLEL_8_BIT) + #ifdef CONFIG_IDF_TARGET_ESP32 + #ifdef USE_HSPI_PORT + SPIClass spi = SPIClass(HSPI); + #elif defined(USE_FSPI_PORT) + SPIClass spi = SPIClass(FSPI); + #else // use default VSPI port + SPIClass spi = SPIClass(VSPI); + #endif + #else + #ifdef USE_HSPI_PORT + SPIClass spi = SPIClass(HSPI); + #elif defined(USE_FSPI_PORT) + SPIClass spi = SPIClass(FSPI); + #else // use FSPI port + SPIClass& spi = SPI; + #endif + #endif +#endif + +#ifdef ESP32_DMA + // DMA SPA handle + spi_device_handle_t dmaHAL; + #ifdef CONFIG_IDF_TARGET_ESP32 + #define DMA_CHANNEL 1 + #ifdef USE_HSPI_PORT + spi_host_device_t spi_host = HSPI_HOST; + #elif defined(USE_FSPI_PORT) + spi_host_device_t spi_host = SPI_HOST; + #else // use VSPI port + spi_host_device_t spi_host = VSPI_HOST; + #endif + #else + #ifdef USE_HSPI_PORT + #define DMA_CHANNEL 2 + spi_host_device_t spi_host = (spi_host_device_t) DMA_CHANNEL; // Draws once then freezes + #else // use FSPI port + #define DMA_CHANNEL 1 + spi_host_device_t spi_host = (spi_host_device_t) DMA_CHANNEL; // Draws once then freezes + #endif + #endif +#endif + +#if !defined (TFT_PARALLEL_8_BIT) + // Volatile for register reads: + volatile uint32_t* _spi_cmd = (volatile uint32_t*)(SPI_CMD_REG(SPI_PORT)); + volatile uint32_t* _spi_user = (volatile uint32_t*)(SPI_USER_REG(SPI_PORT)); + // Register writes only: + volatile uint32_t* _spi_mosi_dlen = (volatile uint32_t*)(SPI_MOSI_DLEN_REG(SPI_PORT)); + volatile uint32_t* _spi_w = (volatile uint32_t*)(SPI_W0_REG(SPI_PORT)); +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +#if defined (TFT_SDA_READ) && !defined (TFT_PARALLEL_8_BIT) +//////////////////////////////////////////////////////////////////////////////////////// + +/*************************************************************************************** +** Function name: beginSDA +** Description: Detach SPI from pin to permit software SPI +***************************************************************************************/ +void TFT_eSPI::begin_SDA_Read(void) +{ + pinMatrixOutDetach(TFT_MOSI, false, false); + pinMode(TFT_MOSI, INPUT); + pinMatrixInAttach(TFT_MOSI, VSPIQ_IN_IDX, false); + SET_BUS_READ_MODE; +} + +/*************************************************************************************** +** Function name: endSDA +** Description: Attach SPI pins after software SPI +***************************************************************************************/ +void TFT_eSPI::end_SDA_Read(void) +{ + pinMode(TFT_MOSI, OUTPUT); + pinMatrixOutAttach(TFT_MOSI, VSPID_OUT_IDX, false, false); + pinMode(TFT_MISO, INPUT); + pinMatrixInAttach(TFT_MISO, VSPIQ_IN_IDX, false); + SET_BUS_WRITE_MODE; +} +//////////////////////////////////////////////////////////////////////////////////////// +#endif // #if defined (TFT_SDA_READ) +//////////////////////////////////////////////////////////////////////////////////////// + + +/*************************************************************************************** +** Function name: read byte - supports class functions +** Description: Read a byte from ESP32 8 bit data port +***************************************************************************************/ +// Parallel bus MUST be set to input before calling this function! +uint8_t TFT_eSPI::readByte(void) +{ + uint8_t b = 0xAA; + +#if defined (TFT_PARALLEL_8_BIT) + RD_L; + uint32_t reg; // Read all GPIO pins 0-31 + reg = gpio_input_get(); // Read three times to allow for bus access time + reg = gpio_input_get(); + reg = gpio_input_get(); // Data should be stable now + RD_H; + + // Check GPIO bits used and build value + b = (((reg>>TFT_D0)&1) << 0); + b |= (((reg>>TFT_D1)&1) << 1); + b |= (((reg>>TFT_D2)&1) << 2); + b |= (((reg>>TFT_D3)&1) << 3); + b |= (((reg>>TFT_D4)&1) << 4); + b |= (((reg>>TFT_D5)&1) << 5); + b |= (((reg>>TFT_D6)&1) << 6); + b |= (((reg>>TFT_D7)&1) << 7); +#endif + + return b; +} + +//////////////////////////////////////////////////////////////////////////////////////// +#ifdef TFT_PARALLEL_8_BIT +//////////////////////////////////////////////////////////////////////////////////////// + +/*************************************************************************************** +** Function name: GPIO direction control - supports class functions +** Description: Set parallel bus to INPUT or OUTPUT +***************************************************************************************/ +void TFT_eSPI::busDir(uint32_t mask, uint8_t mode) +{ + // Arduino generic native function + pinMode(TFT_D0, mode); + pinMode(TFT_D1, mode); + pinMode(TFT_D2, mode); + pinMode(TFT_D3, mode); + pinMode(TFT_D4, mode); + pinMode(TFT_D5, mode); + pinMode(TFT_D6, mode); + pinMode(TFT_D7, mode); + return; +} + +/*************************************************************************************** +** Function name: GPIO direction control - supports class functions +** Description: Set ESP32 GPIO pin to input or output (set high) ASAP +***************************************************************************************/ +void TFT_eSPI::gpioMode(uint8_t gpio, uint8_t mode) +{ + pinMode(gpio, mode); + digitalWrite(gpio, HIGH); +} +//////////////////////////////////////////////////////////////////////////////////////// +#endif // #ifdef TFT_PARALLEL_8_BIT +//////////////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////////////// +#if defined (RPI_WRITE_STROBE) && !defined (TFT_PARALLEL_8_BIT) // Code for RPi TFT +//////////////////////////////////////////////////////////////////////////////////////// + +/*************************************************************************************** +** Function name: pushBlock - for ESP32 or ESP8266 RPi TFT +** Description: Write a block of pixels of the same colour +***************************************************************************************/ +void TFT_eSPI::pushBlock(uint16_t color, uint32_t len) +{ + uint8_t colorBin[] = { (uint8_t) (color >> 8), (uint8_t) color }; + if(len) spi.writePattern(&colorBin[0], 2, 1); len--; + while(len--) {WR_L; WR_H;} +} + +/*************************************************************************************** +** Function name: pushPixels - for ESP32 or ESP8266 RPi TFT +** Description: Write a sequence of pixels +***************************************************************************************/ +void TFT_eSPI::pushPixels(const void* data_in, uint32_t len) +{ + uint8_t *data = (uint8_t*)data_in; + + if(_swapBytes) { + while ( len-- ) {tft_Write_16(*data); data++;} + return; + } + + while ( len >=64 ) {spi.writePattern(data, 64, 1); data += 64; len -= 64; } + if (len) spi.writePattern(data, len, 1); +} + +//////////////////////////////////////////////////////////////////////////////////////// +#elif !defined (SPI_18BIT_DRIVER) && !defined (TFT_PARALLEL_8_BIT) // Most SPI displays +//////////////////////////////////////////////////////////////////////////////////////// + +/*************************************************************************************** +** Function name: pushBlock - for ESP32 +** Description: Write a block of pixels of the same colour +***************************************************************************************/ +/* +void TFT_eSPI::pushBlock(uint16_t color, uint32_t len){ + + uint32_t color32 = (color<<8 | color >>8)<<16 | (color<<8 | color >>8); + bool empty = true; + volatile uint32_t* spi_w = (volatile uint32_t*)_spi_w; + if (len > 31) + { + *_spi_mosi_dlen = 511; + spi_w[0] = color32; + spi_w[1] = color32; + spi_w[2] = color32; + spi_w[3] = color32; + spi_w[4] = color32; + spi_w[5] = color32; + spi_w[6] = color32; + spi_w[7] = color32; + spi_w[8] = color32; + spi_w[9] = color32; + spi_w[10] = color32; + spi_w[11] = color32; + spi_w[12] = color32; + spi_w[13] = color32; + spi_w[14] = color32; + spi_w[15] = color32; + while(len>31) + { + while ((*_spi_cmd)&SPI_USR); + *_spi_cmd = SPI_USR; + len -= 32; + } + empty = false; + } + + if (len) + { + if(empty) { + for (uint32_t i=0; i <= len; i+=2) *spi_w++ = color32; + } + len = (len << 4) - 1; + while (*_spi_cmd&SPI_USR); + *_spi_mosi_dlen = len; + *_spi_cmd = SPI_USR; + } + while ((*_spi_cmd)&SPI_USR); // Move to later in code to use transmit time usefully? +} +//*/ +//* +void TFT_eSPI::pushBlock(uint16_t color, uint32_t len){ + + volatile uint32_t* spi_w = _spi_w; + uint32_t color32 = (color<<8 | color >>8)<<16 | (color<<8 | color >>8); + uint32_t i = 0; + uint32_t rem = len & 0x1F; + len = len - rem; + + // Start with partial buffer pixels + if (rem) + { + while (*_spi_cmd&SPI_USR); + for (i=0; i < rem; i+=2) *spi_w++ = color32; + *_spi_mosi_dlen = (rem << 4) - 1; +#if CONFIG_IDF_TARGET_ESP32C3 + *_spi_cmd = SPI_UPDATE; + while (*_spi_cmd & SPI_UPDATE); +#endif + *_spi_cmd = SPI_USR; + if (!len) return; //{while (*_spi_cmd&SPI_USR); return; } + i = i>>1; while(i++<16) *spi_w++ = color32; + } + + while (*_spi_cmd&SPI_USR); + if (!rem) while (i++<16) *spi_w++ = color32; + *_spi_mosi_dlen = 511; + + // End with full buffer to maximise useful time for downstream code + while(len) + { + while (*_spi_cmd&SPI_USR); +#if CONFIG_IDF_TARGET_ESP32C3 + *_spi_cmd = SPI_UPDATE; + while (*_spi_cmd & SPI_UPDATE); +#endif + *_spi_cmd = SPI_USR; + len -= 32; + } + + // Do not wait here + //while (*_spi_cmd&SPI_USR); +} +//*/ +/*************************************************************************************** +** Function name: pushSwapBytePixels - for ESP32 +** Description: Write a sequence of pixels with swapped bytes +***************************************************************************************/ +void TFT_eSPI::pushSwapBytePixels(const void* data_in, uint32_t len){ + + uint8_t* data = (uint8_t*)data_in; + uint32_t color[16]; + + if (len > 31) + { + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_PORT), 511); + while(len>31) + { + uint32_t i = 0; + while(i<16) + { + color[i++] = DAT8TO32(data); + data+=4; + } + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + WRITE_PERI_REG(SPI_W0_REG(SPI_PORT), color[0]); + WRITE_PERI_REG(SPI_W1_REG(SPI_PORT), color[1]); + WRITE_PERI_REG(SPI_W2_REG(SPI_PORT), color[2]); + WRITE_PERI_REG(SPI_W3_REG(SPI_PORT), color[3]); + WRITE_PERI_REG(SPI_W4_REG(SPI_PORT), color[4]); + WRITE_PERI_REG(SPI_W5_REG(SPI_PORT), color[5]); + WRITE_PERI_REG(SPI_W6_REG(SPI_PORT), color[6]); + WRITE_PERI_REG(SPI_W7_REG(SPI_PORT), color[7]); + WRITE_PERI_REG(SPI_W8_REG(SPI_PORT), color[8]); + WRITE_PERI_REG(SPI_W9_REG(SPI_PORT), color[9]); + WRITE_PERI_REG(SPI_W10_REG(SPI_PORT), color[10]); + WRITE_PERI_REG(SPI_W11_REG(SPI_PORT), color[11]); + WRITE_PERI_REG(SPI_W12_REG(SPI_PORT), color[12]); + WRITE_PERI_REG(SPI_W13_REG(SPI_PORT), color[13]); + WRITE_PERI_REG(SPI_W14_REG(SPI_PORT), color[14]); + WRITE_PERI_REG(SPI_W15_REG(SPI_PORT), color[15]); +#if CONFIG_IDF_TARGET_ESP32C3 + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_UPDATE); + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_UPDATE); +#endif + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); + len -= 32; + } + } + + if (len > 15) + { + uint32_t i = 0; + while(i<8) + { + color[i++] = DAT8TO32(data); + data+=4; + } + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_PORT), 255); + WRITE_PERI_REG(SPI_W0_REG(SPI_PORT), color[0]); + WRITE_PERI_REG(SPI_W1_REG(SPI_PORT), color[1]); + WRITE_PERI_REG(SPI_W2_REG(SPI_PORT), color[2]); + WRITE_PERI_REG(SPI_W3_REG(SPI_PORT), color[3]); + WRITE_PERI_REG(SPI_W4_REG(SPI_PORT), color[4]); + WRITE_PERI_REG(SPI_W5_REG(SPI_PORT), color[5]); + WRITE_PERI_REG(SPI_W6_REG(SPI_PORT), color[6]); + WRITE_PERI_REG(SPI_W7_REG(SPI_PORT), color[7]); +#if CONFIG_IDF_TARGET_ESP32C3 + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_UPDATE); + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_UPDATE); +#endif + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); + len -= 16; + } + + if (len) + { + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_PORT), (len << 4) - 1); + for (uint32_t i=0; i <= (len<<1); i+=4) { + WRITE_PERI_REG(SPI_W0_REG(SPI_PORT)+i, DAT8TO32(data)); data+=4; + } +#if CONFIG_IDF_TARGET_ESP32C3 + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_UPDATE); + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_UPDATE); +#endif + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); + } + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + +} + +/*************************************************************************************** +** Function name: pushPixels - for ESP32 +** Description: Write a sequence of pixels +***************************************************************************************/ +void TFT_eSPI::pushPixels(const void* data_in, uint32_t len){ + + if(_swapBytes) { + pushSwapBytePixels(data_in, len); + return; + } + + uint32_t *data = (uint32_t*)data_in; + + if (len > 31) + { + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_PORT), 511); + while(len>31) + { + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + WRITE_PERI_REG(SPI_W0_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W1_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W2_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W3_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W4_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W5_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W6_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W7_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W8_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W9_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W10_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W11_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W12_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W13_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W14_REG(SPI_PORT), *data++); + WRITE_PERI_REG(SPI_W15_REG(SPI_PORT), *data++); +#if CONFIG_IDF_TARGET_ESP32C3 + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_UPDATE); + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_UPDATE); +#endif + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); + len -= 32; + } + } + + if (len) + { + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_PORT), (len << 4) - 1); + for (uint32_t i=0; i <= (len<<1); i+=4) WRITE_PERI_REG((SPI_W0_REG(SPI_PORT) + i), *data++); +#if CONFIG_IDF_TARGET_ESP32C3 + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_UPDATE); + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_UPDATE); +#endif + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); + } + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); +} + +//////////////////////////////////////////////////////////////////////////////////////// +#elif defined (SPI_18BIT_DRIVER) // SPI 18 bit colour +//////////////////////////////////////////////////////////////////////////////////////// + +/*************************************************************************************** +** Function name: pushBlock - for ESP32 and 3 byte RGB display +** Description: Write a block of pixels of the same colour +***************************************************************************************/ +void TFT_eSPI::pushBlock(uint16_t color, uint32_t len) +{ + // Split out the colours + uint32_t r = (color & 0xF800)>>8; + uint32_t g = (color & 0x07E0)<<5; + uint32_t b = (color & 0x001F)<<19; + // Concatenate 4 pixels into three 32 bit blocks + uint32_t r0 = r<<24 | b | g | r; + uint32_t r1 = r0>>8 | g<<16; + uint32_t r2 = r1>>8 | b<<8; + + if (len > 19) + { + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_PORT), 479); + + while(len>19) + { + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + WRITE_PERI_REG(SPI_W0_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W1_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W2_REG(SPI_PORT), r2); + WRITE_PERI_REG(SPI_W3_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W4_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W5_REG(SPI_PORT), r2); + WRITE_PERI_REG(SPI_W6_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W7_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W8_REG(SPI_PORT), r2); + WRITE_PERI_REG(SPI_W9_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W10_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W11_REG(SPI_PORT), r2); + WRITE_PERI_REG(SPI_W12_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W13_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W14_REG(SPI_PORT), r2); +#if CONFIG_IDF_TARGET_ESP32C3 + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_UPDATE); + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_UPDATE); +#endif + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); + len -= 20; + } + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + } + + if (len) + { + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_PORT), (len * 24) - 1); + WRITE_PERI_REG(SPI_W0_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W1_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W2_REG(SPI_PORT), r2); + WRITE_PERI_REG(SPI_W3_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W4_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W5_REG(SPI_PORT), r2); + if (len > 8 ) + { + WRITE_PERI_REG(SPI_W6_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W7_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W8_REG(SPI_PORT), r2); + WRITE_PERI_REG(SPI_W9_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W10_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W11_REG(SPI_PORT), r2); + WRITE_PERI_REG(SPI_W12_REG(SPI_PORT), r0); + WRITE_PERI_REG(SPI_W13_REG(SPI_PORT), r1); + WRITE_PERI_REG(SPI_W14_REG(SPI_PORT), r2); + } +#if CONFIG_IDF_TARGET_ESP32C3 + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_UPDATE); + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_UPDATE); +#endif + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + } +} + +/*************************************************************************************** +** Function name: pushPixels - for ESP32 and 3 byte RGB display +** Description: Write a sequence of pixels +***************************************************************************************/ +void TFT_eSPI::pushPixels(const void* data_in, uint32_t len){ + + uint16_t *data = (uint16_t*)data_in; + // ILI9488 write macro is not endianess dependant, hence !_swapBytes + if(!_swapBytes) { while ( len-- ) {tft_Write_16S(*data); data++;} } + else { while ( len-- ) {tft_Write_16(*data); data++;} } +} + +/*************************************************************************************** +** Function name: pushSwapBytePixels - for ESP32 and 3 byte RGB display +** Description: Write a sequence of pixels with swapped bytes +***************************************************************************************/ +void TFT_eSPI::pushSwapBytePixels(const void* data_in, uint32_t len){ + + uint16_t *data = (uint16_t*)data_in; + // ILI9488 write macro is not endianess dependant, so swap byte macro not used here + while ( len-- ) {tft_Write_16(*data); data++;} +} + +//////////////////////////////////////////////////////////////////////////////////////// +#elif defined (TFT_PARALLEL_8_BIT) // Now the code for ESP32 8 bit parallel +//////////////////////////////////////////////////////////////////////////////////////// + +/*************************************************************************************** +** Function name: pushBlock - for ESP32 and parallel display +** Description: Write a block of pixels of the same colour +***************************************************************************************/ +void TFT_eSPI::pushBlock(uint16_t color, uint32_t len){ + if ( (color >> 8) == (color & 0x00FF) ) + { if (!len) return; + tft_Write_16(color); + #if defined (SSD1963_DRIVER) + while (--len) {WR_L; WR_H; WR_L; WR_H; WR_L; WR_H;} + #else + #ifdef PSEUDO_16_BIT + while (--len) {WR_L; WR_H;} + #else + while (--len) {WR_L; WR_H; WR_L; WR_H;} + #endif + #endif + } + else while (len--) {tft_Write_16(color);} +} + +/*************************************************************************************** +** Function name: pushSwapBytePixels - for ESP32 and parallel display +** Description: Write a sequence of pixels with swapped bytes +***************************************************************************************/ +void TFT_eSPI::pushSwapBytePixels(const void* data_in, uint32_t len){ + + uint16_t *data = (uint16_t*)data_in; + while ( len-- ) {tft_Write_16(*data); data++;} +} + +/*************************************************************************************** +** Function name: pushPixels - for ESP32 and parallel display +** Description: Write a sequence of pixels +***************************************************************************************/ +void TFT_eSPI::pushPixels(const void* data_in, uint32_t len){ + + uint16_t *data = (uint16_t*)data_in; + if(_swapBytes) { while ( len-- ) {tft_Write_16(*data); data++; } } + else { while ( len-- ) {tft_Write_16S(*data); data++;} } +} + +//////////////////////////////////////////////////////////////////////////////////////// +#endif // End of display interface specific functions +//////////////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////////////// +#if defined (ESP32_DMA) && !defined (TFT_PARALLEL_8_BIT) // DMA FUNCTIONS +//////////////////////////////////////////////////////////////////////////////////////// + +/*************************************************************************************** +** Function name: dmaBusy +** Description: Check if DMA is busy +***************************************************************************************/ +bool TFT_eSPI::dmaBusy(void) +{ + if (!DMA_Enabled || !spiBusyCheck) return false; + + spi_transaction_t *rtrans; + esp_err_t ret; + uint8_t checks = spiBusyCheck; + for (int i = 0; i < checks; ++i) + { + ret = spi_device_get_trans_result(dmaHAL, &rtrans, 0); + if (ret == ESP_OK) spiBusyCheck--; + } + + //Serial.print("spiBusyCheck=");Serial.println(spiBusyCheck); + if (spiBusyCheck ==0) return false; + return true; +} + + +/*************************************************************************************** +** Function name: dmaWait +** Description: Wait until DMA is over (blocking!) +***************************************************************************************/ +void TFT_eSPI::dmaWait(void) +{ + if (!DMA_Enabled || !spiBusyCheck) return; + spi_transaction_t *rtrans; + esp_err_t ret; + for (int i = 0; i < spiBusyCheck; ++i) + { + ret = spi_device_get_trans_result(dmaHAL, &rtrans, portMAX_DELAY); + assert(ret == ESP_OK); + } + spiBusyCheck = 0; +} + + +/*************************************************************************************** +** Function name: pushPixelsDMA +** Description: Push pixels to TFT (len must be less than 32767) +***************************************************************************************/ +// This will byte swap the original image if setSwapBytes(true) was called by sketch. +void TFT_eSPI::pushPixelsDMA(uint16_t* image, uint32_t len) +{ + if ((len == 0) || (!DMA_Enabled)) return; + + dmaWait(); + + if(_swapBytes) { + for (uint32_t i = 0; i < len; i++) (image[i] = image[i] << 8 | image[i] >> 8); + } + + esp_err_t ret; + static spi_transaction_t trans; + + memset(&trans, 0, sizeof(spi_transaction_t)); + + trans.user = (void *)1; + trans.tx_buffer = image; //finally send the line data + trans.length = len * 16; //Data length, in bits + trans.flags = 0; //SPI_TRANS_USE_TXDATA flag + + ret = spi_device_queue_trans(dmaHAL, &trans, portMAX_DELAY); + assert(ret == ESP_OK); + + spiBusyCheck++; +} + + +/*************************************************************************************** +** Function name: pushImageDMA +** Description: Push image to a window (w*h must be less than 65536) +***************************************************************************************/ +// Fixed const data assumed, will NOT clip or swap bytes +void TFT_eSPI::pushImageDMA(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t const* image) +{ + if ((w == 0) || (h == 0) || (!DMA_Enabled)) return; + + uint32_t len = w*h; + + dmaWait(); + + setAddrWindow(x, y, w, h); + + esp_err_t ret; + static spi_transaction_t trans; + + memset(&trans, 0, sizeof(spi_transaction_t)); + + trans.user = (void *)1; + trans.tx_buffer = image; //Data pointer + trans.length = len * 16; //Data length, in bits + trans.flags = 0; //SPI_TRANS_USE_TXDATA flag + + ret = spi_device_queue_trans(dmaHAL, &trans, portMAX_DELAY); + assert(ret == ESP_OK); + + spiBusyCheck++; +} + + +/*************************************************************************************** +** Function name: pushImageDMA +** Description: Push image to a window (w*h must be less than 65536) +***************************************************************************************/ +// This will clip and also swap bytes if setSwapBytes(true) was called by sketch +void TFT_eSPI::pushImageDMA(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t* image, uint16_t* buffer) +{ + if ((x >= _vpW) || (y >= _vpH) || (!DMA_Enabled)) return; + + int32_t dx = 0; + int32_t dy = 0; + int32_t dw = w; + int32_t dh = h; + + if (x < _vpX) { dx = _vpX - x; dw -= dx; x = _vpX; } + if (y < _vpY) { dy = _vpY - y; dh -= dy; y = _vpY; } + + if ((x + dw) > _vpW ) dw = _vpW - x; + if ((y + dh) > _vpH ) dh = _vpH - y; + + if (dw < 1 || dh < 1) return; + + uint32_t len = dw*dh; + + if (buffer == nullptr) { + buffer = image; + dmaWait(); + } + + // If image is clipped, copy pixels into a contiguous block + if ( (dw != w) || (dh != h) ) { + if(_swapBytes) { + for (int32_t yb = 0; yb < dh; yb++) { + for (int32_t xb = 0; xb < dw; xb++) { + uint32_t src = xb + dx + w * (yb + dy); + (buffer[xb + yb * dw] = image[src] << 8 | image[src] >> 8); + } + } + } + else { + for (int32_t yb = 0; yb < dh; yb++) { + memcpy((uint8_t*) (buffer + yb * dw), (uint8_t*) (image + dx + w * (yb + dy)), dw << 1); + } + } + } + // else, if a buffer pointer has been provided copy whole image to the buffer + else if (buffer != image || _swapBytes) { + if(_swapBytes) { + for (uint32_t i = 0; i < len; i++) (buffer[i] = image[i] << 8 | image[i] >> 8); + } + else { + memcpy(buffer, image, len*2); + } + } + + if (spiBusyCheck) dmaWait(); // In case we did not wait earlier + + setAddrWindow(x, y, dw, dh); + + esp_err_t ret; + static spi_transaction_t trans; + + memset(&trans, 0, sizeof(spi_transaction_t)); + + trans.user = (void *)1; + trans.tx_buffer = buffer; //finally send the line data + trans.length = len * 16; //Data length, in bits + trans.flags = 0; //SPI_TRANS_USE_TXDATA flag + + ret = spi_device_queue_trans(dmaHAL, &trans, portMAX_DELAY); + assert(ret == ESP_OK); + + spiBusyCheck++; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// Processor specific DMA initialisation +//////////////////////////////////////////////////////////////////////////////////////// + +// The DMA functions here work with SPI only (not parallel) +/*************************************************************************************** +** Function name: dc_callback +** Description: Toggles DC line during transaction +***************************************************************************************/ +extern "C" void dc_callback(); + +void IRAM_ATTR dc_callback(spi_transaction_t *spi_tx) +{ + if ((bool)spi_tx->user) {DC_D;} + else {DC_C;} +} + +/*************************************************************************************** +** Function name: initDMA +** Description: Initialise the DMA engine - returns true if init OK +***************************************************************************************/ +bool TFT_eSPI::initDMA(bool ctrl_cs) +{ + if (DMA_Enabled) return false; + + esp_err_t ret; + spi_bus_config_t buscfg = { + .mosi_io_num = TFT_MOSI, + .miso_io_num = TFT_MISO, + .sclk_io_num = TFT_SCLK, + .quadwp_io_num = -1, + .quadhd_io_num = -1, + .max_transfer_sz = TFT_WIDTH * TFT_HEIGHT * 2 + 8, // TFT screen size + .flags = 0, + .intr_flags = 0 + }; + + int8_t pin = -1; + if (ctrl_cs) pin = TFT_CS; + + spi_device_interface_config_t devcfg = { + .command_bits = 0, + .address_bits = 0, + .dummy_bits = 0, + .mode = TFT_SPI_MODE, + .duty_cycle_pos = 0, + .cs_ena_pretrans = 0, + .cs_ena_posttrans = 0, + .clock_speed_hz = SPI_FREQUENCY, + .input_delay_ns = 0, + .spics_io_num = pin, + .flags = SPI_DEVICE_NO_DUMMY, //0, + .queue_size = 1, + .pre_cb = 0, //dc_callback, //Callback to handle D/C line + .post_cb = 0 + }; + ret = spi_bus_initialize(spi_host, &buscfg, DMA_CHANNEL); + ESP_ERROR_CHECK(ret); + ret = spi_bus_add_device(spi_host, &devcfg, &dmaHAL); + ESP_ERROR_CHECK(ret); + + DMA_Enabled = true; + spiBusyCheck = 0; + return true; +} + +/*************************************************************************************** +** Function name: deInitDMA +** Description: Disconnect the DMA engine from SPI +***************************************************************************************/ +void TFT_eSPI::deInitDMA(void) +{ + if (!DMA_Enabled) return; + spi_bus_remove_device(dmaHAL); + spi_bus_free(spi_host); + DMA_Enabled = false; +} + +//////////////////////////////////////////////////////////////////////////////////////// +#endif // End of DMA FUNCTIONS +//////////////////////////////////////////////////////////////////////////////////////// diff --git a/Processors/TFT_eSPI_ESP32_C3.h b/Processors/TFT_eSPI_ESP32_C3.h new file mode 100644 index 0000000..247f6c8 --- /dev/null +++ b/Processors/TFT_eSPI_ESP32_C3.h @@ -0,0 +1,608 @@ + //////////////////////////////////////////////////// + // TFT_eSPI driver functions for ESP32 processors // + //////////////////////////////////////////////////// + +#ifndef _TFT_eSPI_ESP32H_ +#define _TFT_eSPI_ESP32H_ + +// Processor ID reported by getSetup() +#define PROCESSOR_ID 0x32 + +// Include processor specific header +#include "soc/spi_reg.h" +#include "driver/spi_master.h" + +#if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32) + #define CONFIG_IDF_TARGET_ESP32 +#endif + +#ifndef VSPI + #define VSPI FSPI +#endif + + + + +// Fix IDF problems with ESP32C3 +#if CONFIG_IDF_TARGET_ESP32C3 + // Fix ESP32C3 IDF bug for missing definition (VSPI/FSPI only tested at the moment) + #ifndef REG_SPI_BASE + //Will this work as per S3? #define REG_SPI_BASE(i) (((i)>1) ? (DR_REG_SPI3_BASE) : (DR_REG_SPI2_BASE)) + #define REG_SPI_BASE(i) (DR_REG_SPI1_BASE + (((i)>1) ? (((i)* 0x1000) + 0x20000) : (((~(i)) & 1)* 0x1000 ))) + #endif + + // Fix ESP32C3 IDF bug for name change + #ifndef SPI_MOSI_DLEN_REG + #define SPI_MOSI_DLEN_REG(x) SPI_MS_DLEN_REG(x) + #endif +#endif + +// SUPPORT_TRANSACTIONS is mandatory for ESP32 so the hal mutex is toggled +#if !defined (SUPPORT_TRANSACTIONS) + #define SUPPORT_TRANSACTIONS +#endif + +/* +ESP32: +FSPI not defined +HSPI = 2, uses SPI2 +VSPI = 3, uses SPI3 + +ESP32-S2: +FSPI = 1, uses SPI2 +HSPI = 2, uses SPI3 +VSPI not defined + +ESP32 C3: +FSPI = 0, uses SPI2 ???? To be checked +HSPI = 1, uses SPI3 ???? To be checked +VSPI not defined + +For ESP32/S2/C3: +SPI1_HOST = 0 +SPI2_HOST = 1 +SPI3_HOST = 2 +*/ + +// ESP32 specific SPI port selection +#ifdef USE_HSPI_PORT + #ifdef CONFIG_IDF_TARGET_ESP32 + #define SPI_PORT HSPI //HSPI is port 2 on ESP32 + #else + #define SPI_PORT 3 //HSPI is port 3 on ESP32 S2 + #endif +#elif defined(USE_FSPI_PORT) + #define SPI_PORT 2 //FSPI(ESP32 S2) +#else + #ifdef CONFIG_IDF_TARGET_ESP32 + #define SPI_PORT VSPI + #elif CONFIG_IDF_TARGET_ESP32S2 + #define SPI_PORT 2 //FSPI(ESP32 S2) + #elif CONFIG_IDF_TARGET_ESP32C3 + #define SPI_PORT FSPI + #endif +#endif + +#ifdef RPI_DISPLAY_TYPE + #define CMD_BITS (16-1) +#else + #define CMD_BITS (8-1) +#endif + +// Initialise processor specific SPI functions, used by init() +#define INIT_TFT_DATA_BUS // Not used + +// Define a generic flag for 8 bit parallel +#if defined (ESP32_PARALLEL) // Specific to ESP32 for backwards compatibility + #if !defined (TFT_PARALLEL_8_BIT) + #define TFT_PARALLEL_8_BIT // Generic parallel flag + #endif +#endif + +// Ensure ESP32 specific flag is defined for 8 bit parallel +#if defined (TFT_PARALLEL_8_BIT) + #if !defined (ESP32_PARALLEL) + #define ESP32_PARALLEL + #endif +#endif + +// Processor specific code used by SPI bus transaction startWrite and endWrite functions +#if !defined (ESP32_PARALLEL) + #if (TFT_SPI_MODE == SPI_MODE1) || (TFT_SPI_MODE == SPI_MODE2) + #define SET_BUS_WRITE_MODE *_spi_user = SPI_USR_MOSI | SPI_CK_OUT_EDGE + #define SET_BUS_READ_MODE *_spi_user = SPI_USR_MOSI | SPI_USR_MISO | SPI_DOUTDIN | SPI_CK_OUT_EDGE + #else + #define SET_BUS_WRITE_MODE *_spi_user = SPI_USR_MOSI + #define SET_BUS_READ_MODE *_spi_user = SPI_USR_MOSI | SPI_USR_MISO | SPI_DOUTDIN + #endif +#else + // Not applicable to parallel bus + #define SET_BUS_WRITE_MODE + #define SET_BUS_READ_MODE +#endif + +// Code to check if DMA is busy, used by SPI bus transaction transaction and endWrite functions +#if !defined(TFT_PARALLEL_8_BIT) && !defined(SPI_18BIT_DRIVER) + #define ESP32_DMA + // Code to check if DMA is busy, used by SPI DMA + transaction + endWrite functions + #define DMA_BUSY_CHECK dmaWait() +#else + #define DMA_BUSY_CHECK +#endif + +#if defined(TFT_PARALLEL_8_BIT) + #define SPI_BUSY_CHECK +#else + #define SPI_BUSY_CHECK while (*_spi_cmd&SPI_USR) +#endif + +// If smooth font is used then it is likely SPIFFS will be needed +#ifdef SMOOTH_FONT + // Call up the SPIFFS (SPI FLASH Filing System) for the anti-aliased fonts + #define FS_NO_GLOBALS + #include + #include "SPIFFS.h" // ESP32 only + #define FONT_FS_AVAILABLE +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +// Define the DC (TFT Data/Command or Register Select (RS))pin drive code +//////////////////////////////////////////////////////////////////////////////////////// +#ifndef TFT_DC + #define DC_C // No macro allocated so it generates no code + #define DC_D // No macro allocated so it generates no code +#else + #if defined (TFT_PARALLEL_8_BIT) + // TFT_DC, by design, must be in range 0-31 for single register parallel write + #if (TFT_DC >= 0) && (TFT_DC < 32) + #define DC_C GPIO.out_w1tc.val = (1 << TFT_DC) + #define DC_D GPIO.out_w1ts.val = (1 << TFT_DC) + #else + #define DC_C + #define DC_D + #endif + #else + #if (TFT_DC >= 32) + #ifdef RPI_DISPLAY_TYPE // RPi displays need a slower DC change + #define DC_C GPIO.out_w1ts.val = (1 << (TFT_DC - 32)); \ + GPIO.out_w1tc.val = (1 << (TFT_DC - 32)) + #define DC_D GPIO.out_w1tc.val = (1 << (TFT_DC - 32)); \ + GPIO.out_w1ts.val = (1 << (TFT_DC - 32)) + #else + #define DC_C GPIO.out_w1tc.val = (1 << (TFT_DC - 32))//;GPIO.out_w1tc.val = (1 << (TFT_DC - 32)) + #define DC_D GPIO.out_w1ts.val = (1 << (TFT_DC - 32))//;GPIO.out_w1ts.val = (1 << (TFT_DC - 32)) + #endif + #elif (TFT_DC >= 0) + #if defined (RPI_DISPLAY_TYPE) + #if defined (ILI9486_DRIVER) + // RPi ILI9486 display needs a slower DC change + #define DC_C GPIO.out_w1tc.val = (1 << TFT_DC); \ + GPIO.out_w1tc.val = (1 << TFT_DC) + #define DC_D GPIO.out_w1tc.val = (1 << TFT_DC); \ + GPIO.out_w1ts.val = (1 << TFT_DC) + #else + // Other RPi displays need a slower C->D change + #define DC_C GPIO.out_w1tc.val = (1 << TFT_DC) + #define DC_D GPIO.out_w1tc.val = (1 << TFT_DC); \ + GPIO.out_w1ts.val = (1 << TFT_DC) + #endif + #else + #define DC_C GPIO.out_w1tc.val = (1 << TFT_DC)//;GPIO.out_w1tc.val = (1 << TFT_DC) + #define DC_D GPIO.out_w1ts.val = (1 << TFT_DC)//;GPIO.out_w1ts.val = (1 << TFT_DC) + #endif + #else + #define DC_C + #define DC_D + #endif + #endif +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +// Define the CS (TFT chip select) pin drive code +//////////////////////////////////////////////////////////////////////////////////////// +#ifndef TFT_CS + #define TFT_CS -1 // Keep DMA code happy + #define CS_L // No macro allocated so it generates no code + #define CS_H // No macro allocated so it generates no code +#else + #if defined (TFT_PARALLEL_8_BIT) + #if TFT_CS >= 32 + #define CS_L GPIO.out_w1tc.val = (1 << (TFT_CS - 32)) + #define CS_H GPIO.out_w1ts.val = (1 << (TFT_CS - 32)) + #elif TFT_CS >= 0 + #define CS_L GPIO.out_w1tc.val = (1 << TFT_CS) + #define CS_H GPIO.out_w1ts.val = (1 << TFT_CS) + #else + #define CS_L + #define CS_H + #endif + #else + #if (TFT_CS >= 32) + #ifdef RPI_DISPLAY_TYPE // RPi display needs a slower CS change + #define CS_L GPIO.out_w1ts.val = (1 << (TFT_CS - 32)); \ + GPIO.out_w1tc.val = (1 << (TFT_CS - 32)) + #define CS_H GPIO.out_w1tc.val = (1 << (TFT_CS - 32)); \ + GPIO.out_w1ts.val = (1 << (TFT_CS - 32)) + #else + #define CS_L GPIO.out_w1tc.val = (1 << (TFT_CS - 32)); GPIO.out_w1tc.val = (1 << (TFT_CS - 32)) + #define CS_H GPIO.out_w1ts.val = (1 << (TFT_CS - 32))//;GPIO.out_w1ts.val = (1 << (TFT_CS - 32)) + #endif + #elif (TFT_CS >= 0) + #ifdef RPI_DISPLAY_TYPE // RPi display needs a slower CS change + #define CS_L GPIO.out_w1ts.val = (1 << TFT_CS); GPIO.out_w1tc.val = (1 << TFT_CS) + #define CS_H GPIO.out_w1tc.val = (1 << TFT_CS); GPIO.out_w1ts.val = (1 << TFT_CS) + #else + #define CS_L GPIO.out_w1tc.val = (1 << TFT_CS); GPIO.out_w1tc.val = (1 << TFT_CS) + #define CS_H GPIO.out_w1ts.val = (1 << TFT_CS)//;GPIO.out_w1ts.val = (1 << TFT_CS) + #endif + #else + #define CS_L + #define CS_H + #endif + #endif +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +// Define the WR (TFT Write) pin drive code +//////////////////////////////////////////////////////////////////////////////////////// +#if defined (TFT_WR) + #if (TFT_WR >= 32) + // Note: it will be ~1.25x faster if the TFT_WR pin uses a GPIO pin lower than 32 + #define WR_L GPIO.out_w1tc.val = (1 << (TFT_WR - 32)) + #define WR_H GPIO.out_w1ts.val = (1 << (TFT_WR - 32)) + #elif (TFT_WR >= 0) + // TFT_WR, for best performance, should be in range 0-31 for single register parallel write + #define WR_L GPIO.out_w1tc.val = (1 << TFT_WR) + #define WR_H GPIO.out_w1ts.val = (1 << TFT_WR) + #else + #define WR_L + #define WR_H + #endif +#else + #define WR_L + #define WR_H +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +// Define the touch screen chip select pin drive code +//////////////////////////////////////////////////////////////////////////////////////// +#ifndef TOUCH_CS + #define T_CS_L // No macro allocated so it generates no code + #define T_CS_H // No macro allocated so it generates no code +#else // XPT2046 is slow, so use slower digitalWrite here + #define T_CS_L digitalWrite(TOUCH_CS, LOW) + #define T_CS_H digitalWrite(TOUCH_CS, HIGH) +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +// Make sure SPI default pins are assigned if not specified by user or set to -1 +//////////////////////////////////////////////////////////////////////////////////////// +#if !defined (TFT_PARALLEL_8_BIT) + + #ifdef USE_HSPI_PORT + + #ifndef TFT_MISO + #define TFT_MISO -1 + #endif + + #ifndef TFT_MOSI + #define TFT_MOSI 13 + #endif + #if (TFT_MOSI == -1) + #undef TFT_MOSI + #define TFT_MOSI 13 + #endif + + #ifndef TFT_SCLK + #define TFT_SCLK 14 + #endif + #if (TFT_SCLK == -1) + #undef TFT_SCLK + #define TFT_SCLK 14 + #endif + + #else // VSPI port + + #ifndef TFT_MISO + #define TFT_MISO -1 + #endif + + #ifndef TFT_MOSI + #define TFT_MOSI 23 + #endif + #if (TFT_MOSI == -1) + #undef TFT_MOSI + #define TFT_MOSI 23 + #endif + + #ifndef TFT_SCLK + #define TFT_SCLK 18 + #endif + #if (TFT_SCLK == -1) + #undef TFT_SCLK + #define TFT_SCLK 18 + #endif + + #if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) + #if (TFT_MISO == -1) + #undef TFT_MISO + #define TFT_MISO TFT_MOSI + #endif + #endif + + #endif + +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +// Define the parallel bus interface chip pin drive code +//////////////////////////////////////////////////////////////////////////////////////// +#if defined (TFT_PARALLEL_8_BIT) + + // Create a bit set lookup table for data bus - wastes 1kbyte of RAM but speeds things up dramatically + // can then use e.g. GPIO.out_w1ts.val = set_mask(0xFF); to set data bus to 0xFF + #define PARALLEL_INIT_TFT_DATA_BUS \ + for (int32_t c = 0; c<256; c++) \ + { \ + xset_mask[c] = 0; \ + if ( c & 0x01 ) xset_mask[c] |= (1 << TFT_D0); \ + if ( c & 0x02 ) xset_mask[c] |= (1 << TFT_D1); \ + if ( c & 0x04 ) xset_mask[c] |= (1 << TFT_D2); \ + if ( c & 0x08 ) xset_mask[c] |= (1 << TFT_D3); \ + if ( c & 0x10 ) xset_mask[c] |= (1 << TFT_D4); \ + if ( c & 0x20 ) xset_mask[c] |= (1 << TFT_D5); \ + if ( c & 0x40 ) xset_mask[c] |= (1 << TFT_D6); \ + if ( c & 0x80 ) xset_mask[c] |= (1 << TFT_D7); \ + } \ + + // Mask for the 8 data bits to set pin directions + #define dir_mask ((1 << TFT_D0) | (1 << TFT_D1) | (1 << TFT_D2) | (1 << TFT_D3) | (1 << TFT_D4) | (1 << TFT_D5) | (1 << TFT_D6) | (1 << TFT_D7)) + + #if (TFT_WR >= 32) + // Data bits and the write line are cleared sequentially + #define clr_mask (dir_mask); WR_L + #elif (TFT_WR >= 0) + // Data bits and the write line are cleared to 0 in one step (1.25x faster) + #define clr_mask (dir_mask | (1 << TFT_WR)) + #else + #define clr_mask + #endif + + // A lookup table is used to set the different bit patterns, this uses 1kByte of RAM + #define set_mask(C) xset_mask[C] // 63fps Sprite rendering test 33% faster, graphicstest only 1.8% faster than shifting in real time + + // Real-time shifting alternative to above to save 1KByte RAM, 47 fps Sprite rendering test + /*#define set_mask(C) (((C)&0x80)>>7)<>6)<>5)<>4)<>3)<>2)<>1)<>0)<> 8)); WR_H; \ + GPIO.out_w1tc.val = clr_mask; GPIO.out_w1ts.val = set_mask((uint8_t) (((C) & 0x07E0)>> 3)); WR_H; \ + GPIO.out_w1tc.val = clr_mask; GPIO.out_w1ts.val = set_mask((uint8_t) (((C) & 0x001F)<< 3)); WR_H + + // 18 bit color write with swapped bytes + #define tft_Write_16S(C) Cswap = ((C) >>8 | (C) << 8); tft_Write_16(Cswap) + + #else + + #ifdef PSEUDO_16_BIT + // One write strobe for both bytes + #define tft_Write_16(C) GPIO.out_w1tc.val = clr_mask; GPIO.out_w1ts.val = set_mask((uint8_t) ((C) >> 0)); WR_H + #define tft_Write_16S(C) GPIO.out_w1tc.val = clr_mask; GPIO.out_w1ts.val = set_mask((uint8_t) ((C) >> 8)); WR_H + #else + // Write 16 bits to TFT + #define tft_Write_16(C) GPIO.out_w1tc.val = clr_mask; GPIO.out_w1ts.val = set_mask((uint8_t) ((C) >> 8)); WR_H; \ + GPIO.out_w1tc.val = clr_mask; GPIO.out_w1ts.val = set_mask((uint8_t) ((C) >> 0)); WR_H + + // 16 bit write with swapped bytes + #define tft_Write_16S(C) GPIO.out_w1tc.val = clr_mask; GPIO.out_w1ts.val = set_mask((uint8_t) ((C) >> 0)); WR_H; \ + GPIO.out_w1tc.val = clr_mask; GPIO.out_w1ts.val = set_mask((uint8_t) ((C) >> 8)); WR_H + #endif + + #endif + + // Write 32 bits to TFT + #define tft_Write_32(C) GPIO.out_w1tc.val = clr_mask; GPIO.out_w1ts.val = set_mask((uint8_t) ((C) >> 24)); WR_H; \ + GPIO.out_w1tc.val = clr_mask; GPIO.out_w1ts.val = set_mask((uint8_t) ((C) >> 16)); WR_H; \ + GPIO.out_w1tc.val = clr_mask; GPIO.out_w1ts.val = set_mask((uint8_t) ((C) >> 8)); WR_H; \ + GPIO.out_w1tc.val = clr_mask; GPIO.out_w1ts.val = set_mask((uint8_t) ((C) >> 0)); WR_H + + // Write two concatenated 16 bit values to TFT + #define tft_Write_32C(C,D) GPIO.out_w1tc.val = clr_mask; GPIO.out_w1ts.val = set_mask((uint8_t) ((C) >> 8)); WR_H; \ + GPIO.out_w1tc.val = clr_mask; GPIO.out_w1ts.val = set_mask((uint8_t) ((C) >> 0)); WR_H; \ + GPIO.out_w1tc.val = clr_mask; GPIO.out_w1ts.val = set_mask((uint8_t) ((D) >> 8)); WR_H; \ + GPIO.out_w1tc.val = clr_mask; GPIO.out_w1ts.val = set_mask((uint8_t) ((D) >> 0)); WR_H + + // Write 16 bit value twice to TFT - used by drawPixel() + #define tft_Write_32D(C) GPIO.out_w1tc.val = clr_mask; GPIO.out_w1ts.val = set_mask((uint8_t) ((C) >> 8)); WR_H; \ + GPIO.out_w1tc.val = clr_mask; GPIO.out_w1ts.val = set_mask((uint8_t) ((C) >> 0)); WR_H; \ + GPIO.out_w1tc.val = clr_mask; GPIO.out_w1ts.val = set_mask((uint8_t) ((C) >> 8)); WR_H; \ + GPIO.out_w1tc.val = clr_mask; GPIO.out_w1ts.val = set_mask((uint8_t) ((C) >> 0)); WR_H + + // Read pin + #ifdef TFT_RD + #if (TFT_RD >= 32) + #define RD_L GPIO.out_w1tc.val = (1 << (TFT_RD - 32)) + #define RD_H GPIO.out_w1ts.val = (1 << (TFT_RD - 32)) + #elif (TFT_RD >= 0) + #define RD_L GPIO.out_w1tc.val = (1 << TFT_RD) + //#define RD_L digitalWrite(TFT_WR, LOW) + #define RD_H GPIO.out_w1ts.val = (1 << TFT_RD) + //#define RD_H digitalWrite(TFT_WR, HIGH) + #else + #define RD_L + #define RD_H + #endif + #else + #define TFT_RD -1 + #define RD_L + #define RD_H + #endif + +//////////////////////////////////////////////////////////////////////////////////////// +// Macros to write commands/pixel colour data to a SPI ILI948x TFT +//////////////////////////////////////////////////////////////////////////////////////// +#elif defined (SPI_18BIT_DRIVER) // SPI 18 bit colour + + // Write 8 bits to TFT + #define tft_Write_8(C) spi.transfer(C) + + // Convert 16 bit colour to 18 bit and write in 3 bytes + #define tft_Write_16(C) spi.transfer(((C) & 0xF800)>>8); \ + spi.transfer(((C) & 0x07E0)>>3); \ + spi.transfer(((C) & 0x001F)<<3) + + // Future option for transfer without wait + #define tft_Write_16N(C) tft_Write_16(C) + + // Convert swapped byte 16 bit colour to 18 bit and write in 3 bytes + #define tft_Write_16S(C) spi.transfer((C) & 0xF8); \ + spi.transfer(((C) & 0xE000)>>11 | ((C) & 0x07)<<5); \ + spi.transfer(((C) & 0x1F00)>>5) + + // Write 32 bits to TFT + #define tft_Write_32(C) spi.write32(C) + + // Write two concatenated 16 bit values to TFT + #define tft_Write_32C(C,D) spi.write32((C)<<16 | (D)) + + // Write 16 bit value twice to TFT + #define tft_Write_32D(C) spi.write32((C)<<16 | (C)) + +//////////////////////////////////////////////////////////////////////////////////////// +// Macros to write commands/pixel colour data to an Raspberry Pi TFT +//////////////////////////////////////////////////////////////////////////////////////// +#elif defined (RPI_DISPLAY_TYPE) + + // ESP32 low level SPI writes for 8, 16 and 32 bit values + // to avoid the function call overhead + #define TFT_WRITE_BITS(D, B) \ + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_PORT), B-1); \ + WRITE_PERI_REG(SPI_W0_REG(SPI_PORT), D); \ + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); \ + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + + // Write 8 bits + #define tft_Write_8(C) TFT_WRITE_BITS((C)<<8, 16) + + // Write 16 bits with corrected endianness for 16 bit colours + #define tft_Write_16(C) TFT_WRITE_BITS((C)<<8 | (C)>>8, 16) + + // Future option for transfer without wait + #define tft_Write_16N(C) tft_Write_16(C) + + // Write 16 bits + #define tft_Write_16S(C) TFT_WRITE_BITS(C, 16) + + // Write 32 bits + #define tft_Write_32(C) TFT_WRITE_BITS(C, 32) + + // Write two address coordinates + #define tft_Write_32C(C,D) TFT_WRITE_BITS((C)<<24 | (C), 32); \ + TFT_WRITE_BITS((D)<<24 | (D), 32) + + // Write same value twice + #define tft_Write_32D(C) tft_Write_32C(C,C) + +//////////////////////////////////////////////////////////////////////////////////////// +// Macros for all other SPI displays +//////////////////////////////////////////////////////////////////////////////////////// +#else +/* Old macros + // ESP32 low level SPI writes for 8, 16 and 32 bit values + // to avoid the function call overhead + #define TFT_WRITE_BITS(D, B) \ + WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_PORT), B-1); \ + WRITE_PERI_REG(SPI_W0_REG(SPI_PORT), D); \ + SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); \ + while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + + // Write 8 bits + #define tft_Write_8(C) TFT_WRITE_BITS(C, 8) + + // Write 16 bits with corrected endianness for 16 bit colours + #define tft_Write_16(C) TFT_WRITE_BITS((C)<<8 | (C)>>8, 16) + + // Write 16 bits + #define tft_Write_16S(C) TFT_WRITE_BITS(C, 16) + + // Write 32 bits + #define tft_Write_32(C) TFT_WRITE_BITS(C, 32) + + // Write two address coordinates + #define tft_Write_32C(C,D) TFT_WRITE_BITS((uint16_t)((D)<<8 | (D)>>8)<<16 | (uint16_t)((C)<<8 | (C)>>8), 32) + + // Write same value twice + #define tft_Write_32D(C) TFT_WRITE_BITS((uint16_t)((C)<<8 | (C)>>8)<<16 | (uint16_t)((C)<<8 | (C)>>8), 32) +//*/ +//* Replacement slimmer macros + #if !defined(CONFIG_IDF_TARGET_ESP32C3) + #define TFT_WRITE_BITS(D, B) *_spi_mosi_dlen = B-1; \ + *_spi_w = D; \ + *_spi_cmd = SPI_USR; \ + while (*_spi_cmd & SPI_USR); + #else + #define TFT_WRITE_BITS(D, B) *_spi_mosi_dlen = B-1; \ + *_spi_w = D; \ + *_spi_cmd = SPI_UPDATE; \ + while (*_spi_cmd & SPI_UPDATE); \ + *_spi_cmd = SPI_USR; \ + while (*_spi_cmd & SPI_USR); + #endif + // Write 8 bits + #define tft_Write_8(C) TFT_WRITE_BITS(C, 8) + + // Write 16 bits with corrected endianness for 16 bit colours + #define tft_Write_16(C) TFT_WRITE_BITS((C)<<8 | (C)>>8, 16) + + // Future option for transfer without wait + #if !defined(CONFIG_IDF_TARGET_ESP32C3) + #define tft_Write_16N(C) *_spi_mosi_dlen = 16-1; \ + *_spi_w = ((C)<<8 | (C)>>8); \ + *_spi_cmd = SPI_USR; +#else + #define tft_Write_16N(C) *_spi_mosi_dlen = 16-1; \ + *_spi_w = ((C)<<8 | (C)>>8); \ + *_spi_cmd = SPI_UPDATE; \ + while (*_spi_cmd & SPI_UPDATE); \ + *_spi_cmd = SPI_USR; +#endif + + // Write 16 bits + #define tft_Write_16S(C) TFT_WRITE_BITS(C, 16) + + // Write 32 bits + #define tft_Write_32(C) TFT_WRITE_BITS(C, 32) + + // Write two address coordinates + #define tft_Write_32C(C,D) TFT_WRITE_BITS((uint16_t)((D)<<8 | (D)>>8)<<16 | (uint16_t)((C)<<8 | (C)>>8), 32) + + // Write same value twice + #define tft_Write_32D(C) TFT_WRITE_BITS((uint16_t)((C)<<8 | (C)>>8)<<16 | (uint16_t)((C)<<8 | (C)>>8), 32) + +//*/ +#endif + +#ifndef tft_Write_16N + #define tft_Write_16N tft_Write_16 +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +// Macros to read from display using SPI or software SPI +//////////////////////////////////////////////////////////////////////////////////////// +#if !defined (TFT_PARALLEL_8_BIT) + // Read from display using SPI or software SPI + // Use a SPI read transfer + #define tft_Read_8() spi.transfer(0) +#endif + +// Concatenate a byte sequence A,B,C,D to CDAB, P is a uint8_t pointer +#define DAT8TO32(P) ( (uint32_t)P[0]<<8 | P[1] | P[2]<<24 | P[3]<<16 ) + +#endif // Header end diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index bfc41b5..114b378 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -19,6 +19,8 @@ #if defined (ESP32) #if defined(CONFIG_IDF_TARGET_ESP32S3) #include "Processors/TFT_eSPI_ESP32_S3.c" + #elif defined(CONFIG_IDF_TARGET_ESP32C3) + #include "Processors/TFT_eSPI_ESP32_C3.c" #else #include "Processors/TFT_eSPI_ESP32.c" #endif diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 264bb39..b13c39d 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -67,6 +67,8 @@ // Include the processor specific drivers #if defined(CONFIG_IDF_TARGET_ESP32S3) #include "Processors/TFT_eSPI_ESP32_S3.h" +#elif defined(CONFIG_IDF_TARGET_ESP32C3) + #include "Processors/TFT_eSPI_ESP32_C3.h" #elif defined (ESP32) #include "Processors/TFT_eSPI_ESP32.h" #elif defined (ESP8266)