diff --git a/Extensions/Touch.cpp b/Extensions/Touch.cpp index 3b20a83..e971c64 100644 --- a/Extensions/Touch.cpp +++ b/Extensions/Touch.cpp @@ -16,7 +16,7 @@ ***************************************************************************************/ // The touch controller has a low SPI clock rate inline void TFT_eSPI::begin_touch_read_write(void){ - DMA_BUSY_CHECK; + DMA_BUSY_CHECK; // Wait for any DMA transfer to complete before changing SPI settings CS_H; // Just in case it has been left low #if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) if (locked) {locked = false; spi.beginTransaction(SPISettings(SPI_TOUCH_FREQUENCY, MSBFIRST, SPI_MODE0));} @@ -38,7 +38,6 @@ inline void TFT_eSPI::end_touch_read_write(void){ #else spi.setFrequency(SPI_FREQUENCY); #endif - //SET_BUS_WRITE_MODE; } /*************************************************************************************** diff --git a/Processors/TFT_eSPI_ESP32.c b/Processors/TFT_eSPI_ESP32.c index a0ea56e..191c694 100644 --- a/Processors/TFT_eSPI_ESP32.c +++ b/Processors/TFT_eSPI_ESP32.c @@ -27,12 +27,14 @@ #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)); + // Write only SPI Tx/Rx register + volatile uint32_t *const _spi_w = (uint32_t*)(SPI_W0_REG(SPI_PORT)); + // Write only SPI interface control register + volatile uint32_t *const _spi_user = (uint32_t*)(SPI_USER_REG(SPI_PORT)); + // Bit length to transmit N bits (512 max), load with N-1 + volatile uint32_t *const _spi_mosi_dlen = (uint32_t*)(SPI_MOSI_DLEN_REG(SPI_PORT)); + // Read/write reister to initiate SPI transfer and check when completed + volatile uint32_t *const _spi_cmd = (volatile uint32_t*)(SPI_CMD_REG(SPI_PORT)); #endif //////////////////////////////////////////////////////////////////////////////////////// @@ -187,54 +189,41 @@ void TFT_eSPI::pushPixels(const void* data_in, uint32_t len) #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) + + 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; + + if (rem) { - *_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; + while (*_spi_cmd&SPI_USR); + for (i=0; i < rem; i+=2) *spi_w++ = color32; + *_spi_mosi_dlen = (rem << 4) - 1; + *_spi_cmd = SPI_USR; + if (!len) return; + i = i>>1; while(i++<16) *spi_w++ = color32; } - if (len) + while (*_spi_cmd&SPI_USR); + if (!rem) while (i++<16) *spi_w++ = color32; + *_spi_mosi_dlen = 511; + + while(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; + len -= 32; } - while ((*_spi_cmd)&SPI_USR); // Move to later in code to use transmit time usefully? + + //while (*_spi_cmd&SPI_USR); } /*************************************************************************************** @@ -248,33 +237,43 @@ void TFT_eSPI::pushSwapBytePixels(const void* data_in, uint32_t len){ if (len > 31) { - WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_PORT), 511); + while (*_spi_cmd&SPI_USR); + *_spi_mosi_dlen = 255; while(len>31) - { + { // Ping-pong the transmit buffer uint32_t i = 0; + while(i<8) + { + color[i++] = DAT8TO32(data); + data+=4; + } + _spi_w[8] = color[0]; + _spi_w[9] = color[1]; + _spi_w[10] = color[2]; + _spi_w[11] = color[3]; + _spi_w[12] = color[4]; + _spi_w[13] = color[5]; + _spi_w[14] = color[6]; + _spi_w[15] = color[7]; + while (*_spi_cmd&SPI_USR); + *_spi_user = SPI_USR_MOSI | SPI_USR_MOSI_HIGHPART; + *_spi_cmd = SPI_USR; 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]); - SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); + _spi_w[0] = color[8]; + _spi_w[1] = color[9]; + _spi_w[2] = color[10]; + _spi_w[3] = color[11]; + _spi_w[4] = color[12]; + _spi_w[5] = color[13]; + _spi_w[6] = color[14]; + _spi_w[7] = color[15]; + while (*_spi_cmd&SPI_USR); + *_spi_user = SPI_USR_MOSI; + *_spi_cmd = SPI_USR; len -= 32; } } @@ -287,31 +286,31 @@ void TFT_eSPI::pushSwapBytePixels(const void* data_in, uint32_t len){ 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]); - SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); + while (*_spi_cmd&SPI_USR); + *_spi_mosi_dlen = 255; + _spi_w[0] = color[0]; + _spi_w[1] = color[1]; + _spi_w[2] = color[2]; + _spi_w[3] = color[3]; + _spi_w[4] = color[4]; + _spi_w[5] = color[5]; + _spi_w[6] = color[6]; + _spi_w[7] = color[7]; + *_spi_cmd = 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); + volatile uint32_t* spi_w = _spi_w; + while (*_spi_cmd&SPI_USR); + *_spi_mosi_dlen = (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; + *spi_w++ = DAT8TO32(data); data+=4; } - SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); + *_spi_cmd = SPI_USR; } - while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); - + //while (*_spi_cmd&SPI_USR); } /*************************************************************************************** @@ -329,39 +328,46 @@ void TFT_eSPI::pushPixels(const void* data_in, uint32_t len){ if (len > 31) { - WRITE_PERI_REG(SPI_MOSI_DLEN_REG(SPI_PORT), 511); + while (*_spi_cmd&SPI_USR); + *_spi_mosi_dlen = 255; + *_spi_user = SPI_USR_MOSI; 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++); - SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); + { // Ping-pong the transmit buffer + _spi_w[8] = *data++; + _spi_w[9] = *data++; + _spi_w[10] = *data++; + _spi_w[11] = *data++; + _spi_w[12] = *data++; + _spi_w[13] = *data++; + _spi_w[14] = *data++; + _spi_w[15] = *data++; + while (*_spi_cmd&SPI_USR); + SET_BUS_WRITE_MODE | SPI_USR_MOSI_HIGHPART; + *_spi_cmd = SPI_USR; + _spi_w[0] = *data++; + _spi_w[1] = *data++; + _spi_w[2] = *data++; + _spi_w[3] = *data++; + _spi_w[4] = *data++; + _spi_w[5] = *data++; + _spi_w[6] = *data++; + _spi_w[7] = *data++; + while (*_spi_cmd&SPI_USR); + SET_BUS_WRITE_MODE; + *_spi_cmd = 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++); - SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); + volatile uint32_t* spi_w = _spi_w; + while (*_spi_cmd&SPI_USR); + *_spi_mosi_dlen = (len << 4) - 1; + for (uint32_t i=0; i <= (len<<1); i+=4) *spi_w++ = *data++; + *_spi_cmd = SPI_USR; } - while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + //while (*_spi_cmd&SPI_USR); } //////////////////////////////////////////////////////////////////////////////////////// @@ -382,59 +388,62 @@ void TFT_eSPI::pushBlock(uint16_t color, uint32_t len) uint32_t r0 = r<<24 | b | g | r; uint32_t r1 = r0>>8 | g<<16; uint32_t r2 = r1>>8 | b<<8; + bool empty = true; if (len > 19) { - SET_PERI_REG_BITS(SPI_MOSI_DLEN_REG(SPI_PORT), SPI_USR_MOSI_DBITLEN, 479, SPI_USR_MOSI_DBITLEN_S); - + while (*_spi_cmd&SPI_USR); + *_spi_mosi_dlen = 479; + _spi_w[ 0] = r0; + _spi_w[ 1] = r1; + _spi_w[ 2] = r2; + _spi_w[ 3] = r0; + _spi_w[ 4] = r1; + _spi_w[ 5] = r2; + _spi_w[ 6] = r0; + _spi_w[ 7] = r1; + _spi_w[ 8] = r2; + _spi_w[ 9] = r0; + _spi_w[10] = r1; + _spi_w[11] = r2; + _spi_w[12] = r0; + _spi_w[13] = r1; + _spi_w[14] = r2; 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); - SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); + while (*_spi_cmd&SPI_USR); + *_spi_cmd = SPI_USR; len -= 20; } - while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + empty = false; } if (len) { - SET_PERI_REG_BITS(SPI_MOSI_DLEN_REG(SPI_PORT), SPI_USR_MOSI_DBITLEN, (len * 24) - 1, SPI_USR_MOSI_DBITLEN_S); - 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); + while (*_spi_cmd&SPI_USR); + *_spi_mosi_dlen = (len * 24) - 1; + if (empty) { + _spi_w[0] = r0; + _spi_w[1] = r1; + _spi_w[2] = r2; + _spi_w[3] = r0; + _spi_w[4] = r1; + _spi_w[5] = r2; + if (len > 8 ) + { + _spi_w[ 6] = r0; + _spi_w[ 7] = r1; + _spi_w[ 8] = r2; + _spi_w[ 9] = r0; + _spi_w[10] = r1; + _spi_w[11] = r2; + _spi_w[12] = r0; + _spi_w[14] = r1; + _spi_w[14] = r2; + } } - - SET_PERI_REG_MASK(SPI_CMD_REG(SPI_PORT), SPI_USR); - while (READ_PERI_REG(SPI_CMD_REG(SPI_PORT))&SPI_USR); + *_spi_cmd = SPI_USR; + //while (*_spi_cmd&SPI_USR); } } @@ -445,9 +454,59 @@ void TFT_eSPI::pushBlock(uint16_t color, uint32_t len) 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++;} } + + if(!_swapBytes) { + + while (*_spi_cmd&SPI_USR); + *_spi_mosi_dlen = 48-1; + + while ( len>1 ) { + // Split out the colours + uint32_t r1 = (*data & 0xF8); + uint32_t g1 = (*data & 0xE000)>>11 | (*data & 0x07)<<5; + uint32_t b1 = (*data++ & 0x1F00)>>5; + uint32_t r2 = (*data & 0xF8); + uint32_t g2 = (*data & 0xE000)>>11 | (*data & 0x07)<<5; + uint32_t b2 = (*data++ & 0x1F00)>>5; + uint32_t rgb1 = r2<<24 | b1<<16 | g1<<8 | r1; + uint32_t rgb2 = b2<<8 | g2; + while (*_spi_cmd&SPI_USR); + _spi_w[0] = rgb1; + _spi_w[1] = rgb2; + *_spi_cmd = SPI_USR; + len -= 2; + } + + if (len) { + // Split out the colours + uint32_t r = (*data & 0xF8); + uint32_t g = (*data & 0xE000)>>11 | (*data & 0x07)<<5; + uint32_t b = (*data++ & 0x1F00)>>5; + uint32_t rgb = b<<16 | g<<8 | r; + while (*_spi_cmd&SPI_USR); + *_spi_mosi_dlen = 24-1; + *_spi_w = rgb; + *_spi_cmd = SPI_USR; + } + //was while ( len-- ) {tft_Write_16S(*data); data++;} + } + else { + while (*_spi_cmd&SPI_USR); + *_spi_mosi_dlen = 24-1; + + while ( len-- ) { + // Split out the colours + uint32_t r = (*data & 0xF800)>>8; + uint32_t g = (*data & 0x07E0)>>3; + uint32_t b = (*data++ & 0x001F)<<3; + uint32_t rgb = b<<16 | g<<8 | r; + while (*_spi_cmd&SPI_USR); + // Concatenate pixel into 32 bits + *_spi_w = rgb; + *_spi_cmd = SPI_USR; + } + //was while ( len-- ) {tft_Write_16(*data); data++;} + } } /*************************************************************************************** @@ -458,7 +517,21 @@ 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++;} + while (*_spi_cmd&SPI_USR); + *_spi_mosi_dlen = 24-1; + + while ( len-- ) { + // Split out the colours + uint32_t r = (*data & 0xF800)>>8; + uint32_t g = (*data & 0x07E0)>>3; + uint32_t b = (*data++ & 0x001F)<<3; + uint32_t rgb = b<<16 | g<<8 | r; + while (*_spi_cmd&SPI_USR); + // Concatenate pixel into 32 bits + *_spi_w = rgb; + *_spi_cmd = SPI_USR; + } + //while ( len-- ) {tft_Write_16(*data); data++;} } //////////////////////////////////////////////////////////////////////////////////////// @@ -575,8 +648,8 @@ void TFT_eSPI::pushPixelsDMA(uint16_t* image, uint32_t len) 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 + 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); @@ -611,7 +684,7 @@ void TFT_eSPI::pushImageDMA(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t if (buffer == nullptr) { buffer = image; - dmaWait(); + if (spiBusyCheck) dmaWait(); } // If image is clipped, copy pixels into a contiguous block @@ -640,13 +713,13 @@ void TFT_eSPI::pushImageDMA(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t } } + static spi_transaction_t trans; + esp_err_t ret; + if (spiBusyCheck) dmaWait(); // Incase 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; @@ -712,7 +785,7 @@ bool TFT_eSPI::initDMA(bool ctrl_cs) .input_delay_ns = 0, .spics_io_num = pin, .flags = SPI_DEVICE_NO_DUMMY, //0, - .queue_size = 1, + .queue_size = DMA_QUEUE_SIZE, .pre_cb = 0, //dc_callback, //Callback to handle D/C line .post_cb = 0 }; diff --git a/Processors/TFT_eSPI_ESP32.h b/Processors/TFT_eSPI_ESP32.h index 7462940..e75e0f6 100644 --- a/Processors/TFT_eSPI_ESP32.h +++ b/Processors/TFT_eSPI_ESP32.h @@ -65,12 +65,19 @@ // 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 + #define DMA_QUEUE_SIZE 1 // 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 @@ -167,7 +174,7 @@ #define CS_L GPIO.out_w1ts = (1 << TFT_CS); GPIO.out_w1tc = (1 << TFT_CS) #define CS_H GPIO.out_w1tc = (1 << TFT_CS); GPIO.out_w1ts = (1 << TFT_CS) #else - #define CS_L GPIO.out_w1tc = (1 << TFT_CS); GPIO.out_w1tc = (1 << TFT_CS) + #define CS_L GPIO.out_w1tc = (1 << TFT_CS)//; GPIO.out_w1tc = (1 << TFT_CS) #define CS_H GPIO.out_w1ts = (1 << TFT_CS)//;GPIO.out_w1ts = (1 << TFT_CS) #endif #else @@ -198,6 +205,7 @@ #define WR_H #endif + //////////////////////////////////////////////////////////////////////////////////////// // Define the touch screen chip select pin drive code //////////////////////////////////////////////////////////////////////////////////////// @@ -217,11 +225,7 @@ #ifdef USE_HSPI_PORT #ifndef TFT_MISO - #define TFT_MISO 12 - #endif - #if (TFT_MISO == -1) - #undef TFT_MISO - #define TFT_MISO 12 + #define TFT_MISO -1 #endif #ifndef TFT_MOSI @@ -243,11 +247,7 @@ #else // VSPI port #ifndef TFT_MISO - #define TFT_MISO 19 - #endif - #if (TFT_MISO == -1) - #undef TFT_MISO - #define TFT_MISO 19 + #define TFT_MISO -1 #endif #ifndef TFT_MOSI @@ -375,6 +375,7 @@ //////////////////////////////////////////////////////////////////////////////////////// #elif defined (SPI_18BIT_DRIVER) // SPI 18 bit colour + // ESP32 low level SPI writes for 8, 16 and 32 bit values too fast for ILI9488 // Write 8 bits to TFT #define tft_Write_8(C) spi.transfer(C) @@ -383,6 +384,11 @@ spi.transfer(((C) & 0x07E0)>>3); \ spi.transfer(((C) & 0x001F)<<3) + // Convert 16 bit colour to 18 bit and write in 3 bytes + #define tft_Write_16N(C) spi.transfer(((C) & 0xF800)>>8); \ + spi.transfer(((C) & 0x07E0)>>3); \ + spi.transfer(((C) & 0x001F)<<3) + // 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); \ @@ -404,11 +410,14 @@ // 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); + #define TFT_WRITE_BITS(D, B) *_spi_mosi_dlen = B-1; \ + *_spi_w = D; \ + *_spi_cmd = SPI_USR; \ + while (*_spi_cmd & SPI_USR); + + #define TFT_WRITE_BITSN(D, B) *_spi_mosi_dlen = B-1; \ + *_spi_w = D; \ + *_spi_cmd = SPI_USR; // Write 8 bits #define tft_Write_8(C) TFT_WRITE_BITS((C)<<8, 16) @@ -416,6 +425,10 @@ // Write 16 bits with corrected endianess for 16 bit colours #define tft_Write_16(C) TFT_WRITE_BITS((C)<<8 | (C)>>8, 16) + #define tft_Write_16N(C) *_spi_mosi_dlen = 16-1; \ + *_spi_w = ((C)<<8 | (C)>>8); \ + *_spi_cmd = SPI_USR; + // Write 16 bits #define tft_Write_16S(C) TFT_WRITE_BITS(C, 16) @@ -429,18 +442,16 @@ // Write same value twice #define tft_Write_32D(C) tft_Write_32C(C,C) + //////////////////////////////////////////////////////////////////////////////////////// // Macros for all other SPI displays //////////////////////////////////////////////////////////////////////////////////////// #else - // 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); + #define TFT_WRITE_BITS(D, B) *_spi_mosi_dlen = B-1; \ + *_spi_w = D; \ + *_spi_cmd = SPI_USR; \ + while (*_spi_cmd & SPI_USR); // Write 8 bits #define tft_Write_8(C) TFT_WRITE_BITS(C, 8) @@ -448,6 +459,10 @@ // Write 16 bits with corrected endianess for 16 bit colours #define tft_Write_16(C) TFT_WRITE_BITS((C)<<8 | (C)>>8, 16) + #define tft_Write_16N(C) *_spi_mosi_dlen = 16-1; \ + *_spi_w = ((C)<<8 | (C)>>8); \ + *_spi_cmd = SPI_USR; + // Write 16 bits #define tft_Write_16S(C) TFT_WRITE_BITS(C, 16) diff --git a/Processors/TFT_eSPI_ESP8266.c b/Processors/TFT_eSPI_ESP8266.c index 83939f9..6c3f001 100644 --- a/Processors/TFT_eSPI_ESP8266.c +++ b/Processors/TFT_eSPI_ESP8266.c @@ -127,6 +127,7 @@ void TFT_eSPI::pushBlock(uint16_t color, uint32_t len) uint32_t r1 = g<<24 | r<<16 | b<<8 | g; uint32_t r2 = b<<24 | g<<16 | r<<8 | b; + while(SPI1CMD & SPIBUSY) {} SPI1W0 = r0; SPI1W1 = r1; SPI1W2 = r2; @@ -163,15 +164,16 @@ void TFT_eSPI::pushBlock(uint16_t color, uint32_t len) SPI1CMD |= SPIBUSY; len -= 21; } - while(SPI1CMD & SPIBUSY) {} + //while(SPI1CMD & SPIBUSY) {} } if (len) { len = (len * 24) - 1; + while(SPI1CMD & SPIBUSY) {} SPI1U1 = (len << SPILMOSI); SPI1CMD |= SPIBUSY; - while(SPI1CMD & SPIBUSY) {} + //while(SPI1CMD & SPIBUSY) {} } } @@ -183,6 +185,7 @@ void TFT_eSPI::pushBlock(uint16_t color, uint32_t len) void TFT_eSPI::pushPixels(const void* data_in, uint32_t len){ uint16_t *data = (uint16_t*)data_in; + while(SPI1CMD & SPIBUSY) {} // Send groups of 4 concatenated pixels if (len > 3) { @@ -222,12 +225,8 @@ void TFT_eSPI::pushPixels(const void* data_in, uint32_t len){ SPI1CMD |= SPIBUSY; len -= 4; } - while(SPI1CMD & SPIBUSY) {} + //while(SPI1CMD & SPIBUSY) {} } - - // 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++;} } /*************************************************************************************** @@ -257,23 +256,10 @@ void TFT_eSPI::pushSwapBytePixels(const void* data_in, uint32_t len){ // void TFT_eSPI::pushBlock(uint16_t color, uint32_t len) { -/* -while (len>1) { tft_Write_32(color<<16 | color); len-=2;} -if (len) tft_Write_16(color); -return; -//*/ uint16_t color16 = (color >> 8) | (color << 8); uint32_t color32 = color16 | color16 << 16; -/* - while(len--) { - SPI1U1 = ((16-1) << SPILMOSI) | ((16-1) << SPILMISO); - SPI1W0 = color16; - SPI1CMD |= SPIBUSY; - while(SPI1CMD & SPIBUSY) {} - } - return; -//*/ - + while(SPI1CMD & SPIBUSY) {} + SPI1W0 = color32; SPI1W1 = color32; SPI1W2 = color32; @@ -311,15 +297,16 @@ return; SPI1CMD |= SPIBUSY; len -= 32; } - while(SPI1CMD & SPIBUSY) {} + //while(SPI1CMD & SPIBUSY) {} } if (len) { len = (len << 4) - 1; + while(SPI1CMD & SPIBUSY) {} SPI1U1 = (len << SPILMOSI); SPI1CMD |= SPIBUSY; - while(SPI1CMD & SPIBUSY) {} + //while(SPI1CMD & SPIBUSY) {} } } @@ -339,6 +326,7 @@ void TFT_eSPI::pushPixels(const void* data_in, uint32_t len){ uint32_t color[8]; + while(SPI1CMD & SPIBUSY) {} SPI1U1 = (255 << SPILMOSI) | (255 << SPILMISO); @@ -381,7 +369,7 @@ void TFT_eSPI::pushPixels(const void* data_in, uint32_t len){ SPI1CMD |= SPIBUSY; } - while(SPI1CMD & SPIBUSY) {} + //while(SPI1CMD & SPIBUSY) {} } @@ -396,6 +384,7 @@ void TFT_eSPI::pushSwapBytePixels(const void* data_in, uint32_t len){ uint32_t color[8]; + while(SPI1CMD & SPIBUSY) {} SPI1U1 = (255 << SPILMOSI) | (255 << SPILMISO); while(len>15) @@ -438,7 +427,7 @@ void TFT_eSPI::pushSwapBytePixels(const void* data_in, uint32_t len){ SPI1CMD |= SPIBUSY; } - while(SPI1CMD & SPIBUSY) {} + //while(SPI1CMD & SPIBUSY) {} } diff --git a/Processors/TFT_eSPI_ESP8266.h b/Processors/TFT_eSPI_ESP8266.h index 7a1a3a0..0247857 100644 --- a/Processors/TFT_eSPI_ESP8266.h +++ b/Processors/TFT_eSPI_ESP8266.h @@ -17,6 +17,7 @@ // Code to check if DMA is busy, used by SPI bus transaction transaction and endWrite functions #define DMA_BUSY_CHECK // DMA not available, leave blank +#define SPI_BUSY_CHECK while(SPI1CMD & SPIBUSY) {;} // Initialise processor specific SPI functions, used by init() #if (!defined (SUPPORT_TRANSACTIONS) && defined (ESP8266)) @@ -127,6 +128,11 @@ spi.transfer(((C) & 0x07E0)>>3); \ spi.transfer(((C) & 0x001F)<<3) + // Convert 16 bit colour to 18 bit and write in 3 bytes + #define tft_Write_16N(C) spi.transfer(((C) & 0xF800)>>8); \ + spi.transfer(((C) & 0x07E0)>>3); \ + spi.transfer(((C) & 0x001F)<<3) + // 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); \ @@ -160,6 +166,10 @@ #define tft_Write_16(C) TFT_WRITE_BITS((C)>>8 | (C)<<8, 16) + #define tft_Write_16N(C) SPI1U1 = ((16-1) << SPILMOSI); \ + SPI1W0 = (C)>>8 | (C)<<8; \ + SPI1CMD |= SPIBUSY; \ + #define tft_Write_16S(C) TFT_WRITE_BITS(C, 16) #define tft_Write_32(C) TFT_WRITE_BITS(C, 32) @@ -191,6 +201,11 @@ SPI1CMD |= SPIBUSY; \ while(SPI1CMD & SPIBUSY) {;} + #define tft_Write_16N(C) \ + SPI1U1 = (15 << SPILMOSI) | (15 << SPILMISO); \ + SPI1W0 = ((C)<<8 | (C)>>8); \ + SPI1CMD |= SPIBUSY; + #define tft_Write_16S(C) \ SPI1U1 = (15 << SPILMOSI) | (15 << SPILMISO); \ SPI1W0 = C; \ diff --git a/Processors/TFT_eSPI_Generic.h b/Processors/TFT_eSPI_Generic.h index c5d3512..adb1486 100644 --- a/Processors/TFT_eSPI_Generic.h +++ b/Processors/TFT_eSPI_Generic.h @@ -18,8 +18,9 @@ #define SET_BUS_WRITE_MODE // Not used #define SET_BUS_READ_MODE // Not used -// Code to check if DMA is busy, used by SPI bus transaction startWrite and endWrite functions +// Code to check if SPI or DMA is busy, used by SPI bus transaction startWrite and/or endWrite functions #define DMA_BUSY_CHECK // Not used so leave blank +#define SPI_BUSY_CHECK // Not used so leave blank // To be safe, SUPPORT_TRANSACTIONS is assumed mandatory #if !defined (SUPPORT_TRANSACTIONS) @@ -104,6 +105,11 @@ spi.transfer(((C) & 0x07E0)>>3); \ spi.transfer(((C) & 0x001F)<<3) + // Convert 16 bit colour to 18 bit and write in 3 bytes + #define tft_Write_16N(C) spi.transfer(((C) & 0xF800)>>8); \ + spi.transfer(((C) & 0x07E0)>>3); \ + spi.transfer(((C) & 0x001F)<<3) + // 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); \ @@ -124,6 +130,7 @@ #if defined (RPI_DISPLAY_TYPE) // RPi TFT type always needs 16 bit transfers #define tft_Write_8(C) spi.transfer(C); spi.transfer(C) #define tft_Write_16(C) spi.transfer((uint8_t)((C)>>8));spi.transfer((uint8_t)((C)>>0)) + #define tft_Write_16N(C) spi.transfer((uint8_t)((C)>>8));spi.transfer((uint8_t)((C)>>0)) #define tft_Write_16S(C) spi.transfer((uint8_t)((C)>>0));spi.transfer((uint8_t)((C)>>8)) #define tft_Write_32(C) \ @@ -144,13 +151,15 @@ #else #ifdef __AVR__ // AVR processors do not have 16 bit transfer - #define tft_Write_8(C) {SPDR=(C); while (!(SPSR&_BV(SPIF)));} - #define tft_Write_16(C) tft_Write_8((uint8_t)((C)>>8));tft_Write_8((uint8_t)((C)>>0)) - #define tft_Write_16S(C) tft_Write_8((uint8_t)((C)>>0));tft_Write_8((uint8_t)((C)>>8)) + #define tft_Write_8(C) {SPDR=(C); while (!(SPSR&_BV(SPIF)));} + #define tft_Write_16(C) tft_Write_8((uint8_t)((C)>>8));tft_Write_8((uint8_t)((C)>>0)) + #define tft_Write_16N(C) tft_Write_8((uint8_t)((C)>>8));tft_Write_8((uint8_t)((C)>>0)) + #define tft_Write_16S(C) tft_Write_8((uint8_t)((C)>>0));tft_Write_8((uint8_t)((C)>>8)) #else - #define tft_Write_8(C) spi.transfer(C) - #define tft_Write_16(C) spi.transfer16(C) - #define tft_Write_16S(C) spi.transfer16(((C)>>8) | ((C)<<8)) + #define tft_Write_8(C) spi.transfer(C) + #define tft_Write_16(C) spi.transfer16(C) + #define tft_Write_16N(C) spi.transfer16(C) + #define tft_Write_16S(C) spi.transfer16(((C)>>8) | ((C)<<8)) #endif // AVR #define tft_Write_32(C) \ diff --git a/Processors/TFT_eSPI_STM32.h b/Processors/TFT_eSPI_STM32.h index 71557dc..96f31dc 100644 --- a/Processors/TFT_eSPI_STM32.h +++ b/Processors/TFT_eSPI_STM32.h @@ -17,6 +17,12 @@ #define SET_BUS_WRITE_MODE // Not used #define SET_BUS_READ_MODE // Not used +#if defined(TFT_PARALLEL_8_BIT) + #define SPI_BUSY_CHECK +#else + #define SPI_BUSY_CHECK while (spiHal.State == HAL_SPI_STATE_BUSY_TX) +#endif + // SUPPORT_TRANSACTIONS is mandatory for STM32 #if !defined (SUPPORT_TRANSACTIONS) #define SUPPORT_TRANSACTIONS @@ -183,7 +189,8 @@ dmaHal.Instance = DMA1_Channel5 #endif #else - // For STM32 processor with no implemented DMA support (yet) + // For other STM32 processor with no implemented DMA support (yet) + // #define STM32_DMA // Commented out - DMA not tested with other processors #if (TFT_SPI_PORT == 1) #define INIT_TFT_DATA_BUS spiHal.Instance = SPI1 #elif (TFT_SPI_PORT == 2) @@ -200,6 +207,7 @@ #define DMA_BUSY_CHECK #endif + // If smooth fonts are enabled the filing system may need to be loaded #ifdef SMOOTH_FONT // Call up the filing system for the anti-aliased fonts <<<==== TODO @@ -1032,6 +1040,10 @@ { spiBuffer[0] = (C)>>8; spiBuffer[1] = C; \ HAL_SPI_Transmit(&spiHal, spiBuffer, 2, 10); } + #define tft_Write_16N(C) \ + { spiBuffer[0] = (C)>>8; spiBuffer[1] = C; \ + HAL_SPI_Transmit(&spiHal, spiBuffer, 2, 10); } + #define tft_Write_16S(C) \ { spiBuffer[0] = C; spiBuffer[1] = (C)>>8; \ HAL_SPI_Transmit(&spiHal, spiBuffer, 2, 10); } @@ -1067,6 +1079,10 @@ { spiBuffer[0] = (C)>>8; spiBuffer[1] = C; \ HAL_SPI_Transmit(&spiHal, spiBuffer, 2, 10); } + #define tft_Write_16N(C) \ + { spiBuffer[0] = (C)>>8; spiBuffer[1] = C; \ + HAL_SPI_Transmit(&spiHal, spiBuffer, 2, 10); } + #define tft_Write_16S(C) \ { spiBuffer[0] = C; spiBuffer[1] = (C)>>8; \ HAL_SPI_Transmit(&spiHal, spiBuffer, 2, 10); } diff --git a/TFT_Drivers/ST7796_Init.h b/TFT_Drivers/ST7796_Init.h index a112815..aff167b 100644 --- a/TFT_Drivers/ST7796_Init.h +++ b/TFT_Drivers/ST7796_Init.h @@ -27,6 +27,8 @@ writecommand(0x3A); //Interface Pixel Format writedata(0x55); //Control interface color format set to 16 + writecommand(0xB0); //Interface mode control + writedata(0x00); //0x00 = SDA input and SDO output, 0x80 bidirections SDA writecommand(0xB4); //Column inversion writedata(0x01); //1-dot inversion diff --git a/TFT_eSPI.cpp b/TFT_eSPI.cpp index fe3a6e6..7d9e81f 100644 --- a/TFT_eSPI.cpp +++ b/TFT_eSPI.cpp @@ -2,7 +2,7 @@ Arduino TFT graphics library targeted at 32 bit processors such as ESP32, ESP8266 and STM32. - This is a standalone library that contains the + This is a stand-alone library that contains the hardware driver, the graphics functions and the proportional fonts. @@ -26,6 +26,10 @@ #include "Processors/TFT_eSPI_Generic.c" #endif +#ifndef SPI_BUSY_CHECK + #define SPI_BUSY_CHECK +#endif + // Clipping macro for pushImage #define PI_CLIP \ if (_vpOoB) return; \ @@ -47,6 +51,28 @@ \ if (dw < 1 || dh < 1) return; +/*************************************************************************************** +** Function name: startWrite +** Description: begin transaction with CS low, MUST later call endWrite +***************************************************************************************/ +void TFT_eSPI::startWrite(void) +{ + begin_tft_write(); + inTransaction = true; +} + +/*************************************************************************************** +** Function name: endWrite +** Description: end transaction with CS high +***************************************************************************************/ +void TFT_eSPI::endWrite(void) +{ + inTransaction = false; + DMA_BUSY_CHECK; // Safety check - user code should have checked this! + end_tft_write(); +} + + /*************************************************************************************** ** Function name: begin_tft_write (was called spi_begin) ** Description: Start SPI transaction for writes and select TFT @@ -75,16 +101,18 @@ inline void TFT_eSPI::end_tft_write(void){ if(!inTransaction) { if (!locked) { locked = true; + SPI_BUSY_CHECK; CS_H; spi.endTransaction(); } SET_BUS_READ_MODE; } #else - if(!inTransaction) {CS_H; SET_BUS_READ_MODE;} + if(!inTransaction) {SPI_BUSY_CHECK; CS_H; SET_BUS_READ_MODE;} #endif } + /*************************************************************************************** ** Function name: begin_tft_read (was called spi_begin_read) ** Description: Start transaction for reads and select TFT @@ -107,6 +135,38 @@ inline void TFT_eSPI::begin_tft_read(void){ SET_BUS_READ_MODE; } +/*************************************************************************************** +** Function name: end_tft_read (was called spi_end_read) +** Description: End transaction for reads and deselect TFT +***************************************************************************************/ +inline void TFT_eSPI::end_tft_read(void){ +#if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) && !defined(TFT_PARALLEL_8_BIT) + if(!inTransaction) { + if (!locked) { + locked = true; + CS_H; + spi.endTransaction(); + } + } +#else + #if !defined(TFT_PARALLEL_8_BIT) + spi.setFrequency(SPI_FREQUENCY); + #endif + if(!inTransaction) {CS_H;} +#endif +} + + +/*************************************************************************************** +** Function name: Legacy - deprecated +** Description: Start/end transaction +***************************************************************************************/ + void TFT_eSPI::spi_begin() {begin_tft_write();} + void TFT_eSPI::spi_end() { end_tft_write();} + void TFT_eSPI::spi_begin_read() {begin_tft_read(); } + void TFT_eSPI::spi_end_read() { end_tft_read(); } + + /*************************************************************************************** ** Function name: setViewport ** Description: Set the clipping region for the TFT screen @@ -133,9 +193,6 @@ void TFT_eSPI::setViewport(int32_t x, int32_t y, int32_t w, int32_t h, bool vpDa if ((x + w) > width() ) { w = width() - x; } if ((y + h) > height() ) { h = height() - y; } - //Serial.print(" x=");Serial.print( x);Serial.print(", y=");Serial.print( y); - //Serial.print(", w=");Serial.print(w);Serial.print(", h=");Serial.println(h); - // Check if viewport is entirely out of bounds if (w < 1 || h < 1) { @@ -162,13 +219,6 @@ void TFT_eSPI::setViewport(int32_t x, int32_t y, int32_t w, int32_t h, bool vpDa _vpW = x + w; _vpH = y + h; _vpDatum = vpDatum; - - //Serial.print(" _xDatum=");Serial.print( _xDatum);Serial.print(", _yDatum=");Serial.print( _yDatum); - //Serial.print(", _xWidth=");Serial.print(_xWidth);Serial.print(", _yHeight=");Serial.println(_yHeight); - - //Serial.print(" _vpX=");Serial.print( _vpX);Serial.print(", _vpY=");Serial.print( _vpY); - //Serial.print(", _vpW=");Serial.print(_vpW);Serial.print(", _vpH=");Serial.println(_vpH); - } /*************************************************************************************** @@ -177,6 +227,16 @@ void TFT_eSPI::setViewport(int32_t x, int32_t y, int32_t w, int32_t h, bool vpDa ***************************************************************************************/ // Note: Setting w and h to 1 will check if coordinate x,y is in area bool TFT_eSPI::checkViewport(int32_t x, int32_t y, int32_t w, int32_t h) +{ + return checkAddrWindow(x, y, w, h); +} + +/*************************************************************************************** +** Function name: checkAddrWindow +** Description: Check if any part of specified area is visible in viewport +***************************************************************************************/ +// Note: Setting w and h to 1 will check if coordinate x,y is in area +bool TFT_eSPI::checkAddrWindow(int32_t x, int32_t y, int32_t w, int32_t h) { if (_vpOoB) return false; x+= _xDatum; @@ -200,9 +260,61 @@ bool TFT_eSPI::checkViewport(int32_t x, int32_t y, int32_t w, int32_t h) return true; } + +/*************************************************************************************** +** Function name: clipAddrWindow +** Description: Shift, check and crop specified screen area to the viewport +***************************************************************************************/ +bool TFT_eSPI::clipAddrWindow(int32_t *x, int32_t *y, int32_t *w, int32_t *h) +{ + if (_vpOoB) return false; // Area is outside of viewport + + *x+= _xDatum; + *y+= _yDatum; + + if ((*x >= _vpW) || (*y >= _vpH)) return false; // Area is outside of viewport + + // Crop drawing area bounds + if (*x < _vpX) { *w -= _vpX - *x; *x = _vpX; } + if (*y < _vpY) { *h -= _vpY - *y; *y = _vpY; } + + if ((*x + *w) > _vpW ) *w = _vpW - *x; + if ((*y + *h) > _vpH ) *h = _vpH - *y; + + if (*w < 1 || *h < 1) return false; // No area is inside viewport + + return true; // Area is wholly or partially inside viewport +} + +/*************************************************************************************** +** Function name: clipWindow +** Description: Shift, check and crop specified screen area to the viewport +***************************************************************************************/ +bool TFT_eSPI::clipWindow(int32_t *xs, int32_t *ys, int32_t *xe, int32_t *ye) +{ + if (_vpOoB) return false; // Area is outside of viewport + + *xs+= _xDatum; + *ys+= _yDatum; + *xe+= _xDatum; + *ye+= _yDatum; + + if ((*xs >= _vpW) || (*ys >= _vpH)) return false; // Area is outside of viewport + if ((*xe < _vpX) || (*ye < _vpY)) return false; // Area is outside of viewport + + // Crop drawing area bounds + if (*xs < _vpX) *xs = _vpX; + if (*ys < _vpY) *ys = _vpY; + + if (*xe > _vpW) *xe = _vpW; + if (*ye > _vpH) *ye = _vpH; + + return true; // Area is wholly or partially inside viewport +} + /*************************************************************************************** ** Function name: resetViewport -** Description: Reset viewport to whle TFT screen, datum at 0,0 +** Description: Reset viewport to whole TFT screen, datum at 0,0 ***************************************************************************************/ void TFT_eSPI::resetViewport(void) { @@ -318,49 +430,13 @@ void TFT_eSPI::frameViewport(uint16_t color, int32_t w) _vpDatum = _dT; } -/*************************************************************************************** -** Function name: end_tft_read (was called spi_end_read) -** Description: End transaction for reads and deselect TFT -***************************************************************************************/ -inline void TFT_eSPI::end_tft_read(void){ -#if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) && !defined(TFT_PARALLEL_8_BIT) - if(!inTransaction) { - if (!locked) { - locked = true; - CS_H; - spi.endTransaction(); - } - } -#else - #if !defined(TFT_PARALLEL_8_BIT) - spi.setFrequency(SPI_FREQUENCY); - #endif - if(!inTransaction) {CS_H;} -#endif - SET_BUS_WRITE_MODE; - -// The ST7796 appears to need a 4ms delay after a CGRAM read, otherwise subsequent writes will fail! -#ifdef ST7796_DRIVER - delay(4); -#endif -} - -/*************************************************************************************** -** Function name: Legacy - deprecated -** Description: Start/end transaction -***************************************************************************************/ - void TFT_eSPI::spi_begin() {begin_tft_write();} - void TFT_eSPI::spi_end() { end_tft_write();} - void TFT_eSPI::spi_begin_read() {begin_tft_read(); } - void TFT_eSPI::spi_end_read() { end_tft_read(); } /*************************************************************************************** ** Function name: TFT_eSPI -** Description: Constructor , we must use hardware SPI pins +** Description: Constructor ***************************************************************************************/ TFT_eSPI::TFT_eSPI(int16_t w, int16_t h) { - // The control pins are deliberately set to the inactive state (CS high) as setup() // might call and initialise other SPI peripherals which would could cause conflicts // if CS is floating or undefined. @@ -554,8 +630,6 @@ void TFT_eSPI::init(uint8_t tc) INIT_TFT_DATA_BUS; - - #ifdef TFT_CS // Set to output once again in case D6 (MISO) is used for CS pinMode(TFT_CS, OUTPUT); @@ -564,8 +638,6 @@ void TFT_eSPI::init(uint8_t tc) spi.setHwCs(1); // Use hardware SS toggling #endif - - // Set to output once again in case D6 (MISO) is used for DC #ifdef TFT_DC pinMode(TFT_DC, OUTPUT); @@ -679,7 +751,6 @@ void TFT_eSPI::init(uint8_t tc) ***************************************************************************************/ void TFT_eSPI::setRotation(uint8_t m) { - begin_tft_write(); // This loads the driver specific rotation code <<<<<<<<<<<<<<<<<<<<< ADD NEW DRIVERS TO THE LIST HERE <<<<<<<<<<<<<<<<<<<<<<< @@ -744,6 +815,15 @@ void TFT_eSPI::setRotation(uint8_t m) resetViewport(); } +/*************************************************************************************** +** Function name: getRotation +** Description: Return the rotation value (as used by setRotation()) +***************************************************************************************/ +uint8_t TFT_eSPI::getRotation(void) +{ + return rotation; +} + /*************************************************************************************** ** Function name: commandList, used for FLASH based lists only (e.g. ST7735) @@ -761,7 +841,7 @@ void TFT_eSPI::commandList (const uint8_t *addr) { writecommand(pgm_read_byte(addr++)); // Read, issue command numArgs = pgm_read_byte(addr++); // Number of args to follow - ms = numArgs & TFT_INIT_DELAY; // If hibit set, delay follows args + ms = numArgs & TFT_INIT_DELAY; // If hi bit set, delay follows args numArgs &= ~TFT_INIT_DELAY; // Mask out delay bit while (numArgs--) // For each argument... @@ -775,7 +855,6 @@ void TFT_eSPI::commandList (const uint8_t *addr) delay( (ms==255 ? 500 : ms) ); } } - } @@ -806,7 +885,6 @@ void TFT_eSPI::writecommand(uint8_t c) DC_D; end_tft_write(); - } @@ -835,22 +913,20 @@ void TFT_eSPI::writedata(uint8_t d) uint8_t TFT_eSPI::readcommand8(uint8_t cmd_function, uint8_t index) { uint8_t reg = 0; + #ifdef TFT_PARALLEL_8_BIT writecommand(cmd_function); // Sets DC and CS high - busDir(dir_mask, INPUT); - CS_L; // Read nth parameter (assumes caller discards 1st parameter or points index to 2nd) while(index--) reg = readByte(); - busDir(dir_mask, OUTPUT); - CS_H; #else // SPI interface + // Tested with ILI9341 set to Interface II i.e. IM [3:0] = "1101" begin_tft_read(); index = 0x10 + (index & 0x0F); @@ -866,7 +942,9 @@ uint8_t TFT_eSPI::readcommand8(uint8_t cmd_function, uint8_t index) reg = tft_Read_8(); end_tft_read(); + #endif + return reg; } @@ -903,6 +981,247 @@ uint32_t TFT_eSPI::readcommand32(uint8_t cmd_function, uint8_t index) } +/*************************************************************************************** +** Function name: setAddrWindow +** Description: define an area to receive a stream of pixels +***************************************************************************************/ +// Chip select is high at the end of this function +void TFT_eSPI::setAddrWindow(int32_t x, int32_t y, int32_t w, int32_t h) +{ + if (_vpOoB) return; + x+= _xDatum; + y+= _yDatum; + + if ((x >= _vpW) || (y >= _vpH)) return; + + if (x < _vpX) { w -= _vpX - x; x = _vpX; } + if (y < _vpY) { h -= _vpY - y; y = _vpY; } + + if ((x + w) > _vpW ) w = _vpW - x; + if ((y + h) > _vpH ) h = _vpH - y; + + if (w < 1 || h < 1) return; + + begin_tft_write(); + + setWindow(x, y, x + w - 1, y + h - 1); + + end_tft_write(); +} + + +/*************************************************************************************** +** Function name: setWindow +** Description: define an area to receive a stream of pixels +***************************************************************************************/ +// Chip select stays low, call begin_tft_write first. Use setAddrWindow() from sketches +inline void TFT_eSPI::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) +{ + //begin_tft_read(); // Must be called before setWindow +#if defined (ILI9225_DRIVER) + if (rotation & 0x01) { swap_coord(x0, y0); swap_coord(x1, y1); } + + addr_row = 0xFFFF; + addr_col = 0xFFFF; + + SPI_BUSY_CHECK; + DC_C; tft_Write_8(TFT_CASET1); + DC_D; tft_Write_16(x0); + DC_C; tft_Write_8(TFT_CASET2); + DC_D; tft_Write_16(x1); + + DC_C; tft_Write_8(TFT_PASET1); + DC_D; tft_Write_16(y0); + DC_C; tft_Write_8(TFT_PASET2); + DC_D; tft_Write_16(y1); + + DC_C; tft_Write_8(TFT_RAM_ADDR1); + DC_D; tft_Write_16(x0); + DC_C; tft_Write_8(TFT_RAM_ADDR2); + DC_D; tft_Write_16(y0); + + // write to RAM + DC_C; tft_Write_8(TFT_RAMWR); + DC_D; + +#else + +#if defined (SSD1963_DRIVER) + if ((rotation & 0x1) == 0) { swap_coord(x0, y0); swap_coord(x1, y1); } +#endif + + addr_row = 0xFFFF; + addr_col = 0xFFFF; + +#ifdef CGRAM_OFFSET + x0+=colstart; + x1+=colstart; + y0+=rowstart; + y1+=rowstart; +#endif + SPI_BUSY_CHECK; + DC_C; tft_Write_8(TFT_CASET); + DC_D; tft_Write_32C(x0, x1); + DC_C; tft_Write_8(TFT_PASET); + DC_D; tft_Write_32C(y0, y1); + DC_C; tft_Write_8(TFT_RAMWR); + DC_D; + + //end_tft_read(); // Must be called after setWindow +#endif +} + +/*************************************************************************************** +** Function name: readAddrWindow +** Description: define an area to read a stream of pixels +***************************************************************************************/ +void TFT_eSPI::readAddrWindow(int32_t xs, int32_t ys, int32_t w, int32_t h) +{ + //begin_tft_read(); // Must be called before readAddrWindow or CS set low + + if (_vpOoB) return; + xs+= _xDatum; + ys+= _yDatum; + + if ((xs >= _vpW) || (ys >= _vpH)) return; + + if (xs < _vpX) { w -= _vpX - xs; xs = _vpX; } + if (ys < _vpY) { h -= _vpY - ys; ys = _vpY; } + + if ((xs + w) > _vpW ) w = _vpW - xs; + if ((ys + h) > _vpH ) h = _vpH - ys; + + if (w < 1 || h < 1) return; + + int32_t xe = xs + w - 1; + int32_t ye = ys + h - 1; + + addr_col = 0xFFFF; + addr_row = 0xFFFF; + +#ifdef CGRAM_OFFSET + xs += colstart; + xe += colstart; + ys += rowstart; + ye += rowstart; +#endif + +#if defined (SSD1963_DRIVER) + if ((rotation & 0x1) == 0) { swap_coord(xs, ys); swap_coord(xe, ye); } +#endif + + // Column addr set + DC_C; tft_Write_8(TFT_CASET); + DC_D; tft_Write_32C(xs, xe); + + // Row addr set + DC_C; tft_Write_8(TFT_PASET); + DC_D; tft_Write_32C(ys, ye); + + // Read CGRAM command + DC_C; tft_Write_8(TFT_RAMRD); + + DC_D; + + //end_tft_read(); // Must be called after readAddrWindow or CS set high +} + + +/*************************************************************************************** +** Function name: drawPixel +** Description: push a single pixel at an arbitrary position +***************************************************************************************/ +void TFT_eSPI::drawPixel(int32_t x, int32_t y, uint32_t color) +{ + if (_vpOoB) return; + + x+= _xDatum; + y+= _yDatum; + + // Range checking + if ((x < _vpX) || (y < _vpY) ||(x >= _vpW) || (y >= _vpH)) return; + +#ifdef CGRAM_OFFSET + x+=colstart; + y+=rowstart; +#endif + + begin_tft_write(); + +#if defined (ILI9225_DRIVER) + + if (rotation & 0x01) { swap_coord(x, y); } + + SPI_BUSY_CHECK; + + // Set window to full screen to optimise sequential pixel rendering + if (addr_row != 0x9225) { + addr_row = 0x9225; // addr_row used for flag + DC_C; tft_Write_8(TFT_CASET1); + DC_D; tft_Write_16(0); + DC_C; tft_Write_8(TFT_CASET2); + DC_D; tft_Write_16(175); + + DC_C; tft_Write_8(TFT_PASET1); + DC_D; tft_Write_16(0); + DC_C; tft_Write_8(TFT_PASET2); + DC_D; tft_Write_16(219); + } + + // Define pixel coordinate + DC_C; tft_Write_8(TFT_RAM_ADDR1); + DC_D; tft_Write_16(x); + DC_C; tft_Write_8(TFT_RAM_ADDR2); + DC_D; tft_Write_16(y); + + // write to RAM + DC_C; tft_Write_8(TFT_RAMWR); + #if defined(TFT_PARALLEL_8_BIT) + DC_D; tft_Write_16(color); + #else + DC_D; tft_Write_16N(color); + #endif +#else + + #if defined (SSD1963_DRIVER) + if ((rotation & 0x1) == 0) { swap_coord(x, y); } + #endif + + SPI_BUSY_CHECK; + + #ifdef MULTI_TFT_SUPPORT + // No optimisation + DC_C; tft_Write_8(TFT_CASET); + DC_D; tft_Write_32D(x); + DC_C; tft_Write_8(TFT_PASET); + DC_D; tft_Write_32D(y); + #else + + // No need to send x if it has not changed (speeds things up) + if (addr_col != x) { + DC_C; tft_Write_8(TFT_CASET); + DC_D; tft_Write_32D(x); + addr_col = x; + } + + // No need to send y if it has not changed (speeds things up) + if (addr_row != y) { + DC_C; tft_Write_8(TFT_PASET); + DC_D; tft_Write_32D(y); + addr_row = y; + } + #endif + DC_C; tft_Write_8(TFT_RAMWR); + #if defined(TFT_PARALLEL_8_BIT) + DC_D; tft_Write_16(color); + #else + DC_D; tft_Write_16N(color); + #endif +#endif + + end_tft_write(); +} + /*************************************************************************************** ** Function name: read pixel (for SPI Interface II i.e. IM [3:0] = "1101") ** Description: Read 565 pixel colours from a pixel @@ -1027,14 +1346,97 @@ uint16_t TFT_eSPI::readPixel(int32_t x0, int32_t y0) #endif } +/*************************************************************************************** +** Function name: setCallback +** Description: Sets the callback to get a pixel colour from sketch +***************************************************************************************/ void TFT_eSPI::setCallback(getColorCallback getCol) { getColor = getCol; } +/*************************************************************************************** +** Function name: read rectangle +** Description: Read RGB pixel colours from a defined area +***************************************************************************************/ +// If w and h are 1, then 1 pixel is read, *data array size must be 3 bytes per pixel +void TFT_eSPI::readRectRGB(int32_t x0, int32_t y0, int32_t w, int32_t h, uint8_t *data) +{ +#if defined(TFT_PARALLEL_8_BIT) + + uint32_t len = w * h; + uint8_t* buf565 = data + len; + + readRect(x0, y0, w, h, (uint16_t*)buf565); + + while (len--) { + uint16_t pixel565 = (*buf565++)<<8; + pixel565 |= *buf565++; + uint8_t red = (pixel565 & 0xF800) >> 8; red |= red >> 5; + uint8_t green = (pixel565 & 0x07E0) >> 3; green |= green >> 6; + uint8_t blue = (pixel565 & 0x001F) << 3; blue |= blue >> 5; + *data++ = red; + *data++ = green; + *data++ = blue; + } + +#else // Not TFT_PARALLEL_8_BIT + + // This function can get called during after a startWrite() + // so a transaction may be in progress + bool wasInTransaction = inTransaction; + if (inTransaction) { inTransaction= false; end_tft_write();} + + begin_tft_read(); + + readAddrWindow(x0, y0, w, h); // Sets CS low + + #ifdef TFT_SDA_READ + begin_SDA_Read(); + #endif + + // Dummy read to throw away don't care value + tft_Read_8(); + + // Read window pixel 24 bit RGB values, buffer must be set in sketch to 3 * w * h + uint32_t len = w * h; + while (len--) { + + #if !defined (ILI9488_DRIVER) + + // Read the 3 RGB bytes, colour is actually only in the top 6 bits of each byte + // as the TFT stores colours as 18 bits + *data++ = tft_Read_8(); + *data++ = tft_Read_8(); + *data++ = tft_Read_8(); + + #else + + // The 6 colour bits are in MS 6 bits of each byte, but the ILI9488 needs an extra clock pulse + // so bits appear shifted right 1 bit, so mask the middle 6 bits then shift 1 place left + *data++ = (tft_Read_8()&0x7E)<<1; + *data++ = (tft_Read_8()&0x7E)<<1; + *data++ = (tft_Read_8()&0x7E)<<1; + + #endif + + } + + CS_H; + + #ifdef TFT_SDA_READ + end_SDA_Read(); + #endif + + end_tft_read(); + + // Reinstate the transaction if one was in progress + if(wasInTransaction) { begin_tft_write(); inTransaction = true; } +#endif +} /*************************************************************************************** -** Function name: read rectangle (for SPI Interface II i.e. IM [3:0] = "1101") +** Function name: read rectangle ** Description: Read 565 pixel colours from a defined area ***************************************************************************************/ void TFT_eSPI::readRect(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *data) @@ -1121,6 +1523,11 @@ void TFT_eSPI::readRect(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *da uint16_t color = 0; + // This function can get called after a startWrite() + // so a transaction may be in progress + bool wasInTransaction = inTransaction; + if (inTransaction) { inTransaction= false; end_tft_write();} + begin_tft_read(); readAddrWindow(x, y, dw, dh); @@ -1178,6 +1585,8 @@ void TFT_eSPI::readRect(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *da end_tft_read(); + // Reinstate the transaction if one was in progress + if(wasInTransaction) { begin_tft_write(); inTransaction = true; } #endif } @@ -1241,7 +1650,7 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *d uint16_t lineBuf[dw]; // Use buffer to minimise setWindow call count - // The little endian transp color must be byte swapped if the image is big endian + // The little endian transp colour must be byte swapped if the image is big endian if (!_swapBytes) transp = transp >> 8 | transp << 8; while (dh--) @@ -1282,7 +1691,6 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t *d end_tft_write(); } - /*************************************************************************************** ** Function name: pushImage - for FLASH (PROGMEM) stored images ** Description: plot 16 bit image @@ -1331,7 +1739,7 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint1 uint16_t lineBuf[dw]; - // The little endian transp color must be byte swapped if the image is big endian + // The little endian transp colour must be byte swapped if the image is big endian if (!_swapBytes) transp = transp >> 8 | transp << 8; while (dh--) { @@ -1369,7 +1777,6 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, const uint1 end_tft_write(); } - /*************************************************************************************** ** Function name: pushImage ** Description: plot 8 bit or 4 bit or 1 bit image or sprite using a line buffer @@ -1502,7 +1909,6 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *da end_tft_write(); } - /*************************************************************************************** ** Function name: pushImage ** Description: plot 8 or 4 or 1 bit image or sprite with a transparent colour @@ -1576,7 +1982,7 @@ void TFT_eSPI::pushImage(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t *da data += w; } } - else if (cmap != nullptr) // 4bpp with color map + else if (cmap != nullptr) // 4bpp with colour map { _swapBytes = true; @@ -1714,7 +2120,6 @@ void TFT_eSPI::setSwapBytes(bool swap) _swapBytes = swap; } - /*************************************************************************************** ** Function name: getSwapBytes ** Description: Return the swap byte order for colours @@ -1726,340 +2131,24 @@ bool TFT_eSPI::getSwapBytes(void) /*************************************************************************************** -** Function name: read rectangle (for SPI Interface II i.e. IM [3:0] = "1101") -** Description: Read RGB pixel colours from a defined area +** Function name: width +** Description: Return the pixel width of display (per current rotation) ***************************************************************************************/ -// If w and h are 1, then 1 pixel is read, *data array size must be 3 bytes per pixel -void TFT_eSPI::readRectRGB(int32_t x0, int32_t y0, int32_t w, int32_t h, uint8_t *data) +// Return the size of the display (per current rotation) +int16_t TFT_eSPI::width(void) { -#if defined(TFT_PARALLEL_8_BIT) - - uint32_t len = w * h; - uint8_t* buf565 = data + len; - - readRect(x0, y0, w, h, (uint16_t*)buf565); - - while (len--) { - uint16_t pixel565 = (*buf565++)<<8; - pixel565 |= *buf565++; - uint8_t red = (pixel565 & 0xF800) >> 8; red |= red >> 5; - uint8_t green = (pixel565 & 0x07E0) >> 3; green |= green >> 6; - uint8_t blue = (pixel565 & 0x001F) << 3; blue |= blue >> 5; - *data++ = red; - *data++ = green; - *data++ = blue; - } - -#else // Not TFT_PARALLEL_8_BIT - - begin_tft_read(); - - readAddrWindow(x0, y0, w, h); // Sets CS low - - #ifdef TFT_SDA_READ - begin_SDA_Read(); - #endif - - // Dummy read to throw away don't care value - tft_Read_8(); - - // Read window pixel 24 bit RGB values, buffer must be set in sketch to 3 * w * h - uint32_t len = w * h; - while (len--) { - - #if !defined (ILI9488_DRIVER) - - // Read the 3 RGB bytes, colour is actually only in the top 6 bits of each byte - // as the TFT stores colours as 18 bits - *data++ = tft_Read_8(); - *data++ = tft_Read_8(); - *data++ = tft_Read_8(); - - #else - - // The 6 colour bits are in MS 6 bits of each byte, but the ILI9488 needs an extra clock pulse - // so bits appear shifted right 1 bit, so mask the middle 6 bits then shift 1 place left - *data++ = (tft_Read_8()&0x7E)<<1; - *data++ = (tft_Read_8()&0x7E)<<1; - *data++ = (tft_Read_8()&0x7E)<<1; - - #endif - - } - - CS_H; - - #ifdef TFT_SDA_READ - end_SDA_Read(); - #endif - - end_tft_read(); - -#endif -} - - -/*************************************************************************************** -** Function name: drawCircle -** Description: Draw a circle outline -***************************************************************************************/ -// Optimised midpoint circle algorithm -void TFT_eSPI::drawCircle(int32_t x0, int32_t y0, int32_t r, uint32_t color) -{ - int32_t x = 1; - int32_t dx = 1; - int32_t dy = r+r; - int32_t p = -(r>>1); - - //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() - inTransaction = true; - - // These are ordered to minimise coordinate changes in x or y - // drawPixel can then send fewer bounding box commands - drawPixel(x0 + r, y0, color); - drawPixel(x0 - r, y0, color); - drawPixel(x0, y0 - r, color); - drawPixel(x0, y0 + r, color); - - while(x=0) { - dy-=2; - p-=dy; - r--; - } - - dx+=2; - p+=dx; - - // These are ordered to minimise coordinate changes in x or y - // drawPixel can then send fewer bounding box commands - drawPixel(x0 + x, y0 + r, color); - drawPixel(x0 - x, y0 + r, color); - drawPixel(x0 - x, y0 - r, color); - drawPixel(x0 + x, y0 - r, color); - if (r != x) { - drawPixel(x0 + r, y0 + x, color); - drawPixel(x0 - r, y0 + x, color); - drawPixel(x0 - r, y0 - x, color); - drawPixel(x0 + r, y0 - x, color); - } - x++; - } - - inTransaction = false; - end_tft_write(); // Does nothing if Sprite class uses this function -} - - -/*************************************************************************************** -** Function name: drawCircleHelper -** Description: Support function for drawRoundRect() -***************************************************************************************/ -void TFT_eSPI::drawCircleHelper( int32_t x0, int32_t y0, int32_t r, uint8_t cornername, uint32_t color) -{ - int32_t f = 1 - r; - int32_t ddF_x = 1; - int32_t ddF_y = -2 * r; - int32_t x = 0; - - while (x < r) { - if (f >= 0) { - r--; - ddF_y += 2; - f += ddF_y; - } - x++; - ddF_x += 2; - f += ddF_x; - if (cornername & 0x4) { - drawPixel(x0 + x, y0 + r, color); - drawPixel(x0 + r, y0 + x, color); - } - if (cornername & 0x2) { - drawPixel(x0 + x, y0 - r, color); - drawPixel(x0 + r, y0 - x, color); - } - if (cornername & 0x8) { - drawPixel(x0 - r, y0 + x, color); - drawPixel(x0 - x, y0 + r, color); - } - if (cornername & 0x1) { - drawPixel(x0 - r, y0 - x, color); - drawPixel(x0 - x, y0 - r, color); - } - } -} - - -/*************************************************************************************** -** Function name: fillCircle -** Description: draw a filled circle -***************************************************************************************/ -// Optimised midpoint circle algorithm, changed to horizontal lines (faster in sprites) -// Improved algorithm avoids repetition of lines -void TFT_eSPI::fillCircle(int32_t x0, int32_t y0, int32_t r, uint32_t color) -{ - int32_t x = 0; - int32_t dx = 1; - int32_t dy = r+r; - int32_t p = -(r>>1); - - //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() - inTransaction = true; - - drawFastHLine(x0 - r, y0, dy+1, color); - - while(x=0) { - drawFastHLine(x0 - x, y0 + r, dx, color); - drawFastHLine(x0 - x, y0 - r, dx, color); - dy-=2; - p-=dy; - r--; - } - - dx+=2; - p+=dx; - x++; - - drawFastHLine(x0 - r, y0 + x, dy+1, color); - drawFastHLine(x0 - r, y0 - x, dy+1, color); - - } - - inTransaction = false; - end_tft_write(); // Does nothing if Sprite class uses this function + if (_vpDatum) return _xWidth; + return _width; } /*************************************************************************************** -** Function name: fillCircleHelper -** Description: Support function for fillRoundRect() +** Function name: height +** Description: Return the pixel height of display (per current rotation) ***************************************************************************************/ -// Support drawing roundrects, changed to horizontal lines (faster in sprites) -void TFT_eSPI::fillCircleHelper(int32_t x0, int32_t y0, int32_t r, uint8_t cornername, int32_t delta, uint32_t color) +int16_t TFT_eSPI::height(void) { - int32_t f = 1 - r; - int32_t ddF_x = 1; - int32_t ddF_y = -r - r; - int32_t y = 0; - - delta++; - - while (y < r) { - if (f >= 0) { - if (cornername & 0x1) drawFastHLine(x0 - y, y0 + r, y + y + delta, color); - if (cornername & 0x2) drawFastHLine(x0 - y, y0 - r, y + y + delta, color); - r--; - ddF_y += 2; - f += ddF_y; - } - - y++; - ddF_x += 2; - f += ddF_x; - - if (cornername & 0x1) drawFastHLine(x0 - r, y0 + y, r + r + delta, color); - if (cornername & 0x2) drawFastHLine(x0 - r, y0 - y, r + r + delta, color); - } -} - - -/*************************************************************************************** -** Function name: drawEllipse -** Description: Draw a ellipse outline -***************************************************************************************/ -void TFT_eSPI::drawEllipse(int16_t x0, int16_t y0, int32_t rx, int32_t ry, uint16_t color) -{ - if (rx<2) return; - if (ry<2) return; - int32_t x, y; - int32_t rx2 = rx * rx; - int32_t ry2 = ry * ry; - int32_t fx2 = 4 * rx2; - int32_t fy2 = 4 * ry2; - int32_t s; - - //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() - inTransaction = true; - - for (x = 0, y = ry, s = 2*ry2+rx2*(1-2*ry); ry2*x <= rx2*y; x++) { - // These are ordered to minimise coordinate changes in x or y - // drawPixel can then send fewer bounding box commands - drawPixel(x0 + x, y0 + y, color); - drawPixel(x0 - x, y0 + y, color); - drawPixel(x0 - x, y0 - y, color); - drawPixel(x0 + x, y0 - y, color); - if (s >= 0) { - s += fx2 * (1 - y); - y--; - } - s += ry2 * ((4 * x) + 6); - } - - for (x = rx, y = 0, s = 2*rx2+ry2*(1-2*rx); rx2*y <= ry2*x; y++) { - // These are ordered to minimise coordinate changes in x or y - // drawPixel can then send fewer bounding box commands - drawPixel(x0 + x, y0 + y, color); - drawPixel(x0 - x, y0 + y, color); - drawPixel(x0 - x, y0 - y, color); - drawPixel(x0 + x, y0 - y, color); - if (s >= 0) - { - s += fy2 * (1 - x); - x--; - } - s += rx2 * ((4 * y) + 6); - } - - inTransaction = false; - end_tft_write(); // Does nothing if Sprite class uses this function -} - - -/*************************************************************************************** -** Function name: fillEllipse -** Description: draw a filled ellipse -***************************************************************************************/ -void TFT_eSPI::fillEllipse(int16_t x0, int16_t y0, int32_t rx, int32_t ry, uint16_t color) -{ - if (rx<2) return; - if (ry<2) return; - int32_t x, y; - int32_t rx2 = rx * rx; - int32_t ry2 = ry * ry; - int32_t fx2 = 4 * rx2; - int32_t fy2 = 4 * ry2; - int32_t s; - - //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() - inTransaction = true; - - for (x = 0, y = ry, s = 2*ry2+rx2*(1-2*ry); ry2*x <= rx2*y; x++) { - drawFastHLine(x0 - x, y0 - y, x + x + 1, color); - drawFastHLine(x0 - x, y0 + y, x + x + 1, color); - - if (s >= 0) { - s += fx2 * (1 - y); - y--; - } - s += ry2 * ((4 * x) + 6); - } - - for (x = rx, y = 0, s = 2*rx2+ry2*(1-2*rx); rx2*y <= ry2*x; y++) { - drawFastHLine(x0 - x, y0 - y, x + x + 1, color); - drawFastHLine(x0 - x, y0 + y, x + x + 1, color); - - if (s >= 0) { - s += fy2 * (1 - x); - x--; - } - s += rx2 * ((4 * y) + 6); - } - - inTransaction = false; - end_tft_write(); // Does nothing if Sprite class uses this function + if (_vpDatum) return _yHeight; + return _height; } @@ -2073,6 +2162,402 @@ void TFT_eSPI::fillScreen(uint32_t color) } +/*************************************************************************************** +** Function name: drawLine +** Description: draw a line between 2 arbitrary points +***************************************************************************************/ +// Bresenham's algorithm - thx wikipedia - speed enhanced by Bodmer to use +// an efficient FastH/V Line draw routine for line segments of 2 pixels or more +void TFT_eSPI::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color) +{ + if (_vpOoB) return; + + //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() + inTransaction = true; + + //x+= _xDatum; // Not added here, added by drawPixel & drawFastXLine + //y+= _yDatum; + + bool steep = abs(y1 - y0) > abs(x1 - x0); + if (steep) { + swap_coord(x0, y0); + swap_coord(x1, y1); + } + + if (x0 > x1) { + swap_coord(x0, x1); + swap_coord(y0, y1); + } + + int32_t dx = x1 - x0, dy = abs(y1 - y0);; + + int32_t err = dx >> 1, ystep = -1, xs = x0, dlen = 0; + + if (y0 < y1) ystep = 1; + + // Split into steep and not steep for FastH/V separation + if (steep) { + for (; x0 <= x1; x0++) { + dlen++; + err -= dy; + if (err < 0) { + if (dlen == 1) drawPixel(y0, xs, color); + else drawFastVLine(y0, xs, dlen, color); + dlen = 0; + y0 += ystep; xs = x0 + 1; + err += dx; + } + } + if (dlen) drawFastVLine(y0, xs, dlen, color); + } + else + { + for (; x0 <= x1; x0++) { + dlen++; + err -= dy; + if (err < 0) { + + if (dlen == 1) drawPixel(xs, y0, color); + else drawFastHLine(xs, y0, dlen, color); + dlen = 0; + y0 += ystep; xs = x0 + 1; + err += dx; + } + } + if (dlen) drawFastHLine(xs, y0, dlen, color); + } + + inTransaction = false; + end_tft_write(); +} + + +/*************************************************************************************** +** Description: Constants for anti-aliased line drawing +***************************************************************************************/ +constexpr float PixelAlphaGain = 255.0; +constexpr float LoAlphaTheshold = 64.0/255.0; +constexpr float HiAlphaTheshold = 1.0 - LoAlphaTheshold; + +/*************************************************************************************** +** Function name: drawWideLine - background colour specified +** Description: draw an anti-aliased line with rounded ends, width 2*r +***************************************************************************************/ +void TFT_eSPI::drawWideLine(float ax, float ay, float bx, float by, float wd, uint16_t fg_color, uint16_t bg_color) +{ + if ( (abs(ax - bx) < 0.01f) && (abs(ay - by) < 0.01f) ) bx += 0.01f; // Avoid divide by zero + + wd = wd / 2.0; // wd is now end radius of line + + // Find line bounding box + int32_t x0 = (int32_t)floorf(fminf(ax, bx) - wd); + int32_t x1 = (int32_t) ceilf(fmaxf(ax, bx) + wd); + int32_t y0 = (int32_t)floorf(fminf(ay, by) - wd); + int32_t y1 = (int32_t) ceilf(fmaxf(ay, by) + wd); + + if (!clipWindow(&x0, &y0, &x1, &y1)) return; + + // Establish slope direction + int32_t xs = x0, yp = y1, yinc = -1; + if ((ax>bx && ay>by) || (ax (y0+ri)) && (xp > xs)) xs = xp; } + if (alpha > HiAlphaTheshold) { drawPixel(xp, yp, fg_color); continue; } + //Blend colour with background and plot + drawPixel(xp, yp, alphaBlend((uint8_t)(alpha * PixelAlphaGain), fg_color, bg_color)); + } + yp+=yinc; + } + + inTransaction = false; + end_tft_write(); +} + + +/*************************************************************************************** +** Function name: drawWideLine - background colour obtained via pixel read +** Description: draw an anti-aliased line with radiused ends, width wd +***************************************************************************************/ +// For sprites and also TFT screens where the background pixel colours can be read +void TFT_eSPI::drawWideLine(float ax, float ay, float bx, float by, float wd, uint16_t fg_color) +{ + if ( (abs(ax - bx) < 0.01f) && (abs(ay - by) < 0.01f) ) bx += 0.01f; // Avoid divide by zero + + wd = wd / 2.0; // wd is now end radius of line + + // Find line bounding box + int32_t x0 = (int32_t)floorf(fminf(ax, bx) - wd); + int32_t x1 = (int32_t) ceilf(fmaxf(ax, bx) + wd); + int32_t y0 = (int32_t)floorf(fminf(ay, by) - wd); + int32_t y1 = (int32_t) ceilf(fmaxf(ay, by) + wd); + + if (!clipWindow(&x0, &y0, &x1, &y1)) return; + + // Establish slope direction + int32_t xs = x0, yp = y1, yinc = -1; + if ((ax>bx && ay>by) || (ax (y0+ri)) && (xp > xs)) xs = xp; } + if (alpha > HiAlphaTheshold) { drawPixel(xp, yp, fg_color); continue; } + //Blend colour with background and plot + drawPixel(xp, yp, alphaBlend((uint8_t)(alpha * PixelAlphaGain), fg_color, readPixel(xp, yp))); + } + yp+=yinc; + } + + inTransaction = false; + end_tft_write(); +} + +// Calculate distance of px,py to closest part of line +// ESP32 has FPU so floats are quite quick +/*************************************************************************************** +** Function name: lineDistance - private helper function for drawWideLine +** Description: returns distance of px,py to closest part of a to b line +***************************************************************************************/ +float TFT_eSPI::wideLineDistance(float pax, float pay, float bax, float bay, float dr) +{ + float h = fmaxf(fminf((pax * bax + pay * bay) / (bax * bax + bay * bay), 1.0f), 0.0f); + float dx = pax - bax * h, dy = pay - bay * h; + dx = dx * dx + dy * dy; + if (dx < dr) return 0.0f; + return sqrtf(dx); +} + +/*************************************************************************************** +** Function name: drawWedgeLine +** Description: draw an anti-aliased line with different width radiused ends +***************************************************************************************/ +// For sprites and TFT screens where the background pixel colour is fixed +void TFT_eSPI::drawWedgeLine(float ax, float ay, float bx, float by, float aw, float bw, uint16_t fg_color, uint16_t bg_color) +{ + + if ( (abs(ax - bx) < 0.01f) && (abs(ay - by) < 0.01f) ) bx += 0.01f; // Avoid divide by zero + + // Find line bounding box + int32_t x0 = (int32_t)floorf(fminf(ax-aw, bx-bw)); + int32_t x1 = (int32_t) ceilf(fmaxf(ax+aw, bx+bw)); + int32_t y0 = (int32_t)floorf(fminf(ay-aw, by-bw)); + int32_t y1 = (int32_t) ceilf(fmaxf(ay+aw, by+bw)); + + if (!clipWindow(&x0, &y0, &x1, &y1)) return; + + // Establish slope direction + int32_t xs = x0, yp = y1, yinc = -1; + if (((ax-aw)>(bx-bw) && (ay>by)) || ((ax-aw)<(bx-bw) && ay (y0+ri)) && (xp > xs)) xs = xp; } + if (alpha > HiAlphaTheshold) { drawPixel(xp, yp, fg_color); continue; } + //Blend color with background and plot + drawPixel(xp, yp, alphaBlend((uint8_t)(alpha * PixelAlphaGain), fg_color, bg_color)); + } + yp+=yinc; + } + + inTransaction = false; + end_tft_write(); +} + +/*************************************************************************************** +** Function name: drawWedgeLine +** Description: draw an anti-aliased line with different width radiused ends +***************************************************************************************/ +// For sprites and TFT screens where the background pixel colours can be read +void TFT_eSPI::drawWedgeLine(float ax, float ay, float bx, float by, float aw, float bw, uint16_t fg_color) +{ + if ( (abs(ax - bx) < 0.01f) && (abs(ay - by) < 0.01f) ) bx += 0.01f; // Avoid divide by zero + + // Find line bounding box + int32_t x0 = (int32_t)floorf(fminf(ax-aw, bx-bw)); + int32_t x1 = (int32_t) ceilf(fmaxf(ax+aw, bx+bw)); + int32_t y0 = (int32_t)floorf(fminf(ay-aw, by-bw)); + int32_t y1 = (int32_t) ceilf(fmaxf(ay+aw, by+bw)); + + if (!clipWindow(&x0, &y0, &x1, &y1)) return; + + // Establish slope direction + int32_t xs = x0, yp = y1, yinc = -1; + if (((ax-aw)>(bx-bw) && (ay>by)) || ((ax-aw)<(bx-bw) && ay (y0+ri)) && (xp > xs)) xs = xp; } + if (alpha > HiAlphaTheshold) { drawPixel(xp, yp, fg_color); continue; } + //Blend color with background and plot + drawPixel(xp, yp, alphaBlend((uint8_t)(alpha * PixelAlphaGain), fg_color, readPixel(xp, yp))); + } + yp+=yinc; + } + + inTransaction = false; + end_tft_write(); +} + +// Calculate distance of px,py to closest part of line +// ESP32 has FPU so floats are quite quick +/*************************************************************************************** +** Function name: lineDistance - private helper function for drawWedgeLine +** Description: returns distance of px,py to closest part of a to b wedge +***************************************************************************************/ +float TFT_eSPI::wedgeLineDistance(float pax, float pay, float bax, float bay, float dr) +{ + float h = fmaxf(fminf((pax * bax + pay * bay) / (bax * bax + bay * bay), 1.0f), 0.0f); + float dx = pax - bax * h, dy = pay - bay * h; + return sqrtf(dx * dx + dy * dy) + h * dr; +} + + +/*************************************************************************************** +** Function name: drawSpot - maths intensive, so for small filled circles +** Description: Draw an anti-aliased filled circle at ax,ay with radius r +***************************************************************************************/ + // For TFT screens where the background pixel colours cannot be read +void TFT_eSPI::drawSpot(float ax, float ay, float r, uint16_t fg_color, uint16_t bg_color) +{ + // Filled circle can be created by the wide line function with length zero + drawWideLine( ax, ay, ax, ay, r, fg_color, bg_color); +} + +/*************************************************************************************** +** Function name: lineDistance - helper function for wedge line +** Description: returns distance of px,py to closest part of a to b wedge +***************************************************************************************/ + // For sprites and also TFT screens where the background pixel colours can be read +void TFT_eSPI::drawSpot(float ax, float ay, float r, uint16_t fg_color) +{ + // Filled circle can be created by the wide line function with length zero + drawWideLine( ax, ay, ax, ay, r, fg_color); +} + +/*************************************************************************************** +** Function name: drawFastVLine +** Description: draw a vertical line +***************************************************************************************/ +void TFT_eSPI::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color) +{ + if (_vpOoB) return; + + x+= _xDatum; + y+= _yDatum; + + // Clipping + if ((x < _vpX) || (x >= _vpW) || (y >= _vpH)) return; + + if (y < _vpY) { h += y - _vpY; y = _vpY; } + + if ((y + h) > _vpH) h = _vpH - y; + + if (h < 1) return; + + begin_tft_write(); + + setWindow(x, y, x, y + h - 1); + + pushBlock(color, h); + + end_tft_write(); +} + + +/*************************************************************************************** +** Function name: drawFastHLine +** Description: draw a horizontal line +***************************************************************************************/ +void TFT_eSPI::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) +{ + if (_vpOoB) return; + + x+= _xDatum; + y+= _yDatum; + + // Clipping + if ((y < _vpY) || (x >= _vpW) || (y >= _vpH)) return; + + if (x < _vpX) { w += x - _vpX; x = _vpX; } + + if ((x + w) > _vpW) w = _vpW - x; + + if (w < 1) return; + + begin_tft_write(); + + setWindow(x, y, x + w - 1, y); + + pushBlock(color, w); + + end_tft_write(); +} + /*************************************************************************************** ** Function name: drawRect ** Description: Draw a rectangle outline @@ -2093,6 +2578,36 @@ void TFT_eSPI::drawRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t col end_tft_write(); // Does nothing if Sprite class uses this function } +/*************************************************************************************** +** Function name: fillRect +** Description: draw a filled rectangle +***************************************************************************************/ +void TFT_eSPI::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) +{ + if (_vpOoB) return; + + x+= _xDatum; + y+= _yDatum; + + // Clipping + if ((x >= _vpW) || (y >= _vpH)) return; + + if (x < _vpX) { w += x - _vpX; x = _vpX; } + if (y < _vpY) { h += y - _vpY; y = _vpY; } + + if ((x + w) > _vpW) w = _vpW - x; + if ((y + h) > _vpH) h = _vpH - y; + + if ((w < 1) || (h < 1)) return; + + begin_tft_write(); + + setWindow(x, y, x + w - 1, y + h - 1); + + pushBlock(color, w * h); + + end_tft_write(); +} /*************************************************************************************** ** Function name: drawRoundRect @@ -2119,7 +2634,6 @@ void TFT_eSPI::drawRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t end_tft_write(); // Does nothing if Sprite class uses this function } - /*************************************************************************************** ** Function name: fillRoundRect ** Description: Draw a rounded corner filled rectangle @@ -2141,7 +2655,6 @@ void TFT_eSPI::fillRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t end_tft_write(); // Does nothing if Sprite class uses this function } - /*************************************************************************************** ** Function name: drawTriangle ** Description: Draw a triangle outline using 3 arbitrary points @@ -2160,7 +2673,6 @@ void TFT_eSPI::drawTriangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int3 end_tft_write(); // Does nothing if Sprite class uses this function } - /*************************************************************************************** ** Function name: fillTriangle ** Description: Draw a filled triangle using 3 arbitrary points @@ -2235,12 +2747,323 @@ void TFT_eSPI::fillTriangle ( int32_t x0, int32_t y0, int32_t x1, int32_t y1, in if (a > b) swap_coord(a, b); drawFastHLine(a, y, b - a + 1, color); + } inTransaction = false; end_tft_write(); // Does nothing if Sprite class uses this function } +/*************************************************************************************** +** Function name: drawCircle +** Description: Draw a circle outline +***************************************************************************************/ +void TFT_eSPI::drawCircle(int32_t x0, int32_t y0, int32_t r, uint32_t color) +{ + if ( r <= 0 ) return; + + //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() + inTransaction = true; + + int32_t f = 1 - r; + int32_t ddF_y = -2 * r; + int32_t ddF_x = 1; + int32_t xs = -1; + int32_t xe = 0; + int32_t len = 0; + + bool first = true; + do { + while (f < 0) { + ++xe; + f += (ddF_x += 2); + } + f += (ddF_y += 2); + + if (xe-xs>1) { + if (first) { + len = 2*(xe - xs)-1; + drawFastHLine(x0 - xe, y0 + r, len, color); + drawFastHLine(x0 - xe, y0 - r, len, color); + drawFastVLine(x0 + r, y0 - xe, len, color); + drawFastVLine(x0 - r, y0 - xe, len, color); + first = false; + } + else { + len = xe - xs++; + drawFastHLine(x0 - xe, y0 + r, len, color); + drawFastHLine(x0 - xe, y0 - r, len, color); + drawFastHLine(x0 + xs, y0 - r, len, color); + drawFastHLine(x0 + xs, y0 + r, len, color); + + drawFastVLine(x0 + r, y0 + xs, len, color); + drawFastVLine(x0 + r, y0 - xe, len, color); + drawFastVLine(x0 - r, y0 - xe, len, color); + drawFastVLine(x0 - r, y0 + xs, len, color); + } + } + else { + ++xs; + drawPixel(x0 - xe, y0 + r, color); + drawPixel(x0 - xe, y0 - r, color); + drawPixel(x0 + xs, y0 - r, color); + drawPixel(x0 + xs, y0 + r, color); + + drawPixel(x0 + r, y0 + xs, color); + drawPixel(x0 + r, y0 - xe, color); + drawPixel(x0 - r, y0 - xe, color); + drawPixel(x0 - r, y0 + xs, color); + } + xs = xe; + } while (xe < --r); + + inTransaction = false; + end_tft_write(); // Does nothing if Sprite class uses this function +} + +/*************************************************************************************** +** Function name: drawCircleHelper +** Description: Support function for drawRoundRect() +***************************************************************************************/ +void TFT_eSPI::drawCircleHelper( int32_t x0, int32_t y0, int32_t rr, uint8_t cornername, uint32_t color) +{ + if (rr <= 0) return; + int32_t f = 1 - rr; + int32_t ddF_x = 1; + int32_t ddF_y = -2 * rr; + int32_t xe = 0; + int32_t xs = 0; + int32_t len = 0; + + //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() + inTransaction = true; + + while (xe < rr--) + { + while (f < 0) { + ++xe; + f += (ddF_x += 2); + } + f += (ddF_y += 2); + + if (xe-xs==1) { + if (cornername & 0x1) { // left top + drawPixel(x0 - xe, y0 - rr, color); + drawPixel(x0 - rr, y0 - xe, color); + } + if (cornername & 0x2) { // right top + drawPixel(x0 + rr , y0 - xe, color); + drawPixel(x0 + xs + 1, y0 - rr, color); + } + if (cornername & 0x4) { // right bottom + drawPixel(x0 + xs + 1, y0 + rr , color); + drawPixel(x0 + rr, y0 + xs + 1, color); + } + if (cornername & 0x8) { // left bottom + drawPixel(x0 - rr, y0 + xs + 1, color); + drawPixel(x0 - xe, y0 + rr , color); + } + } + else { + len = xe - xs++; + if (cornername & 0x1) { // left top + drawFastHLine(x0 - xe, y0 - rr, len, color); + drawFastVLine(x0 - rr, y0 - xe, len, color); + } + if (cornername & 0x2) { // right top + drawFastVLine(x0 + rr, y0 - xe, len, color); + drawFastHLine(x0 + xs, y0 - rr, len, color); + } + if (cornername & 0x4) { // right bottom + drawFastHLine(x0 + xs, y0 + rr, len, color); + drawFastVLine(x0 + rr, y0 + xs, len, color); + } + if (cornername & 0x8) { // left bottom + drawFastVLine(x0 - rr, y0 + xs, len, color); + drawFastHLine(x0 - xe, y0 + rr, len, color); + } + } + xs = xe; + } + inTransaction = false; + end_tft_write(); // Does nothing if Sprite class uses this function +} + +/*************************************************************************************** +** Function name: fillCircle +** Description: draw a filled circle +***************************************************************************************/ +// Optimised midpoint circle algorithm, changed to horizontal lines (faster in sprites) +// Improved algorithm avoids repetition of lines +void TFT_eSPI::fillCircle(int32_t x0, int32_t y0, int32_t r, uint32_t color) +{ + int32_t x = 0; + int32_t dx = 1; + int32_t dy = r+r; + int32_t p = -(r>>1); + + //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() + inTransaction = true; + + drawFastHLine(x0 - r, y0, dy+1, color); + + while(x=0) { + drawFastHLine(x0 - x, y0 + r, dx, color); + dy-=2; + p-=dy; + drawFastHLine(x0 - x, y0 - r, dx, color); + r--; + } + x++; + drawFastHLine(x0 - r, y0 + x, dy+1, color); + dx+=2; + p+=dx; + drawFastHLine(x0 - r, y0 - x, dy+1, color); + } + + inTransaction = false; + end_tft_write(); // Does nothing if Sprite class uses this function +} + +/*************************************************************************************** +** Function name: fillCircleHelper +** Description: Support function for fillRoundRect() +***************************************************************************************/ +// Support drawing roundrects, changed to horizontal lines (faster in sprites) +void TFT_eSPI::fillCircleHelper(int32_t x0, int32_t y0, int32_t r, uint8_t cornername, int32_t delta, uint32_t color) +{ + int32_t f = 1 - r; + int32_t ddF_x = 1; + int32_t ddF_y = -r - r; + int32_t y = 0; + + delta++; + + while (y < r) { + if (f >= 0) { + if (cornername & 0x1) drawFastHLine(x0 - y, y0 + r, y + y + delta, color); + ddF_y += 2; + f += ddF_y; + if (cornername & 0x2) drawFastHLine(x0 - y, y0 - r, y + y + delta, color); + r--; + } + + y++; + if (cornername & 0x1) drawFastHLine(x0 - r, y0 + y, r + r + delta, color); + ddF_x += 2; + f += ddF_x; + if (cornername & 0x2) drawFastHLine(x0 - r, y0 - y, r + r + delta, color); + } +} + +/*************************************************************************************** +** Function name: drawEllipse +** Description: Draw a ellipse outline +***************************************************************************************/ +void TFT_eSPI::drawEllipse(int16_t x0, int16_t y0, int32_t rx, int32_t ry, uint16_t color) +{ + if (rx<2) return; + if (ry<2) return; + int32_t x, y; + int32_t rx2 = rx * rx; + int32_t ry2 = ry * ry; + int32_t fx2 = 4 * rx2; + int32_t fy2 = 4 * ry2; + int32_t s; + + //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() + inTransaction = true; + + for (x = 0, y = ry, s = 2*ry2+rx2*(1-2*ry); ry2*x <= rx2*y; x++) { + // These are ordered to minimise coordinate changes in x or y + // drawPixel can then send fewer bounding box commands + drawPixel(x0 + x, y0 + y, color); + drawPixel(x0 - x, y0 + y, color); + drawPixel(x0 - x, y0 - y, color); + drawPixel(x0 + x, y0 - y, color); + if (s >= 0) { + s += fx2 * (1 - y); + y--; + } + s += ry2 * ((4 * x) + 6); + } + + for (x = rx, y = 0, s = 2*rx2+ry2*(1-2*rx); rx2*y <= ry2*x; y++) { + // These are ordered to minimise coordinate changes in x or y + // drawPixel can then send fewer bounding box commands + drawPixel(x0 + x, y0 + y, color); + drawPixel(x0 - x, y0 + y, color); + drawPixel(x0 - x, y0 - y, color); + drawPixel(x0 + x, y0 - y, color); + if (s >= 0) + { + s += fy2 * (1 - x); + x--; + } + s += rx2 * ((4 * y) + 6); + } + + inTransaction = false; + end_tft_write(); // Does nothing if Sprite class uses this function +} + +/*************************************************************************************** +** Function name: fillEllipse +** Description: draw a filled ellipse +***************************************************************************************/ +void TFT_eSPI::fillEllipse(int16_t x0, int16_t y0, int32_t rx, int32_t ry, uint16_t color) +{ + if (rx<2) return; + if (ry<2) return; + int32_t x, y; + int32_t rx2 = rx * rx; + int32_t ry2 = ry * ry; + int32_t fx2 = 4 * rx2; + int32_t fy2 = 4 * ry2; + int32_t s; + + //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() + inTransaction = true; + + for (x = 0, y = ry, s = 2*ry2+rx2*(1-2*ry); ry2*x <= rx2*y; x++) { + drawFastHLine(x0 - x, y0 - y, x + x + 1, color); + drawFastHLine(x0 - x, y0 + y, x + x + 1, color); + + if (s >= 0) { + s += fx2 * (1 - y); + y--; + } + s += ry2 * ((4 * x) + 6); + } + + for (x = rx, y = 0, s = 2*rx2+ry2*(1-2*rx); rx2*y <= ry2*x; y++) { + drawFastHLine(x0 - x, y0 - y, x + x + 1, color); + drawFastHLine(x0 - x, y0 + y, x + x + 1, color); + + if (s >= 0) { + s += fy2 * (1 - x); + x--; + } + s += rx2 * ((4 * y) + 6); + } + + inTransaction = false; + end_tft_write(); // Does nothing if Sprite class uses this function +} + + +/*************************************************************************************** +** Function name: setBitmapColor +** Description: Set the foreground foreground and background colour +***************************************************************************************/ +void TFT_eSPI::setBitmapColor(uint16_t c, uint16_t b) +{ + if (c == b) b = ~c; + bitmap_fg = c; + bitmap_bg = b; +} /*************************************************************************************** ** Function name: drawBitmap @@ -2265,7 +3088,6 @@ void TFT_eSPI::drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w end_tft_write(); // Does nothing if Sprite class uses this function } - /*************************************************************************************** ** Function name: drawBitmap ** Description: Draw an image stored in an array on the TFT @@ -2312,7 +3134,6 @@ void TFT_eSPI::drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t end_tft_write(); // Does nothing if Sprite class uses this function } - /*************************************************************************************** ** Function name: drawXBitmap ** Description: Draw an XBM image with foreground and background colors @@ -2337,6 +3158,35 @@ void TFT_eSPI::drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t } +/*************************************************************************************** +** Function name: setPivot +** Description: Set the pivot point on the TFT +*************************************************************************************x*/ +void TFT_eSPI::setPivot(int16_t x, int16_t y) +{ + _xPivot = x; + _yPivot = y; +} + +/*************************************************************************************** +** Function name: getPivotX +** Description: Get the x pivot position +***************************************************************************************/ +int16_t TFT_eSPI::getPivotX(void) +{ + return _xPivot; +} + +/*************************************************************************************** +** Function name: getPivotY +** Description: Get the y pivot position +***************************************************************************************/ +int16_t TFT_eSPI::getPivotY(void) +{ + return _yPivot; +} + + /*************************************************************************************** ** Function name: setCursor ** Description: Set the text cursor x,y position @@ -2347,7 +3197,6 @@ void TFT_eSPI::setCursor(int16_t x, int16_t y) cursor_y = y; } - /*************************************************************************************** ** Function name: setCursor ** Description: Set the text cursor x,y position and font @@ -2359,7 +3208,6 @@ void TFT_eSPI::setCursor(int16_t x, int16_t y, uint8_t font) cursor_y = y; } - /*************************************************************************************** ** Function name: getCursorX ** Description: Get the text cursor x position @@ -2378,7 +3226,6 @@ int16_t TFT_eSPI::getCursorY(void) return cursor_y; } - /*************************************************************************************** ** Function name: setTextSize ** Description: Set the text size multiplier @@ -2389,7 +3236,6 @@ void TFT_eSPI::setTextSize(uint8_t s) textsize = (s > 0) ? s : 1; // Don't allow font size 0 } - /*************************************************************************************** ** Function name: setTextColor ** Description: Set the font foreground colour (background is transparent) @@ -2401,7 +3247,6 @@ void TFT_eSPI::setTextColor(uint16_t c) textcolor = textbgcolor = c; } - /*************************************************************************************** ** Function name: setTextColor ** Description: Set the font foreground and background colour @@ -2412,50 +3257,6 @@ void TFT_eSPI::setTextColor(uint16_t c, uint16_t b) textbgcolor = b; } - -/*************************************************************************************** -** Function name: setPivot -** Description: Set the pivot point on the TFT -*************************************************************************************x*/ -void TFT_eSPI::setPivot(int16_t x, int16_t y) -{ - _xPivot = x; - _yPivot = y; -} - - -/*************************************************************************************** -** Function name: getPivotX -** Description: Get the x pivot position -***************************************************************************************/ -int16_t TFT_eSPI::getPivotX(void) -{ - return _xPivot; -} - - -/*************************************************************************************** -** Function name: getPivotY -** Description: Get the y pivot position -***************************************************************************************/ -int16_t TFT_eSPI::getPivotY(void) -{ - return _yPivot; -} - - -/*************************************************************************************** -** Function name: setBitmapColor -** Description: Set the foreground foreground and background colour -***************************************************************************************/ -void TFT_eSPI::setBitmapColor(uint16_t c, uint16_t b) -{ - if (c == b) b = ~c; - bitmap_fg = c; - bitmap_bg = b; -} - - /*************************************************************************************** ** Function name: setTextWrap ** Description: Define if text should wrap at end of line @@ -2466,7 +3267,6 @@ void TFT_eSPI::setTextWrap(bool wrapX, bool wrapY) textwrapY = wrapY; } - /*************************************************************************************** ** Function name: setTextDatum ** Description: Set the text position reference datum @@ -2476,6 +3276,14 @@ void TFT_eSPI::setTextDatum(uint8_t d) textdatum = d; } +/*************************************************************************************** +** Function name: getTextDatum +** Description: Return the text datum value (as used by setTextDatum()) +***************************************************************************************/ +uint8_t TFT_eSPI::getTextDatum(void) +{ + return textdatum; +} /*************************************************************************************** ** Function name: setTextPadding @@ -2495,48 +3303,6 @@ uint16_t TFT_eSPI::getTextPadding(void) return padX; } -/*************************************************************************************** -** Function name: getRotation -** Description: Return the rotation value (as used by setRotation()) -***************************************************************************************/ -uint8_t TFT_eSPI::getRotation(void) -{ - return rotation; -} - -/*************************************************************************************** -** Function name: getTextDatum -** Description: Return the text datum value (as used by setTextDatum()) -***************************************************************************************/ -uint8_t TFT_eSPI::getTextDatum(void) -{ - return textdatum; -} - - -/*************************************************************************************** -** Function name: width -** Description: Return the pixel width of display (per current rotation) -***************************************************************************************/ -// Return the size of the display (per current rotation) -int16_t TFT_eSPI::width(void) -{ - if (_vpDatum) return _xWidth; - return _width; -} - - -/*************************************************************************************** -** Function name: height -** Description: Return the pixel height of display (per current rotation) -***************************************************************************************/ -int16_t TFT_eSPI::height(void) -{ - if (_vpDatum) return _yHeight; - return _height; -} - - /*************************************************************************************** ** Function name: textWidth ** Description: Return the width in pixels of a string in a given font @@ -2641,7 +3407,6 @@ uint16_t TFT_eSPI::fontsLoaded(void) return fontsloaded; } - /*************************************************************************************** ** Function name: fontHeight ** Description: return the height of a font (yAdvance for free fonts) @@ -2693,7 +3458,7 @@ void TFT_eSPI::drawChar(int32_t x, int32_t y, uint16_t c, uint32_t color, uint32 return; bool fillbg = (bg != color); - bool clip = xd < _vpX || xd + 6 * textsize >= _vpW || yd < _vpY || yd + 8 * textsize >= _vpH; + bool clip = (xd < _vpX) || (xd + 6 * textsize >= _vpW) || (yd < _vpY) || (yd + 8 * textsize >= _vpH); if ((size==1) && fillbg && !clip) { uint8_t column[6]; @@ -2817,207 +3582,6 @@ void TFT_eSPI::drawChar(int32_t x, int32_t y, uint16_t c, uint32_t color, uint32 } -/*************************************************************************************** -** Function name: setAddrWindow -** Description: define an area to receive a stream of pixels -***************************************************************************************/ -// Chip select is high at the end of this function -void TFT_eSPI::setAddrWindow(int32_t x0, int32_t y0, int32_t w, int32_t h) -{ - begin_tft_write(); - - setWindow(x0, y0, x0 + w - 1, y0 + h - 1); - - end_tft_write(); -} - - -/*************************************************************************************** -** Function name: setWindow -** Description: define an area to receive a stream of pixels -***************************************************************************************/ -// Chip select stays low, call begin_tft_write first. Use setAddrWindow() from sketches -void TFT_eSPI::setWindow(int32_t x0, int32_t y0, int32_t x1, int32_t y1) -{ - //begin_tft_write(); // Must be called before setWindow -#if defined (ILI9225_DRIVER) - if (rotation & 0x01) { swap_coord(x0, y0); swap_coord(x1, y1); } - - addr_row = 0xFFFF; - addr_col = 0xFFFF; - - DC_C; tft_Write_8(TFT_CASET1); - DC_D; tft_Write_16(x0); - DC_C; tft_Write_8(TFT_CASET2); - DC_D; tft_Write_16(x1); - - DC_C; tft_Write_8(TFT_PASET1); - DC_D; tft_Write_16(y0); - DC_C; tft_Write_8(TFT_PASET2); - DC_D; tft_Write_16(y1); - - DC_C; tft_Write_8(TFT_RAM_ADDR1); - DC_D; tft_Write_16(x0); - DC_C; tft_Write_8(TFT_RAM_ADDR2); - DC_D; tft_Write_16(y0); - - // write to RAM - DC_C; tft_Write_8(TFT_RAMWR); - DC_D; - -#else - -#if defined (SSD1963_DRIVER) - if ((rotation & 0x1) == 0) { swap_coord(x0, y0); swap_coord(x1, y1); } -#endif - - addr_row = 0xFFFF; - addr_col = 0xFFFF; - -#ifdef CGRAM_OFFSET - x0+=colstart; - x1+=colstart; - y0+=rowstart; - y1+=rowstart; -#endif - - DC_C; tft_Write_8(TFT_CASET); - DC_D; tft_Write_32C(x0, x1); - DC_C; tft_Write_8(TFT_PASET); - DC_D; tft_Write_32C(y0, y1); - DC_C; tft_Write_8(TFT_RAMWR); - DC_D; - - //end_tft_write(); // Must be called after setWindow -#endif -} - - -/*************************************************************************************** -** Function name: readAddrWindow -** Description: define an area to read a stream of pixels -***************************************************************************************/ -void TFT_eSPI::readAddrWindow(int32_t xs, int32_t ys, int32_t w, int32_t h) -{ - //begin_tft_write(); // Must be called before readAddrWindow or CS set low - - int32_t xe = xs + w - 1; - int32_t ye = ys + h - 1; - - addr_col = 0xFFFF; - addr_row = 0xFFFF; - -#ifdef CGRAM_OFFSET - xs += colstart; - xe += colstart; - ys += rowstart; - ye += rowstart; -#endif - -#if defined (SSD1963_DRIVER) - if ((rotation & 0x1) == 0) { swap_coord(xs, ys); swap_coord(xe, ye); } -#endif - - // Column addr set - DC_C; tft_Write_8(TFT_CASET); - DC_D; tft_Write_32C(xs, xe); - - // Row addr set - DC_C; tft_Write_8(TFT_PASET); - DC_D; tft_Write_32C(ys, ye); - - // Read CGRAM command - DC_C; tft_Write_8(TFT_RAMRD); - - DC_D; - - //end_tft_write(); // Must be called after readAddrWindow or CS set high -} - - -/*************************************************************************************** -** Function name: drawPixel -** Description: push a single pixel at an arbitrary position -***************************************************************************************/ -void TFT_eSPI::drawPixel(int32_t x, int32_t y, uint32_t color) -{ - if (_vpOoB) return; - - x+= _xDatum; - y+= _yDatum; - - // Range checking - if ((x < _vpX) || (y < _vpY) ||(x >= _vpW) || (y >= _vpH)) return; - -#ifdef CGRAM_OFFSET - x+=colstart; - y+=rowstart; -#endif - - begin_tft_write(); - -#if defined (ILI9225_DRIVER) - - if (rotation & 0x01) { swap_coord(x, y); } - - // Set window to full screen to optimise sequential pixel rendering - if (addr_row != 0x9225) { - addr_row = 0x9225; // addr_row used for flag - DC_C; tft_Write_8(TFT_CASET1); - DC_D; tft_Write_16(0); - DC_C; tft_Write_8(TFT_CASET2); - DC_D; tft_Write_16(175); - - DC_C; tft_Write_8(TFT_PASET1); - DC_D; tft_Write_16(0); - DC_C; tft_Write_8(TFT_PASET2); - DC_D; tft_Write_16(219); - } - - // Define pixel coordinate - DC_C; tft_Write_8(TFT_RAM_ADDR1); - DC_D; tft_Write_16(x); - DC_C; tft_Write_8(TFT_RAM_ADDR2); - DC_D; tft_Write_16(y); - - // write to RAM - DC_C; tft_Write_8(TFT_RAMWR); - DC_D; tft_Write_16(color); - -#else - -#if defined (SSD1963_DRIVER) - if ((rotation & 0x1) == 0) { swap_coord(x, y); } -#endif - -#ifdef MULTI_TFT_SUPPORT - // No optimisation - DC_C; tft_Write_8(TFT_CASET); - DC_D; tft_Write_32D(x); - DC_C; tft_Write_8(TFT_PASET); - DC_D; tft_Write_32D(y); -#else - // No need to send x if it has not changed (speeds things up) - if (addr_col != (x<<16 | x)) { - DC_C; tft_Write_8(TFT_CASET); - DC_D; tft_Write_32D(x); - addr_col = (x<<16 | x); - } - - // No need to send y if it has not changed (speeds things up) - if (addr_row != (y<<16 | y)) { - DC_C; tft_Write_8(TFT_PASET); - DC_D; tft_Write_32D(y); - addr_row = (y<<16 | y); - } -#endif - DC_C; tft_Write_8(TFT_RAMWR); - DC_D; tft_Write_16(color); -#endif - - end_tft_write(); -} - /*************************************************************************************** ** Function name: pushColor ** Description: push a single pixel @@ -3045,26 +3609,6 @@ void TFT_eSPI::pushColor(uint16_t color, uint32_t len) end_tft_write(); } -/*************************************************************************************** -** Function name: startWrite -** Description: begin transaction with CS low, MUST later call endWrite -***************************************************************************************/ -void TFT_eSPI::startWrite(void) -{ - begin_tft_write(); - inTransaction = true; -} - -/*************************************************************************************** -** Function name: endWrite -** Description: end transaction with CS high -***************************************************************************************/ -void TFT_eSPI::endWrite(void) -{ - inTransaction = false; - DMA_BUSY_CHECK; // Safety check - user code should have checked this! - end_tft_write(); -} /*************************************************************************************** ** Function name: writeColor (use startWrite() and endWrite() before & after) @@ -3107,176 +3651,6 @@ void TFT_eSPI::pushColors(uint16_t *data, uint32_t len, bool swap) } -/*************************************************************************************** -** Function name: drawLine -** Description: draw a line between 2 arbitrary points -***************************************************************************************/ -// Bresenham's algorithm - thx wikipedia - speed enhanced by Bodmer to use -// an efficient FastH/V Line draw routine for line segments of 2 pixels or more -void TFT_eSPI::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color) -{ - if (_vpOoB) return; - - //begin_tft_write(); // Sprite class can use this function, avoiding begin_tft_write() - inTransaction = true; - - //x+= _xDatum; // Not added here, added by drawPixel & drawFastXLine - //y+= _yDatum; - - bool steep = abs(y1 - y0) > abs(x1 - x0); - if (steep) { - swap_coord(x0, y0); - swap_coord(x1, y1); - } - - if (x0 > x1) { - swap_coord(x0, x1); - swap_coord(y0, y1); - } - - int32_t dx = x1 - x0, dy = abs(y1 - y0);; - - int32_t err = dx >> 1, ystep = -1, xs = x0, dlen = 0; - - if (y0 < y1) ystep = 1; - - // Split into steep and not steep for FastH/V separation - if (steep) { - for (; x0 <= x1; x0++) { - dlen++; - err -= dy; - if (err < 0) { - err += dx; - if (dlen == 1) drawPixel(y0, xs, color); - else drawFastVLine(y0, xs, dlen, color); - dlen = 0; - y0 += ystep; xs = x0 + 1; - } - } - if (dlen) drawFastVLine(y0, xs, dlen, color); - } - else - { - for (; x0 <= x1; x0++) { - dlen++; - err -= dy; - if (err < 0) { - err += dx; - if (dlen == 1) drawPixel(xs, y0, color); - else drawFastHLine(xs, y0, dlen, color); - dlen = 0; - y0 += ystep; xs = x0 + 1; - } - } - if (dlen) drawFastHLine(xs, y0, dlen, color); - } - - inTransaction = false; - end_tft_write(); -} - - -/*************************************************************************************** -** Function name: drawFastVLine -** Description: draw a vertical line -***************************************************************************************/ -void TFT_eSPI::drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color) -{ - if (_vpOoB) return; - - x+= _xDatum; - y+= _yDatum; - - // Clipping - if ((x < _vpX) || (x >= _vpW) || (y >= _vpH)) return; - - if (y < _vpY) { h += y - _vpY; y = _vpY; } - - if ((y + h) > _vpH) h = _vpH - y; - - if (h < 1) return; - - begin_tft_write(); - - setWindow(x, y, x, y + h - 1); - - pushBlock(color, h); - - end_tft_write(); -} - - -/*************************************************************************************** -** Function name: drawFastHLine -** Description: draw a horizontal line -***************************************************************************************/ -void TFT_eSPI::drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color) -{ - if (_vpOoB) return; - - x+= _xDatum; - y+= _yDatum; - - // Clipping - if ((y < _vpY) || (x >= _vpW) || (y >= _vpH)) return; - - if (x < _vpX) { w += x - _vpX; x = _vpX; } - - if ((x + w) > _vpW) w = _vpW - x; - - if (w < 1) return; - - begin_tft_write(); - - setWindow(x, y, x + w - 1, y); - - pushBlock(color, w); - - end_tft_write(); -} - - -/*************************************************************************************** -** Function name: fillRect -** Description: draw a filled rectangle -***************************************************************************************/ -void TFT_eSPI::fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) -{ - if (_vpOoB) return; - - x+= _xDatum; - y+= _yDatum; - - // Clipping - if ((x >= _vpW) || (y >= _vpH)) return; - - if (x < _vpX) { w += x - _vpX; x = _vpX; } - if (y < _vpY) { h += y - _vpY; y = _vpY; } - - if ((x + w) > _vpW) w = _vpW - x; - if ((y + h) > _vpH) h = _vpH - y; - - if ((w < 1) || (h < 1)) return; - - //Serial.print(" _xDatum=");Serial.print( _xDatum);Serial.print(", _yDatum=");Serial.print( _yDatum); - //Serial.print(", _xWidth=");Serial.print(_xWidth);Serial.print(", _yHeight=");Serial.println(_yHeight); - - //Serial.print(" _vpX=");Serial.print( _vpX);Serial.print(", _vpY=");Serial.print( _vpY); - //Serial.print(", _vpW=");Serial.print(_vpW);Serial.print(", _vpH=");Serial.println(_vpH); - - //Serial.print(" x=");Serial.print( y);Serial.print(", y=");Serial.print( y); - //Serial.print(", w=");Serial.print(w);Serial.print(", h=");Serial.println(h); - - begin_tft_write(); - - setWindow(x, y, x + w - 1, y + h - 1); - - pushBlock(color, w * h); - - end_tft_write(); -} - - /*************************************************************************************** ** Function name: color565 ** Description: convert three 8 bit RGB levels to a 16 bit colour value @@ -3340,6 +3714,81 @@ uint32_t TFT_eSPI::color24to16(uint32_t color888) return (r | g | b); } +/*************************************************************************************** +** Function name: alphaBlend +** Description: Blend 16bit foreground and background +*************************************************************************************x*/ +uint16_t TFT_eSPI::alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc) +{ + // For speed use fixed point maths and rounding to permit a power of 2 division + uint16_t fgR = ((fgc >> 10) & 0x3E) + 1; + uint16_t fgG = ((fgc >> 4) & 0x7E) + 1; + uint16_t fgB = ((fgc << 1) & 0x3E) + 1; + + uint16_t bgR = ((bgc >> 10) & 0x3E) + 1; + uint16_t bgG = ((bgc >> 4) & 0x7E) + 1; + uint16_t bgB = ((bgc << 1) & 0x3E) + 1; + + // Shift right 1 to drop rounding bit and shift right 8 to divide by 256 + uint16_t r = (((fgR * alpha) + (bgR * (255 - alpha))) >> 9); + uint16_t g = (((fgG * alpha) + (bgG * (255 - alpha))) >> 9); + uint16_t b = (((fgB * alpha) + (bgB * (255 - alpha))) >> 9); + + // Combine RGB565 colours into 16 bits + //return ((r&0x18) << 11) | ((g&0x30) << 5) | ((b&0x18) << 0); // 2 bit greyscale + //return ((r&0x1E) << 11) | ((g&0x3C) << 5) | ((b&0x1E) << 0); // 4 bit greyscale + return (r << 11) | (g << 5) | (b << 0); +} + +/*************************************************************************************** +** Function name: alphaBlend +** Description: Blend 16bit foreground and background with dither +*************************************************************************************x*/ +uint16_t TFT_eSPI::alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc, uint8_t dither) +{ + if (dither) { + int16_t alphaDither = (int16_t)alpha - dither + random(2*dither+1); // +/-4 randomised + alpha = (uint8_t)alphaDither; + if (alphaDither < 0) alpha = 0; + if (alphaDither >255) alpha = 255; + } + + return alphaBlend(alpha, fgc, bgc); +} + +/*************************************************************************************** +** Function name: alphaBlend +** Description: Blend 24bit foreground and background with optional dither +*************************************************************************************x*/ +uint32_t TFT_eSPI::alphaBlend24(uint8_t alpha, uint32_t fgc, uint32_t bgc, uint8_t dither) +{ + + if (dither) { + int16_t alphaDither = (int16_t)alpha - dither + random(2*dither+1); // +/-dither randomised + alpha = (uint8_t)alphaDither; + if (alphaDither < 0) alpha = 0; + if (alphaDither >255) alpha = 255; + } + + // For speed use fixed point maths and rounding to permit a power of 2 division + uint16_t fgR = ((fgc >> 15) & 0x1FE) + 1; + uint16_t fgG = ((fgc >> 7) & 0x1FE) + 1; + uint16_t fgB = ((fgc << 1) & 0x1FE) + 1; + + uint16_t bgR = ((bgc >> 15) & 0x1FE) + 1; + uint16_t bgG = ((bgc >> 7) & 0x1FE) + 1; + uint16_t bgB = ((bgc << 1) & 0x1FE) + 1; + + // Shift right 1 to drop rounding bit and shift right 8 to divide by 256 + uint32_t r = (((fgR * alpha) + (bgR * (255 - alpha))) >> 9); + uint32_t g = (((fgG * alpha) + (bgG * (255 - alpha))) >> 9); + uint32_t b = (((fgB * alpha) + (bgB * (255 - alpha))) >> 9); + + // Combine RGB colours into 24 bits + return (r << 16) | (g << 8) | (b << 0); +} + + /*************************************************************************************** ** Function name: invertDisplay ** Description: invert the display colours i = 1 invert, i = 0 normal @@ -3459,7 +3908,6 @@ uint16_t TFT_eSPI::decodeUTF8(uint8_t c) uint16_t TFT_eSPI::decodeUTF8(uint8_t *buf, uint16_t *index, uint16_t remaining) { uint16_t c = buf[(*index)++]; - //Serial.print("Byte from string = 0x"); Serial.println(c, HEX); #ifdef DECODE_UTF8 // 7 bit Unicode @@ -3483,80 +3931,6 @@ uint16_t TFT_eSPI::decodeUTF8(uint8_t *buf, uint16_t *index, uint16_t remaining) } -/*************************************************************************************** -** Function name: alphaBlend -** Description: Blend 16bit foreground and background -*************************************************************************************x*/ -uint16_t TFT_eSPI::alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc) -{ - // For speed use fixed point maths and rounding to permit a power of 2 division - uint16_t fgR = ((fgc >> 10) & 0x3E) + 1; - uint16_t fgG = ((fgc >> 4) & 0x7E) + 1; - uint16_t fgB = ((fgc << 1) & 0x3E) + 1; - - uint16_t bgR = ((bgc >> 10) & 0x3E) + 1; - uint16_t bgG = ((bgc >> 4) & 0x7E) + 1; - uint16_t bgB = ((bgc << 1) & 0x3E) + 1; - - // Shift right 1 to drop rounding bit and shift right 8 to divide by 256 - uint16_t r = (((fgR * alpha) + (bgR * (255 - alpha))) >> 9); - uint16_t g = (((fgG * alpha) + (bgG * (255 - alpha))) >> 9); - uint16_t b = (((fgB * alpha) + (bgB * (255 - alpha))) >> 9); - - // Combine RGB565 colours into 16 bits - //return ((r&0x18) << 11) | ((g&0x30) << 5) | ((b&0x18) << 0); // 2 bit greyscale - //return ((r&0x1E) << 11) | ((g&0x3C) << 5) | ((b&0x1E) << 0); // 4 bit greyscale - return (r << 11) | (g << 5) | (b << 0); -} - -/*************************************************************************************** -** Function name: alphaBlend -** Description: Blend 16bit foreground and background with dither -*************************************************************************************x*/ -uint16_t TFT_eSPI::alphaBlend(uint8_t alpha, uint16_t fgc, uint16_t bgc, uint8_t dither) -{ - if (dither) { - int16_t alphaDither = (int16_t)alpha - dither + random(2*dither+1); // +/-4 randomised - alpha = (uint8_t)alphaDither; - if (alphaDither < 0) alpha = 0; - if (alphaDither >255) alpha = 255; - } - - return alphaBlend(alpha, fgc, bgc); -} - -/*************************************************************************************** -** Function name: alphaBlend -** Description: Blend 24bit foreground and background with optional dither -*************************************************************************************x*/ -uint32_t TFT_eSPI::alphaBlend24(uint8_t alpha, uint32_t fgc, uint32_t bgc, uint8_t dither) -{ - - if (dither) { - int16_t alphaDither = (int16_t)alpha - dither + random(2*dither+1); // +/-dither randomised - alpha = (uint8_t)alphaDither; - if (alphaDither < 0) alpha = 0; - if (alphaDither >255) alpha = 255; - } - - // For speed use fixed point maths and rounding to permit a power of 2 division - uint16_t fgR = ((fgc >> 15) & 0x1FE) + 1; - uint16_t fgG = ((fgc >> 7) & 0x1FE) + 1; - uint16_t fgB = ((fgc << 1) & 0x1FE) + 1; - - uint16_t bgR = ((bgc >> 15) & 0x1FE) + 1; - uint16_t bgG = ((bgc >> 7) & 0x1FE) + 1; - uint16_t bgB = ((bgc << 1) & 0x1FE) + 1; - - // Shift right 1 to drop rounding bit and shift right 8 to divide by 256 - uint16_t r = (((fgR * alpha) + (bgR * (255 - alpha))) >> 9); - uint16_t g = (((fgG * alpha) + (bgG * (255 - alpha))) >> 9); - uint16_t b = (((fgB * alpha) + (bgB * (255 - alpha))) >> 9); - - // Combine RGB colours into 24 bits - return (r << 16) | (g << 8) | (b << 0); -} - /*************************************************************************************** ** Function name: write ** Description: draw characters piped through serial stream @@ -3587,12 +3961,6 @@ size_t TFT_eSPI::write(uint8_t utf8) uint16_t cwidth = 0; uint16_t cheight = 0; -//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv DEBUG vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv - //Serial.print((uint8_t) uniCode); // Debug line sends all printed TFT text to serial port - //Serial.println(uniCode, HEX); // Debug line sends all printed TFT text to serial port - //delay(5); // Debug optional wait for serial port to flush through -//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ DEBUG ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< #ifdef LOAD_GFXFF if(!gfxFont) { diff --git a/TFT_eSPI.h b/TFT_eSPI.h index ca50402..40a2b81 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -16,7 +16,7 @@ #ifndef _TFT_eSPIH_ #define _TFT_eSPIH_ -#define TFT_ESPI_VERSION "2.3.59" +#define TFT_ESPI_VERSION "2.4.0" // Bit level feature flags // Bit 0 set: viewport capability @@ -295,7 +295,7 @@ typedef struct { String version = TFT_ESPI_VERSION; int32_t esp; // Processor code -uint8_t trans; // SPI transaction supoort +uint8_t trans; // SPI transaction support uint8_t serial; // Serial (SPI) or parallel uint8_t overlap; // ESP8266 overlap mode @@ -384,6 +384,9 @@ class TFT_eSPI : public Print { friend class TFT_eSprite; // Sprite class has ac height(void), width(void); + // Read the colour of a pixel at x,y and return value in 565 format + virtual uint16_t readPixel(int32_t x, int32_t y); + void setRotation(uint8_t r); // Set the display image orientation to 0, 1, 2 or 3 uint8_t getRotation(void); // Read the current rotation @@ -391,12 +394,21 @@ class TFT_eSPI : public Print { friend class TFT_eSprite; // Sprite class has ac // The TFT_eSprite class inherits the following functions (not all are useful to Sprite class - void setAddrWindow(int32_t xs, int32_t ys, int32_t w, int32_t h), // Note: start coordinates + width and height - setWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye); // Note: start + end coordinates + void setAddrWindow(int32_t x, int32_t y, int32_t w, int32_t h); // Note: start coordinates + width and height + void setWindow(int32_t xs, int32_t ys, int32_t xe, int32_t ye); // Note: start + end coordinates // Viewport commands, see "Viewport_Demo" sketch void setViewport(int32_t x, int32_t y, int32_t w, int32_t h, bool vpDatum = true); - bool checkViewport(int32_t x, int32_t y, int32_t w, int32_t h); + + // Check if whole of specified area is within viewport area, returns true if clipping is not required + bool checkViewport (int32_t x, int32_t y, int32_t w, int32_t h); // Deprecated use checkAddrWindow + bool checkAddrWindow(int32_t x, int32_t y, int32_t w, int32_t h); + + // Clip input window to viewport bounds, return false if whole area is out of bounds + bool clipAddrWindow(int32_t* x, int32_t* y, int32_t* w, int32_t* h); + // Clip input window area to viewport bounds, return false if whole area is out of bounds + bool clipWindow(int32_t* xs, int32_t* ys, int32_t* xe, int32_t* ye); + int32_t getViewportX(void); int32_t getViewportY(void); int32_t getViewportWidth(void); @@ -417,9 +429,6 @@ class TFT_eSPI : public Print { friend class TFT_eSprite; // Sprite class has ac // Write a set of pixels stored in memory, use setSwapBytes(true/false) function to correct endianess void pushPixels(const void * data_in, uint32_t len); - // Read the colour of a pixel at x,y and return value in 565 format - uint16_t readPixel(int32_t x, int32_t y); - // Support for half duplex (bi-directional SDA) SPI bus where MOSI must be switched to input #ifdef TFT_SDA_READ #if defined (TFT_eSPI_ENABLE_8_BIT_READ) @@ -435,6 +444,20 @@ class TFT_eSPI : public Print { friend class TFT_eSprite; // Sprite class has ac drawRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t radius, uint32_t color), fillRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t radius, uint32_t color); + // Draw an anti-aliased wide line from ax,ay to bx,by width wd with radiused ends (radius is wd/2) + void drawWideLine(float ax, float ay, float bx, float by, float wd, uint16_t fg_color, uint16_t bg_color); + // For sprites and also TFT screens where the background pixel colours can be read + void drawWideLine(float ax, float ay, float bx, float by, float wd, uint16_t fg_color); + + // Draw an anti-aliased wide line from ax,ay to bx,by with different width at each end aw, bw and with radiused ends + void drawWedgeLine(float ax, float ay, float bx, float by, float aw, float bw, uint16_t fg_color, uint16_t bg_color); + // For sprites and also TFT screens where the background pixel colours can be read + void drawWedgeLine(float ax, float ay, float bx, float by, float aw, float bw, uint16_t fg_color); + + // Draw an anti-aliased filled circle at ax,ay with radius r (uses drawWideLine) + void drawSpot(float ax, float ay, float r, uint16_t fg_color, uint16_t bg_color); + // For sprites and also TFT screens where the background pixel colours can be read + void drawSpot(float ax, float ay, float r, uint16_t fg_color); void drawCircle(int32_t x, int32_t y, int32_t r, uint32_t color), drawCircleHelper(int32_t x, int32_t y, int32_t r, uint8_t cornername, uint32_t color), @@ -521,6 +544,7 @@ class TFT_eSPI : public Print { friend class TFT_eSprite; // Sprite class has ac void setTextColor(uint16_t color), // Set character (glyph) color only (background not over-written) setTextColor(uint16_t fgcolor, uint16_t bgcolor),// Set character (glyph) foreground and backgorund colour setTextSize(uint8_t size); // Set character size multiplier (this increases pixel size) + // Note: Smooth fonts cannot be scaled using setTextSize void setTextWrap(bool wrapX, bool wrapY = false); // Turn on/off wrapping of text in TFT width and/or height @@ -702,6 +726,11 @@ class TFT_eSPI : public Print { friend class TFT_eSprite; // Sprite class has ac // Same as setAddrWindow but exits with CGRAM in read mode void readAddrWindow(int32_t xs, int32_t ys, int32_t w, int32_t h); + // Helper function: calculate distance of a point from a finite length line between two points + float wideLineDistance(float pax, float pay, float bax, float bay, float dr) __attribute__((always_inline)); + // As above but adds delta distance for wedge shaped lines + float wedgeLineDistance(float pax, float pay, float bax, float bay, float dr) __attribute__((always_inline)); + // Byte read prototype uint8_t readByte(void); diff --git a/examples/DMA test/SpriteRotatingCube/SpriteRotatingCube.ino b/examples/DMA test/SpriteRotatingCube/SpriteRotatingCube.ino index f87cabd..9e33405 100644 --- a/examples/DMA test/SpriteRotatingCube/SpriteRotatingCube.ino +++ b/examples/DMA test/SpriteRotatingCube/SpriteRotatingCube.ino @@ -24,7 +24,7 @@ // STM32F767 27MHz SPI 0% processor load: Non DMA 97 fps, with DMA 102 fps // ESP32 27MHz SPI 0% processor load: Non DMA 90 fps, with DMA 101 fps -// ESP32 40MHz SPI 0% processor load: Non DMA 127 fps, with DMA 145 fps +// ESP32 40MHz SPI 0% processor load: Non DMA 127 fps, with DMA 152 fps // NOTE: FOR SPI DISPLAYS ONLY #define USE_DMA_TO_TFT diff --git a/examples/DMA test/boing_ball/boing_ball.ino b/examples/DMA test/boing_ball/boing_ball.ino index 0d9a9c8..36bf22e 100644 --- a/examples/DMA test/boing_ball/boing_ball.ino +++ b/examples/DMA test/boing_ball/boing_ball.ino @@ -20,11 +20,19 @@ // Blue Pill overclocked to 128MHz with DMA - 32MHz SPI 116 fps // ESP32 - 8 bit parallel 110 fps (no DMA) -// ESP32 - 40MHz SPI *no* DMA 93 fps -// ESP32 - 40MHz SPI with DMA 112 fps +// ESP32 - 27MHz SPI *no* DMA 75 fps +// ESP32 - 27MHz SPI with DMA 110 fps +// ESP32 - 40MHz SPI *no* DMA 94 fps +// ESP32 - 40MHz SPI with DMA 160 fps +// ESP32 - 80MHz SPI *no* DMA 128 fps +// ESP32 - 80MHz SPI with DMA 187 fps -#define SCREENWIDTH 320 +// Comment out next line for no DMA +#define USE_DMA + +#define SCREENWIDTH 320 #define SCREENHEIGHT 240 +#define BUFFER_SIZE 1024 // DMA buffers (4 kbytes reserved) #include "graphic.h" @@ -51,7 +59,7 @@ int balloldx = ballx, balloldy = bally; // Prior ball position // Working buffer for ball rendering...2 scanlines that alternate, // one is rendered while the other is transferred via DMA. -uint16_t renderbuf[2][SCREENWIDTH]; +uint16_t renderbuf[2][BUFFER_SIZE]; uint16_t palette[16]; // Color table for ball rotation effect @@ -64,12 +72,12 @@ void setup() { tft.begin(); tft.setRotation(3); // Landscape orientation, USB at bottom right tft.setSwapBytes(false); - // Draw initial framebuffer contents: - //tft.setBitmapColor(GRIDCOLOR, BGCOLOR); - tft.fillScreen(BGCOLOR); - +#ifdef USE_DMA tft.initDMA(); +#endif + // Draw initial framebuffer contents: + tft.fillScreen(BGCOLOR); tft.drawBitmap(0, 0, (const uint8_t *)background, SCREENWIDTH, SCREENHEIGHT, GRIDCOLOR); startTime = millis(); @@ -127,16 +135,17 @@ void loop() { x, y, bx1, bgx1; // Loop counters and working vars uint8_t p; // 'packed' value of 2 ball pixels int8_t bufIdx = 0; + uint32_t pCount = 0; // Start SPI transaction and drop TFT_CS - avoids transaction overhead in loop tft.startWrite(); - // Set window area to pour pixels into tft.setAddrWindow(minx, miny, width, height); + destPtr = &renderbuf[bufIdx][0]; // Draw line by line loop for(y=0; y> (bgx1 & 7)) ? GRIDCOLOR : BGCOLOR; } *destPtr++ = c<<8 | c>>8; // Store pixel color + pCount++; + if (pCount >= BUFFER_SIZE) { +#ifdef USE_DMA + tft.pushPixelsDMA(&renderbuf[bufIdx][0], BUFFER_SIZE); // Push line to screen +#else + tft.pushPixels(&renderbuf[bufIdx][0], BUFFER_SIZE); +#endif + bufIdx = 1 - bufIdx; + destPtr = &renderbuf[bufIdx][0]; + pCount = 0; + } bx1++; // Increment bitmap position counters (X axis) bgx1++; } - - tft.pushPixelsDMA(&renderbuf[bufIdx][0], width); // Push line to screen - - // Push line to screen (swap bytes false for STM/ESP32) - //tft.pushPixels(&renderbuf[bufIdx][0], width); - - bufIdx = 1 - bufIdx; by++; // Increment bitmap position counters (Y axis) bgy++; } - //if (random(100) == 1) delay(2000); - tft.endWrite(); - //delay(5); +#ifdef USE_DMA + if (pCount) tft.pushPixelsDMA(&renderbuf[bufIdx][0], pCount); // Push line to screen +#else + if (pCount) tft.pushPixels(&renderbuf[bufIdx][0], pCount); +#endif + // Show approximate frame rate if(!(++frame & 255)) { // Every 256 frames... uint32_t elapsed = (millis() - startTime) / 1000; // Seconds