Forum Rule: Always post complete source code & details to reproduce any issue!
Page 1 of 5 1 2 3 ... LastLast
Results 1 to 25 of 106

Thread: ILI9341 with fullscreen DMA Buffer for Teensy 3.5 / Teensy 3.6 only

  1. #1
    Moderator Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    4,346

    ILI9341 with fullscreen DMA Buffer for Teensy 3.5 / Teensy 3.6 only

    Hi,

    i'm working on a library that provides DMA for the ILI9341. It uses a full screen buffer (size:153600 Bytes).
    A first minimal version is already working.
    First tests show that full-screen refreshes with >50 Hz (@ 144MHz CPU) are possible, with overclocking the bus.

    I plan to adopt some of the functions of the original ILI9341_t3 lib - so here is my Question:

    Which functions of ILI9341_t3 do you need, and which are not needed anymore ?

    50Hz is fast enough for video - which is the best way to feed the Teensy with videodata ? Which software on PC-side can be used ?

    Edit:
    Preview here:
    https://github.com/FrankBoesing/ILI9...9341_t3DMA.cpp
    (but don't expect a flawlessly working version or documentation...)
    Last edited by Frank B; 09-04-2016 at 09:13 PM.

  2. #2
    Moderator Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    4,346
    Here is a very first test (CPU only 144MHz in this test.. will be much faster with 240Mhz) :
    Code:
    ILI9341 Test!
    Display Power Mode: 0xEF
    MADCTL Mode: 0x36
    Pixel Format: 0x3
    Image Format: 0x0
    Self Diagnostic: 0xF0
    Benchmark                Time (microseconds)
    Screen fill              1947
    Text                     0
    Lines                    8289
    Horiz/Vert Lines         2279
    Rectangles (outline)     1449
    Rectangles (filled)      22524
    Circles (filled)         8550
    Circles (outline)        3529
    Triangles (outline)      2219
    Triangles (filled)       18537
    Rounded rects (outline)  2459
    Rounded rects (filled)   37538
    Done!
    Text is not yet implemented.

    Both libs, the ILI9341_t3 and the new one, ILI9341_t3DMA can be used concurrently. Of course, you must stop the dma-refresh to use the old ones.


    There are some new functions:

    Code:
        void refresh(void);    //starts continously refreshing the screen
        void stopRefresh(void);  //stops refreshing the screen
        void refreshOnce(void); //one single screen refresh
        void wait(void); //waits until current refresh is done
    Edit: Test with 240MHz:
    Code:
    ILI9341 Test! Display Power Mode: 0xCE
     MADCTL Mode: 0x24
     Pixel Format: 0x2
     Image Format: 0x0
     Self Diagnostic: 0xE0
     Benchmark                Time (microseconds)
     Screen fill              1165
     Lines                    4966
     Horiz/Vert Lines         1366
     Rectangles (outline)     869
     Rectangles (filled)      13500
     Circles (filled)         5126
     Circles (outline)        2112
     Triangles (outline)      1328
     Triangles (filled)       11117
     Rounded rects (outline)  1473
     Rounded rects (filled)   22506
     Done!
    Automatic screen refresh was enabled during these tests.



    There are ony still-pictures... this is because, the test runs way too fast. For example 1.1 milliseconds for screen-fill - test ... (which is internally 5 screen-fills)

    EDIT:
    Test with original ILI93411_t3: (240MHz)
    Code:
    ILI9341 Test! Display Power Mode: 0xCE
     MADCTL Mode: 0x24
     Pixel Format: 0x2
     Image Format: 0x0
     Self Diagnostic: 0xE0
     Benchmark                Time (microseconds)
     Screen fill              112458
     Text                     6297
     Lines                    29230
     Horiz/Vert Lines         9205
     Rectangles (outline)     5844
     Rectangles (filled)      233934
     Circles (filled)         35479
     Circles (outline)        28396
     Triangles (outline)      7068
     Triangles (filled)       77386
     Rounded rects (outline)  12811
     Rounded rects (filled)   255385
     Done!
    Last edited by Frank B; 09-04-2016 at 09:31 PM.

  3. #3
    Moderator KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    3,072
    Hi Frank,

    Looks interesting. Would be interesting to see the difference in speed here versus standard one.

    Also assuming you are currently only doing for SPI and not SPI1 and SPI2? As I mentioned in beta threads, I have version that runs on the different SPI busses on both 3.5 and 3.6.
    However you run into issues like:

    SPI1/2 only have a queue of 1 item not 4.

    You are limited on CS pins. On SPI1 there is only 1 CS configured (So made library work using hardware CS for DC but software for CS. If SDCard pins available then have 3 cs pins

    More importantly for your case, DMA for SPI on T3.5, SPI1 and SPI2 have both source and target as the same DMA source... So not sure how well it will work. So for example DMASPI test cases fail on SPI1 and SPI2 on T3.5.

  4. #4
    Moderator Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    4,346
    Quote Originally Posted by KurtE View Post
    Hi Frank,

    Looks interesting. Would be interesting to see the difference in speed here versus standard one.

    Also assuming you are currently only doing for SPI and not SPI1 and SPI2? As I mentioned in beta threads, I have version that runs on the different SPI busses on both 3.5 and 3.6.
    However you run into issues like:

    SPI1/2 only have a queue of 1 item not 4.

    You are limited on CS pins. On SPI1 there is only 1 CS configured (So made library work using hardware CS for DC but software for CS. If SDCard pins available then have 3 cs pins

    More importantly for your case, DMA for SPI on T3.5, SPI1 and SPI2 have both source and target as the same DMA source... So not sure how well it will work. So for example DMASPI test cases fail on SPI1 and SPI2 on T3.5.
    Hi Kurt,
    the ili9341_t3 benchmarks are here in the forum. There is a large ILI9341-thread ... (sry, i have no link at the moment). The test is the same (ok, "text" isn't ready, yet)
    It is not directly comparable, because the new library-functions (like dfillRect or ddrawLine) do not more than accessing the RAM - all DMA is done in the background, (almost) without cpu-usage.
    The old lib had to write to the SPI and wait for transfers. I guess it is minimum 10..100 times slower.

    If DMA works with SPI1 or SPI2 - would be great ! I need no fifo.
    It will take some time until i can test it.

    Edit:
    first, i want make the text-outputs and some anti-aliasing or alphablending (which is way more easy now, due to the ram-buffer..)

    Edit: i guess, the next teensy needs 1MB RAM.... this would allow really cool things..
    Last edited by Frank B; 09-04-2016 at 09:17 PM.

  5. #5
    Moderator Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    4,346
    @kurt, i added a benchmark for the original ILI9341_t3 in post #2

  6. #6
    Moderator KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    3,072
    Sounds great:

    I did a quick run of the display on Beta 1 board using Standard ILI9341_t3 library and see:
    Code:
    ILI9341 Test!
    Display Power Mode: 0xCE
    MADCTL Mode: 0x24
    Pixel Format: 0x2
    Image Format: 0x0
    Self Diagnostic: 0xE0
    Benchmark                Time (microseconds)
    Screen fill              224684
    Text                     10682
    Lines                    58320
    Horiz/Vert Lines         18360
    Rectangles (outline)     11671
    Rectangles (filled)      467095
    Circles (filled)         68159
    Circles (outline)        52127
    Triangles (outline)      14080
    Triangles (filled)       153120
    Rounded rects (outline)  24026
    Rounded rects (filled)   509053
    Done!
    But I am guessing that the times are maybe Apples and Oranges? These times show times for the start of the test until end and made sure that all screen updates are done.
    Warning I may be complete off here, but I would expect that if you did:
    Code:
    unsigned long testFillScreen() {
      unsigned long start = micros();
      tft.fillScreen(ILI9341_BLACK);
      tft.fillScreen(ILI9341_RED);
      tft.fillScreen(ILI9341_GREEN);
      tft.fillScreen(ILI9341_BLUE);
      tft.fillScreen(ILI9341_BLACK);
      return micros() - start;
    }
    And the goal was that you would see a full black screen and then a full red screen and... Then my gut tells me that the tests would take similar time, as I believe we are pretty much maxing out the SPI buss between the processor and the display? as you can see in the Logic Analyzer output:
    Click image for larger version. 

Name:	screenshot.jpg 
Views:	241 
Size:	54.4 KB 
ID:	8018
    But of course the big gains will be that the CPU is not having to hang around while the screen update is happening. Also having the offscreen version will be great for doing some cool stuff.

    Will be very interesting to see how this all plays out.

    Great stuff

  7. #7
    Moderator Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    4,346
    Yes, if you want to see the screen-fills you have to add a wait() or refreshOnce() for the screen-refresh. Then it will be almost equal to the max refresh-rate which depends on the SPI-speed.
    You're right, as i said in post #4, the benchmarks are not directly compareable. But the additional speeds helps to do more screen-updates in less time (and, hopefully, with less flickering)

  8. #8
    Moderator Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    4,346
    Maybe can show a video-player (from SD or USB) in a few days

  9. #9
    Senior Member
    Join Date
    Mar 2013
    Posts
    599
    Quote Originally Posted by Frank B View Post
    Here is a very first test (CPU only 144MHz in this test.. will be much faster with 240Mhz) :
    Code:
    ILI9341 Test!
    Display Power Mode: 0xEF
    MADCTL Mode: 0x36
    Pixel Format: 0x3
    Image Format: 0x0
    Self Diagnostic: 0xF0
    Benchmark                Time (microseconds)
    Screen fill              1947
    Text                     0
    Lines                    8289
    Horiz/Vert Lines         2279
    Rectangles (outline)     1449
    Rectangles (filled)      22524
    Circles (filled)         8550
    Circles (outline)        3529
    Triangles (outline)      2219
    Triangles (filled)       18537
    Rounded rects (outline)  2459
    Rounded rects (filled)   37538
    Done!
    [/code]
    Hello Frank, is that the time the micro needed or the time to complete the entire task and be ready to do more with the screen?

  10. #10
    Moderator Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    4,346
    It is the time of the entire task. Screenfill for example is called 5 times.

  11. #11
    Moderator KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    3,072
    Great stuff, but as you said in #7, you might call Screenfill 5 times, but probably at most the screen has actually drawn once. I think it is great stuff as the majority of the time you are not wanting to flash the screen or the like, but simply doing outputs.

    But to give a better sense of this, it might be interesting if you actually gave two numbers here per test.
    For example to do the Text output, I am sure it did not take 0 time for the stuff to actually show up on the screen.

    So might be nice if you showed delta time for code to run to end of test, plus then do a wait or refresh to actually get the stuff on the screen and then show the delta time including that.

  12. #12
    Moderator Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    4,346
    Quote Originally Posted by KurtE View Post
    Great stuff, but as you said in #7, you might call Screenfill 5 times, but probably at most the screen has actually drawn once. I think it is great stuff as the majority of the time you are not wanting to flash the screen or the like, but simply doing outputs.

    But to give a better sense of this, it might be interesting if you actually gave two numbers here per test.
    For example to do the Text output, I am sure it did not take 0 time for the stuff to actually show up on the screen.

    So might be nice if you showed delta time for code to run to end of test, plus then do a wait or refresh to actually get the stuff on the screen and then show the delta time including that.
    Let me explain how it works..
    There are two ways to refresh the display -

    a) it refreshes automatically, as fast as possible (up to more than 50 Hz). This way is used in the tests above, because i wanted to use the same test sketch which is used by the original library. Indeed, the code is almost identical (I'll upload it). The same way is used in the "Play video" sketch https://forum.pjrc.com/threads/36751...-Card?p=114615 ("play video" uses this library)

    b) you can all a function to refresh it one time (on request, with a) disabled ).

    All drawing operations are done in Teensy-RAM, and are not visible until it's contens are copied to the screen (with a) or b) from above).
    Usually, it makes no sense to call fillScreen and wait for the result. Instead, you want to fillScreen, and call many other functions afterwards - like write some text draw lines or copy an "icon" to the screen. You want to display it when this "page" is ready to display, not every single step. And exactly this is the point where it makes sense that your tasks get done as fast as possible You can wait after all operations are done. Waiting before makes no sense, cause you'll see only a part of the "page".

    Then, a benchmarks which measures delays is not really good.. and pretty useless. The "Graphicstest"-sketch spends most of the time in delays between the tasks ( well, not on other Arduinos.. ;-) )

    No, the textoutput was not ready.... i wrote that in post#2. That was the only "test" which did not work that time. Therfore the "0".
    If you want a "delta"..well, use your own test from above, or an other lib and the "graphicstest" sketch.



    To make it more clear , here is drawPixel():
    Code:
    DMAMEM uint16_t screen[ILI9341_TFTHEIGHT][ILI9341_TFTWIDTH];
    .
    .
    
    void ILI9341_t3DMA::ddrawPixel(int16_t x, int16_t y, uint16_t color) {
      if ((x < 0) || (x >= _width) || (y < 0) || (y >= _height)) return;
      screen[y][x] = color;
    
    }

    p.s.
    way c) is, how all other libraries work. Draw a single pixel and send it to the display. You can see how your "page" renders... you have much overhead to send x/y screen-coordinates (most extreme with a 45 line)
    Last edited by Frank B; 09-09-2016 at 06:25 PM.

  13. #13
    Moderator Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    4,346
    This is the sketch (textoutput works now):
    Code:
    /***************************************************
      This is our GFX example for the Adafruit ILI9341 Breakout and Shield
      ----> http://www.adafruit.com/products/1651
    
      Check out the links above for our tutorials and wiring diagrams
      These displays use SPI to communicate, 4 or 5 pins are required to
      interface (RST is optional)
      Adafruit invests time and resources providing this open source code,
      please support Adafruit and open-source hardware by purchasing
      products from Adafruit!
    
      Written by Limor Fried/Ladyada for Adafruit Industries.
      MIT license, all text above must be included in any redistribution
     ****************************************************/
    
    
    #include "SPI.h"
    #include <ILI9341_t3DMA.h>
    
    #define TFT_DC      15
    #define TFT_CS      10
    #define TFT_RST     4  // 255 = unused, connect to 3.3V
    #define TFT_MOSI    11
    #define TFT_SCLK    13
    #define TFT_MISO    12
    
    ILI9341_t3DMA tft = ILI9341_t3DMA(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCLK, TFT_MISO);
    
    void setup() {
      tft.begin();
    
      //START DMA MODE
      tft.refresh();
        
      tft.dfillScreen(ILI9341_BLACK);
      tft.setTextColor(ILI9341_YELLOW);
      tft.setTextSize(2);
      tft.println("Waiting for Arduino Serial Monitor...");
    
      Serial.begin(9600);
      while (!Serial) ; // wait for Arduino Serial Monitor
      Serial.println("ILI9341 Test!"); 
    /*
      // read diagnostics (optional but can help debug problems)
      uint8_t x = tft.readcommand8(ILI9341_RDMODE);
      Serial.print("Display Power Mode: 0x"); Serial.println(x, HEX);
      x = tft.readcommand8(ILI9341_RDMADCTL);
      Serial.print("MADCTL Mode: 0x"); Serial.println(x, HEX);
      x = tft.readcommand8(ILI9341_RDPIXFMT);
      Serial.print("Pixel Format: 0x"); Serial.println(x, HEX);
      x = tft.readcommand8(ILI9341_RDIMGFMT);
      Serial.print("Image Format: 0x"); Serial.println(x, HEX);
      x = tft.readcommand8(ILI9341_RDSELFDIAG);
      Serial.print("Self Diagnostic: 0x"); Serial.println(x, HEX); 
    */
    
    
      
      Serial.println(F("Benchmark                Time (microseconds)"));
    
      Serial.print(F("Screen fill              "));
      Serial.println(testFillScreen());
      delay(200);
    
      Serial.print(F("Text                     "));
      Serial.println(testText());
      delay(600);
    
      Serial.print(F("Lines                    "));
      Serial.println(testLines(ILI9341_CYAN));
      delay(200);
    
      Serial.print(F("Horiz/Vert Lines         "));
      Serial.println(testFastLines(ILI9341_RED, ILI9341_BLUE));
      delay(200);
    
      Serial.print(F("Rectangles (outline)     "));
      Serial.println(testRects(ILI9341_GREEN));
      delay(200);
    
      Serial.print(F("Rectangles (filled)      "));
      Serial.println(testFilledRects(ILI9341_YELLOW, ILI9341_MAGENTA));
      delay(200);
    
      Serial.print(F("Circles (filled)         "));
      Serial.println(testFilledCircles(10, ILI9341_MAGENTA));
    
      Serial.print(F("Circles (outline)        "));
      Serial.println(testCircles(10, ILI9341_WHITE));
      delay(200);
    
      Serial.print(F("Triangles (outline)      "));
      Serial.println(testTriangles());
      delay(200);
    
      Serial.print(F("Triangles (filled)       "));
      Serial.println(testFilledTriangles());
      delay(200);
    
      Serial.print(F("Rounded rects (outline)  "));
      Serial.println(testRoundRects());
      delay(200);
    
      Serial.print(F("Rounded rects (filled)   "));
      Serial.println(testFilledRoundRects());
      delay(200);
    
      Serial.println(F("Done!"));
      tft.stopRefresh();
    
    
    }
    
    
    void loop(void) {
        
     //Rotation does not work (only on init..) :-(
     /*
      for(uint8_t rotation=0; rotation<4; rotation++) {
        
        testText();
        tft.start();
        tft.fill();   
        tft.setRotation(rotation);  
        delay(1000);
      }
      */
    }
    
    unsigned long testFillScreen() {
      unsigned long start = micros();
      tft.dfillScreen(ILI9341_BLACK);
      tft.dfillScreen(ILI9341_RED);
      tft.dfillScreen(ILI9341_GREEN);
      tft.dfillScreen(ILI9341_BLUE);
      tft.dfillScreen(ILI9341_BLACK);
      return micros() - start;
    }
    
    unsigned long testText() {
    
      tft.dfillScreen(ILI9341_BLACK);
      unsigned long start = micros();
     
      tft.setCursor(0, 0);
      tft.setTextColor(ILI9341_WHITE);  tft.setTextSize(1);
      tft.println("Hello World!");
      tft.setTextColor(ILI9341_YELLOW); tft.setTextSize(2);
      tft.println(1234.56);
      tft.setTextColor(ILI9341_RED);    tft.setTextSize(3);
      tft.println(0xDEADBEEF, HEX);
      tft.println();
      tft.setTextColor(ILI9341_GREEN);
      tft.setTextSize(5);
      tft.println("Groop");
      tft.setTextSize(2);
      tft.println("I implore thee,");
      tft.setTextSize(1);
      tft.println("my foonting turlingdromes.");
      tft.println("And hooptiously drangle me");
      tft.println("with crinkly bindlewurdles,");
      tft.println("Or I will rend thee");
      tft.println("in the gobberwarts");
      tft.println("with my blurglecruncheon,");
      tft.println("see if I don't!");
     
      return micros() - start;
    }
    
    unsigned long testLines(uint16_t color) {
      unsigned long start, t;
      int           x1, y1, x2, y2,
                    w = tft.width(),
                    h = tft.height();
    
      tft.dfillScreen(ILI9341_BLACK);
    
      x1 = y1 = 0;
      y2    = h - 1;
      start = micros();
      for(x2=0; x2<w; x2+=6) tft.ddrawLine(x1, y1, x2, y2, color);
      x2    = w - 1;
      for(y2=0; y2<h; y2+=6) tft.ddrawLine(x1, y1, x2, y2, color);
      t     = micros() - start; // fillScreen doesn't count against timing
    
      tft.dfillScreen(ILI9341_BLACK);
    
      x1    = w - 1;
      y1    = 0;
      y2    = h - 1;
      start = micros();
      for(x2=0; x2<w; x2+=6) tft.ddrawLine(x1, y1, x2, y2, color);
      x2    = 0;
      for(y2=0; y2<h; y2+=6) tft.ddrawLine(x1, y1, x2, y2, color);
      t    += micros() - start;
    
      tft.dfillScreen(ILI9341_BLACK);
    
      x1    = 0;
      y1    = h - 1;
      y2    = 0;
      start = micros();
      for(x2=0; x2<w; x2+=6) tft.ddrawLine(x1, y1, x2, y2, color);
      x2    = w - 1;
      for(y2=0; y2<h; y2+=6) tft.ddrawLine(x1, y1, x2, y2, color);
      t    += micros() - start;
    
      tft.dfillScreen(ILI9341_BLACK);
    
      x1    = w - 1;
      y1    = h - 1;
      y2    = 0;
      start = micros();
      for(x2=0; x2<w; x2+=6) tft.ddrawLine(x1, y1, x2, y2, color);
      x2    = 0;
      for(y2=0; y2<h; y2+=6) tft.ddrawLine(x1, y1, x2, y2, color);
    
      return micros() - start;
    }
    
    unsigned long testFastLines(uint16_t color1, uint16_t color2) {
      unsigned long start;
      int           x, y, w = tft.width(), h = tft.height();
    
      tft.dfillScreen(ILI9341_BLACK);
      start = micros();
      for(y=0; y<h; y+=5) tft.ddrawFastHLine(0, y, w, color1);
      for(x=0; x<w; x+=5) tft.ddrawFastVLine(x, 0, h, color2);
    
      return micros() - start;
    }
    
    unsigned long testRects(uint16_t color) {
      unsigned long start;
      int           n, i, i2,
                    cx = tft.width()  / 2,
                    cy = tft.height() / 2;
    
      tft.dfillScreen(ILI9341_BLACK);
      n     = min(tft.width(), tft.height());
      start = micros();
      for(i=2; i<n; i+=6) {
        i2 = i / 2;
        tft.ddrawRect(cx-i2, cy-i2, i, i, color);
      }
    
      return micros() - start;
    }
    
    unsigned long testFilledRects(uint16_t color1, uint16_t color2) {
      unsigned long start, t = 0;
      int           n, i, i2,
                    cx = tft.width()  / 2 - 1,
                    cy = tft.height() / 2 - 1;
    
      tft.dfillScreen(ILI9341_BLACK);
      n = min(tft.width(), tft.height());
      for(i=n; i>0; i-=6) {
        i2    = i / 2;
        start = micros();
        tft.dfillRect(cx-i2, cy-i2, i, i, color1);
        t    += micros() - start;
        // Outlines are not included in timing results
        tft.ddrawRect(cx-i2, cy-i2, i, i, color2);
      }
    
      return t;
    }
    
    unsigned long testFilledCircles(uint8_t radius, uint16_t color) {
      unsigned long start;
      int x, y, w = tft.width(), h = tft.height(), r2 = radius * 2;
    
      tft.dfillScreen(ILI9341_BLACK);
      start = micros();
      for(x=radius; x<w; x+=r2) {
        for(y=radius; y<h; y+=r2) {
          tft.dfillCircle(x, y, radius, color);
        }
      }
    
      return micros() - start;
    }
    
    unsigned long testCircles(uint8_t radius, uint16_t color) {
      unsigned long start;
      int           x, y, r2 = radius * 2,
                    w = tft.width()  + radius,
                    h = tft.height() + radius;
    
      // Screen is not cleared for this one -- this is
      // intentional and does not affect the reported time.
      start = micros();
      for(x=0; x<w; x+=r2) {
        for(y=0; y<h; y+=r2) {
          tft.ddrawCircle(x, y, radius, color);
        }
      }
    
      return micros() - start;
    }
    
    unsigned long testTriangles() {
      unsigned long start;
      int           n, i, cx = tft.width()  / 2 - 1,
                          cy = tft.height() / 2 - 1;
    
      tft.dfillScreen(ILI9341_BLACK);
      n     = min(cx, cy);
      start = micros();
      for(i=0; i<n; i+=5) {
        tft.ddrawTriangle(
          cx    , cy - i, // peak
          cx - i, cy + i, // bottom left
          cx + i, cy + i, // bottom right
          tft.color565(0, 0, i));
      }
    
      return micros() - start;
    }
    
    unsigned long testFilledTriangles() {
      unsigned long start, t = 0;
      int           i, cx = tft.width()  / 2 - 1,
                       cy = tft.height() / 2 - 1;
    
      tft.dfillScreen(ILI9341_BLACK);
      start = micros();
      for(i=min(cx,cy); i>10; i-=5) {
        start = micros();
        tft.dfillTriangle(cx, cy - i, cx - i, cy + i, cx + i, cy + i,
          tft.color565(0, i, i));
        t += micros() - start;
        tft.ddrawTriangle(cx, cy - i, cx - i, cy + i, cx + i, cy + i,
          tft.color565(i, i, 0));
      }
    
      return t;
    }
    
    unsigned long testRoundRects() {
      unsigned long start;
      int           w, i, i2,
                    cx = tft.width()  / 2 - 1,
                    cy = tft.height() / 2 - 1;
    
      tft.dfillScreen(ILI9341_BLACK);
      w     = min(tft.width(), tft.height());
      start = micros();
      for(i=0; i<w; i+=6) {
        i2 = i / 2;
        tft.ddrawRoundRect(cx-i2, cy-i2, i, i, i/8, tft.color565(i, 0, 0));
      }
    
      return micros() - start;
    }
    
    unsigned long testFilledRoundRects() {
      unsigned long start;
      int           i, i2,
                    cx = tft.width()  / 2 - 1,
                    cy = tft.height() / 2 - 1;
    
      tft.dfillScreen(ILI9341_BLACK);
      start = micros();
      for(i=min(tft.width(), tft.height()); i>20; i-=6) {
        i2 = i / 2;
        tft.dfillRoundRect(cx-i2, cy-i2, i, i, i/8, tft.color565(0, i, 0));
      }
    
      return micros() - start;
    }
    There only minor differences is upon initialization, and the drawing functions are prepended with a "d".

    I think i'll change this and don't use the original ILI9341_t3 lib - too many drawbacks. Have to think about it...
    Last edited by Frank B; 09-09-2016 at 06:34 PM.

  14. #14
    Senior Member
    Join Date
    Mar 2013
    Posts
    599
    The reason I ask about timing is I would like to have more then 1 screen option for the CC Dummy Load project, right now my only easily viable screen is the 2x20 and 4x20 screens. And I would be forced to break up a screen refresh into 10 character segments to avoid colliding with the ADC loop (limitation is the screen not the micro).

    As it stands for me based on my coding skills, the only way I see using any graphical LCD's is if I use a second micro to do all the LCD controls and just send data to it via serial. Using a second micro adds cost and complexity. Which if the screen capabilities are nice enough may be worth it.

    Im currently just trying to figure out what options are available so that I can place any needed circuits on the board. I would like to give anyone making the Dummy Load the option to choose what kind of screen they want. It just comes down to what is feasible based on space/connections.

  15. #15
    Moderator Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    4,346
    Using the text-output on the ili9341 is as easy as printing via serial..
    Just take a look at the library-examples..

  16. #16
    Senior Member
    Join Date
    Mar 2013
    Posts
    599
    Hmm, a touch screen CC Dummy Load is looking far more plausible

    Thank You Frank!

  17. #17
    Moderator Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    4,346
    Hm, i can't promise that touch will work with my DMA-Lib..
    I did not try it.. but it's likely that the ILI-touch-library needs an update..

  18. #18
    Senior Member
    Join Date
    Mar 2013
    Posts
    599
    Quote Originally Posted by Frank B View Post
    Hm, i can't promise that touch will work with my DMA-Lib..
    I did not try it.. but it's likely that the ILI-touch-library needs an update..
    I can use the normal ADC library, I actually had it working with the T3.1 when I was playing with them for my EgoCart Project. It was not insanely accurate but it got the job done. Speaking of the gocart, just got some parts from Digi so back to the shed for me.

  19. #19
    Moderator KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    3,072
    Hi Frank,

    Thought I would mention, that now that the Kickstarter/Beta is over, I have taken a little time playing with my UP board. In particular I have an ILI9341 display connected up to its "RPI Connector" and I had earlier converted the Adafruit code to work on Linux using MRAA (earlier for Edison), which uses spidev to do SPI at user mode. Note: I could take the simple route and use the FBTFT device driver, but was sort of curious how well there user level stuff is working.

    Got it sort of working now on the UP and while it works, the speed is not exactly spectacular...
    Code:
    ILI9341 Test!
    Display Power Mode: 0x9C
    MADCTL Mode: 0x48
    Pixel Format: 0x5
    Image Format: 0x9C
    Self Diagnostic: 0xC0
    Benchmark                Time (microseconds)
    Screen fill              991839
    Text                     680243
    Text 2                   186166
    Lines                    13200669
    Horiz/Vert Lines         144368
    Rectangles (outline)     137679
    Rectangles (filled)      2065566
    Circles (filled)         2922998
    Circles (outline)        5957815
    Triangles (outline)      4217531
    Triangles (filled)       2110649
    Rounded rects (outline)  1840688
    Rounded rects (filled)   3128059
    Why I mention this, is I remember you going to the frame buffer like setup here (although you are using DMA), but wondered how much it would help in my case. So I made a version of the code that output everything to a 320x240x2 byte array and currently I am doing an explicit call of update to say put it to the screen. Again this can go so far speed wise as the SPIDEV appears to have a max transfer size of 4K, so I am updating the display with 3840 byte writes (40 calls for full screen). Again timings are not spectacular, and would expect fill screen might be slower as doing the work to fill memory first and then more or less same calls to SPI... But some of the others are showing pretty good speed ups...

    Code:
    ILI9341 Test!
    Display Power Mode: 0x9C
    MADCTL Mode: 0x48
    Pixel Format: 0x5
    Image Format: 0x9C
    Self Diagnostic: 0x0
    Benchmark                Time (microseconds)
    Screen fill              995694
    Text                     199784
    Text 2                   204201
    Lines                    199464
    Horiz/Vert Lines         203455
    Rectangles (outline)     199383
    Rectangles (filled)      5087
    Circles (filled)         203179
    Circles (outline)        199966
    Triangles (outline)      202983
    Triangles (filled)       2386
    Rounded rects (outline)  203553
    Rounded rects (filled)   210488
    Done!
    Also code still in place for the 4 different orientations...
    Some of the slower timings like Text is because: I am redrawing the whole screen, if I play much more, may keep a dirty range and only update those portions that need updating....

    Or could use the real hardware driver

  20. #20
    Moderator Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    4,346
    Kurt, what is the SPI-Speed in MHz ?

  21. #21
    Moderator KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    3,072
    Hi Frank, that is sort of a hard question to answer. Right now I have it outputting the bytes at 8mhz or 8.33... The UP hardware can go to 25mhz...

    BUT! - The user level to system level SPI driver is a killer! Maybe easier to demonstrate by showing a Logic Analyzer output:

    Click image for larger version. 

Name:	screenshot.jpg 
Views:	168 
Size:	46.6 KB 
ID:	8379
    As you can see, there is a BIG gap between the output of each call to SPIDEV, the good news is once it gets there, there are no gaps between bytes (unlike Edison), so for the fill screen, I can get the 4K of bytes output pretty fast, which I will put in. Currently doing it as even divide of screen size which takes 40 calls with 3840, but at output of 4096 bytes, can get down to 38...

    Also I am having to drive CS/DC using software calls, which adds to these delays as well. But again it is for my own learning, to decide how I wish for UP to talk to Teensy and who does what. More details on my playing with the different things like GPIO, I2C... up on the thread:
    https://up-community.org/forum/gener...d-the-up-board

  22. #22
    Interested. Subbed. thanks.

  23. #23
    Thanks for sharing this awesome work! I've been watching it but I just now got the opportunity to try it out. I have very long wire lengths so I was not really planning on trying it, but I couldn't stop wondering if it would work. So yesterday I opened up my enclosure of my 3.2-based project where I've basically just completed the hardware and starting to write software. I made the necessary modifications to get my new 3.6 in there optionally (i.e. insert either thanks to the pin-compatibility). Long story short, it works beautifully! I haven't gotten to much aside from altering the example to my hardware as a proof of concept, but the performance is pretty astonishing.

    As for your initial question to people using the library - based on my extremely limited experience so far I think the biggest thing I would personally need is to have the rotation function. Not dynamically though like the previous library; I'm not even really sure if that's possible here. But doing it once before allocating the RAM seems possible, correct?

  24. #24
    Moderator Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    4,346
    Rotation is a bit complicated.. but i guess a Single rotation for setup is OK? That would be doable.

    On the 3.6, the lib is fast enough to play video

  25. #25
    Moderator KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    3,072
    Quote Originally Posted by Frank B View Post
    Rotation is a bit complicated.. but i guess a Single rotation for setup is OK? That would be doable.

    On the 3.6, the lib is fast enough to play video
    Hi Frank,

    I think I have rotation working on my Linux version I was playing with yesterday and it did not add that much complication.
    If you interested in looking at it, it is in my RPI github project: https://github.com/KurtE/Raspberry_P...fruit_ILI9341B

    I am not doing DMA, but the stuff I think would work the same. I simply keep the "Frame Buffer" as a single dimensional array. Mine is a member variable:
    Code:
     // Define Frame buffer
      uint8_t _fbtft[320*240*2]; // Define a memory location big enough to hold full screen image
    .

    My drawPixel looks like:
    Code:
    void Adafruit_ILI9341::drawPixel(int16_t x, int16_t y, uint16_t color) {
    
      if((x < 0) ||(x >= _width) || (y < 0) || (y >= _height)) return;
    
      // Lets start off by simply addressing memory
      register uint8_t *pfbtft_pixel = &_fbtft[(y*_width + x) * 2];
      *pfbtft_pixel++  = (color>>8) & 0xff;
      *pfbtft_pixel = color & 0xff;
    }
    Note: I suspect this may add a slight more overhead as I am now multiplying by a member variable versus the two dimensional array case, where you are multiplying by a constant

    And the setRotation looks like:
    Code:
    void Adafruit_ILI9341::setRotation(uint8_t m) {
    
      spi_begin();
      writecommand(ILI9341_MADCTL);
      rotation = m % 4; // can't be higher than 3
      switch (rotation) {
       case 0:
         writedata(MADCTL_MX | MADCTL_BGR);
         _width  = ILI9341_TFTWIDTH;
         _height = ILI9341_TFTHEIGHT;
         break;
       case 1:
         writedata(MADCTL_MV | MADCTL_BGR);
         _width  = ILI9341_TFTHEIGHT;
         _height = ILI9341_TFTWIDTH;
         break;
      case 2:
        writedata(MADCTL_MY | MADCTL_BGR);
         _width  = ILI9341_TFTWIDTH;
         _height = ILI9341_TFTHEIGHT;
        break;
       case 3:
         writedata(MADCTL_MX | MADCTL_MY | MADCTL_MV | MADCTL_BGR);
         _width  = ILI9341_TFTHEIGHT;
         _height = ILI9341_TFTWIDTH;
         break;
      }
      spi_end();
    }
    Again I am explicitly doing the SPI calls to write the data out (SPIDEV) versus using DMA... Actually to be honest, it may be using DMA under me...
    Last edited by KurtE; 10-09-2016 at 11:29 PM.

Posting Permissions

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