acid-drop- Hacking the planet from a LilyGo T-Deck using custom firmware |
git clone git://git.acid.vegas/acid-drop.git |
Log | Files | Refs | Archive | README | LICENSE |
TFT_eSPI_RP2040.c (25768B)
1 //////////////////////////////////////////////////// 2 // TFT_eSPI generic driver functions // 3 //////////////////////////////////////////////////// 4 5 //////////////////////////////////////////////////////////////////////////////////////// 6 // Global variables 7 //////////////////////////////////////////////////////////////////////////////////////// 8 9 #if !defined (RP2040_PIO_INTERFACE) // SPI 10 11 // Select the SPI port and board package to use 12 #ifdef ARDUINO_ARCH_MBED 13 // Arduino RP2040 board package 14 MbedSPI spi = MbedSPI(TFT_MISO, TFT_MOSI, TFT_SCLK); 15 #else 16 // Community RP2040 board package by Earle Philhower 17 //SPIClass& spi = SPI; // will use board package default pins 18 SPIClassRP2040 spi = SPIClassRP2040(SPI_X, TFT_MISO, -1, TFT_SCLK, TFT_MOSI); 19 #endif 20 21 #else // PIO interface used (8 bit parallel or SPI) 22 23 #ifdef RP2040_PIO_SPI 24 #if defined (SPI_18BIT_DRIVER) 25 // SPI PIO code for 18 bit colour transmit 26 #include "pio_SPI_18bit.pio.h" 27 #else 28 // SPI PIO code for 16 bit colour transmit 29 #include "pio_SPI.pio.h" 30 #endif 31 #elif defined (TFT_PARALLEL_8_BIT) 32 #if defined (SSD1963_DRIVER) 33 // PIO code for 8 bit parallel interface (18 bit colour) 34 #include "pio_8bit_parallel_18bpp.pio.h" 35 #else 36 // PIO code for 8 bit parallel interface (16 bit colour) 37 #include "pio_8bit_parallel.pio.h" 38 #endif 39 #else // must be TFT_PARALLEL_16_BIT 40 // PIO code for 16 bit parallel interface (16 bit colour) 41 #include "pio_16bit_parallel.pio.h" 42 #endif 43 44 // Board package specific differences 45 #ifdef ARDUINO_ARCH_MBED 46 // Not supported at the moment 47 #error The Arduino RP2040 MBED board package is not supported when PIO is used. Use the community package by Earle Philhower. 48 #endif 49 50 // Community RP2040 board package by Earle Philhower 51 PIO tft_pio = pio0; // Code will try both pio's to find a free SM 52 int8_t pio_sm = 0; // pioinit will claim a free one 53 // Updated later with the loading offset of the PIO program. 54 uint32_t program_offset = 0; 55 56 // SM stalled mask 57 uint32_t pull_stall_mask = 0; 58 59 // SM jump instructions to change SM behaviour 60 uint32_t pio_instr_jmp8 = 0; 61 uint32_t pio_instr_fill = 0; 62 uint32_t pio_instr_addr = 0; 63 64 // SM "set" instructions to control DC control signal 65 uint32_t pio_instr_set_dc = 0; 66 uint32_t pio_instr_clr_dc = 0; 67 68 #endif 69 70 #ifdef RP2040_DMA 71 int32_t dma_tx_channel; 72 dma_channel_config dma_tx_config; 73 #endif 74 75 //////////////////////////////////////////////////////////////////////////////////////// 76 #if defined (TFT_SDA_READ) && !defined (RP2040_PIO_INTERFACE) 77 //////////////////////////////////////////////////////////////////////////////////////// 78 79 /*************************************************************************************** 80 ** Function name: tft_Read_8 81 ** Description: Bit bashed SPI to read bidirectional SDA line 82 ***************************************************************************************/ 83 uint8_t TFT_eSPI::tft_Read_8(void) 84 { 85 uint8_t ret = 0; 86 87 /* 88 for (uint8_t i = 0; i < 8; i++) { // read results 89 ret <<= 1; 90 SCLK_L; 91 if (digitalRead(TFT_MOSI)) ret |= 1; 92 SCLK_H; 93 } 94 */ 95 96 ret = spi.transfer(0x00); 97 98 return ret; 99 } 100 101 /*************************************************************************************** 102 ** Function name: beginSDA 103 ** Description: Detach SPI from pin to permit software SPI 104 ***************************************************************************************/ 105 void TFT_eSPI::begin_SDA_Read(void) 106 { 107 // Release configured SPI port for SDA read 108 spi.end(); 109 } 110 111 /*************************************************************************************** 112 ** Function name: endSDA 113 ** Description: Attach SPI pins after software SPI 114 ***************************************************************************************/ 115 void TFT_eSPI::end_SDA_Read(void) 116 { 117 // Configure SPI port ready for next TFT access 118 spi.begin(); 119 } 120 121 //////////////////////////////////////////////////////////////////////////////////////// 122 #endif // #if defined (TFT_SDA_READ) 123 //////////////////////////////////////////////////////////////////////////////////////// 124 125 //////////////////////////////////////////////////////////////////////////////////////// 126 #if defined (RP2040_PIO_INTERFACE) 127 //////////////////////////////////////////////////////////////////////////////////////// 128 #ifdef RP2040_PIO_SPI 129 void pioinit(uint32_t clock_freq) { 130 131 // Find a free SM on one of the PIO's 132 tft_pio = pio0; 133 134 /* 135 pio_sm = pio_claim_unused_sm(tft_pio, false); // false means don't panic 136 // Try pio1 if SM not found 137 if (pio_sm < 0) { 138 tft_pio = pio1; 139 pio_sm = pio_claim_unused_sm(tft_pio, true); // panic this time if no SM is free 140 } 141 */ 142 143 // Find enough free space on one of the PIO's 144 tft_pio = pio0; 145 if (!pio_can_add_program(tft_pio, &tft_io_program)) { 146 tft_pio = pio1; 147 if (!pio_can_add_program(tft_pio, &tft_io_program)) { 148 Serial.println("No room for PIO program!"); 149 return; 150 } 151 } 152 153 pio_sm = pio_claim_unused_sm(tft_pio, false); 154 155 // Load the PIO program 156 program_offset = pio_add_program(tft_pio, &tft_io_program); 157 158 // Associate pins with the PIO 159 pio_gpio_init(tft_pio, TFT_DC); 160 pio_gpio_init(tft_pio, TFT_SCLK); 161 pio_gpio_init(tft_pio, TFT_MOSI); 162 163 // Configure the pins to be outputs 164 pio_sm_set_consecutive_pindirs(tft_pio, pio_sm, TFT_DC, 1, true); 165 pio_sm_set_consecutive_pindirs(tft_pio, pio_sm, TFT_SCLK, 1, true); 166 pio_sm_set_consecutive_pindirs(tft_pio, pio_sm, TFT_MOSI, 1, true); 167 168 // Configure the state machine 169 pio_sm_config c = tft_io_program_get_default_config(program_offset); 170 171 sm_config_set_set_pins(&c, TFT_DC, 1); 172 // Define the single side-set pin 173 sm_config_set_sideset_pins(&c, TFT_SCLK); 174 // Define the pin used for data output 175 sm_config_set_out_pins(&c, TFT_MOSI, 1); 176 // Set clock divider, frequency is set up to 2% faster than specified, or next division down 177 uint16_t clock_div = 0.98 + clock_get_hz(clk_sys) / (clock_freq * 2.0); // 2 cycles per bit 178 sm_config_set_clkdiv(&c, clock_div); 179 // Make a single 8 words FIFO from the 4 words TX and RX FIFOs 180 sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); 181 // The OSR register shifts to the left, sm designed to send MS byte of a colour first, autopull off 182 sm_config_set_out_shift(&c, false, false, 0); 183 // Now load the configuration 184 pio_sm_init(tft_pio, pio_sm, program_offset + tft_io_offset_start_tx, &c); 185 186 // Start the state machine. 187 pio_sm_set_enabled(tft_pio, pio_sm, true); 188 189 // Create the pull stall bit mask 190 pull_stall_mask = 1u << (PIO_FDEBUG_TXSTALL_LSB + pio_sm); 191 192 // Create the assembler instruction for the jump to byte send routine 193 pio_instr_jmp8 = pio_encode_jmp(program_offset + tft_io_offset_start_8); 194 pio_instr_fill = pio_encode_jmp(program_offset + tft_io_offset_block_fill); 195 pio_instr_addr = pio_encode_jmp(program_offset + tft_io_offset_set_addr_window); 196 197 pio_instr_set_dc = pio_encode_set((pio_src_dest)0, 1); 198 pio_instr_clr_dc = pio_encode_set((pio_src_dest)0, 0); 199 } 200 #else // 8 or 16 bit parallel 201 void pioinit(uint16_t clock_div, uint16_t fract_div) { 202 203 // Find a free SM on one of the PIO's 204 tft_pio = pio0; 205 pio_sm = pio_claim_unused_sm(tft_pio, false); // false means don't panic 206 // Try pio1 if SM not found 207 if (pio_sm < 0) { 208 tft_pio = pio1; 209 pio_sm = pio_claim_unused_sm(tft_pio, true); // panic this time if no SM is free 210 } 211 /* 212 // Find enough free space on one of the PIO's 213 tft_pio = pio0; 214 if (!pio_can_add_program(tft_pio, &tft_io_program) { 215 tft_pio = pio1; 216 if (!pio_can_add_program(tft_pio, &tft_io_program) { 217 Serial.println("No room for PIO program!"); 218 while(1) delay(100); 219 return; 220 } 221 } 222 */ 223 #if defined (TFT_PARALLEL_8_BIT) 224 uint8_t bits = 8; 225 #else // must be TFT_PARALLEL_16_BIT 226 uint8_t bits = 16; 227 #endif 228 229 // Load the PIO program 230 program_offset = pio_add_program(tft_pio, &tft_io_program); 231 232 // Associate pins with the PIO 233 pio_gpio_init(tft_pio, TFT_DC); 234 pio_gpio_init(tft_pio, TFT_WR); 235 236 for (int i = 0; i < bits; i++) { 237 pio_gpio_init(tft_pio, TFT_D0 + i); 238 } 239 240 // Configure the pins to be outputs 241 pio_sm_set_consecutive_pindirs(tft_pio, pio_sm, TFT_DC, 1, true); 242 pio_sm_set_consecutive_pindirs(tft_pio, pio_sm, TFT_WR, 1, true); 243 pio_sm_set_consecutive_pindirs(tft_pio, pio_sm, TFT_D0, bits, true); 244 245 // Configure the state machine 246 pio_sm_config c = tft_io_program_get_default_config(program_offset); 247 // Define the set pin 248 sm_config_set_set_pins(&c, TFT_DC, 1); 249 // Define the single side-set pin 250 sm_config_set_sideset_pins(&c, TFT_WR); 251 // Define the consecutive pins that are used for data output 252 sm_config_set_out_pins(&c, TFT_D0, bits); 253 // Set clock divider and fractional divider 254 sm_config_set_clkdiv_int_frac(&c, clock_div, fract_div); 255 // Make a single 8 words FIFO from the 4 words TX and RX FIFOs 256 sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); 257 // The OSR register shifts to the left, sm designed to send MS byte of a colour first 258 sm_config_set_out_shift(&c, false, false, 0); 259 // Now load the configuration 260 pio_sm_init(tft_pio, pio_sm, program_offset + tft_io_offset_start_tx, &c); 261 262 // Start the state machine. 263 pio_sm_set_enabled(tft_pio, pio_sm, true); 264 265 // Create the pull stall bit mask 266 pull_stall_mask = 1u << (PIO_FDEBUG_TXSTALL_LSB + pio_sm); 267 268 // Create the instructions for the jumps to send routines 269 pio_instr_jmp8 = pio_encode_jmp(program_offset + tft_io_offset_start_8); 270 pio_instr_fill = pio_encode_jmp(program_offset + tft_io_offset_block_fill); 271 pio_instr_addr = pio_encode_jmp(program_offset + tft_io_offset_set_addr_window); 272 273 // Create the instructions to set and clear the DC signal 274 pio_instr_set_dc = pio_encode_set((pio_src_dest)0, 1); 275 pio_instr_clr_dc = pio_encode_set((pio_src_dest)0, 0); 276 } 277 #endif 278 279 /*************************************************************************************** 280 ** Function name: pushBlock - for generic processor and parallel display 281 ** Description: Write a block of pixels of the same colour 282 ***************************************************************************************/ 283 #ifdef RP2040_PIO_PUSHBLOCK 284 // PIO handles pixel block fill writes 285 void TFT_eSPI::pushBlock(uint16_t color, uint32_t len) 286 { 287 #if defined (SPI_18BIT_DRIVER) || (defined (SSD1963_DRIVER) && defined (TFT_PARALLEL_8_BIT)) 288 uint32_t col = ((color & 0xF800)<<8) | ((color & 0x07E0)<<5) | ((color & 0x001F)<<3); 289 if (len) { 290 WAIT_FOR_STALL; 291 tft_pio->sm[pio_sm].instr = pio_instr_fill; 292 293 TX_FIFO = col; 294 TX_FIFO = --len; // Decrement first as PIO sends n+1 295 } 296 #else 297 if (len) { 298 WAIT_FOR_STALL; 299 tft_pio->sm[pio_sm].instr = pio_instr_fill; 300 301 TX_FIFO = color; 302 TX_FIFO = --len; // Decrement first as PIO sends n+1 303 } 304 #endif 305 } 306 307 #else 308 void TFT_eSPI::pushBlock(uint16_t color, uint32_t len){ 309 310 while (len > 4) { 311 // 5 seems to be the optimum for maximum transfer rate 312 WAIT_FOR_FIFO_FREE(5); 313 TX_FIFO = color; 314 TX_FIFO = color; 315 TX_FIFO = color; 316 TX_FIFO = color; 317 TX_FIFO = color; 318 319 len -= 5; 320 } 321 322 if (len) { 323 // There could be a maximum of 4 words left to send 324 WAIT_FOR_FIFO_FREE(4); 325 while (len--) TX_FIFO = color; 326 } 327 } 328 #endif 329 330 /*************************************************************************************** 331 ** Function name: pushPixels - for generic processor and parallel display 332 ** Description: Write a sequence of pixels 333 ***************************************************************************************/ 334 void TFT_eSPI::pushPixels(const void* data_in, uint32_t len){ 335 #if defined (SPI_18BIT_DRIVER) || (defined (SSD1963_DRIVER) && defined (TFT_PARALLEL_8_BIT)) 336 uint16_t *data = (uint16_t*)data_in; 337 if (_swapBytes) { 338 while ( len-- ) { 339 uint32_t col = *data++; 340 tft_Write_16(col); 341 } 342 } 343 else { 344 while ( len-- ) { 345 uint32_t col = *data++; 346 tft_Write_16S(col); 347 } 348 } 349 #else 350 const uint16_t *data = (uint16_t*)data_in; 351 352 // PIO sends MS byte first, so bytes are already swapped on transmit 353 if(_swapBytes) { 354 while (len > 4) { 355 WAIT_FOR_FIFO_FREE(5); 356 TX_FIFO = data[0]; 357 TX_FIFO = data[1]; 358 TX_FIFO = data[2]; 359 TX_FIFO = data[3]; 360 TX_FIFO = data[4]; 361 data += 5; 362 len -= 5; 363 } 364 365 if (len) { 366 WAIT_FOR_FIFO_FREE(4); 367 while(len--) TX_FIFO = *data++; 368 } 369 } 370 else { 371 while (len > 4) { 372 WAIT_FOR_FIFO_FREE(5); 373 TX_FIFO = data[0] << 8 | data[0] >> 8; 374 TX_FIFO = data[1] << 8 | data[1] >> 8; 375 TX_FIFO = data[2] << 8 | data[2] >> 8; 376 TX_FIFO = data[3] << 8 | data[3] >> 8; 377 TX_FIFO = data[4] << 8 | data[4] >> 8; 378 data += 5; 379 len -= 5; 380 } 381 382 if (len) { 383 WAIT_FOR_FIFO_FREE(4); 384 while(len--) { 385 TX_FIFO = *data << 8 | *data >> 8; 386 data++; 387 } 388 } 389 } 390 #endif 391 } 392 393 /*************************************************************************************** 394 ** Function name: GPIO direction control - supports class functions 395 ** Description: Set parallel bus to INPUT or OUTPUT 396 ***************************************************************************************/ 397 void TFT_eSPI::busDir(uint32_t mask, uint8_t mode) 398 { 399 // Avoid warnings 400 mask = mask; 401 mode = mode; 402 /* 403 // mask is unused for generic processor 404 // Arduino native functions suited well to a generic driver 405 pinMode(TFT_D0, mode); 406 pinMode(TFT_D1, mode); 407 pinMode(TFT_D2, mode); 408 pinMode(TFT_D3, mode); 409 pinMode(TFT_D4, mode); 410 pinMode(TFT_D5, mode); 411 pinMode(TFT_D6, mode); 412 pinMode(TFT_D7, mode); 413 */ 414 } 415 416 /*************************************************************************************** 417 ** Function name: GPIO direction control - supports class functions 418 ** Description: Faster GPIO pin input/output switch 419 ***************************************************************************************/ 420 void TFT_eSPI::gpioMode(uint8_t gpio, uint8_t mode) 421 { 422 // Avoid warnings 423 gpio = gpio; 424 mode = mode; 425 426 } 427 428 /*************************************************************************************** 429 ** Function name: read byte - supports class functions 430 ** Description: Read a byte - parallel bus only - not supported yet 431 ***************************************************************************************/ 432 uint8_t TFT_eSPI::readByte(void) 433 { 434 uint8_t b = 0; 435 /* 436 busDir(0, INPUT); 437 digitalWrite(TFT_RD, LOW); 438 439 b |= digitalRead(TFT_D0) << 0; 440 b |= digitalRead(TFT_D1) << 1; 441 b |= digitalRead(TFT_D2) << 2; 442 b |= digitalRead(TFT_D3) << 3; 443 b |= digitalRead(TFT_D4) << 4; 444 b |= digitalRead(TFT_D5) << 5; 445 b |= digitalRead(TFT_D6) << 6; 446 b |= digitalRead(TFT_D7) << 7; 447 448 digitalWrite(TFT_RD, HIGH); 449 busDir(0, OUTPUT); 450 */ 451 return b; 452 } 453 454 //////////////////////////////////////////////////////////////////////////////////////// 455 #elif defined (RPI_WRITE_STROBE) // For RPi TFT with write strobe 456 //////////////////////////////////////////////////////////////////////////////////////// 457 458 /*************************************************************************************** 459 ** Function name: pushBlock - for ESP32 or RP2040 RPi TFT 460 ** Description: Write a block of pixels of the same colour 461 ***************************************************************************************/ 462 void TFT_eSPI::pushBlock(uint16_t color, uint32_t len){ 463 464 if(len) { tft_Write_16(color); len--; } 465 while(len--) {WR_L; WR_H;} 466 } 467 468 /*************************************************************************************** 469 ** Function name: pushPixels - for ESP32 or RP2040 RPi TFT 470 ** Description: Write a sequence of pixels 471 ***************************************************************************************/ 472 void TFT_eSPI::pushPixels(const void* data_in, uint32_t len) 473 { 474 uint16_t *data = (uint16_t*)data_in; 475 476 if (_swapBytes) while ( len-- ) {tft_Write_16S(*data); data++;} 477 else while ( len-- ) {tft_Write_16(*data); data++;} 478 } 479 480 //////////////////////////////////////////////////////////////////////////////////////// 481 #elif defined (SPI_18BIT_DRIVER) // SPI 18 bit colour 482 //////////////////////////////////////////////////////////////////////////////////////// 483 484 /*************************************************************************************** 485 ** Function name: pushBlock - for RP2040 and 3 byte RGB display 486 ** Description: Write a block of pixels of the same colour 487 ***************************************************************************************/ 488 void TFT_eSPI::pushBlock(uint16_t color, uint32_t len) 489 { 490 uint16_t r = (color & 0xF800)>>8; 491 uint16_t g = (color & 0x07E0)>>3; 492 uint16_t b = (color & 0x001F)<<3; 493 494 // If more than 32 pixels then change to 16 bit transfers with concatenated pixels 495 if (len > 32) { 496 uint32_t rg = r<<8 | g; 497 uint32_t br = b<<8 | r; 498 uint32_t gb = g<<8 | b; 499 // Must wait before changing to 16 bit 500 while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; 501 hw_write_masked(&spi_get_hw(SPI_X)->cr0, (16 - 1) << SPI_SSPCR0_DSS_LSB, SPI_SSPCR0_DSS_BITS); 502 while ( len > 1 ) { 503 while (!spi_is_writable(SPI_X)){}; spi_get_hw(SPI_X)->dr = rg; 504 while (!spi_is_writable(SPI_X)){}; spi_get_hw(SPI_X)->dr = br; 505 while (!spi_is_writable(SPI_X)){}; spi_get_hw(SPI_X)->dr = gb; 506 len -= 2; 507 } 508 // Must wait before changing back to 8 bit 509 while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; 510 hw_write_masked(&spi_get_hw(SPI_X)->cr0, (8 - 1) << SPI_SSPCR0_DSS_LSB, SPI_SSPCR0_DSS_BITS); 511 } 512 513 // Mop up the remaining pixels 514 while ( len-- ) {tft_Write_8N(r);tft_Write_8N(g);tft_Write_8N(b);} 515 } 516 517 /*************************************************************************************** 518 ** Function name: pushPixels - for RP2040 and 3 byte RGB display 519 ** Description: Write a sequence of pixels 520 ***************************************************************************************/ 521 void TFT_eSPI::pushPixels(const void* data_in, uint32_t len){ 522 523 uint16_t *data = (uint16_t*)data_in; 524 if (_swapBytes) { 525 while ( len-- ) { 526 uint32_t col = *data++; 527 tft_Write_16(col); 528 } 529 } 530 else { 531 while ( len-- ) { 532 uint32_t col = *data++; 533 tft_Write_16S(col); 534 } 535 } 536 } 537 538 //////////////////////////////////////////////////////////////////////////////////////// 539 #else // Standard SPI 16 bit colour TFT 540 //////////////////////////////////////////////////////////////////////////////////////// 541 542 /*************************************************************************************** 543 ** Function name: pushBlock - for RP2040 544 ** Description: Write a block of pixels of the same colour 545 ***************************************************************************************/ 546 void TFT_eSPI::pushBlock(uint16_t color, uint32_t len){ 547 while(len--) 548 { 549 while (!spi_is_writable(SPI_X)){}; 550 spi_get_hw(SPI_X)->dr = (uint32_t)color; 551 } 552 } 553 554 /*************************************************************************************** 555 ** Function name: pushPixels - for RP2040 556 ** Description: Write a sequence of pixels 557 ***************************************************************************************/ 558 void TFT_eSPI::pushPixels(const void* data_in, uint32_t len){ 559 uint16_t *data = (uint16_t*)data_in; 560 if (_swapBytes) { 561 while(len--) 562 { 563 while (!spi_is_writable(SPI_X)){}; 564 spi_get_hw(SPI_X)->dr = (uint32_t)(*data++); 565 } 566 } 567 else 568 { 569 while(len--) 570 { 571 uint16_t color = *data++; 572 color = color >> 8 | color << 8; 573 while (!spi_is_writable(SPI_X)){}; 574 spi_get_hw(SPI_X)->dr = (uint32_t)color; 575 } 576 } 577 } 578 579 //////////////////////////////////////////////////////////////////////////////////////// 580 #endif // End of display interface specific functions 581 //////////////////////////////////////////////////////////////////////////////////////// 582 583 584 //////////////////////////////////////////////////////////////////////////////////////// 585 #ifdef RP2040_DMA // DMA functions for 16 bit SPI and 8/16 bit parallel displays 586 //////////////////////////////////////////////////////////////////////////////////////// 587 /* 588 These are created in header file: 589 uint32_t dma_tx_channel; 590 dma_channel_config dma_tx_config; 591 */ 592 593 /*************************************************************************************** 594 ** Function name: dmaBusy 595 ** Description: Check if DMA is busy 596 ***************************************************************************************/ 597 bool TFT_eSPI::dmaBusy(void) { 598 if (!DMA_Enabled) return false; 599 600 if (dma_channel_is_busy(dma_tx_channel)) return true; 601 602 #if !defined (RP2040_PIO_INTERFACE) 603 // For SPI must also wait for FIFO to flush and reset format 604 while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; 605 hw_write_masked(&spi_get_hw(SPI_X)->cr0, (16 - 1) << SPI_SSPCR0_DSS_LSB, SPI_SSPCR0_DSS_BITS); 606 #endif 607 608 return false; 609 } 610 611 /*************************************************************************************** 612 ** Function name: dmaWait 613 ** Description: Wait until DMA is over (blocking!) 614 ***************************************************************************************/ 615 void TFT_eSPI::dmaWait(void) 616 { 617 while (dma_channel_is_busy(dma_tx_channel)); 618 619 #if !defined (RP2040_PIO_INTERFACE) 620 // For SPI must also wait for FIFO to flush and reset format 621 while (spi_get_hw(SPI_X)->sr & SPI_SSPSR_BSY_BITS) {}; 622 hw_write_masked(&spi_get_hw(SPI_X)->cr0, (16 - 1) << SPI_SSPCR0_DSS_LSB, SPI_SSPCR0_DSS_BITS); 623 #endif 624 } 625 626 /*************************************************************************************** 627 ** Function name: pushPixelsDMA 628 ** Description: Push pixels to TFT 629 ***************************************************************************************/ 630 void TFT_eSPI::pushPixelsDMA(uint16_t* image, uint32_t len) 631 { 632 if ((len == 0) || (!DMA_Enabled)) return; 633 634 dmaWait(); 635 636 channel_config_set_bswap(&dma_tx_config, !_swapBytes); 637 638 #if !defined (RP2040_PIO_INTERFACE) 639 dma_channel_configure(dma_tx_channel, &dma_tx_config, &spi_get_hw(SPI_X)->dr, (uint16_t*)image, len, true); 640 #else 641 dma_channel_configure(dma_tx_channel, &dma_tx_config, &tft_pio->txf[pio_sm], (uint16_t*)image, len, true); 642 #endif 643 } 644 645 /*************************************************************************************** 646 ** Function name: pushImageDMA 647 ** Description: Push image to a window 648 ***************************************************************************************/ 649 // This will clip to the viewport 650 void TFT_eSPI::pushImageDMA(int32_t x, int32_t y, int32_t w, int32_t h, uint16_t* image, uint16_t* buffer) 651 { 652 if ((x >= _vpW) || (y >= _vpH) || (!DMA_Enabled)) return; 653 654 int32_t dx = 0; 655 int32_t dy = 0; 656 int32_t dw = w; 657 int32_t dh = h; 658 659 if (x < _vpX) { dx = _vpX - x; dw -= dx; x = _vpX; } 660 if (y < _vpY) { dy = _vpY - y; dh -= dy; y = _vpY; } 661 662 if ((x + dw) > _vpW ) dw = _vpW - x; 663 if ((y + dh) > _vpH ) dh = _vpH - y; 664 665 if (dw < 1 || dh < 1) return; 666 667 uint32_t len = dw*dh; 668 669 if (buffer == nullptr) { 670 buffer = image; 671 dmaWait(); 672 } 673 674 // If image is clipped, copy pixels into a contiguous block 675 if ( (dw != w) || (dh != h) ) { 676 for (int32_t yb = 0; yb < dh; yb++) { 677 memmove((uint8_t*) (buffer + yb * dw), (uint8_t*) (image + dx + w * (yb + dy)), dw << 1); 678 } 679 } 680 // else, if a buffer pointer has been provided copy whole image to the buffer 681 else if (buffer != image || _swapBytes) { 682 memcpy(buffer, image, len*2); 683 } 684 685 dmaWait(); // In case we did not wait earlier 686 687 setAddrWindow(x, y, dw, dh); 688 689 channel_config_set_bswap(&dma_tx_config, !_swapBytes); 690 691 #if !defined (RP2040_PIO_INTERFACE) 692 dma_channel_configure(dma_tx_channel, &dma_tx_config, &spi_get_hw(SPI_X)->dr, (uint16_t*)buffer, len, true); 693 #else 694 dma_channel_configure(dma_tx_channel, &dma_tx_config, &tft_pio->txf[pio_sm], (uint16_t*)buffer, len, true); 695 #endif 696 } 697 698 /*************************************************************************************** 699 ** Function name: initDMA 700 ** Description: Initialise the DMA engine - returns true if init OK 701 ***************************************************************************************/ 702 bool TFT_eSPI::initDMA(bool ctrl_cs) 703 { 704 if (DMA_Enabled) return false; 705 706 ctrl_cs = ctrl_cs; // stop unused parameter warning 707 708 dma_tx_channel = dma_claim_unused_channel(false); 709 710 if (dma_tx_channel < 0) return false; 711 712 dma_tx_config = dma_channel_get_default_config(dma_tx_channel); 713 714 channel_config_set_transfer_data_size(&dma_tx_config, DMA_SIZE_16); 715 #if !defined (RP2040_PIO_INTERFACE) 716 channel_config_set_dreq(&dma_tx_config, spi_get_index(SPI_X) ? DREQ_SPI1_TX : DREQ_SPI0_TX); 717 #else 718 channel_config_set_dreq(&dma_tx_config, pio_get_dreq(tft_pio, pio_sm, true)); 719 #endif 720 721 DMA_Enabled = true; 722 return true; 723 } 724 725 /*************************************************************************************** 726 ** Function name: deInitDMA 727 ** Description: Disconnect the DMA engine from SPI 728 ***************************************************************************************/ 729 void TFT_eSPI::deInitDMA(void) 730 { 731 if (!DMA_Enabled) return; 732 dma_channel_unclaim(dma_tx_channel); 733 DMA_Enabled = false; 734 } 735 736 //////////////////////////////////////////////////////////////////////////////////////// 737 #endif // End of DMA FUNCTIONS 738 ////////////////////////////////////////////////////////////////////////////////////////