Forum Rule: Always post complete source code & details to reproduce any issue!
Page 26 of 26 FirstFirst ... 16 24 25 26
Results 626 to 639 of 639

Thread: Highly optimized ILI9341 (320x240 TFT color display) library

  1. #626
    Senior Member CorBee's Avatar
    Join Date
    Jun 2018
    Location
    Netherlands
    Posts
    507
    Hi,

    Thanks to KurtE for adding the setscrollmargins function, this works completely as planned in our TeensyBat project. I am currently looking for an example that uses this library with a framebuffer located in PSRAM.

    Looking through the examples I havent seen one (maybe missed it) but I saw the library now has a function:
    Code:
    void	setFrameBuffer(uint16_t *frame_buffer);
    Would a standard PSRAM on the T4.1 be fast enough for this purpose ? If nobody has tested this I might dive into that and see what comes up.

    regards
    Cor

  2. #627
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    15,496
    Quote Originally Posted by CorBee View Post
    Hi,

    Thanks to KurtE for adding the setscrollmargins function, this works completely as planned in our TeensyBat project. I am currently looking for an example that uses this library with a framebuffer located in PSRAM.

    Looking through the examples I havent seen one (maybe missed it) but I saw the library now has a function:
    Code:
    void	setFrameBuffer(uint16_t *frame_buffer);
    Would a standard PSRAM on the T4.1 be fast enough for this purpose ? If nobody has tested this I might dive into that and see what comes up.

    regards
    Cor
    KurtE likely to reply in hours ... but it should be worth expecting it to work.

    AFAIK - larger displays needing a buffer that fits only in PSRAM have been tested and designed to work.

    Seems there was testing or a sample on that ... though too long ago to expect to find it in context ... it may have been on MMod or other ...

  3. #628
    Senior Member CorBee's Avatar
    Join Date
    Jun 2018
    Location
    Netherlands
    Posts
    507
    Quote Originally Posted by defragster View Post
    KurtE likely to reply in hours ... but it should be worth expecting it to work.

    AFAIK - larger displays needing a buffer that fits only in PSRAM have been tested and designed to work.

    Seems there was testing or a sample on that ... though too long ago to expect to find it in context ... it may have been on MMod or other ...
    It at least seems to work from code without any problems, since I do use scrolling 1pixel lines in the code I will have to find a good way to do that in memory. The recent
    addition of setscrollmargins was not yet (as KurtE mentions on Github) tested much and scrolling in memory is another "affair". But I am sure that there can be a good
    way to achieve this.

    cheers
    Cor

  4. #629
    My app includes a filled rect which must move left and right one pixel at a time. The simplistic way to move it is to call fillRect with background color and call it again with new coordinates and foreground color. That is slow and causes flicker. A better way is to redraw only the pixels which must change. Call drawFastVLine for the leading edge in foreground color and call it again for the trailing edge in background color. It's efficient and prevents flicker. I call it differential animation.

    I also have a filled circle to move, but that's more complex. You might think to call drawCircleHelper for a semicircle at the trailing edge in background color and call it again for a shifted semicircle in foreground color. But that doesn't quite work because the trailing semicircle removes too many edge pixels in the upper and lower octants. One fix is to draw a shifted full circle in foreground color to restore the edges. That looks good but it really draws about twice as many pixels as needed. An optimal solution is to draw leading and trailing semicircles, but replace the horizontal line segments with only endpoints.

    Here's the code, adapted from drawCircleHelper. I also pulled the first segments out of the loop and combined them to cover the left and right edges completely. I hope someone will find it useful.
    Code:
    // Shift a filled circle left or right by 1 pixel. Draws only leading and trailing edge pixels.
    // Adapted from ILI9341_t3::drawCircleHelper
    void shiftCircle( int16_t x0, int16_t y0, int16_t r, uint16_t lcol, uint16_t rcol) {
      int16_t f = 1 - r;
      int16_t ddFx = 1;
      int16_t ddFy = -2 * r;
      int16_t x = 0;
      int16_t y = r;
      int xold;
    
      xold = x;
      while (f<0) {
        x++;
        ddFx += 2;
        f += ddFx;
      } // draw first line segments
      tft.drawFastVLine(x0+y+1, y0-x, x-xold+x-xold+1, rcol);
      tft.drawPixel(x0+x+1, y0+y, rcol);
      tft.drawPixel(x0+x+1, y0-y, rcol);
      tft.drawPixel(x0-x, y0+y, lcol);
      tft.drawPixel(x0-x, y0-y, lcol);
      tft.drawFastVLine(x0-y, y0-x, x-xold+x-xold+1, lcol);
      xold = x;
      while (x<y) {
        if (f >= 0) {
          y--;
          ddFy += 2;
          f += ddFy;
        }
        x++;
        ddFx += 2;
        f += ddFx;
        if (f >= 0 || x == y) { // time to draw the next line segments
          tft.drawPixel(x0+x+1, y0+y, rcol);
          tft.drawFastVLine(x0+y+1, y0+xold+1, x-xold, rcol);
          tft.drawPixel(x0+x+1, y0-y, rcol);
          tft.drawFastVLine(x0+y+1, y0-x, x-xold, rcol);
          tft.drawFastVLine(x0-y, y0+xold+1, x-xold, lcol);
          tft.drawPixel(x0-x, y0+y, lcol);
          tft.drawFastVLine(x0-y, y0-x, x-xold, lcol);
          tft.drawPixel(x0-x, y0-y, lcol);
          xold = x;
        }
      }
    }

  5. #630
    Member DIYLAB's Avatar
    Join Date
    Jun 2020
    Location
    Germany
    Posts
    64
    Hi,

    is this the right thread to talk about KurtE's ILI9341_t3n?

    I'll make it very short ;o)
    There is a "driver" optimized for the Teensy 4.x that does nothing more than get a framebuffer onto the display as fast as it can, the ILI9341_T4.
    There is an example program which is smoothly impressive at 30MHz SPI, "99 Luftballons".

    Here the test: https://youtu.be/0VGeh5ThRIw
    *It even goes much faster!

    I have now ported the program for the ILI9341_t3n and also used 30MHz SPI clock, here the result: https://youtu.be/0e9oGl-4Ht0

    What am I doing wrong?
    I would like to reach this speed with the ILI9341_t3n.

    Here the used test program for ILI9341_t3n:

    Code:
    #include "SPI.h"
    #include <ILI9341_t3n.h>
    
    // set the pins: here for SPI0 on Teensy 4.0
    // ***  Recall that DC must be on a valid cs pin !!! ***
    #define PIN_SCK         13  // (needed) SCK pin for SPI0 on Teensy 4.0
    #define PIN_MISO        12  // (needed) MISO pin for SPI0 on Teensy 4.0
    #define PIN_MOSI        11  // (needed) MOSI pin for SPI0 on Teensy 4.0
    #define PIN_DC          10  // (needed) CS pin for SPI0 on Teensy 4.0
    #define PIN_RESET        6  // (needed) any pin can be used 
    #define PIN_CS           9  // (needed) any pin can be used
    #define PIN_BACKLIGHT    5  // only required if LED pin from screen is connected to Teensy 
    #define PIN_TOUCH_IRQ  255  // 255 if touch not connected
    #define PIN_TOUCH_CS   255  // 255 if touch not connected
    
    // drawing size in portrait mode
    #define LX  240
    #define LY  320
    
    /** fill a framebuffer with a given color*/
    void clear(uint16_t* fb, uint16_t color = 0) {
        for (int i = 0; i < LX * LY; i++) fb[i] = color;
    }
    
    /** draw a disk centered at (x,y) with radius r and color col on the framebuffer fb */
    void drawDisk(uint16_t* fb, double x, double y, double r, uint16_t col) {
        int xmin = (int)(x - r);
        int xmax = (int)(x + r);
        int ymin = (int)(y - r);
        int ymax = (int)(y + r);
        if (xmin < 0) xmin = 0;
        if (xmax >= LX) xmax = LX - 1;
        if (ymin < 0) ymin = 0;
        if (ymax >= LY) ymax = LY - 1;
        const double r2 = r * r;
        for (int j = ymin; j <= ymax; j++) {
            double dy2 = (y - j) * (y - j);
            for (int i = xmin; i <= xmax; i++) {
                const double dx2 = (x - i) * (x - i);
                if (dx2 + dy2 <= r2) fb[i + (j * LX)] = col;
            }
        }
    }
    
    /** return a uniform in [0,1) */
    double unif() {
        return random(2147483647) / 2147483647.0;
    }
    
    /** a bouncing ball */
    struct Ball {
        double x, y, dirx, diry, r; // position, direction, radius. 
        uint16_t color;
    
        Ball() {
            r = unif() * 25; // random radius
            x = r; // start at the corner
            y = r; //
            dirx = unif() * 5; // direction and speed are random...
            diry = unif() * 5; // ...but not isotropic !
            color = random(65536); // random color
        }
    
        void move() {
            // move
            x += dirx;
            y += diry;
            // and bounce against border
            if (x - r < 0) { x = r;  dirx = -dirx; }
            if (y - r < 0) { y = r;  diry = -diry; }
            if (x > LX - r) { x = LX - r;  dirx = -dirx; }
            if (y > LY - r) { y = LY - r;  diry = -diry; }
        }
    
        void draw(uint16_t* fb) {
            drawDisk(fb, x, y, r, color);
        }
    };
    
    // 99 luftballons
    Ball balls[99];
    
    // Instantiate display object.
    ILI9341_t3n tft = ILI9341_t3n(PIN_CS, PIN_DC, PIN_RESET, PIN_MOSI, PIN_SCK, PIN_MISO);
    
    // Framebuffer
    DMAMEM uint16_t fb[LX * LY];
    
    void setup() {
        tft.begin(30000000);
        tft.setFrameBuffer(fb);
        tft.useFrameBuffer(true);
    
        // make sure backlight is on
        if (PIN_BACKLIGHT != 255) {
            pinMode(PIN_BACKLIGHT, OUTPUT);
            digitalWrite(PIN_BACKLIGHT, HIGH);
        }
    
        tft.setRotation(0);
    }
    
    void loop() {
        tft.fillScreen(ILI9341_BLACK);
    
        // move and then draw all the balls onto the framebuffer
        for (auto& b : balls) {
            b.move();
            b.draw(fb);
        }
    
        tft.updateScreen();
    }

  6. #631
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    7,857
    @DIYLAB
    Tried your sketch on a T4 and a teensy micro mod and worked with begin(30000000). I did use my arducam config for cs= 10. DC =9. and rat = 8. Still have to try on a t41

  7. #632
    Member DIYLAB's Avatar
    Join Date
    Jun 2020
    Location
    Germany
    Posts
    64
    Quote Originally Posted by mjs513 View Post
    Tried your sketch on a T4 and a teensy micro mod and worked with begin(30000000).
    Nice, then you can now also try the original demo from here: https://github.com/vindar/ILI9341_T4.../99luftballons
    The question is, how do you get the ILI9341_t3n just as fast?
    Please compare the two videos I made of both versions.

  8. #633
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    7,857
    Quote Originally Posted by DIYLAB View Post
    Nice, then you can now also try the original demo from here: https://github.com/vindar/ILI9341_T4.../99luftballons
    The question is, how do you get the ILI9341_t3n just as fast?
    Please compare the two videos I made of both versions.
    Well I just finished testing the T4.1 up to 50Mhz without an issue - at 60Mhz your screen stays white with your example sketch.

    As to the second part of your question as to how do you get the ILI9341_t3n just as fast that is a whole different question.

    You need to look at what that library is doing and why the author says its optimized for the T4.x only and does not support the T3.x etc. You might be able to realize faster frame rates by only updating the sections of the screen that you are changing or trying double or triple buffering as is done in the library but that will only be good for the t4.1.

  9. #634
    @KurtE @mjs513
    Hi, can someone list the color TFT libraries that use frame buffers? I am currently writing a library for a graphical user interface and would like to increase the compatibility.

    I thank you!

    https://github.com/sepp89117/Teensy_UI

  10. #635
    Hello, everyone,

    I found a way to accelerate ILI9341_t3n with a double buffer, for my purposes. Here are the results of the graphic test in three stages:
    Code:
    ----- None buffered -----
    Benchmark                Time (microseconds)
    Screen fill              205325
    Text                     10503
    Lines                    70012
    Horiz/Vert Lines         17663
    Rectangles (outline)     11341
    Rectangles (filled)      421561
    Circles (filled)         69425
    Circles (outline)        58405
    Triangles (outline)      16459
    Triangles (filled)       148537
    Rounded rects (outline)  25366
    Rounded rects (filled)   467870
    
    ---- Single buffered ----
    Benchmark                Time (microseconds)
    Screen fill              215476
    Text                     43358
    Lines                    3196949
    Horiz/Vert Lines         1411518
    Rectangles (outline)     680040
    Rectangles (filled)      2294
    Circles (filled)         428258
    Circles (outline)        409702
    Triangles (outline)      413635
    Triangles (filled)       1236
    Rounded rects (outline)  665510
    Rounded rects (filled)   661642
    
    ---- Double buffered ----
    Benchmark                Time (microseconds)
    Screen fill              176706
    Text                     24565
    Lines                    293851
    Horiz/Vert Lines         324239
    Rectangles (outline)     231521
    Rectangles (filled)      2099
    Circles (filled)         368022
    Circles (outline)        332165
    Triangles (outline)      120915
    Triangles (filled)       1238
    Rounded rects (outline)  197219
    Rounded rects (filled)   516751
    With the following code I was able to achieve significantly higher frame rates in the GameBoy emulator:
    Code:
    /*
        for double buffer
    */
    #define SCREEN_WIDTH 240
    #define SCREEN_HEIGHT 320
    #define TFT_BUFFERSIZE (SCREEN_WIDTH * SCREEN_HEIGHT)
    #define DRAW_SCALEFACTOR 1
    #define DRAW_ON_X 0
    #define DRAW_ON_Y 0
    #define CHECK_LENGTH 14 // A value between 10 and 14 gives the best results
    //buffers
    uint16_t frame[TFT_BUFFERSIZE];
    uint16_t dblFrameBuffer[TFT_BUFFERSIZE];
    
    /*
       updateScreen for double buffer
    */
    void updateScreen()
    {
      tft.startWrite(); // Small changes to ILI9341_t3n were necessary so that the data could be written continuously
    
      uint32_t bufferIndex = 0; // Start at index 0
    
      while (bufferIndex < TFT_BUFFERSIZE)
      {
        uint32_t testIndex = bufferIndex;
        uint32_t unchangedLength = 0;
    
        // Get changed length
        while (unchangedLength < CHECK_LENGTH && testIndex < TFT_BUFFERSIZE)
        {
          if (frame[testIndex] != dblFrameBuffer[testIndex])
          {
            dblFrameBuffer[testIndex] = frame[testIndex]; //copy new byte
            unchangedLength = 0;                          //reset unchanged length to 0
          }
          else
          {
            unchangedLength++;
          }
          testIndex++;
        }
        uint32_t changedLength = testIndex - bufferIndex - unchangedLength;
    
        // If something was changed within the CHECK_LENGTH:
        if (changedLength > 0)
        {
          uint16_t currentX = bufferIndex % SCREEN_WIDTH;
          uint32_t changedEndBufferIndex = bufferIndex + changedLength - 1;
          uint16_t changedEndX = changedEndBufferIndex % SCREEN_WIDTH;
          uint16_t changedLines = (currentX + changedLength) / SCREEN_WIDTH + ((currentX + changedLength) % SCREEN_WIDTH != 0 ? 1 : 0);
          uint16_t pxW;
          uint16_t pxH;
          uint16_t ix0;
          uint16_t iy0;
          uint16_t ix1;
          uint16_t iy1;
          uint16_t oneLineLength;
    
          if (changedLines == 1) // If the changes are within a single line
          {
            //Calculate the necessary data for writing the new line
            oneLineLength = changedLength;
            pxW = changedLength * DRAW_SCALEFACTOR;
            pxH = DRAW_SCALEFACTOR;
            ix0 = currentX * DRAW_SCALEFACTOR;
            iy0 = bufferIndex / SCREEN_WIDTH * DRAW_SCALEFACTOR;
            ix1 = ix0 + pxW - 1;
            iy1 = iy0 + pxH - 1;
          }
          else // If the changes affect more than one line
          {
            uint16_t remainToFullLineEnd = SCREEN_WIDTH - 1 - changedEndX;
    
            // Copy the remaining bytes of the line in case there is something new, as we will write that too.
            for (uint16_t r = 0; r < remainToFullLineEnd; r++)
            {
              dblFrameBuffer[changedEndBufferIndex + r] = frame[changedEndBufferIndex + r];
            }
            //Calculate the necessary data for writing the new window
            oneLineLength = SCREEN_WIDTH;
            changedLength = changedLength + remainToFullLineEnd + currentX;
            bufferIndex -= currentX; //set bufferIndex to x0
            pxW = SCREEN_WIDTH * DRAW_SCALEFACTOR;
            pxH = changedLines * DRAW_SCALEFACTOR;
            ix0 = 0;
            iy0 = bufferIndex / SCREEN_WIDTH * DRAW_SCALEFACTOR;
            ix1 = pxW - 1;
            iy1 = iy0 + pxH - 1;
          }
    
          tft.setAddrWindow(DRAW_ON_X + ix0, DRAW_ON_Y + iy0, DRAW_ON_X + ix1, DRAW_ON_Y + iy1, true);
    
          // Write to SPI
          for (uint16_t iCL = 0; iCL < changedLines; iCL++) // Number of changed lines
          {
            for (uint8_t yScale = 0; yScale < DRAW_SCALEFACTOR; yScale++) // Repeat the line in the Y-direction according to the scale
            {
              for (uint32_t iLL = 0; iLL < oneLineLength; iLL++) // Number of bytes per line
              {
                for (uint8_t xScale = 0; xScale < DRAW_SCALEFACTOR; xScale++) // Repeat writing a pixel according to scale
                {
                  tft.pushColor(dblFrameBuffer[bufferIndex + iCL * oneLineLength + iLL], true);
                }
              }
            }
          }
          bufferIndex += changedLength; // Continue with bufferIndex after the changes found
        }
        else
        {
          bufferIndex += CHECK_LENGTH; // No changes within CHECK_LENGTH
        }
      }
      tft.endWrite();
    }
    @KurtE, @mjs513
    I would be happy if there is interest in it and it may be included in your libs!

  11. #636
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    9,583
    I've compiled ttf2bdf for windows: https://github.com/FrankBoesing/TFT_...tf2bdf_windows

  12. #637
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    9,583
    ...updated the google-fronts repo. It now contains more than 3300 converted *.ttf

    https://github.com/FrankBoesing/fonts

    Github reaches a limit here... tried to just upload a *.zip... did not work.. so back to individual directories.. but it shows max 1000
    So, only way to use it, is to download the whole repo.

    I also extended the font sizes: 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22, 24, 26, 28, 32, 40, 48, 60, 72, 96

    Converting took more than 3 hrs

  13. #638
    @frankB. You are my hero! Thanks for converting these fonts. I'm sure to make use of them.

  14. #639
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    9,583
    You're welcome.

    Anyone can do it themselves now. Either with Linux - as described by Paul - or with Windows now. I have translated the necessary programs to windows, and written a batch that does everything.

    https://github.com/FrankBoesing/TFT_...extras/windows

    Call of the batch:
    Code:
    tftcovert.cmd filemask [-r] [sizes]
    - filemask is a (optional path +) + file(s)
    - r recourse subdirectories (optional)
    -optional sizes

    Example:
    Code:
    tftconvert e:\fonts\*.ttf -r "8,9,10"
    Converts all *.tff in e:\fonts + subdirectories with sizes 8,9,10

    Code:
    tftconvert e:\fonts\pretty.ttf
    Converts pretty.ttf only.

    Without the sizes parameter, a default is used.
    All three files (both .exe and the batch) must be in the same directory (of your choice)

    It was intended for an other library, but hey, it's useful here, too - I hope.

    Have fun,
    Frank

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •