acidportal

- 😈 Worlds smallest Evil Portal on a LilyGo T-QT
git clone git://git.acid.vegas/acidportal.git
Log | Files | Refs | Archive | README | LICENSE

TFT_eSPI_STM32.c (28199B)

      1         ////////////////////////////////////////////////////
      2         // TFT_eSPI Driver functions for STM32 processors //
      3         ////////////////////////////////////////////////////
      4 
      5 ////////////////////////////////////////////////////////////////////////////////////////
      6 // Global variables
      7 ////////////////////////////////////////////////////////////////////////////////////////
      8 
      9 #if defined (TFT_PARALLEL_8_BIT)
     10   // No globals
     11 #else
     12   // Use STM32 default SPI port
     13   #if !defined (TFT_MOSI) || !defined (TFT_MISO) || !defined (TFT_SCLK)
     14     SPIClass& spi = SPI;
     15   #else
     16     SPIClass spi(TFT_MOSI, TFT_MISO, TFT_SCLK);
     17   #endif
     18   // SPI HAL peripheral handle
     19   SPI_HandleTypeDef spiHal;
     20 #endif
     21 
     22 #ifdef STM32_DMA
     23   // DMA HAL handle
     24   DMA_HandleTypeDef dmaHal;
     25 #endif
     26 
     27   // Buffer for SPI transmit byte padding and byte order manipulation
     28   uint8_t   spiBuffer[8];
     29 
     30 ////////////////////////////////////////////////////////////////////////////////////////
     31 #if defined (TFT_SDA_READ) && !defined (TFT_PARALLEL_8_BIT)
     32 ////////////////////////////////////////////////////////////////////////////////////////
     33 
     34 /***************************************************************************************############# UNTESTED ###################
     35 ** Function name:           tft_Read_8
     36 ** Description:             STM32 software SPI to read bidirectional SDA line
     37 ***************************************************************************************/
     38 uint8_t TFT_eSPI::tft_Read_8(void)
     39 {
     40   uint8_t  ret = 0;
     41   uint32_t reg = 0;
     42 
     43   for (uint8_t i = 0; i < 8; i++) {  // read results
     44     ret <<= 1;
     45     SCLK_L;
     46     if (digitalRead(TFT_MOSI)) ret |= 1;
     47     SCLK_H;
     48   }
     49 
     50   return ret;
     51 }
     52 
     53 /***************************************************************************************############# UNTESTED ###################
     54 ** Function name:           beginSDA
     55 ** Description:             Detach SPI from pin to permit software SPI
     56 ***************************************************************************************/
     57 void TFT_eSPI::begin_SDA_Read(void)
     58 {
     59   // Release configured SPI port for SDA read
     60   spi.end();// Code missing here!                                                      <<<<<<<<<<<<<<Missing code<<<<<<<<<<<<<<<<<
     61 }
     62 
     63 /***************************************************************************************############# UNTESTED ###################
     64 ** Function name:           endSDA
     65 ** Description:             Attach SPI pins after software SPI
     66 ***************************************************************************************/
     67 void TFT_eSPI::end_SDA_Read(void)
     68 {
     69   // Configure SPI port ready for next TFT access
     70   spi.begin();// Code missing here!                                                   <<<<<<<<<<<<<<Missing code<<<<<<<<<<<<<<<<<
     71 }
     72 
     73 ////////////////////////////////////////////////////////////////////////////////////////
     74 #endif // #if defined (TFT_SDA_READ)
     75 ////////////////////////////////////////////////////////////////////////////////////////
     76 
     77 
     78 ////////////////////////////////////////////////////////////////////////////////////////
     79 #if defined (TFT_PARALLEL_8_BIT) // Code for STM32 8 bit parallel
     80 ////////////////////////////////////////////////////////////////////////////////////////
     81 
     82 /***************************************************************************************
     83 ** Function name:           pushBlock - for ESP32 and parallel display
     84 ** Description:             Write a block of pixels of the same colour
     85 ***************************************************************************************/
     86 void TFT_eSPI::pushBlock(uint16_t color, uint32_t len){
     87     // Loop unrolling improves speed dramatically graphics test  0.634s => 0.374s
     88     while (len>31) {
     89     #if !defined (SSD1963_DRIVER)
     90       // 32D macro writes 16 bits twice
     91       tft_Write_32D(color); tft_Write_32D(color);
     92       tft_Write_32D(color); tft_Write_32D(color);
     93       tft_Write_32D(color); tft_Write_32D(color);
     94       tft_Write_32D(color); tft_Write_32D(color);
     95       tft_Write_32D(color); tft_Write_32D(color);
     96       tft_Write_32D(color); tft_Write_32D(color);
     97       tft_Write_32D(color); tft_Write_32D(color);
     98       tft_Write_32D(color); tft_Write_32D(color);
     99     #else
    100       tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); tft_Write_16(color);
    101       tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); tft_Write_16(color);
    102       tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); tft_Write_16(color);
    103       tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); tft_Write_16(color);
    104       tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); tft_Write_16(color);
    105       tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); tft_Write_16(color);
    106       tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); tft_Write_16(color);
    107       tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); tft_Write_16(color);
    108     #endif
    109       len-=32;
    110     }
    111 
    112     while (len>7) {
    113     #if !defined (SSD1963_DRIVER)
    114       tft_Write_32D(color); tft_Write_32D(color);
    115       tft_Write_32D(color); tft_Write_32D(color);
    116     #else
    117       tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); tft_Write_16(color);
    118       tft_Write_16(color); tft_Write_16(color); tft_Write_16(color); tft_Write_16(color);
    119     #endif
    120       len-=8;
    121     }
    122 
    123   while (len--) {tft_Write_16(color);}
    124 }
    125 
    126 
    127 /***************************************************************************************
    128 ** Function name:           pushPixels - for ESP32 and parallel display
    129 ** Description:             Write a sequence of pixels
    130 ***************************************************************************************/
    131 void TFT_eSPI::pushPixels(const void* data_in, uint32_t len){
    132 
    133   uint16_t *data = (uint16_t*)data_in;
    134 
    135   if(_swapBytes) {
    136     while (len>1) {tft_Write_16(*data); data++; tft_Write_16(*data); data++; len -=2;}
    137     if (len) {tft_Write_16(*data);}
    138     return;
    139   }
    140 
    141   while (len>1) {tft_Write_16S(*data); data++; tft_Write_16S(*data); data++; len -=2;}
    142   if (len) {tft_Write_16S(*data);}
    143 }
    144 
    145 
    146 /***************************************************************************************
    147 ** Function name:           GPIO direction control  - supports class functions
    148 ** Description:             Set parallel bus to INPUT or OUTPUT
    149 ***************************************************************************************/
    150 void TFT_eSPI::busDir(uint32_t mask, uint8_t mode)
    151 {
    152 #if defined (STM_PORTA_DATA_BUS)
    153   #if defined (STM32F1xx)
    154     if (mode == OUTPUT) GPIOA->CRL = 0x33333333;
    155     else GPIOA->CRL = 0x88888888;
    156   #else
    157     if (mode == OUTPUT) GPIOA->MODER = (GPIOA->MODER & 0xFFFF0000) | 0x00005555;
    158     else GPIOA->MODER &= 0xFFFF0000;
    159   #endif
    160 #elif defined (STM_PORTB_DATA_BUS)
    161   #if defined (STM32F1xx)
    162     if (mode == OUTPUT) GPIOB->CRL = 0x33333333;
    163     else GPIOB->CRL = 0x88888888;
    164   #else
    165     if (mode == OUTPUT) GPIOB->MODER = (GPIOB->MODER & 0xFFFF0000) | 0x00005555;
    166     else GPIOB->MODER &= 0xFFFF0000;
    167   #endif
    168 #elif defined (STM_PORTC_DATA_BUS)
    169   #if defined (STM32F1xx)
    170     if (mode == OUTPUT) GPIOC->CRL = 0x33333333;
    171     else GPIOC->CRL = 0x88888888;
    172   #else
    173     if (mode == OUTPUT) GPIOC->MODER = (GPIOC->MODER & 0xFFFF0000) | 0x00005555;
    174     else GPIOC->MODER &= 0xFFFF0000;
    175   #endif
    176 #elif defined (STM_PORTD_DATA_BUS)
    177   #if defined (STM32F1xx)
    178     if (mode == OUTPUT) GPIOD->CRL = 0x33333333;
    179     else GPIOD->CRL = 0x88888888;
    180   #else
    181     if (mode == OUTPUT) GPIOD->MODER = (GPIOD->MODER & 0xFFFF0000) | 0x00005555;
    182     else GPIOD->MODER &= 0xFFFF0000;
    183   #endif
    184 #else
    185   if (mode == OUTPUT) {
    186     LL_GPIO_SetPinMode(D0_PIN_PORT, D0_PIN_MASK, LL_GPIO_MODE_OUTPUT);
    187     LL_GPIO_SetPinMode(D1_PIN_PORT, D1_PIN_MASK, LL_GPIO_MODE_OUTPUT);
    188     LL_GPIO_SetPinMode(D2_PIN_PORT, D2_PIN_MASK, LL_GPIO_MODE_OUTPUT);
    189     LL_GPIO_SetPinMode(D3_PIN_PORT, D3_PIN_MASK, LL_GPIO_MODE_OUTPUT);
    190     LL_GPIO_SetPinMode(D4_PIN_PORT, D4_PIN_MASK, LL_GPIO_MODE_OUTPUT);
    191     LL_GPIO_SetPinMode(D5_PIN_PORT, D5_PIN_MASK, LL_GPIO_MODE_OUTPUT);
    192     LL_GPIO_SetPinMode(D6_PIN_PORT, D6_PIN_MASK, LL_GPIO_MODE_OUTPUT);
    193     LL_GPIO_SetPinMode(D7_PIN_PORT, D7_PIN_MASK, LL_GPIO_MODE_OUTPUT);
    194   }
    195   else {
    196     LL_GPIO_SetPinMode(D0_PIN_PORT, D0_PIN_MASK, LL_GPIO_MODE_INPUT);
    197     LL_GPIO_SetPinMode(D1_PIN_PORT, D1_PIN_MASK, LL_GPIO_MODE_INPUT);
    198     LL_GPIO_SetPinMode(D2_PIN_PORT, D2_PIN_MASK, LL_GPIO_MODE_INPUT);
    199     LL_GPIO_SetPinMode(D3_PIN_PORT, D3_PIN_MASK, LL_GPIO_MODE_INPUT);
    200     LL_GPIO_SetPinMode(D4_PIN_PORT, D4_PIN_MASK, LL_GPIO_MODE_INPUT);
    201     LL_GPIO_SetPinMode(D5_PIN_PORT, D5_PIN_MASK, LL_GPIO_MODE_INPUT);
    202     LL_GPIO_SetPinMode(D6_PIN_PORT, D6_PIN_MASK, LL_GPIO_MODE_INPUT);
    203     LL_GPIO_SetPinMode(D7_PIN_PORT, D7_PIN_MASK, LL_GPIO_MODE_INPUT);
    204   }
    205 #endif
    206 }
    207 
    208 
    209 /***************************************************************************************
    210 ** Function name:           GPIO direction control  - supports class functions
    211 ** Description:             Set STM32 GPIO pin to input or output (set high) ASAP
    212 ***************************************************************************************/
    213 void TFT_eSPI::gpioMode(uint8_t gpio, uint8_t mode)
    214 {
    215   PinName pn = digitalPinToPinName(gpio);
    216   // Push-pull output with no pullup
    217   if (mode == OUTPUT) pin_function(pn, STM_PIN_DATA(STM_MODE_OUTPUT_PP, GPIO_NOPULL, 0));
    218   // Input with pullup
    219   else pin_function(pn, STM_PIN_DATA(STM_MODE_INPUT, GPIO_PULLUP, 0));
    220 }
    221 
    222 /***************************************************************************************############# UNTESTED ###################
    223 ** Function name:           read byte  - supports class functions
    224 ** Description:             Read a byte - parallel bus only
    225 ***************************************************************************************/
    226 uint8_t TFT_eSPI::readByte(void)
    227 {
    228   uint8_t b = 0;
    229 
    230   RD_L;
    231 #if defined (STM_PORTA_DATA_BUS)
    232   b = GPIOA->IDR;
    233   b = GPIOA->IDR;
    234   b = GPIOA->IDR;
    235   b = (GPIOA->IDR) & 0xFF;
    236 #elif defined (STM_PORTB_DATA_BUS)
    237   b = GPIOB->IDR;
    238   b = GPIOB->IDR;
    239   b = GPIOB->IDR;
    240   b = (GPIOB->IDR) & 0xFF;
    241 #elif defined (STM_PORTC_DATA_BUS)
    242   b = GPIOC->IDR;
    243   b = GPIOC->IDR;
    244   b = GPIOC->IDR;
    245   b = (GPIOC->IDR) & 0xFF;
    246 #elif defined (STM_PORTD_DATA_BUS)
    247   b = GPIOD->IDR;
    248   b = GPIOD->IDR;
    249   b = GPIOD->IDR;
    250   b = (GPIOD->IDR) & 0xFF;
    251 #else
    252   b  = RD_TFT_D0 | RD_TFT_D0 | RD_TFT_D0 | RD_TFT_D0; //Delay for bits to settle
    253 
    254   b  = RD_TFT_D0 | RD_TFT_D1 | RD_TFT_D2 | RD_TFT_D3;
    255   b |= RD_TFT_D4 | RD_TFT_D5 | RD_TFT_D6 | RD_TFT_D7;
    256 #endif
    257   RD_H;
    258 
    259   return b;
    260 }
    261 
    262 ////////////////////////////////////////////////////////////////////////////////////////
    263 #elif defined (RPI_WRITE_STROBE)  // For RPi TFT with write strobe                        ############# UNTESTED ###################
    264 ////////////////////////////////////////////////////////////////////////////////////////
    265 
    266 /***************************************************************************************
    267 ** Function name:           pushBlock - for ESP32 or STM32 RPi TFT
    268 ** Description:             Write a block of pixels of the same colour
    269 ***************************************************************************************/
    270 void TFT_eSPI::pushBlock(uint16_t color, uint32_t len)
    271 {
    272   if(len) { tft_Write_16(color); len--; }
    273   while(len--) {WR_L; WR_H;}
    274 }
    275 
    276 /***************************************************************************************
    277 ** Function name:           pushPixels - for ESP32 or STM32 RPi TFT
    278 ** Description:             Write a sequence of pixels
    279 ***************************************************************************************/
    280 void TFT_eSPI::pushPixels(const void* data_in, uint32_t len)
    281 {
    282   uint16_t *data = (uint16_t*)data_in;
    283 
    284   if (_swapBytes) while ( len-- ) { tft_Write_16S(*data); data++;}
    285   else while ( len-- ) {tft_Write_16(*data); data++;}
    286 }
    287 
    288 ////////////////////////////////////////////////////////////////////////////////////////
    289 #elif defined (SPI_18BIT_DRIVER) // SPI 18 bit colour
    290 ////////////////////////////////////////////////////////////////////////////////////////
    291 
    292 /***************************************************************************************
    293 ** Function name:           pushBlock - for STM32 and 3 byte RGB display
    294 ** Description:             Write a block of pixels of the same colour
    295 ***************************************************************************************/
    296 #define BUF_SIZE 240*3
    297 void TFT_eSPI::pushBlock(uint16_t color, uint32_t len)
    298 {
    299   uint8_t col[BUF_SIZE];
    300   // Always using swapped bytes is a peculiarity of this function...
    301   //color = color>>8 | color<<8;
    302   uint8_t r = (color & 0xF800)>>8; // Red
    303   uint8_t g = (color & 0x07E0)>>3; // Green
    304   uint8_t b = (color & 0x001F)<<3; // Blue
    305 
    306   if  (len<BUF_SIZE/3) {
    307     for (uint32_t i = 0; i < len*3; i++) {
    308       col[i]   = r;
    309       col[++i] = g;
    310       col[++i] = b;
    311     }
    312     HAL_SPI_Transmit(&spiHal, col, len*3, HAL_MAX_DELAY);
    313     return;
    314   }
    315 
    316   for (uint32_t i = 0; i < BUF_SIZE; i++) {
    317       col[i]   = r;
    318       col[++i] = g;
    319       col[++i] = b;
    320   }
    321   do {
    322     HAL_SPI_Transmit(&spiHal, col, BUF_SIZE, HAL_MAX_DELAY);
    323     len -= BUF_SIZE/3;
    324   } while ( len>=BUF_SIZE/3 ) ;
    325   // Send remaining pixels
    326   if (len) HAL_SPI_Transmit(&spiHal, col, len*3, HAL_MAX_DELAY); //*/
    327 }
    328 /***************************************************************************************
    329 ** Function name:           pushPixels - for STM32 and 3 byte RGB display
    330 ** Description:             Write a sequence of pixels
    331 ***************************************************************************************/
    332 void TFT_eSPI::pushPixels(const void* data_in, uint32_t len)
    333 {
    334   uint16_t *data = (uint16_t*)data_in;
    335 
    336   if(_swapBytes) {
    337     while ( len-- ) {
    338       // Split out the colours
    339       spiBuffer[0] = (*data & 0xF8); // Red
    340       spiBuffer[1] = (*data & 0xE000)>>11 | (*data & 0x07)<<5; // Green
    341       spiBuffer[2] = (*data & 0x1F00)>>5; // Blue
    342       data++;
    343       HAL_SPI_Transmit(&spiHal, spiBuffer, 3, HAL_MAX_DELAY);
    344     }
    345   }
    346   else {
    347     while ( len-- ) {
    348       // Split out the colours
    349       spiBuffer[0] = (*data & 0xF800)>>8; // Red
    350       spiBuffer[1] = (*data & 0x07E0)>>3; // Green
    351       spiBuffer[2] = (*data & 0x001F)<<3; // Blue
    352       data++;
    353       HAL_SPI_Transmit(&spiHal, spiBuffer, 3, HAL_MAX_DELAY);
    354     }
    355   }
    356 }
    357 
    358 ////////////////////////////////////////////////////////////////////////////////////////
    359 #else //                   Standard SPI 16 bit colour TFT                                                 All Tested
    360 ////////////////////////////////////////////////////////////////////////////////////////
    361 
    362 /***************************************************************************************
    363 ** Function name:           pushBlock - for STM32
    364 ** Description:             Write a block of pixels of the same colour
    365 ***************************************************************************************/
    366 #define BUF_SIZE 480
    367 void TFT_eSPI::pushBlock(uint16_t color, uint32_t len)
    368 {
    369   uint16_t col[BUF_SIZE];
    370   // Always using swapped bytes is a peculiarity of this function...
    371   uint16_t swapColor = color>>8 | color<<8;
    372   if  (len<BUF_SIZE) {
    373     for (uint32_t i = 0; i < len; i++) col[i] = swapColor;
    374     HAL_SPI_Transmit(&spiHal, (uint8_t*)col, len<<1, HAL_MAX_DELAY);
    375     return;
    376   }
    377 
    378   for (uint32_t i = 0; i < BUF_SIZE; i++) col[i] = swapColor;
    379   do {
    380     HAL_SPI_Transmit(&spiHal, (uint8_t*)col, BUF_SIZE<<1, HAL_MAX_DELAY);
    381     len -= BUF_SIZE;
    382   } while ( len>=BUF_SIZE ) ;
    383   // Send remaining pixels
    384   if (len) HAL_SPI_Transmit(&spiHal, (uint8_t*)col, len<<1, HAL_MAX_DELAY); //*/
    385 }
    386 
    387 
    388 /***************************************************************************************
    389 ** Function name:           pushPixels - for STM32
    390 ** Description:             Write a sequence of pixels
    391 ***************************************************************************************/
    392 void TFT_eSPI::pushPixels(const void* data_in, uint32_t len)
    393 {
    394   uint16_t *data = (uint16_t*)data_in;
    395   if(_swapBytes) {
    396     uint16_t col[BUF_SIZE]; // Buffer for swapped bytes
    397     while ( len>=BUF_SIZE ) {
    398       for (uint32_t i = 0; i < BUF_SIZE; i++) { col[i] = (*data>>8) | (*data<<8); data++; }
    399       HAL_SPI_Transmit(&spiHal, (uint8_t*)col, BUF_SIZE<<1, HAL_MAX_DELAY);
    400       len -= BUF_SIZE;
    401     }
    402     for (uint32_t i = 0; i < len; i++) { col[i] = (*data>>8) | (*data<<8); data++; }
    403     HAL_SPI_Transmit(&spiHal, (uint8_t*)col, len<<1, HAL_MAX_DELAY);
    404   }
    405   else {
    406   // HAL byte count for transmit is only 16 bits maximum so to avoid this constraint
    407   // transfers of small blocks are performed until HAL capacity is reached.
    408     while(len>0x7FFF) { // Transfer 16 bit pixels in blocks if len*2 over 65534 bytes
    409       HAL_SPI_Transmit(&spiHal, (uint8_t*)data, 0x800<<1, HAL_MAX_DELAY);
    410       len -= 0x800; data+= 0x800; // Arbitrarily use 2KByte blocks
    411     }
    412     // Send remaining pixels (max 65534 bytes)
    413     HAL_SPI_Transmit(&spiHal, (uint8_t*)data, len<<1, HAL_MAX_DELAY);
    414   }
    415 }
    416 
    417 ////////////////////////////////////////////////////////////////////////////////////////
    418 #endif // End of display interface specific functions
    419 ////////////////////////////////////////////////////////////////////////////////////////
    420 
    421 
    422 ////////////////////////////////////////////////////////////////////////////////////////
    423 #if defined STM32_DMA && !defined (TFT_PARALLEL_8_BIT) //       DMA FUNCTIONS
    424 ////////////////////////////////////////////////////////////////////////////////////////
    425 
    426 /***************************************************************************************
    427 ** Function name:           dmaBusy
    428 ** Description:             Check if DMA is busy (usefully non-blocking!)
    429 ***************************************************************************************/
    430 // Use while( tft.dmaBusy() ) {Do-something-useful;}"
    431 bool TFT_eSPI::dmaBusy(void)
    432 {
    433   //return (dmaHal.State == HAL_DMA_STATE_BUSY);  // Do not use, SPI may still be busy
    434   return (spiHal.State == HAL_SPI_STATE_BUSY_TX); // Check if SPI Tx is busy
    435 }
    436 
    437 
    438 /***************************************************************************************
    439 ** Function name:           dmaWait
    440 ** Description:             Wait until DMA is over (blocking!)
    441 ***************************************************************************************/
    442 void TFT_eSPI::dmaWait(void)
    443 {
    444   //return (dmaHal.State == HAL_DMA_STATE_BUSY);  // Do not use, SPI may still be busy
    445   while (spiHal.State == HAL_SPI_STATE_BUSY_TX); // Check if SPI Tx is busy
    446 }
    447 
    448 
    449 /***************************************************************************************
    450 ** Function name:           pushPixelsDMA
    451 ** Description:             Push pixels to TFT (len must be less than 32767)
    452 ***************************************************************************************/
    453 // This will byte swap the original image if setSwapBytes(true) was called by sketch.
    454 void TFT_eSPI::pushPixelsDMA(uint16_t* image, uint32_t len)
    455 {
    456   if (len == 0) return;
    457 
    458   // Wait for any current DMA transaction to end
    459   while (spiHal.State == HAL_SPI_STATE_BUSY_TX); // Check if SPI Tx is busy
    460 
    461   if(_swapBytes) {
    462     for (uint32_t i = 0; i < len; i++) (image[i] = image[i] << 8 | image[i] >> 8);
    463   }
    464 
    465   HAL_SPI_Transmit_DMA(&spiHal, (uint8_t*)image, len << 1);
    466 }
    467 
    468 
    469 /***************************************************************************************
    470 ** Function name:           pushImageDMA
    471 ** Description:             Push image to a window (w*h must be less than 65536)
    472 ***************************************************************************************/
    473 // This will clip and also swap bytes if setSwapBytes(true) was called by sketch
    474 void TFT_eSPI::pushImageDMA(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t* image, uint16_t* buffer)
    475 {
    476   if ((x >= _vpW) || (y >= _vpH)) return;
    477 
    478   int32_t dx = 0;
    479   int32_t dy = 0;
    480   int32_t dw = w;
    481   int32_t dh = h;
    482 
    483   if (x < _vpX) { dx = _vpX - x; dw -= dx; x = _vpX; }
    484   if (y < _vpY) { dy = _vpY - y; dh -= dy; y = _vpY; }
    485 
    486   if ((x + dw) > _vpW ) dw = _vpW - x;
    487   if ((y + dh) > _vpH ) dh = _vpH - y;
    488 
    489   if (dw < 1 || dh < 1) return;
    490 
    491   uint32_t len = dw*dh;
    492 
    493   if (buffer == nullptr) {
    494     buffer = image;
    495     while (spiHal.State == HAL_SPI_STATE_BUSY_TX); // Check if SPI Tx is busy
    496   }
    497 
    498   // If image is clipped, copy pixels into a contiguous block
    499   if ( (dw != w) || (dh != h) ) {
    500     if(_swapBytes) {
    501       for (int32_t yb = 0; yb < dh; yb++) {
    502         for (int32_t xb = 0; xb < dw; xb++) {
    503           uint32_t src = xb + dx + w * (yb + dy);
    504           (buffer[xb + yb * dw] = image[src] << 8 | image[src] >> 8);
    505         }
    506       }
    507     }
    508     else {
    509       for (int32_t yb = 0; yb < dh; yb++) {
    510         memcpy((uint8_t*) (buffer + yb * dw), (uint8_t*) (image + dx + w * (yb + dy)), dw << 1);
    511       }
    512     }
    513   }
    514   // else, if a buffer pointer has been provided copy whole image to the buffer
    515   else if (buffer != image || _swapBytes) {
    516     if(_swapBytes) {
    517       for (uint32_t i = 0; i < len; i++) (buffer[i] = image[i] << 8 | image[i] >> 8);
    518     }
    519     else {
    520       memcpy(buffer, image, len*2);
    521     }
    522   }
    523 
    524   setWindow(x, y, x + dw - 1, y + dh - 1);
    525 
    526   // DMA byte count for transmit is only 16 bits maximum, so to avoid this constraint
    527   // small transfers are performed using a blocking call until DMA capacity is reached.
    528   // User sketch can prevent blocking by managing pixel count and splitting into blocks
    529   // of 32767 pixels maximum. (equivalent to an area of ~320 x 100 pixels)
    530   while(len>0x7FFF) { // Transfer 16 bit pixels in blocks if len*2 over 65534 bytes
    531     HAL_SPI_Transmit(&spiHal, (uint8_t*)buffer, 0x800<<1, HAL_MAX_DELAY);
    532     len -= 0x800; buffer+= 0x800; // Arbitrarily send 1K pixel blocks (2Kbytes)
    533   }
    534   // Send remaining pixels using DMA (max 65534 bytes)
    535   HAL_SPI_Transmit_DMA(&spiHal, (uint8_t*)buffer, len << 1);
    536 }
    537 
    538 ////////////////////////////////////////////////////////////////////////////////////////
    539 // Processor specific DMA initialisation
    540 ////////////////////////////////////////////////////////////////////////////////////////
    541 
    542 // The DMA functions here work with SPI only (not parallel)
    543 #if defined (STM32F2xx) || defined (STM32F4xx) || defined (STM32F7xx)
    544 /***************************************************************************************
    545 ** Function name:           DMAX_StreamX_IRQHandler
    546 ** Description:             Override the default HAL stream X interrupt handler
    547 ***************************************************************************************/
    548   #if (TFT_SPI_PORT == 1)
    549     extern "C" void DMA2_Stream3_IRQHandler();
    550     void DMA2_Stream3_IRQHandler(void)
    551   #elif (TFT_SPI_PORT == 2)
    552     extern "C" void DMA1_Stream4_IRQHandler();
    553     void DMA1_Stream4_IRQHandler(void)
    554   #elif (TFT_SPI_PORT == 3)
    555     extern "C" void DMA1_Stream5_IRQHandler();
    556     void DMA1_Stream5_IRQHandler(void)
    557   #endif
    558   {
    559     // Call the default end of buffer handler
    560     HAL_DMA_IRQHandler(&dmaHal);
    561   }
    562 
    563 /***************************************************************************************
    564 ** Function name:           initDMA
    565 ** Description:             Initialise the DMA engine - returns true if init OK
    566 ***************************************************************************************/
    567 // This initialisation is for STM32F2xx/4xx/7xx processors and may not work on others
    568 // Dual core H7xx series not supported yet, they are different and have a DMA MUX: 
    569 // https://electronics.stackexchange.com/questions/379813/configuring-the-dma-request-multiplexer-on-a-stm32h7-mcu
    570 bool TFT_eSPI::initDMA(bool ctrl_cs)
    571 {
    572   ctrl_cs = ctrl_cs; // Not used for STM32, so stop compiler warning
    573 
    574   #if (TFT_SPI_PORT == 1)
    575     __HAL_RCC_DMA2_CLK_ENABLE();                           // Enable DMA2 clock
    576     dmaHal.Init.Channel = DMA_CHANNEL_3;                   // DMA channel 3 is for SPI1 TX
    577   #elif (TFT_SPI_PORT == 2)
    578     __HAL_RCC_DMA1_CLK_ENABLE();                           // Enable DMA1 clock
    579     dmaHal.Init.Channel = DMA_CHANNEL_0;                   // DMA channel 0 is for SPI2 TX
    580   #elif (TFT_SPI_PORT == 3)
    581     __HAL_RCC_DMA1_CLK_ENABLE();                           // Enable DMA1 clock
    582     dmaHal.Init.Channel = DMA_CHANNEL_0;                   // DMA channel 0 is for SPI3 TX
    583   
    584   #endif
    585 
    586   dmaHal.Init.Mode =  DMA_NORMAL; //DMA_CIRCULAR;   //   // Normal = send buffer once
    587   dmaHal.Init.Direction = DMA_MEMORY_TO_PERIPH;          // Copy memory to the peripheral
    588   dmaHal.Init.PeriphInc = DMA_PINC_DISABLE;              // Don't increment peripheral address
    589   dmaHal.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; // Peripheral is byte aligned
    590   dmaHal.Init.MemInc = DMA_MINC_ENABLE;                  // Increment memory address
    591   dmaHal.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;    // Memory is byte aligned
    592 
    593   if (HAL_DMA_Init(&dmaHal) != HAL_OK){                  // Init DMA with settings
    594     // Insert error message here?
    595     return DMA_Enabled = false;
    596   };
    597   #if (TFT_SPI_PORT == 1)
    598     HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn);  // Enable DMA end interrupt handler
    599   #elif (TFT_SPI_PORT == 2)
    600     HAL_NVIC_EnableIRQ(DMA1_Stream4_IRQn);  // Enable DMA end interrupt handler
    601   #elif (TFT_SPI_PORT == 3)
    602     HAL_NVIC_EnableIRQ(DMA1_Stream5_IRQn);
    603   #endif
    604 
    605   __HAL_LINKDMA(&spiHal, hdmatx, dmaHal);   // Attach DMA engine to SPI peripheral
    606 
    607   return DMA_Enabled = true;
    608 }
    609 
    610 #elif defined (STM32F1xx) // Supports "Blue Pill" boards
    611 /***************************************************************************************
    612 ** Function name:           DMA1_ChannelX_IRQHandler
    613 ** Description:             Override the default HAL stream 3 interrupt handler
    614 ***************************************************************************************/
    615   #if (TFT_SPI_PORT == 1)
    616     extern "C" void DMA1_Channel3_IRQHandler();
    617     void DMA1_Channel3_IRQHandler(void)
    618   #elif (TFT_SPI_PORT == 2)
    619     extern "C" void DMA1_Channel5_IRQHandler();
    620     void DMA1_Channel5_IRQHandler(void)
    621   #endif
    622   {
    623     // Call the default end of buffer handler
    624     HAL_DMA_IRQHandler(&dmaHal);
    625   }
    626 
    627 //*/
    628 /***************************************************************************************
    629 ** Function name:           initDMA
    630 ** Description:             Initialise the DMA engine - returns true if init OK
    631 ***************************************************************************************/
    632 bool TFT_eSPI::initDMA(bool ctrl_cs)
    633 {
    634   ctrl_cs = ctrl_cs; // Not used for STM32, so stop compiler warning
    635 
    636   __HAL_RCC_DMA1_CLK_ENABLE();                           // Enable DMA1 clock
    637 
    638   dmaHal.Init.Mode =  DMA_NORMAL; //DMA_CIRCULAR;   //   // Normal = send buffer once
    639   dmaHal.Init.Direction = DMA_MEMORY_TO_PERIPH;          // Copy memory to the peripheral
    640   dmaHal.Init.PeriphInc = DMA_PINC_DISABLE;              // Don't increment peripheral address
    641   dmaHal.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; // Peripheral is byte aligned
    642   dmaHal.Init.MemInc = DMA_MINC_ENABLE;                  // Increment memory address
    643   dmaHal.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;    // Memory is byte aligned
    644   dmaHal.Init.Priority = DMA_PRIORITY_LOW;               // Added this line - needed ?
    645 
    646   __HAL_LINKDMA(&spiHal, hdmatx, dmaHal); // Attach DMA engine to SPI peripheral
    647 
    648   if (HAL_DMA_Init(&dmaHal) != HAL_OK){                  // Init DMA with settings
    649     // Insert error message here?
    650     return DMA_Enabled = false;
    651   };
    652 
    653   #if (TFT_SPI_PORT == 1)
    654     HAL_NVIC_SetPriority(DMA1_Channel3_IRQn, 1, 0);
    655     HAL_NVIC_EnableIRQ(DMA1_Channel3_IRQn);  // Enable DMA end interrupt handler
    656   #elif (TFT_SPI_PORT == 2)
    657     HAL_NVIC_SetPriority(DMA1_Channel5_IRQn, 1, 0);
    658     HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);  // Enable DMA end interrupt handler
    659   #endif
    660 
    661   return DMA_Enabled = true;
    662 }
    663 #endif // End of STM32F1/2/4/7xx
    664 
    665 /***************************************************************************************
    666 ** Function name:           deInitDMA
    667 ** Description:             Disconnect the DMA engine from SPI
    668 ***************************************************************************************/
    669 void TFT_eSPI::deInitDMA(void)
    670 {
    671   HAL_DMA_DeInit(&dmaHal);
    672   DMA_Enabled = false;
    673 }
    674 
    675 ////////////////////////////////////////////////////////////////////////////////////////
    676 #endif // End of DMA FUNCTIONS    
    677 ////////////////////////////////////////////////////////////////////////////////////////