// Sketch to display images on a 480 x 320 ILI9486 Raspberry Pi 3.5" TFT (Waveshare design) // which has a 16 bit serial interface based on 74HC04, 74HC4040 and 2 x 74HC4094 logic chips // Renders images stored in an array in program (FLASH)JPEG images are stored in header files // (see jpeg1.h etc) // The sketch does not need the SD or sdFat libraries since it does not access an SD Card. // As well as the TFT_eSPI library: // https://github.com/Bodmer/TFT_eSPI // the sketch need the JPEG Decoder library. This can be loaded via the Library Manager. // or can be downloaded here: // https://github.com/Bodmer/JPEGDecoder //---------------------------------------------------------------------------------------------------- #include #include TFT_eSPI tft = TFT_eSPI(); // JPEG decoder library #include // Return the minimum of two values a and b #define minimum(a,b) (((a) < (b)) ? (a) : (b)) // Include the sketch header file that contains the image stored as an array of bytes // More than one image array could be stored in each header file. #include "jpeg1.h" #include "jpeg2.h" #include "jpeg3.h" #include "jpeg4.h" // Count how many times the image is drawn for test purposes uint32_t icount = 0; //---------------------------------------------------------------------------------------------------- //#################################################################################################### // Setup //#################################################################################################### void setup() { Serial.begin(115200); tft.begin(); } //#################################################################################################### // Main loop //#################################################################################################### void loop() { tft.setRotation(2); // portrait tft.fillScreen(random(0xFFFF)); // The image is 300 x 300 pixels so we do some sums to position image in the middle of the screen! // Doing this by reading the image width and height from the jpeg info is left as an exercise! int x = (tft.width() - 300) / 2 - 1; int y = (tft.height() - 300) / 2 - 1; drawArrayJpeg(EagleEye, sizeof(EagleEye), x, y); // Draw a jpeg image stored in memory at x,y delay(2000); tft.setRotation(2); // portrait tft.fillScreen(random(0xFFFF)); drawArrayJpeg(Baboon40, sizeof(Baboon40), 0, 0); // Draw a jpeg image stored in memory delay(2000); tft.setRotation(2); // portrait tft.fillScreen(random(0xFFFF)); drawArrayJpeg(lena20k, sizeof(lena20k), 0, 0); // Draw a jpeg image stored in memory delay(2000); tft.setRotation(1); // landscape tft.fillScreen(random(0xFFFF)); // This image will be deliberately cropped as it is 480 x 320 thes extends off the screen when plotted // at coordinate 100,100 drawArrayJpeg(Mouse480, sizeof(Mouse480), 100, 100); // Draw a jpeg image stored in memory, test cropping //drawArrayJpeg(Mouse480, sizeof(Mouse480), 0, 0); // Draw a jpeg image stored in memory delay(2000); } //#################################################################################################### // Draw a JPEG on the TFT pulled from a program memory array //#################################################################################################### void drawArrayJpeg(const uint8_t arrayname[], uint32_t array_size, int xpos, int ypos) { int x = xpos; int y = ypos; JpegDec.decodeArray(arrayname, array_size); jpegInfo(); // Print information from the JPEG file (could comment this line out) renderJPEG(x, y); Serial.println("#########################"); } //#################################################################################################### // Draw a JPEG on the TFT, images will be cropped on the right/bottom sides if they do not fit //#################################################################################################### // This function assumes xpos,ypos is a valid screen coordinate. For convenience images that do not // fit totally on the screen are cropped to the nearest MCU size and may leave right/bottom borders. void renderJPEG(int xpos, int ypos) { // retrieve infomration about the image uint16_t *pImg; uint16_t mcu_w = JpegDec.MCUWidth; uint16_t mcu_h = JpegDec.MCUHeight; uint32_t max_x = JpegDec.width; uint32_t max_y = JpegDec.height; // Jpeg images are draw as a set of image block (tiles) called Minimum Coding Units (MCUs) // Typically these MCUs are 16x16 pixel blocks // Determine the width and height of the right and bottom edge image blocks uint32_t min_w = minimum(mcu_w, max_x % mcu_w); uint32_t min_h = minimum(mcu_h, max_y % mcu_h); // save the current image block size uint32_t win_w = mcu_w; uint32_t win_h = mcu_h; // record the current time so we can measure how long it takes to draw an image uint32_t drawTime = millis(); // save the coordinate of the right and bottom edges to assist image cropping // to the screen size max_x += xpos; max_y += ypos; // read each MCU block until there are no more while (JpegDec.read()) { // save a pointer to the image block pImg = JpegDec.pImage ; // calculate where the image block should be drawn on the screen int mcu_x = JpegDec.MCUx * mcu_w + xpos; // Calculate coordinates of top left corner of current MCU int mcu_y = JpegDec.MCUy * mcu_h + ypos; // check if the image block size needs to be changed for the right edge if (mcu_x + mcu_w <= max_x) win_w = mcu_w; else win_w = min_w; // check if the image block size needs to be changed for the bottom edge if (mcu_y + mcu_h <= max_y) win_h = mcu_h; else win_h = min_h; // copy pixels into a contiguous block if (win_w != mcu_w) { uint16_t *cImg; int p = 0; cImg = pImg + win_w; for (int h = 1; h < win_h; h++) { p += mcu_w; for (int w = 0; w < win_w; w++) { *cImg = *(pImg + w + p); cImg++; } } } // calculate how many pixels must be drawn uint32_t mcu_pixels = win_w * win_h; tft.startWrite(); // draw image MCU block only if it will fit on the screen if (( mcu_x + win_w ) <= tft.width() && ( mcu_y + win_h ) <= tft.height()) { // Now set a MCU bounding window on the TFT to push pixels into (x, y, x + width - 1, y + height - 1) tft.setAddrWindow(mcu_x, mcu_y, win_w, win_h); // Write all MCU pixels to the TFT window while (mcu_pixels--) { // Push each pixel to the TFT MCU area tft.pushColor(*pImg++); } } else if ( (mcu_y + win_h) >= tft.height()) JpegDec.abort(); // Image has run off bottom of screen so abort decoding tft.endWrite(); } // calculate how long it took to draw the image drawTime = millis() - drawTime; // print the results to the serial port Serial.print(F( "Total render time was : ")); Serial.print(drawTime); Serial.println(F(" ms")); Serial.println(F("")); } //#################################################################################################### // Print image information to the serial port (optional) //#################################################################################################### void jpegInfo() { Serial.println(F("===============")); Serial.println(F("JPEG image info")); Serial.println(F("===============")); Serial.print(F( "Width :")); Serial.println(JpegDec.width); Serial.print(F( "Height :")); Serial.println(JpegDec.height); Serial.print(F( "Components :")); Serial.println(JpegDec.comps); Serial.print(F( "MCU / row :")); Serial.println(JpegDec.MCUSPerRow); Serial.print(F( "MCU / col :")); Serial.println(JpegDec.MCUSPerCol); Serial.print(F( "Scan type :")); Serial.println(JpegDec.scanType); Serial.print(F( "MCU width :")); Serial.println(JpegDec.MCUWidth); Serial.print(F( "MCU height :")); Serial.println(JpegDec.MCUHeight); Serial.println(F("===============")); } //#################################################################################################### // Show the execution time (optional) //#################################################################################################### // WARNING: for UNO/AVR legacy reasons printing text to the screen with the Mega might not work for // sketch sizes greater than ~70KBytes because 16 bit address pointers are used in some libraries. // The Due will work fine with the HX8357_Due library. void showTime(uint32_t msTime) { //tft.setCursor(0, 0); //tft.setTextFont(1); //tft.setTextSize(2); //tft.setTextColor(TFT_WHITE, TFT_BLACK); //tft.print(F(" JPEG drawn in ")); //tft.print(msTime); //tft.println(F(" ms ")); Serial.print(F(" JPEG drawn in ")); Serial.print(msTime); Serial.println(F(" ms ")); }