From cbf06d7a214938d884b21d5aeb465241c25ce774 Mon Sep 17 00:00:00 2001 From: Bodmer Date: Fri, 22 Dec 2023 01:07:33 +0000 Subject: [PATCH] Fix #3036 Remove SPI LL HAL functions that seem to be broken in latest STM32duino board package. Replace with low level register access writes and reads. This change also has significant performance benefits for SPI display update speed! --- Processors/TFT_eSPI_STM32.c | 115 ++++++++++++++++++-------------- Processors/TFT_eSPI_STM32.h | 126 ++++++++++++++++++------------------ TFT_eSPI.h | 2 +- library.json | 2 +- library.properties | 2 +- 5 files changed, 132 insertions(+), 115 deletions(-) diff --git a/Processors/TFT_eSPI_STM32.c b/Processors/TFT_eSPI_STM32.c index 776cc84..e20c6a2 100644 --- a/Processors/TFT_eSPI_STM32.c +++ b/Processors/TFT_eSPI_STM32.c @@ -24,9 +24,6 @@ DMA_HandleTypeDef dmaHal; #endif - // Buffer for SPI transmit byte padding and byte order manipulation - uint8_t spiBuffer[8]; - //////////////////////////////////////////////////////////////////////////////////////// #if defined (TFT_SDA_READ) && !defined (TFT_PARALLEL_8_BIT) //////////////////////////////////////////////////////////////////////////////////////// @@ -296,34 +293,19 @@ void TFT_eSPI::pushPixels(const void* data_in, uint32_t len) #define BUF_SIZE 240*3 void TFT_eSPI::pushBlock(uint16_t color, uint32_t len) { - uint8_t col[BUF_SIZE]; + //uint8_t col[BUF_SIZE]; // Always using swapped bytes is a peculiarity of this function... //color = color>>8 | color<<8; uint8_t r = (color & 0xF800)>>8; // Red uint8_t g = (color & 0x07E0)>>3; // Green uint8_t b = (color & 0x001F)<<3; // Blue - if (len=BUF_SIZE/3 ) ; - // Send remaining pixels - if (len) HAL_SPI_Transmit(&spiHal, col, len*3, HAL_MAX_DELAY); //*/ + SPI_BUSY_CHECK; } /*************************************************************************************** ** Function name: pushPixels - for STM32 and 3 byte RGB display @@ -333,26 +315,25 @@ void TFT_eSPI::pushPixels(const void* data_in, uint32_t len) { uint16_t *data = (uint16_t*)data_in; - if(_swapBytes) { + if(!_swapBytes) { while ( len-- ) { // Split out the colours - spiBuffer[0] = (*data & 0xF8); // Red - spiBuffer[1] = (*data & 0xE000)>>11 | (*data & 0x07)<<5; // Green - spiBuffer[2] = (*data & 0x1F00)>>5; // Blue + TX_FIFO = (*data & 0xF8); // Red + TX_FIFO = (*data & 0xE000)>>11 | (*data & 0x07)<<5; // Green + TX_FIFO = (*data & 0x1F00)>>5; // Blue data++; - HAL_SPI_Transmit(&spiHal, spiBuffer, 3, HAL_MAX_DELAY); } } else { while ( len-- ) { // Split out the colours - spiBuffer[0] = (*data & 0xF800)>>8; // Red - spiBuffer[1] = (*data & 0x07E0)>>3; // Green - spiBuffer[2] = (*data & 0x001F)<<3; // Blue + TX_FIFO = (*data & 0xF800)>>8; // Red + TX_FIFO = (*data & 0x07E0)>>3; // Green + TX_FIFO = (*data & 0x001F)<<3; // Blue data++; - HAL_SPI_Transmit(&spiHal, spiBuffer, 3, HAL_MAX_DELAY); } } + SPI_BUSY_CHECK; } //////////////////////////////////////////////////////////////////////////////////////// @@ -364,6 +345,7 @@ void TFT_eSPI::pushPixels(const void* data_in, uint32_t len) ** Description: Write a block of pixels of the same colour ***************************************************************************************/ #define BUF_SIZE 480 +/* void TFT_eSPI::pushBlock(uint16_t color, uint32_t len) { uint16_t col[BUF_SIZE]; @@ -381,7 +363,47 @@ void TFT_eSPI::pushBlock(uint16_t color, uint32_t len) len -= BUF_SIZE; } while ( len>=BUF_SIZE ) ; // Send remaining pixels - if (len) HAL_SPI_Transmit(&spiHal, (uint8_t*)col, len<<1, HAL_MAX_DELAY); //*/ + if (len) HAL_SPI_Transmit(&spiHal, (uint8_t*)col, len<<1, HAL_MAX_DELAY); +} + //*/ +void TFT_eSPI::pushBlock(uint16_t color, uint32_t len){ + // Loop unrolling improves speed dramatically graphics test 0.634s => 0.374s + while (len>31) { + #if !defined (SSD1963_DRIVER) + // 32D macro writes 16 bits twice + tft_Write_32D(color); tft_Write_32D(color); + tft_Write_32D(color); tft_Write_32D(color); + tft_Write_32D(color); tft_Write_32D(color); + tft_Write_32D(color); tft_Write_32D(color); + tft_Write_32D(color); tft_Write_32D(color); + tft_Write_32D(color); tft_Write_32D(color); + tft_Write_32D(color); tft_Write_32D(color); + tft_Write_32D(color); tft_Write_32D(color); + #else + tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); + tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); + tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); + tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); + tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); + tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); + tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); + tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); + #endif + len-=32; + } + + while (len>7) { + #if !defined (SSD1963_DRIVER) + tft_Write_32D(color); tft_Write_32D(color); + tft_Write_32D(color); tft_Write_32D(color); + #else + tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); + tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); + #endif + len-=8; + } + + while (len--) {tft_Write_16(color);} } @@ -392,26 +414,23 @@ 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; + if(_swapBytes) { - uint16_t col[BUF_SIZE]; // Buffer for swapped bytes - while ( len>=BUF_SIZE ) { - for (uint32_t i = 0; i < BUF_SIZE; i++) { col[i] = (*data>>8) | (*data<<8); data++; } - HAL_SPI_Transmit(&spiHal, (uint8_t*)col, BUF_SIZE<<1, HAL_MAX_DELAY); - len -= BUF_SIZE; + while ( len-- ) { + TX_FIFO = (uint8_t)(*data>>8); + TX_FIFO = (uint8_t)(*data<<8); + data++; } - for (uint32_t i = 0; i < len; i++) { col[i] = (*data>>8) | (*data<<8); data++; } - HAL_SPI_Transmit(&spiHal, (uint8_t*)col, len<<1, HAL_MAX_DELAY); } else { - // HAL byte count for transmit is only 16 bits maximum so to avoid this constraint - // transfers of small blocks are performed until HAL capacity is reached. - while(len>0x7FFF) { // Transfer 16-bit pixels in blocks if len*2 over 65534 bytes - HAL_SPI_Transmit(&spiHal, (uint8_t*)data, 0x800<<1, HAL_MAX_DELAY); - len -= 0x800; data+= 0x800; // Arbitrarily use 2KByte blocks + while ( len-- ) { + // Split out the colours + TX_FIFO = (uint8_t)(*data); + TX_FIFO = (uint8_t)(*data>>8); + data++; } - // Send remaining pixels (max 65534 bytes) - HAL_SPI_Transmit(&spiHal, (uint8_t*)data, len<<1, HAL_MAX_DELAY); } + SPI_BUSY_CHECK; } //////////////////////////////////////////////////////////////////////////////////////// diff --git a/Processors/TFT_eSPI_STM32.h b/Processors/TFT_eSPI_STM32.h index 65e710d..73a22cb 100644 --- a/Processors/TFT_eSPI_STM32.h +++ b/Processors/TFT_eSPI_STM32.h @@ -151,6 +151,15 @@ #define TFT_SPI_PORT 1 #endif + #if (TFT_SPI_PORT == 1) + #define SPIX SPI1 + #elif (TFT_SPI_PORT == 2) + #define SPIX SPI2 + #elif (TFT_SPI_PORT == 3) + #define SPIX SPI3 + #elif (TFT_SPI_PORT == 4) + #define SPIX SPI4 + #endif // Global define is _VARIANT_ARDUINO_STM32_, see board package stm32_def.h for specific variants #if defined (STM32F2xx) || defined (STM32F4xx) || defined (STM32F7xx) @@ -226,8 +235,8 @@ #define DC_PORT digitalPinToPort(TFT_DC) #define DC_PIN_MASK digitalPinToBitMask(TFT_DC) // Use bit set reset register - #define DC_C DC_PORT->BSRR = DC_PIN_MASK<<16 - #define DC_D DC_PORT->BSRR = DC_PIN_MASK + #define DC_C DC_DELAY; DC_PORT->BSRR = DC_PIN_MASK<<16 + #define DC_D DC_DELAY; DC_PORT->BSRR = DC_PIN_MASK #endif //////////////////////////////////////////////////////////////////////////////////////// @@ -962,64 +971,56 @@ //////////////////////////////////////////////////////////////////////////////////////// #elif defined (SPI_18BIT_DRIVER) // SPI 18-bit colour - // Write 8 bits to TFT - #define tft_Write_8(C) \ - { spiBuffer[0] = C; \ - HAL_SPI_Transmit(&spiHal, spiBuffer, 1, 10); } + #define SPI_TXE_CHECK while(!__HAL_SPI_GET_FLAG(&spiHal, SPI_FLAG_TXE)){} + //BSY check must allow for APB clock delay by checking TXE flag first + #define SPI_BUSY_CHECK SPI_TXE_CHECK; while( __HAL_SPI_GET_FLAG(&spiHal, SPI_FLAG_BSY)){} + #define TX_FIFO SPI_TXE_CHECK; *((__IO uint8_t *)&SPIX->DR) - // Convert 16-bit colour to 18-bit and write in 3 bytes - #define tft_Write_16(C) \ - { spiBuffer[0] = ((C) & 0xF800)>>8; spiBuffer[1] = ((C) & 0x07E0)>>3; spiBuffer[2] = ((C) & 0x001F)<<3; \ - HAL_SPI_Transmit(&spiHal, spiBuffer, 3, 10); } + //#define tft_Write_8(C) spi.transfer(C) + #define tft_Write_8(C) TX_FIFO = (C); SPI_BUSY_CHECK + #define tft_Write_16(C) TX_FIFO = ((C) & 0xF800)>>8; TX_FIFO = ((C) & 0x07E0)>>3; TX_FIFO = ((C) & 0x001F)<<3; SPI_BUSY_CHECK + #define tft_Write_16S(C) TX_FIFO = (C) & 0xF8; TX_FIFO = ((C) & 0xE000)>>11 | ((C) & 0x07)<<5; TX_FIFO = ((C) & 0x1F00)>>5; SPI_BUSY_CHECK + #define tft_Write_16N(C) TX_FIFO = ((C) & 0xF800)>>8; TX_FIFO = ((C) & 0x07E0)>>3; TX_FIFO = ((C) & 0x001F)<<3 - // Convert swapped byte 16-bit colour to 18-bit and write in 3 bytes - #define tft_Write_16S(C) \ - { spiBuffer[0] = (C) & 0xF8; spiBuffer[1] = ((C) & 0xE000)>>11 | ((C) & 0x07)<<5; spiBuffer[2] = ((C) & 0x1F00)>>5; \ - HAL_SPI_Transmit(&spiHal, spiBuffer, 3, 10); } - - // Write 32 bits to TFT #define tft_Write_32(C) \ - { spiBuffer[0] = (C)>>24; spiBuffer[1] = (C)>>16; spiBuffer[2] = (C)>>8; spiBuffer[3] = C; \ - HAL_SPI_Transmit(&spiHal, spiBuffer, 4, 10); } + TX_FIFO = (C)>>24; TX_FIFO = (C)>>16; \ + TX_FIFO = (C)>>8; TX_FIFO = (C); SPI_BUSY_CHECK - // Write two address coordinates #define tft_Write_32C(C,D) \ - { spiBuffer[0] = (C)>>8; spiBuffer[1] = C; spiBuffer[2] = (D)>>8; spiBuffer[3] = D; \ - HAL_SPI_Transmit(&spiHal, spiBuffer, 4, 10); } + TX_FIFO = (C)>>8; TX_FIFO = (C); \ + TX_FIFO = (D)>>8; TX_FIFO = (D); SPI_BUSY_CHECK - // Write same value twice #define tft_Write_32D(C) \ - { spiBuffer[0] = spiBuffer[2] = (C)>>8; spiBuffer[1] = spiBuffer[3] = C; \ - HAL_SPI_Transmit(&spiHal, spiBuffer, 4, 10); } + TX_FIFO = (C)>>8; TX_FIFO = (C); \ + TX_FIFO = (C)>>8; TX_FIFO = (C); SPI_BUSY_CHECK //////////////////////////////////////////////////////////////////////////////////////// // Macros to write commands/pixel colour data to a SPI Raspberry Pi TFT //////////////////////////////////////////////////////////////////////////////////////// #elif defined (RPI_DISPLAY_TYPE) - #define tft_Write_8(C) \ - { spiBuffer[0] = 0; spiBuffer[1] = C; \ - HAL_SPI_Transmit(&spiHal, spiBuffer, 2, 10); } + #define SPI_TXE_CHECK while(!__HAL_SPI_GET_FLAG(&spiHal, SPI_FLAG_TXE)){} + //BSY check must allow for APB clock delay by checking TXE flag first + #define SPI_BUSY_CHECK SPI_TXE_CHECK; while( __HAL_SPI_GET_FLAG(&spiHal, SPI_FLAG_BSY)){} + #define TX_FIFO SPI_TXE_CHECK; *((__IO uint8_t *)&SPIX->DR) - #define tft_Write_16(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); } + //#define tft_Write_8(C) spi.transfer(C) + #define tft_Write_8(C) TX_FIFO = (0); TX_FIFO = (C); SPI_BUSY_CHECK + #define tft_Write_16(C) TX_FIFO = (C)>>8; TX_FIFO = (C); SPI_BUSY_CHECK + #define tft_Write_16S(C) TX_FIFO = (C); TX_FIFO = (C)>>8; SPI_BUSY_CHECK + #define tft_Write_16N(C) TX_FIFO = (C)>>8; TX_FIFO = (C) #define tft_Write_32(C) \ - { spiBuffer[0] = (C)>>24; spiBuffer[1] = (C)>>16; spiBuffer[2] = (C)>>8; spiBuffer[3] = C; \ - HAL_SPI_Transmit(&spiHal, spiBuffer, 4, 10); } + TX_FIFO = (C)>>24; TX_FIFO = (C)>>16; \ + TX_FIFO = (C)>>8; TX_FIFO = (C); SPI_BUSY_CHECK #define tft_Write_32C(C,D) \ - { spiBuffer[1] = ((C)>>8); spiBuffer[3] = (C); spiBuffer[5] = ((D)>>8); spiBuffer[7] = D; \ - HAL_SPI_Transmit(&spiHal, spiBuffer, 8, 10); } + TX_FIFO = (0); TX_FIFO = (C)>>8; TX_FIFO = (0); TX_FIFO = (C); \ + TX_FIFO = (0); TX_FIFO = (D)>>8; TX_FIFO = (0); TX_FIFO = (D); SPI_BUSY_CHECK #define tft_Write_32D(C) \ - { spiBuffer[1] = ((C)>>8); spiBuffer[3] = (C); spiBuffer[5] = ((C)>>8); spiBuffer[7] = C; \ - HAL_SPI_Transmit(&spiHal, spiBuffer, 8, 10); } + TX_FIFO = (0); TX_FIFO = (C)>>8; TX_FIFO = (0); TX_FIFO = (C); \ + TX_FIFO = (0); TX_FIFO = (C)>>8; TX_FIFO = (0); TX_FIFO = (C); SPI_BUSY_CHECK //////////////////////////////////////////////////////////////////////////////////////// // Macros for all other SPI displays @@ -1027,40 +1028,37 @@ #else -/* Remove patch to fix #2933 - #if defined(ST7789_DRIVER) || defined(ST7789_2_DRIVER) - // Temporary workaround for issue #510 part 2 - #define tft_Write_8(C) spi.transfer(C) - #else -*/ - #define tft_Write_8(C) \ - { spiBuffer[0] = C; \ - HAL_SPI_Transmit(&spiHal, spiBuffer, 1, 10); delayMicroseconds(1);} -/* Remove patch to fix #2933 - #endif -*/ - #define tft_Write_16(C) \ - { spiBuffer[0] = (C)>>8; spiBuffer[1] = C; \ - HAL_SPI_Transmit(&spiHal, spiBuffer, 2, 10); } + //#define DC_DELAY delayMicroseconds(1) // Premature BSY clear Hardware bug? - #define tft_Write_16S(C) \ - { spiBuffer[0] = C; spiBuffer[1] = (C)>>8; \ - HAL_SPI_Transmit(&spiHal, spiBuffer, 2, 10); } + #define SPI_TXE_CHECK while(!__HAL_SPI_GET_FLAG(&spiHal, SPI_FLAG_TXE)){} + //BSY check must allow for APB clock delay by checking TXE flag first + #define SPI_BUSY_CHECK SPI_TXE_CHECK; while( __HAL_SPI_GET_FLAG(&spiHal, SPI_FLAG_BSY)){} + #define TX_FIFO SPI_TXE_CHECK; *((__IO uint8_t *)&SPIX->DR) + + #define tft_Write_8(C) TX_FIFO = (C); SPI_BUSY_CHECK + #define tft_Write_16(C) TX_FIFO = (C)>>8; TX_FIFO = (C); SPI_BUSY_CHECK + #define tft_Write_16S(C) TX_FIFO = (C); TX_FIFO = (C)>>8; SPI_BUSY_CHECK + #define tft_Write_16N(C) TX_FIFO = (C)>>8; TX_FIFO = (C) #define tft_Write_32(C) \ - { spiBuffer[0] = (C)>>24; spiBuffer[1] = (C)>>16; spiBuffer[2] = (C)>>8; spiBuffer[3] = C; \ - HAL_SPI_Transmit(&spiHal, spiBuffer, 4, 10); } + TX_FIFO = (C)>>24; TX_FIFO = (C)>>16; \ + tft_Write_16((uint16_t) ((C)>>0)) #define tft_Write_32C(C,D) \ - { spiBuffer[0] = (C)>>8; spiBuffer[1] = C; spiBuffer[2] = (D)>>8; spiBuffer[3] = D; \ - HAL_SPI_Transmit(&spiHal, spiBuffer, 4, 10); } + TX_FIFO = (C)>>8; TX_FIFO = (C); \ + tft_Write_16((uint16_t) (D)) #define tft_Write_32D(C) \ - { spiBuffer[0] = spiBuffer[2] = (C)>>8; spiBuffer[1] = spiBuffer[3] = C; \ - HAL_SPI_Transmit(&spiHal, spiBuffer, 4, 10); } + TX_FIFO = (C)>>8; TX_FIFO = (C); \ + tft_Write_16((uint16_t) (C)) #endif +#ifndef DC_DELAY + //#define DC_DELAY delayMicroseconds(1) // Premature BSY clear hardware bug? + #define DC_DELAY +#endif + #ifndef tft_Write_16N #define tft_Write_16N tft_Write_16 #endif diff --git a/TFT_eSPI.h b/TFT_eSPI.h index 3ce8554..1a0370b 100644 --- a/TFT_eSPI.h +++ b/TFT_eSPI.h @@ -16,7 +16,7 @@ #ifndef _TFT_eSPIH_ #define _TFT_eSPIH_ -#define TFT_ESPI_VERSION "2.5.42" +#define TFT_ESPI_VERSION "2.5.43" // Bit level feature flags // Bit 0 set: viewport capability diff --git a/library.json b/library.json index be2db5f..f590f3e 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "TFT_eSPI", - "version": "2.5.42", + "version": "2.5.43", "keywords": "Arduino, tft, display, ttgo, LilyPi, WT32-SC01, ePaper, display, Pico, RP2040 Nano Connect, RP2040, STM32, ESP8266, NodeMCU, ESP32, M5Stack, ILI9341, ST7735, ILI9163, S6D02A1, ILI9481, ILI9486, ILI9488, ST7789, ST7796, RM68140, SSD1351, SSD1963, ILI9225, HX8357D, GC9A01, R61581", "description": "A TFT and ePaper (SPI or parallel interface) graphics library with optimisation for Raspberry Pi Pico, RP2040, ESP8266, ESP32 and STM32 processors", "repository": diff --git a/library.properties b/library.properties index 69237f0..319d4ec 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=TFT_eSPI -version=2.5.42 +version=2.5.43 author=Bodmer maintainer=Bodmer sentence=TFT graphics library for Arduino processors with performance optimisation for RP2040, STM32, ESP8266 and ESP32