// Example for library: // https://github.com/Bodmer/TJpg_Decoder // This example renders a Jpeg file that is stored in an array within Flash (program) memory // see panda.h tab. The panda image file being ~13Kbytes. #define USE_DMA // Include the array #include "panda.h" // Include the jpeg decoder library #include #ifdef USE_DMA uint16_t dmaBuffer1[16*16]; // Toggle buffer for 16*16 MCU block, 512bytes uint16_t dmaBuffer2[16*16]; // Toggle buffer for 16*16 MCU block, 512bytes uint16_t* dmaBufferPtr = dmaBuffer1; bool dmaBufferSel = 0; #endif // Include the TFT library https://github.com/Bodmer/TFT_eSPI #include "SPI.h" #include // Hardware-specific library TFT_eSPI tft = TFT_eSPI(); // Invoke custom library // This next function will be called during decoding of the jpeg file to render each // 16x16 or 8x8 image tile (Minimum Coding Unit) to the TFT. bool tft_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t* bitmap) { // Stop further decoding as image is running off bottom of screen if ( y >= tft.height() ) return 0; // STM32F767 processor takes 43ms just to decode (and not draw) jpeg (-Os compile option) // Total time to decode and also draw to TFT: // SPI 54MHz=71ms, with DMA 50ms, 71-43 = 28ms spent drawing, so DMA is complete before next MCU block is ready // Apparent performance benefit of DMA = 71/50 = 42%, 50 - 43 = 7ms lost elsewhere // SPI 27MHz=95ms, with DMA 52ms. 95-43 = 52ms spent drawing, so DMA is *just* complete before next MCU block is ready! // Apparent performance benefit of DMA = 95/52 = 83%, 52 - 43 = 9ms lost elsewhere #ifdef USE_DMA // Double buffering is used, the bitmap is copied to the buffer by pushImageDMA() the // bitmap can then be updated by the jpeg decoder while DMA is in progress if (dmaBufferSel) dmaBufferPtr = dmaBuffer2; else dmaBufferPtr = dmaBuffer1; dmaBufferSel != dmaBufferSel; // Toggle buffer selection // pushImageDMA() will clip the image block at screen boundaries before initiating DMA tft.pushImageDMA(x, y, w, h, bitmap, dmaBufferPtr); // Initiate DMA - blocking only if last DMA is not complete // The DMA transfer of image block to the TFT is now in progress... #else // Non-DMA blocking alternative tft.pushImage(x, y, w, h, bitmap); // Blocking, so only returns when image block is drawn #endif // Return 1 to decode next block. return 1; } void setup() { Serial.begin(115200); Serial.println("\n\n Testing TJpg_Decoder library"); // Initialise the TFT tft.begin(); tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.fillScreen(TFT_BLACK); #ifdef USE_DMA tft.initDMA(); // To use SPI DMA you must call initDMA() to setup the DMA engine #endif // The jpeg image can be scaled down by a factor of 1, 2, 4, or 8 TJpgDec.setJpgScale(1); // The colour byte order can be swapped by the decoder // using TJpgDec.setSwapBytes(true); or by the TFT_eSPI library: tft.setSwapBytes(true); // The decoder must be given the exact name of the rendering function above TJpgDec.setCallback(tft_output); } void loop() { // Show a contrasting colour for demo of draw speed tft.fillScreen(TFT_RED); // Get the width and height in pixels of the jpeg if you wish: uint16_t w = 0, h = 0; TJpgDec.getJpgSize(&w, &h, panda, sizeof(panda)); Serial.print("Width = "); Serial.print(w); Serial.print(", height = "); Serial.println(h); // Time recorded for test purposes uint32_t dt = millis(); // Must use startWrite first so TFT chip select stays low during DMA and SPI channel settings remain configured tft.startWrite(); // Draw the image, top left at 0,0 - DMA request is handled in the call-back tft_output() in this sketch TJpgDec.drawJpg(0, 0, panda, sizeof(panda)); // Must use endWrite to release the TFT chip select and release the SPI channel tft.endWrite(); // How much time did rendering take (ESP8266 80MHz 262ms, 160MHz 149ms, ESP32 SPI 111ms, 8bit parallel 90ms dt = millis() - dt; Serial.print(dt); Serial.println(" ms"); // Wait before drawing again delay(2000); }