Forum Rule: Always post complete source code & details to reproduce any issue!
Page 5 of 10 FirstFirst ... 3 4 5 6 7 ... LastLast
Results 101 to 125 of 229

Thread: Playing around with yet another version of ILI9341_t3 library (ILI9341_t3n)

  1. #101
    Senior Member
    Join Date
    Mar 2013
    Posts
    651
    Quote Originally Posted by KurtE View Post
    - Looks like I have not migrated the SPIN stuff to the master branch :0 -

    In the mean time you might try the SPIN branch - WIP-SPIN-1-CLASS

    I probably should have archived the earlier master of ILI9341_t3n to use with older SPI implementations...
    Does not like that either. BTW i'm using Arduino 1.6.12 and Teensyduino 1.32.
    Going to install latest of both.


    Code:
    In file included from D:\Arduino\hardware\teensy\avr\libraries\ILI9341_t3n/ILI9341_t3n.h:75:0,
    
                     from C:\Users\Stephen\Box Sync\arduino-1.06\ILI9341\ILI9341_Kurt_Async\ILI9341_Kurt_Async.ino:2:
    
    D:\Arduino\hardware\teensy\avr\libraries\SPIN/SPIN.h: In member function 'bool SPINClass::pinIsMOSI(uint8_t)':
    
    D:\Arduino\hardware\teensy\avr\libraries\SPIN/SPIN.h:56:55: error: 'class SPIClass' has no member named 'pinIsMOSI'
    
         inline bool pinIsMOSI(uint8_t pin)  {return spi().pinIsMOSI(pin);}
    
                                                           ^
    
    D:\Arduino\hardware\teensy\avr\libraries\SPIN/SPIN.h: In member function 'bool SPINClass::pinIsMISO(uint8_t)':
    
    D:\Arduino\hardware\teensy\avr\libraries\SPIN/SPIN.h:57:54: error: 'class SPIClass' has no member named 'pinIsMISO'
    
         inline bool pinIsMISO(uint8_t pin) {return spi().pinIsMISO(pin);}
    
                                                          ^
    
    D:\Arduino\hardware\teensy\avr\libraries\SPIN/SPIN.h: In member function 'bool SPINClass::pinIsSCK(uint8_t)':
    
    D:\Arduino\hardware\teensy\avr\libraries\SPIN/SPIN.h:58:53: error: 'class SPIClass' has no member named 'pinIsSCK'
    
         inline bool pinIsSCK(uint8_t pin) {return spi().pinIsSCK(pin);}
    
                                                         ^
    
    Error compiling for board Teensy 3.6.
    Last edited by Donziboy2; 09-29-2017 at 12:13 AM.

  2. #102
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    5,562
    Sounds like you need the version before Teensyduino 1.37... There is a branch in SPIN for it (Not master)

    Actually looks like my default branch (master) for the ILI9341_t3n is setup for this old version... Was in the process of renaming master to a save version and then rename Async support branch to master, which depends on the newer version of SPIN.

  3. #103
    Senior Member
    Join Date
    Mar 2013
    Posts
    651
    Got it working on Teensyduino 1.39, or at-least I can get it to compile.
    It does not like me calling the Frame buffer, i'm probably doing something wrong now... :/

    Think i'm at the point of opening a bottle and getting drunk for the night lol.

    Code:
    #include "SPI.h"
    #include <ILI9341_t3n.h>
    #define TFT_DC      9
    #define TFT_CS      10
    #define TFT_RST     8  // 255 = unused, connect to 3.3V
    #define TFT_MOSI    11
    #define TFT_SCLK    13
    #define TFT_MISO    12
    
    ILI9341_t3n tft = ILI9341_t3n(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCLK, TFT_MISO);
    uint16_t tft_frame_buffer[ILI9341_TFTWIDTH * ILI9341_TFTHEIGHT];
    
    
    
    void setup() {
    
      tft.begin();
      tft.setFrameBuffer(tft_frame_buffer);
      tft.setRotation(1);
      tft.fillScreen(ILI9341_BLACK);
      tft.setTextColor(ILI9341_YELLOW);
      tft.setTextSize(2);
      tft.println("Waiting for Arduino Serial Monitor...");
      tft.updateScreen();
      Serial.begin(9600);
      while (!Serial) ; // wait for Arduino Serial Monitor
      Serial.println("ILI9341 Test!");
      tft.println("Connected!");
      tft.updateScreen();
    
    }
    
    
    void loop(void) {
    
    
    }
    Code:
    C:\Users\Stephen\AppData\Local\Temp\arduino_build_407557\sketch\ILI9341_Kurt_Async.ino.cpp.o: In function `setup':
    
    C:\Users\Stephen\AppData\Local\Temp\arduino_modified_sketch_559641/ILI9341_Kurt_Async.ino:18: undefined reference to `ILI9341_t3n::setFrameBuffer(unsigned short*)'
    
    collect2.exe: error: ld returned 1 exit status
    
    Error compiling for board Teensy 3.6.

  4. #104
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    5,562
    That is in the newer branch: https://github.com/KurtE/ILI9341_t3n.../ILI9341_t3n.h

    Which I am planning to soon rename master... So you might select that branch... Again this assumes new SPIN...

  5. #105
    Senior Member
    Join Date
    Mar 2013
    Posts
    651
    Yep, going back to main for now, few things I want to work on.

    KurtE, is the frame buffer and DMA transfers tied togeather or can we just send commands with DMA. I've seen in the past where large amounts of time are spent just sending commands to the screen, can that be mitigated with DMA Transfers of commands or is the Frame Buffer required? The two big enemies to me are CPU time and Screen Flicker when it comes to the ILI9341.

  6. #106
    Senior Member
    Join Date
    Mar 2013
    Posts
    651
    Back on the master ILI9341_T3n, got my screen updating pretty well, numbers are still a PITA when your values get large enough to move things around. Need to figure out how to implement drawing text/numbers from right to left instead of just left to right.

    KurtE, i'm going to start playing with touch, will the future addition of the Frame Buffer mess with touch if both share the same pins?

    Edit..
    Also played with powering the LED Backlight directly from 3.32V. Seems to draw around 78.3mA, if the screen is similar to whats used over at Buydisplay then it has 4 Led's each rated at 20mA @ 3.2V.

    Edit2. Updated measured Backlight voltage and current. Crap jumper wires are CRAP!!!!!
    Last edited by Donziboy2; 09-30-2017 at 11:45 PM.

  7. #107
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    5,562
    Sounds good, I still want to cleanup for old versus new versions of the Teensyduino... I know I could probably roll into one version that checks Teensyduino version. Or maybe SPI version and work on both... Probably should, just need to get motivated

    Text output: Not sure when you say right to left, if you are talking about things like output in Hebrew or Arabic or ... Or are we simply talking about right justify text?

    I have no idea of how much support there is in any of the library for right to left text.

    For Right justified numbers, in the past I have done things like, output a number right justified, example for 16 bit numbers assuming <= 65535
    Code:
    void outputNumberRightJutified(uint6_t num) {
      if (num < 10)             tft.print("    ");
      else if (num < 100)     tft.print("   ");
      else if (num < 1000)   tft.print("  ");
      else if (num < 10000) tft.print(" ");
      tft.print(num, DEC);
    }
    Again this is a brute force version...

    As for Touch and Frame buffer. Again depends on things like:
    If you are trying to run the frame buffer update Async, then you should not do any other SPI on the same SPI buss. However as long as the Update is not currently in progress, you should be able to do query to touch device.
    This is assuming SPI interface from your description, and that the Touch controller has different chip select pin.

  8. #108
    Senior Member
    Join Date
    Mar 2013
    Posts
    651
    Ok, maybe im expecting something that is not implemented.... The below code outputs the same time for drawtimelast regardless of it being before or after the tft.updateScreen();
    IR confused

    EDIT... Ok, added tft.useFrameBuffer(true); and got it from 30.27mS to 6.7mS before the update call but oddly its now 51.5mS vs 30.27mS before I added the buffer after calling update. Both seem to take a little while to setup the buffer.

    EDIT2... Yep just confirmed it takes more time(+21mS) to complete the loop using frame buffer then not using it.

    EDIT3... Just poking some sections, it takes 635uS to do the WattHours section of the code with the frame buffer and about 2.8mS using no frame buffer.

    EDIT4... Another interesting thing. Overclocking to 240Mhz with an SPI of 60Mhz halves the times, the increase in cpu speed only slightly lowers the time it takes to do the operations. Frame buffered only saw about a 20% reduction in time needed to setup the buffer before transmission.

    Code:
    #include "SPI.h"
    #include <ILI9341_t3n.h>
    
    
    #define TFT_DC      9
    #define TFT_CS      10
    #define TFT_RST     8  // 255 = unused, connect to 3.3V
    #define TFT_MOSI    11
    #define TFT_SCLK    13
    #define TFT_MISO    12
    
    ILI9341_t3n tft = ILI9341_t3n(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCLK, TFT_MISO);
    
    float WattHours = 0.000;
    
    
    void setup() {
      
    
      tft.begin();
      tft.setRotation(1);
      tft.fillScreen(ILI9341_BLACK);
      tft.setTextColor(ILI9341_YELLOW);
      tft.setTextSize(2);
      tft.println("Waiting for Arduino Serial Monitor...");
      tft.updateScreen();
      Serial.begin(9600);
      while (!Serial) ; // wait for Arduino Serial Monitor
      Serial.println("ILI9341 Test!");
      tft.println("Connected!");
      tft.updateScreen();
    
        delay(5000);
        tft.fillScreen(ILI9341_BLACK);
    
     
    }
     elapsedMillis drawtimer;
     elapsedMicros drawtime;
     uint32_t drawtimelast;
    
    
    void loop(void) {
    
    
     if(drawtimer > 100){
      drawtime = 0;
      tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
      tft.setTextSize(3);     //Upper Left Side
      tft.setCursor(3, 3);
      tft.print("Q1 ");
      tft.print("4.095");
      tft.setCursor(3, 27);
      tft.print("Q2 ");
      tft.print("4.095");
      tft.setCursor(3, 51);
      tft.print("Q3 ");
      tft.print("4.095");
      tft.setCursor(3, 75);
      tft.print("Q4 ");
      tft.print("4.095");
      tft.setCursor(3, 99);
      tft.print("Q5 ");
      tft.print("4.095");
        tft.setTextSize(4);     //Upper Right Side
        tft.setCursor(175, 3);
        tft.print("81.92");
        tft.setCursor(295, 3);    
        tft.print("V");
        tft.setCursor(150, 36);    
        tft.print("300.00");
        tft.setCursor(295, 36);    
        tft.print("W");
        tft.setCursor(175, 69);
        tft.print("20.48");    
        tft.setCursor(295, 69);    
        tft.print("A");
          tft.setTextSize(2);     //Lower Right Side
          tft.setCursor(3, 125);
          tft.print("DAC0 ");
          tft.print("100");
          tft.print(" %");      
          tft.setCursor(143, 125);
          tft.print("DAC1 ");
          tft.print("100");
          tft.print(" %"); 
          tft.setCursor(3, 145);
          tft.print("FAN ");
          tft.setCursor(63, 145);
          tft.print("100");
          tft.print(" %");      
          tft.setCursor(143, 145);
          tft.print("FAN ");
          tft.setCursor(203, 145);
          tft.print("9999");
          tft.print(" RPM");
          tft.setTextSize(3);
          tft.setCursor(3, 180);   //cursor set here unless one of the if is true.      
          if(WattHours < 10){
          tft.print("   "); }
          else if(WattHours < 100){
          tft.print("  ");        }
          else if (WattHours < 1000){      
          tft.print(" ");           }
          tft.print(WattHours, 3);
          WattHours = WattHours + 1.101;    
          tft.print(" WH");
          tft.setCursor(3, 210);
          tft.print("24H 60M 60S ");
          tft.setTextSize(2);      
          tft.print(drawtimelast);
      drawtimelast = drawtime;
        
      tft.updateScreen();
      drawtimer = 0;
    //  drawtimelast = drawtime;
     }
    
      
    }

    Edit4 latest code i'm playing with. Added frame buffer and CPU/FBUS/SPI readout to serial.

    Code:
    #include "SPI.h"
    #include <ILI9341_t3n.h>
    
    
    #define TFT_DC      9
    #define TFT_CS      10
    #define TFT_RST     8  // 255 = unused, connect to 3.3V
    #define TFT_MOSI    11
    #define TFT_SCLK    13
    #define TFT_MISO    12
    
    ILI9341_t3n tft = ILI9341_t3n(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCLK, TFT_MISO);
    
    float WattHours = 0.000;
    
    
    void setup() {
      
    
      tft.begin();
    //  tft.useFrameBuffer(true);
      tft.setRotation(1);
      tft.fillScreen(ILI9341_BLACK);
      tft.setTextColor(ILI9341_YELLOW);
      tft.setTextSize(2);
      tft.println("Waiting for Arduino Serial Monitor...");
      tft.updateScreen();
      Serial.begin(9600);
      while (!Serial) ; // wait for Arduino Serial Monitor
      Serial.println("ILI9341 Test!");
      Serial.print("F_CPU ");
      Serial.print(F_CPU);
      Serial.println(" MHZ");
      Serial.print("F_BUS ");
      Serial.print(F_BUS);
      Serial.println(" MHZ");
      Serial.print("SPICLOCK ");
      Serial.print(F_BUS / 2);
      Serial.println(" MHZ");
     
      tft.println("Connected!");
      tft.updateScreen();
    
        delay(5000);
        tft.fillScreen(ILI9341_BLACK);
    
     
    }
     elapsedMillis drawtimer;
     elapsedMicros drawtime;
     uint32_t drawtimelast;
    
    
    void loop(void) {
    
    
     if(drawtimer > 100){
      drawtime = 0;
      tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
      tft.setTextSize(3);     //Upper Left Side
      tft.setCursor(3, 3);
      tft.print("Q1 ");
      tft.print("4.095");
      tft.setCursor(3, 27);
      tft.print("Q2 ");
      tft.print("4.095");
      tft.setCursor(3, 51);
      tft.print("Q3 ");
      tft.print("4.095");
      tft.setCursor(3, 75);
      tft.print("Q4 ");
      tft.print("4.095");
      tft.setCursor(3, 99);
      tft.print("Q5 ");
      tft.print("4.095");
        tft.setTextSize(4);     //Upper Right Side
        tft.setCursor(175, 3);
        tft.print("81.92");
        tft.setCursor(295, 3);    
        tft.print("V");
        tft.setCursor(150, 36);    
        tft.print("300.00");
        tft.setCursor(295, 36);    
        tft.print("W");
        tft.setCursor(175, 69);
        tft.print("20.48");    
        tft.setCursor(295, 69);    
        tft.print("A");
          tft.setTextSize(2);     //Lower Right Side
          tft.setCursor(3, 125);
          tft.print("DAC0 ");
          tft.print("100");
          tft.print(" %");      
          tft.setCursor(143, 125);
          tft.print("DAC1 ");
          tft.print("100");
          tft.print(" %"); 
          tft.setCursor(3, 145);
          tft.print("FAN ");
          tft.setCursor(63, 145);
          tft.print("100");
          tft.print(" %");      
          tft.setCursor(143, 145);
          tft.print("FAN ");
          tft.setCursor(203, 145);
          tft.print("9999");
          tft.print(" RPM");
          tft.setTextSize(3);
    
          tft.setCursor(3, 180);   //cursor set here unless one of the if is true.      
          if(WattHours < 10){
          tft.print("   "); }
          else if(WattHours < 100){
          tft.print("  ");        }
          else if (WattHours < 1000){      
          tft.print(" ");           }
          tft.print(WattHours, 3);
          WattHours = WattHours + 1.101;    
          tft.print(" WH");
    
          tft.setCursor(3, 210);
          tft.print("24H 60M 60S ");
          tft.setTextSize(2);      
          tft.print(drawtimelast);
          
    //  drawtimelast = drawtime;
    //  drawtime = 0;    
      tft.updateScreen();
      drawtimer = 0;
      drawtimelast = drawtime;
     }
    
      
    }
    Last edited by Donziboy2; 09-30-2017 at 11:08 PM.

  9. #109
    Senior Member
    Join Date
    Mar 2013
    Posts
    651
    Here is a more optimized version of my code from this weekend, it removes a lot of the redrawing of things that wont normally change. This saves about 10mS when not using the frame buffer, it only saves like 3mS with the frame buffer, I believe because much of the time used with the frame buffer is sending the data to the screen. Which makes sense since doubling the SPI from 30 to 60Mhz halved the amount of time the frame buffer needed to complete but sadly added an odd screen flicker, almost like the LED's were flickering. Hoping Kurt will have time to help me with DMA at some point.

    Ive also added the XPT2046 Touch library and it sends the touch data to the screen and to the attached PC. The worse time I've seen for the touch section is about 84uS. I may stop sending to the PC and just start displaying min/max/current times for the 2 time outputs I'm using since its possible the serial communications are affecting the times.


    Code:
    #include "SPI.h"
    #include <ILI9341_t3n.h>
    #include <XPT2046_Touchscreen.h>
    
    #define TFT_DC      9
    #define TFT_CS      10
    #define TFT_RST     8  // 255 = unused, connect to 3.3V
    #define TFT_MOSI    11
    #define TFT_SCLK    13
    #define TFT_MISO    12
    
    #define CS_PIN  4   //Touch CS
    #define TIRQ_PIN  2  //Touch Interupt pin
    
    ILI9341_t3n tft = ILI9341_t3n(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCLK, TFT_MISO);
    XPT2046_Touchscreen ts(CS_PIN, TIRQ_PIN);  // Param 2 - Touch IRQ Pin - interrupt enabled polling
    
    
    float WattHours = 0.000;
    
    
    void setup() {
      
    
      tft.begin();
      ts.begin();
      tft.useFrameBuffer(true);
      tft.setRotation(1);
      tft.fillScreen(ILI9341_BLACK);
      tft.setTextColor(ILI9341_YELLOW);
      tft.setTextSize(2);
      tft.println("Waiting for Arduino Serial Monitor...");
      tft.updateScreen();
      Serial.begin(9600);
      while (!Serial) ; // wait for Arduino Serial Monitor
      Serial.println("ILI9341 Test!");
      Serial.print("F_CPU ");
      Serial.print(F_CPU);
      Serial.println(" MHZ");
      Serial.print("F_BUS ");
      Serial.print(F_BUS);
      Serial.println(" MHZ");
      Serial.print("SPICLOCK ");
      Serial.print(F_BUS / 2);
      Serial.println(" MHZ");
     
      tft.println("Connected!");
      tft.updateScreen();
    
        delay(5000);
        tft.fillScreen(ILI9341_BLACK);
    
     
    }
     elapsedMillis drawtimer;
     elapsedMillis TouchTimer;
     elapsedMicros drawtime;
     elapsedMicros touchtime;
     uint32_t drawtimelast;
     uint32_t touchtimelast;
     byte debugmenu = 0;
     uint16_t posx = 0;
     uint16_t posy = 0;
    
    
    void loop(void) {
    
    if(debugmenu == 0){
      tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
      tft.setTextSize(3);     //Upper Left Side
      tft.setCursor(3, 3);
      tft.print("Q1");
      tft.setCursor(3, 27);  
      tft.print("Q2");
      tft.setCursor(3, 51);  
      tft.print("Q3");
      tft.setCursor(3, 75);  
      tft.print("Q4");
      tft.setCursor(3, 99);  
      tft.print("Q5");
    
        tft.setTextSize(4);     //Upper Right Side
        tft.setCursor(295, 3);    
        tft.print("V");
        tft.setCursor(295, 36);    
        tft.print("W");
        tft.setCursor(295, 69);    
        tft.print("A");
        
          tft.setTextSize(2);     //Lower Right Side
          tft.setCursor(3, 125);
          tft.print("DAC0");
          tft.setCursor(110, 125);
          tft.print("%");    
          tft.setCursor(143, 125);
          tft.print("DAC1");
          tft.setCursor(251, 125);
          tft.print("%"); 
    
          tft.setCursor(3, 145);
          tft.print("FAN");
          tft.setCursor(110, 145);
          tft.print("%");
          tft.setCursor(143, 145);
          tft.print("FAN");
          tft.setCursor(251, 145);      
          tft.print("RPM");
          
          tft.setTextSize(3);
          tft.setCursor(165, 180);
          tft.print("WH");
    
          tft.setCursor(39, 210);
          tft.print("H");
          tft.setCursor(103, 210);
          tft.print("M");
          tft.setCursor(167, 210);
          tft.print("S");
    
          tft.setTextSize(2);
          tft.setCursor(224, 180);            
          tft.print("T");
          tft.setCursor(224, 200);            
          tft.print("X");
          tft.setCursor(224, 220);            
          tft.print("Y");
    
    
          
      debugmenu = 1;
    }
    
    
    
     if(drawtimer > 100){
      drawtime = 0;
      tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
      tft.setTextSize(3);     //Upper Left Side
      tft.setCursor(50, 3);
      tft.print("4.095");     //Q1
      tft.setCursor(50, 27);
      tft.print("4.095");     //Q2
      tft.setCursor(50, 51);
      tft.print("4.095");     //Q3
      tft.setCursor(50, 75);
      tft.print("4.095");     //Q4
      tft.setCursor(50, 99);
      tft.print("4.095");     //Q5
      drawtimelast = drawtime;  
        tft.setTextSize(4);     //Upper Right Side
        tft.setCursor(175, 3);
        tft.print("81.92");    //V
        tft.setCursor(150, 36);    
        tft.print("300.00");   //W
        tft.setCursor(175, 69);
        tft.print("20.48");    //A
          tft.setTextSize(2);     //Lower Right Side
          tft.setCursor(63, 125);
          tft.print("100");    //DAC0    
          tft.setCursor(203, 125);
          tft.print("100");    //DAC1
          tft.setCursor(63, 145);
          tft.print("100");    //FAN
          tft.setCursor(203, 145);
          tft.print("9999");   //RPM
    //drawtime = 0;
          tft.setTextSize(3);
          tft.setCursor(3, 180);   //cursor set here unless one of the if is true.      
          if(WattHours < 10){
          tft.print("   "); }
          else if(WattHours < 100){
          tft.print("  ");        }
          else if (WattHours < 1000){      
          tft.print(" ");           }
          tft.print(WattHours, 3);
          WattHours = WattHours + 0.100;    
    //drawtimelast = drawtime;
          tft.setCursor(3, 210);
          tft.print("24");
          tft.setCursor(67, 210);      
          tft.print("60");
          tft.setCursor(131, 210);      
          tft.print("60");
          tft.setTextSize(2);
          
          tft.setCursor(240, 180);            
          tft.print(drawtimelast);
          tft.setCursor(240, 200);            
          tft.print(posx);
          tft.print(" ");
          tft.setCursor(240, 220);            
          tft.print(posy);      
          tft.print(" ");
                
    //  drawtimelast = drawtime;
    //  drawtime = 0;    
      tft.updateScreen();
      drawtimer = 0;
    //  drawtimelast = drawtime;
     }
    
      if ((ts.touched()) && (TouchTimer > 100)) {
        touchtime = 0;
        TS_Point p = ts.getPoint();
        posx = p.x;
        posy = p.y;
        touchtimelast = touchtime;
        Serial.print("Pressure = ");
        Serial.print(p.z);
        Serial.print(", x = ");
        Serial.print(p.x);
        Serial.print(", y = ");
        Serial.print(p.y);
        Serial.print(", time = ");
        Serial.print(touchtimelast); 
        Serial.println("uS");       
    
        TouchTimer = 0;
      }
    
    
    
      
    }

  10. #110
    Senior Member
    Join Date
    Mar 2013
    Posts
    651
    Its ALIVE!!!!!!!!! Reread post 95 and started poking it with a stick.

    Ok, after a few hours of banging my keyboard and giving my Teensy funny looks I got Async working. I had several gotcha's, one being I was calling tft.updateScreen(); in several places that would cause the Teensy to freeze in it tracks. Another is that the touch screen appears to be causing the Async to go nuts and lock up the Teensy. Not sure if its noise triggering the interrupt or what.

    Loop time is roughly 4.226mS, which is much better then it was. Will have to find ways to improve it if possible.

    Edit It seems to be unstable, it will randomly stop and have to be restarted. Latest run was about 810 seconds.

    Code:
    #include "SPI.h"
    #include <ILI9341_t3n.h>
    //#include <XPT2046_Touchscreen.h>
    
    #define TFT_DC      9
    #define TFT_CS      10
    #define TFT_RST     8  // 255 = unused, connect to 3.3V
    #define TFT_MOSI    11
    #define TFT_SCLK    13
    #define TFT_MISO    12
    
    #define CS_PIN  4   //Touch CS
    #define TIRQ_PIN  2  //Touch Interupt pin
    
    //uint16_t tft_frame_buffer[ILI9341_TFTWIDTH * ILI9341_TFTHEIGHT];
    
    DMAMEM uint16_t ili9341_frame_buffer[ILI9341_TFTWIDTH*ILI9341_TFTHEIGHT];
    
    ILI9341_t3n tft = ILI9341_t3n(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCLK, TFT_MISO, &SPIN);
    //XPT2046_Touchscreen ts(CS_PIN, TIRQ_PIN);  // Param 2 - Touch IRQ Pin - interrupt enabled polling
    
    
    float WattHours = 0.000;
    
    
    void setup() {
      
    
      tft.begin();
    //  ts.begin();
      tft.setFrameBuffer(ili9341_frame_buffer);
      tft.useFrameBuffer(true);  
      tft.setRotation(1);
      tft.fillScreen(ILI9341_BLACK);   
      tft.setTextColor(ILI9341_YELLOW);
      tft.setTextSize(2);
      tft.println("Waiting for Arduino Serial Monitor...");
    //  tft.updateScreen();
      tft.updateScreenAsync();
      Serial.begin(9600);
      while (!Serial) ; // wait for Arduino Serial Monitor
      Serial.println("ILI9341 Test!");
      Serial.print("F_CPU ");
      Serial.print(F_CPU);
      Serial.println(" MHZ");
      Serial.print("F_BUS ");
      Serial.print(F_BUS);
      Serial.println(" MHZ");
      Serial.print("SPICLOCK ");
      Serial.print(F_BUS / 2);
      Serial.println(" MHZ");
     
      tft.println("Connected!");
      tft.updateScreenAsync();  
    //  tft.updateScreen();
    
        delay(5000);
        tft.fillScreen(ILI9341_BLACK);
    //    tft.updateScreenAsync(); 
     
    }
     elapsedMillis drawtimer;
     elapsedMillis TouchTimer;
     elapsedMicros drawtime;
     elapsedMicros touchtime;
     uint32_t drawtimelast;
     uint32_t touchtimelast;
     byte debugmenu = 0;
     uint16_t posx = 0;
     uint16_t posy = 0;
    
    
    void loop(void) {
    
    if(debugmenu == 0){
      tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
      tft.setTextSize(3);     //Upper Left Side
      tft.setCursor(3, 3);
      tft.print("Q1");
      tft.setCursor(3, 27);  
      tft.print("Q2");
      tft.setCursor(3, 51);  
      tft.print("Q3");
      tft.setCursor(3, 75);  
      tft.print("Q4");
      tft.setCursor(3, 99);  
      tft.print("Q5");
    
        tft.setTextSize(4);     //Upper Right Side
        tft.setCursor(295, 3);    
        tft.print("V");
        tft.setCursor(295, 36);    
        tft.print("W");
        tft.setCursor(295, 69);    
        tft.print("A");
        
          tft.setTextSize(2);     //Lower Right Side
          tft.setCursor(3, 125);
          tft.print("DAC0");
          tft.setCursor(110, 125);
          tft.print("%");    
          tft.setCursor(143, 125);
          tft.print("DAC1");
          tft.setCursor(251, 125);
          tft.print("%"); 
    
          tft.setCursor(3, 145);
          tft.print("FAN");
          tft.setCursor(110, 145);
          tft.print("%");
          tft.setCursor(143, 145);
          tft.print("FAN");
          tft.setCursor(251, 145);      
          tft.print("RPM");
          
          tft.setTextSize(3);
          tft.setCursor(165, 180);
          tft.print("WH");
    
          tft.setCursor(39, 210);
          tft.print("H");
          tft.setCursor(103, 210);
          tft.print("M");
          tft.setCursor(167, 210);
          tft.print("S");
    
          tft.setTextSize(2);
          tft.setCursor(224, 180);            
          tft.print("T");
          tft.setCursor(224, 200);            
          tft.print("X");
          tft.setCursor(224, 220);            
          tft.print("Y");
      tft.updateScreenAsync();
    
     TouchTimer = 0;      
      debugmenu = 1;
      drawtimer = 0;
    }
    
    
    
     if(drawtimer > 100){
      drawtime = 0;
      tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
      tft.setTextSize(3);     //Upper Left Side
      tft.setCursor(50, 3);
      tft.print("4.095");     //Q1
      tft.setCursor(50, 27);
      tft.print("4.095");     //Q2
      tft.setCursor(50, 51);
      tft.print("4.095");     //Q3
      tft.setCursor(50, 75);
      tft.print("4.095");     //Q4
      tft.setCursor(50, 99);
      tft.print("4.095");     //Q5
    //  drawtimelast = drawtime;  
        tft.setTextSize(4);     //Upper Right Side
        tft.setCursor(175, 3);
        tft.print("81.92");    //V
        tft.setCursor(150, 36);    
        tft.print("300.00");   //W
        tft.setCursor(175, 69);
        tft.print("20.48");    //A
          tft.setTextSize(2);     //Lower Right Side
          tft.setCursor(63, 125);
          tft.print("100");    //DAC0    
          tft.setCursor(203, 125);
          tft.print("100");    //DAC1
          tft.setCursor(63, 145);
          tft.print("100");    //FAN
          tft.setCursor(203, 145);
          tft.print("9999");   //RPM
    //drawtime = 0;
          tft.setTextSize(3);
          tft.setCursor(3, 180);   //cursor set here unless one of the if is true.      
          if(WattHours < 10){
          tft.print("   "); }
          else if(WattHours < 100){
          tft.print("  ");        }
          else if (WattHours < 1000){      
          tft.print(" ");           }
          tft.print(WattHours, 3);
          WattHours = WattHours + 0.100;    
    //drawtimelast = drawtime;
          tft.setCursor(3, 210);
          tft.print("24");
          tft.setCursor(67, 210);      
          tft.print("60");
          tft.setCursor(131, 210);      
          tft.print("60");
          tft.setTextSize(2);
          
          tft.setCursor(240, 180);            
          tft.print(drawtimelast);
          tft.setCursor(240, 200);            
          tft.print(posx);
          tft.print(" ");
          tft.setCursor(240, 220);            
          tft.print(posy);      
          tft.print(" ");
                
    //  drawtimelast = drawtime;
    //  drawtime = 0;  
         tft.updateScreenAsync();
    //  tft.updateScreen();
      drawtimer = 0;
      drawtimelast = drawtime;
     }
    /*
      if ((ts.touched()) && (TouchTimer > 1000)) {
        touchtime = 0;
        TS_Point p = ts.getPoint();
        posx = p.x;
        posy = p.y;
        touchtimelast = touchtime;
        Serial.print("Pressure = ");
        Serial.print(p.z);
        Serial.print(", x = ");
        Serial.print(p.x);
        Serial.print(", y = ");
        Serial.print(p.y);
        Serial.print(", time = ");
        Serial.print(touchtimelast); 
        Serial.println("uS");       
    
        TouchTimer = 0;
      }
    */
    
    
      
    }

  11. #111
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    9,945
    Just catching this - saw touch. With DMA xfer the touchscreen should be on another SPI ideally? With interrupt if the DMA is off you can then call the Touch test and use the same SPI bus for that.

    I haven't looked at this in a long time and not the DMA - using the INT pin should work - but as soon as touch is detected with INT it will immediately hit the bus to get the points.

    It might be nice - when INT is in use - to return the value of this through a new interface?:: isrWake. Then you could check that value ( always true when no INT ) - and when true a TOUCH interrupt was detected - and the next .update will poll SPI for touch data.

  12. #112
    Senior Member
    Join Date
    Mar 2013
    Posts
    651
    Right now I have Touch all commented out, trying to figure out why its unstable, I left it running and did some other things and found it locked up again, this time it got to 1053 seconds.

    Edit.. And just stopped at 935 Seconds, oddly the Watt Hours value I'm using as a timer is incrementing strange. The tenths of a second is going but im seing hundredths and thousands also, but they should not be changing from zero.....

    Going to set it to a lower CPU rate and leave it for the night, it locks up almost instantly if I overclock to 240Mhz.
    Last edited by Donziboy2; 10-03-2017 at 03:41 AM.

  13. #113
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    9,945
    I saw it was commented. You could stop the drawing and test with TOUCH only and then know the bus is healthy - without int pin it will continuously poll on each update - when touched points data could go out serial?

  14. #114
    Senior Member
    Join Date
    Mar 2013
    Posts
    651
    The Touch and ILI9341 are healthy when not using the DMA Async. I tried increasing the Touchtimer to 1000 Seconds and it still froze a few second in so i'm wondering if the library is doing something to the SPI while DMA is running, I noticed a few times early on that I was receiving touch data over serial, which should only occur if the IRQ pin is firing, which it should not since I was not touching the screen. Worst case I move touch back to another SPI like I originally planned, for now I want to keep touch off and focus on the DMA Async and why it keeps freezing.

    I'm not sure why the thing just stops when touch is disabled, I leave it running and come back and its stuck, left it running at 144Mhz and it was stuck around 3400 seconds. At 180Mhz it sticks around the 900-1100 Second mark. I just tried allocating most of the memory and seeing if it stopped sooner, that had no effect. I increased the refresh rate from 10hz to 12.5hz, which oddly seems to have increased the amount of time needed before it freezes......

    Removed the XPT connections to the SPI going to the LCD, seems to have helped, going to leave it running while i'm at work and see if its still going it 8-9 hours.

  15. #115
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    5,562
    Do you have a simple test, like what is running in #110, that I should try to see if it reproduces? Hopefully using the PJRC touch screen? i.e. Those I have...

  16. #116
    Senior Member
    Join Date
    Mar 2013
    Posts
    651
    Quote Originally Posted by KurtE View Post
    Do you have a simple test, like what is running in #110, that I should try to see if it reproduces? Hopefully using the PJRC touch screen? i.e. Those I have...
    The code in #110 should work provided you use the Async branch and make a few changes. I tried creating an issue on Github but screwed it up because of the way they have us posting code. And they wont let me edit my original post.....

    I'm hoping that the screen is still running when I get home today, if so that means its some kind of noise issue. Which would be weird since the none Async version ran for hours at 48Mhz SPI with no issue. Yet the Async DMA is falling on its face at 30Mhz.

  17. #117
    Senior Member
    Join Date
    Mar 2013
    Posts
    651
    Came home, found it at 4063 seconds. If I try to use any of the 3 overclock levels it at the most gets 1 screen update after serial monitor opens before locking up. (192, 216 and 240Mhz)

    Running a none async version with 240Mhz OC to see if it will lock up after a while.

    Kurt does the DMA get feedback from the screen or is it a 1 way transfer?

  18. #118
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    5,562
    Sorry, I have been outside all day playing with Tractors, box blade, ... (Working on trails...)

    I am seeing lockups as well. Will investigate.

    I do see some issues in the test code...

    That is suppose you do an updateAsync()...

    Then right after that you issues some other tft.draw... command. The problem will be the Async draw may not be done and as such you are writing to the same pixels you are updating to screen. So can get partial updates... And obviously should not try to start an updateAsync if another one is active.

    Also doing anything with SPI while the Async update is active will likely screw up SPI as the DMA is setup... The code is in a beginTransaction at the time. When the DMA completes it then does the endTransaction.

    There is a functions you can call to see if a dma Transaction is active or not. likewise wait for them. But occasionally I have seen these fail... Not sure if there is a compile issue as the code is comparing to variable that is volatile... Will look some more.

    In the CPP file there is a #define DEBUG_ASYNC_UPDATE that is commented out that prints lots of debug stuff.

    The dma stuff only goes to the screen, we are not looking at what is coming back from MISO pin.

  19. #119
    Senior Member
    Join Date
    Mar 2013
    Posts
    651
    Totally alright Kurt, everyone has a life

    So far its up to 8000 Seconds using the normal Non Async code. Going to let it run overnight.
    Ya, updateAsync() was one of the first issues I ran into, it took me a few tries to figure out why it kept freezing due to things I left in. I had a few old tft.updateScreen(); commands I forgot to remove, those also caused it to lock up. Im using the elapsedMillis drawtimer; to avoid it but its possible that for whatever reason a transfer takes much longer and they collide. From what I can tell at 30Mhz it should take about 41mS to update the full buffer to the screen.

  20. #120
    Senior Member
    Join Date
    Mar 2013
    Posts
    651
    Ran all night using tft.updateScreen(); , I just tried using the #define DEBUG_ASYNC_UPDATE with 240Mhz, I get 2 dumps from it and it freezes, it never even makes it to displaying numbers. Using it at 180Mhz lasted a little longer.... 1-50 seconds at 500mS updates. 2 printouts below.

    If I change SPI to 15Mhz it does not last any longer then 30Mhz. It all seems somewhat random, it does not freeze in the same spot with multiple tests at different clocks for CPU and SPI. It also only lasts a few seconds with the CPU at 180, FBUS at 90Mhz and SPI at 45Mhz.

    Going to run it at 120Mhz/30Mhz SPI all day and see what it does. It does not seem to be SPI reliant except when it takes to long to update and the next update collides with the existing one. Something is tripping the DMA up since it does not seem to matter if its updating at 10Hz or 2Hz when its at 180Mhz it will randomly freeze within the first minute.



    240Mhz tft.updateScreenAsync();
    Code:
    DMA dump TCDs 2
    20016c20 40009040:1fff0202 1010002 2 ffff3802 4002c034 63ff0000 20016b80 63ff0010 
    20016b20 20016b40:1fff0202 1010002 2 ffff3802 4002c034 63ff0000 20016b80 63ff0010 
    20016b60 20016b80:1fffca00 1010002 2 ffff3800 4002c034 64000000 20016bc0 64000010 
    20016ba0 20016bc0:20009200 1010002 2 ffff3800 4002c034 64000000 20016b40 6400001a 
    20016be0 20016c00:1fff0200 1010002 2 fffffffe 4002c034 10000 20016b40 10010 
    ILI9341 Test!
    F_CPU 240000000 MHZ
    F_BUS 60000000 MHZ
    SPICLOCK 30000000 MHZ
    DMA dump TCDs 2
    20016c20 40009040:1fff0202 1010002 2 ffff3802 4002c034 63ff0000 20016b80 63ff0090 
    20016b20 20016b40:1fff0202 1010002 2 ffff3802 4002c034 63ff0000 20016b80 63ff0010 
    20016b60 20016b80:1fffca00 1010002 2 ffff3800 4002c034 64000000 20016bc0 64000010 
    20016ba0 20016bc0:20009200 1010002 2 ffff3800 4002c034 64000000 20016b40 6400001a 
    20016be0 20016c00:1fff0200 1010002 2 fffffffe 4002c034 10000 20016b40 10010

    180Mhz 500mS tft.updateScreenAsync();
    Code:
    DMA dump TCDs 2
    20016c20 40009040:1fff0202 1010002 2 ffff3802 4002c034 63ff0000 20016b80 63ff0010 
    20016b20 20016b40:1fff0202 1010002 2 ffff3802 4002c034 63ff0000 20016b80 63ff0010 
    20016b60 20016b80:1fffca00 1010002 2 ffff3800 4002c034 64000000 20016bc0 64000010 
    20016ba0 20016bc0:20009200 1010002 2 ffff3800 4002c034 64000000 20016b40 6400001a 
    20016be0 20016c00:1fff0200 1010002 2 fffffffe 4002c034 10000 20016b40 10010 
    ILI9341 Test!
    F_CPU 180000000 MHZ
    F_BUS 60000000 MHZ
    SPICLOCK 30000000 MHZ
    DMA dump TCDs 2
    20016c20 40009040:1fff0202 1010002 2 ffff3802 4002c034 63ff0000 20016b80 63ff0090 
    20016b20 20016b40:1fff0202 1010002 2 ffff3802 4002c034 63ff0000 20016b80 63ff0010 
    20016b60 20016b80:1fffca00 1010002 2 ffff3800 4002c034 64000000 20016bc0 64000010 
    20016ba0 20016bc0:20009200 1010002 2 ffff3800 4002c034 64000000 20016b40 6400001a 
    20016be0 20016c00:1fff0200 1010002 2 fffffffe 4002c034 10000 20016b40 10010 
    DMA dump TCDs 2
    20016c20 40009040:1fff0202 1010002 2 ffff3802 4002c034 63ff0000 20016b80 63ff0090 
    20016b20 20016b40:1fff0202 1010002 2 ffff3802 4002c034 63ff0000 20016b80 63ff0010 
    20016b60 20016b80:1fffca00 1010002 2 ffff3800 4002c034 64000000 20016bc0 64000010 
    20016ba0 20016bc0:20009200 1010002 2 ffff3800 4002c034 64000000 20016b40 6400001a 
    20016be0 20016c00:1fff0200 1010002 2 fffffffe 4002c034 10000 20016b40 10010 
    DMA dump TCDs 2
    20016c20 40009040:1fff0202 1010002 2 ffff3802 4002c034 63ff0000 20016b80 63ff0090 
    20016b20 20016b40:1fff0202 1010002 2 ffff3802 4002c034 63ff0000 20016b80 63ff0010 
    20016b60 20016b80:1fffca00 1010002 2 ffff3800 4002c034 64000000 20016bc0 64000010 
    20016ba0 20016bc0:20009200 1010002 2 ffff3800 4002c034 64000000 20016b40 6400001a 
    20016be0 20016c00:1fff0200 1010002 2 fffffffe 4002c034 10000 20016b40 10010 
    DMA dump TCDs 2
    20016c20 40009040:1fff0202 1010002 2 ffff3802 4002c034 63ff0000 20016b80 63ff0090 
    20016b20 20016b40:1fff0202 1010002 2 ffff3802 4002c034 63ff0000 20016b80 63ff0010 
    20016b60 20016b80:1fffca00 1010002 2 ffff3800 4002c034 64000000 20016bc0 64000010 
    20016ba0 20016bc0:20009200 1010002 2 ffff3800 4002c034 64000000 20016b40 6400001a 
    20016be0 20016c00:1fff0200 1010002 2 fffffffe 4002c034 10000 20016b40 10010 
    DMA dump TCDs 2
    20016c20 40009040:1fff0202 1010002 2 ffff3802 4002c034 63ff0000 20016b80 63ff0090 
    20016b20 20016b40:1fff0202 1010002 2 ffff3802 4002c034 63ff0000 20016b80 63ff0010 
    20016b60 20016b80:1fffca00 1010002 2 ffff3800 4002c034 64000000 20016bc0 64000010 
    20016ba0 20016bc0:20009200 1010002 2 ffff3800 4002c034 64000000 20016b40 6400001a 
    20016be0 20016c00:1fff0200 1010002 2 fffffffe 4002c034 10000 20016b40 10010 
    DMA dump TCDs 2
    20016c20 40009040:1fff0202 1010002 2 ffff3802 4002c034 63ff0000 20016b80 63ff0090 
    20016b20 20016b40:1fff0202 1010002 2 ffff3802 4002c034 63ff0000 20016b80 63ff0010 
    20016b60 20016b80:1fffca00 1010002 2 ffff3800 4002c034 64000000 20016bc0 64000010 
    20016ba0 20016bc0:20009200 1010002 2 ffff3800 4002c034 64000000 20016b40 6400001a 
    20016be0 20016c00:1fff0200 1010002 2 fffffffe 4002c034 10000 20016b40 10010 
    DMA dump TCDs 2
    20016c20 40009040:1fff0202 1010002 2 ffff3802 4002c034 63ff0000 20016b80 63ff0090 
    20016b20 20016b40:1fff0202 1010002 2 ffff3802 4002c034 63ff0000 20016b80 63ff0010 
    20016b60 20016b80:1fffca00 1010002 2 ffff3800 4002c034 64000000 20016bc0 64000010 
    20016ba0 20016bc0:20009200 1010002 2 ffff3800 4002c034 64000000 20016b40 6400001a 
    20016be0 20016c00:1fff0200 1010002 2 fffffffe 4002c034 10000 20016b40 10010 
    DMA dump TCDs 2
    20016c20 40009040:1fff0202 1010002 2 ffff3802 4002c034 63ff0000 20016b80 63ff0090 
    20016b20 20016b40:1fff0202 1010002 2 ffff3802 4002c034 63ff0000 20016b80 63ff0010 
    20016b60 20016b80:1fffca00 1010002 2 ffff3800 4002c034 64000000 20016bc0 64000010 
    20016ba0 20016bc0:20009200 1010002 2 ffff3800 4002c034 64000000 20016b40 6400001a 
    20016be0 20016c00:1fff0200 1010002 2 fffffffe 4002c034 10000 20016b40 10010 
    DMA dump TCDs 2
    20016c20 40009040:1fff0202 1010002 2 ffff3802 4002c034 63ff0000 20016b80 63ff0090 
    20016b20 20016b40:1fff0202 1010002 2 ffff3802 4002c034 63ff0000 20016b80 63ff0010 
    20016b60 20016b80:1fffca00 1010002 2 ffff3800 4002c034 64000000 20016bc0 64000010 
    20016ba0 20016bc0:20009200 1010002 2 ffff3800 4002c034 64000000 20016b40 6400001a 
    20016be0 20016c00:1fff0200 1010002 2 fffffffe 4002c034 10000 20016b40 10010 
    DMA dump TCDs 2
    20016c20 40009040:1fff0202 1010002 2 ffff3802 4002c034 63ff0000 20016b80 63ff0090 
    20016b20 20016b40:1fff0202 1010002 2 ffff3802 4002c034 63ff0000 20016b80 63ff0010 
    20016b60 20016b80:1fffca00 1010002 2 ffff3800 4002c034 64000000 20016bc0 64000010 
    20016ba0 20016bc0:20009200 1010002 2 ffff3800 4002c034 64000000 20016b40 6400001a 
    20016be0 20016c00:1fff0200 1010002 2 fffffffe 4002c034 10000 20016b40 10010 
    DMA dump TCDs 2
    20016c20 40009040:1fff0202 1010002 2 ffff3802 4002c034 63ff0000 20016b80 63ff0090 
    20016b20 20016b40:1fff0202 1010002 2 ffff3802 4002c034 63ff0000 20016b80 63ff0010 
    20016b60 20016b80:1fffca00 1010002 2 ffff3800 4002c034 64000000 20016bc0 64000010 
    20016ba0 20016bc0:20009200 1010002 2 ffff3800 4002c034 64000000 20016b40 6400001a 
    20016be0 20016c00:1fff0200 1010002 2 fffffffe 4002c034 10000 20016b40 10010 
    DMA dump TCDs 2
    20016c20 40009040:1fff0202 1010002 2 ffff3802 4002c034 63ff0000 20016b80 63ff0090 
    20016b20 20016b40:1fff0202 1010002 2 ffff3802 4002c034 63ff0000 20016b80 63ff0010 
    20016b60 20016b80:1fffca00 1010002 2 ffff3800 4002c034 64000000 20016bc0 64000010 
    20016ba0 20016bc0:20009200 1010002 2 ffff3800 4002c034 64000000 20016b40 6400001a 
    20016be0 20016c00:1fff0200 1010002 2 fffffffe 4002c034 10000 20016b40 10010 
    DMA dump TCDs 2
    20016c20 40009040:1fff0202 1010002 2 ffff3802 4002c034 63ff0000 20016b80 63ff0090 
    20016b20 20016b40:1fff0202 1010002 2 ffff3802 4002c034 63ff0000 20016b80 63ff0010 
    20016b60 20016b80:1fffca00 1010002 2 ffff3800 4002c034 64000000 20016bc0 64000010 
    20016ba0 20016bc0:20009200 1010002 2 ffff3800 4002c034 64000000 20016b40 6400001a 
    20016be0 20016c00:1fff0200 1010002 2 fffffffe 4002c034 10000 20016b40 10010

  21. #121
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    5,562
    I will play around some more today. Have more trail work to do...

    When I tried it yesterday with the debug printing on, your updates appeared to be working fine for at least 10 minutes... (I stopped it then to try other things)... I think there is a timing issue, where some state is getting set or cleared in appropriately... I tried fixing one such issue that is most likely not it... Where I:
    Would do something like: <Start dma> followed by setting variable saying dma is active.
    The dma interrupt: Clears the variable... But what happens if the interrupt happens before the code returns to initially set that variable... Should not happen, but better to set first and then start dma...

    Change did not help your case. Next up. I am going to hook up logic analyzer, add some digitalWriteFast calls to key functions and see if anything looks obvious.

  22. #122
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    5,562
    - Hopefully a fix!

    Try Sync to update on github...

    It appears like the code was hanging in the end DMA interrupt handler. Issue was that when it got here we wanted to push a NULL command onto the stack as to release the CS pins if it is hardware CS and also remove the CONT state...

    Problem was if the SPI queue was full when we did this it screwed up... So wait for queue not full before push... Your slightly modified program is now running on my board at 180... So far for 445...

    Most of my mods are for IO pin differences. But did add in a few calls to wait for display to complete before doing any more updates...

    Code:
    #include "SPI.h"
    #include <ILI9341_t3n.h>
    //#include <XPT2046_Touchscreen.h>
    
    #define KURTS_FLEXI
    #ifdef KURTS_FLEXI
    #define TFT_DC 22
    #define TFT_CS 15
    #define TFT_RST -1
    #define TFT_SCLK 14
    #define TFT_MISO 12
    #define TFT_MOSI 7
    #define DEBUG_PIN 13
    
    #define CS_PIN  8   //Touch CS
    #define TIRQ_PIN  3  //Touch Interupt pin
    #else
    #define TFT_DC      9
    #define TFT_CS      10
    #define TFT_RST     8  // 255 = unused, connect to 3.3V
    #define TFT_MOSI    11
    #define TFT_SCLK    13
    #define TFT_MISO    12
    #define CS_PIN  4   //Touch CS
    #define TIRQ_PIN  2  //Touch Interupt pin
    #endif
    
    
    //uint16_t tft_frame_buffer[ILI9341_TFTWIDTH * ILI9341_TFTHEIGHT];
    
    DMAMEM uint16_t ili9341_frame_buffer[ILI9341_TFTWIDTH * ILI9341_TFTHEIGHT];
    
    ILI9341_t3n tft = ILI9341_t3n(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCLK, TFT_MISO, &SPIN);
    //XPT2046_Touchscreen ts(CS_PIN, TIRQ_PIN);  // Param 2 - Touch IRQ Pin - interrupt enabled polling
    
    
    float WattHours = 0.000;
    
    
    void setup() {
    
    
      tft.begin();
      //  ts.begin();
      tft.setFrameBuffer(ili9341_frame_buffer);
      tft.useFrameBuffer(true);
      tft.setRotation(1);
      tft.fillScreen(ILI9341_BLACK);
      tft.setTextColor(ILI9341_YELLOW);
      tft.setTextSize(2);
      tft.println("Waiting for Arduino Serial Monitor...");
      //  tft.updateScreen();
      tft.updateScreenAsync();
      Serial.begin(9600);
      while (!Serial) ; // wait for Arduino Serial Monitor
      Serial.println("ILI9341 Test!");
      Serial.print("F_CPU ");
      Serial.print(F_CPU);
      Serial.println(" MHZ");
      Serial.print("F_BUS ");
      Serial.print(F_BUS);
      Serial.println(" MHZ");
      Serial.print("SPICLOCK ");
      Serial.print(F_BUS / 2);
      Serial.println(" MHZ");
    
      tft.waitUpdateAsyncComplete() ;
      tft.println("Connected!");
      tft.updateScreenAsync();
      //  tft.updateScreen();
      Serial.println("Before Delay");
      delay(1000);
      Serial.println("After Delay");
      tft.waitUpdateAsyncComplete() ;
      tft.fillScreen(ILI9341_BLACK);
      Serial.println("Before Update async");
      tft.updateScreenAsync();
      Serial.println("End Setup");
    }
    elapsedMillis drawtimer;
    elapsedMillis TouchTimer;
    elapsedMicros drawtime;
    elapsedMicros touchtime;
    uint32_t drawtimelast;
    uint32_t touchtimelast;
    byte debugmenu = 0;
    uint16_t posx = 0;
    uint16_t posy = 0;
    
    
    void loop(void) {
    
      if (debugmenu == 0) {
        Serial.println("Before Wait Async");
        tft.waitUpdateAsyncComplete() ;
        //tft.tft.waitUpdateAsyncComplete();  // Don't change stuff until any async update has completed
        tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
        tft.setTextSize(3);     //Upper Left Side
        tft.setCursor(3, 3);
        tft.print("Q1");
        tft.setCursor(3, 27);
        tft.print("Q2");
        tft.setCursor(3, 51);
        tft.print("Q3");
        tft.setCursor(3, 75);
        tft.print("Q4");
        tft.setCursor(3, 99);
        tft.print("Q5");
    
        tft.setTextSize(4);     //Upper Right Side
        tft.setCursor(295, 3);
        tft.print("V");
        tft.setCursor(295, 36);
        tft.print("W");
        tft.setCursor(295, 69);
        tft.print("A");
    
        tft.setTextSize(2);     //Lower Right Side
        tft.setCursor(3, 125);
        tft.print("DAC0");
        tft.setCursor(110, 125);
        tft.print("%");
        tft.setCursor(143, 125);
        tft.print("DAC1");
        tft.setCursor(251, 125);
        tft.print("%");
    
        tft.setCursor(3, 145);
        tft.print("FAN");
        tft.setCursor(110, 145);
        tft.print("%");
        tft.setCursor(143, 145);
        tft.print("FAN");
        tft.setCursor(251, 145);
        tft.print("RPM");
    
        tft.setTextSize(3);
        tft.setCursor(165, 180);
        tft.print("WH");
    
        tft.setCursor(39, 210);
        tft.print("H");
        tft.setCursor(103, 210);
        tft.print("M");
        tft.setCursor(167, 210);
        tft.print("S");
    
        tft.setTextSize(2);
        tft.setCursor(224, 180);
        tft.print("T");
        tft.setCursor(224, 200);
        tft.print("X");
        tft.setCursor(224, 220);
        tft.print("Y");
        Serial.println("Before update async menu");
        tft.updateScreenAsync();
        //tft.updateScreen();
    
        TouchTimer = 0;
        debugmenu = 1;
        drawtimer = 0;
      }
    
    
    
      if (drawtimer > 100) {
        drawtime = 0;
        tft.waitUpdateAsyncComplete() ;
        tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
        tft.setTextSize(3);     //Upper Left Side
        tft.setCursor(50, 3);
        tft.print("4.095");     //Q1
        tft.setCursor(50, 27);
        tft.print("4.095");     //Q2
        tft.setCursor(50, 51);
        tft.print("4.095");     //Q3
        tft.setCursor(50, 75);
        tft.print("4.095");     //Q4
        tft.setCursor(50, 99);
        tft.print("4.095");     //Q5
        //  drawtimelast = drawtime;
        tft.setTextSize(4);     //Upper Right Side
        tft.setCursor(175, 3);
        tft.print("81.92");    //V
        tft.setCursor(150, 36);
        tft.print("300.00");   //W
        tft.setCursor(175, 69);
        tft.print("20.48");    //A
        tft.setTextSize(2);     //Lower Right Side
        tft.setCursor(63, 125);
        tft.print("100");    //DAC0
        tft.setCursor(203, 125);
        tft.print("100");    //DAC1
        tft.setCursor(63, 145);
        tft.print("100");    //FAN
        tft.setCursor(203, 145);
        tft.print("9999");   //RPM
        //drawtime = 0;
        tft.setTextSize(3);
        tft.setCursor(3, 180);   //cursor set here unless one of the if is true.
        if (WattHours < 10) {
          tft.print("   ");
        }
        else if (WattHours < 100) {
          tft.print("  ");
        }
        else if (WattHours < 1000) {
          tft.print(" ");
        }
        tft.print(WattHours, 3);
        WattHours = WattHours + 0.100;
        //drawtimelast = drawtime;
        tft.setCursor(3, 210);
        tft.print("24");
        tft.setCursor(67, 210);
        tft.print("60");
        tft.setCursor(131, 210);
        tft.print("60");
        tft.setTextSize(2);
    
        tft.setCursor(240, 180);
        tft.print(drawtimelast);
        tft.setCursor(240, 200);
        tft.print(posx);
        tft.print(" ");
        tft.setCursor(240, 220);
        tft.print(posy);
        tft.print(" ");
    
        //  drawtimelast = drawtime;
        //  drawtime = 0;
        tft.updateScreenAsync();
        //  tft.updateScreen();
        drawtimer = 0;
        drawtimelast = drawtime;
      }
      /*
        if ((ts.touched()) && (TouchTimer > 1000)) {
          touchtime = 0;
          TS_Point p = ts.getPoint();
          posx = p.x;
          posy = p.y;
          touchtimelast = touchtime;
          Serial.print("Pressure = ");
          Serial.print(p.z);
          Serial.print(", x = ");
          Serial.print(p.x);
          Serial.print(", y = ");
          Serial.print(p.y);
          Serial.print(", time = ");
          Serial.print(touchtimelast);
          Serial.println("uS");
    
          TouchTimer = 0;
        }
      */
    }
    Hopefully it works for you... Sorry about that.

    Edit: Still running, counter is now over 4142...
    Last edited by KurtE; 10-04-2017 at 04:35 PM.

  23. #123
    Senior Member
    Join Date
    Mar 2013
    Posts
    651
    Nice! I actually came home and it was still running from dis-morning, its over 35K seconds and still going.
    Have you noticed the odd counting of the Watt hours Float, after a short while it starts producing what I think is rounding errors.

    Replaced Serial.print(F_BUS / 2); with Serial.print(ILI9341_SPICLOCK); ,gives much better results. Its funny when you do something simple like that and then spend 30 minutes scratching your head trying to figure out why #define ILI9341_SPICLOCK 60000000 isn't doing anything lol.


    Its on FIRE!!!!!
    Code:
    ILI9341 Test!
    F_CPU 240000000 MHZ
    F_BUS 120000000 MHZ
    SPICLOCK 60000000 MHZ


    Here is the modified Async .cpp i'm using. Without those it wont compile using the latest Teensy Loader (1.39) and the &SPIN call inside
    Code:
    ILI9341_t3n tft = ILI9341_t3n(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCLK, TFT_MISO, &SPIN);
    .

    ILI9341_t3n.cpp Async
    Code:
    // https://github.com/PaulStoffregen/ILI9341_t3n
    // http://forum.pjrc.com/threads/26305-Highly-optimized-ILI9341-(320x240-TFT-color-display)-library
    
    /***************************************************
      This is our library 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
     ****************************************************/
    // <SoftEgg>
    
    //Additional graphics routines by Tim Trzepacz, SoftEgg LLC added December 2015
    //(And then accidentally deleted and rewritten March 2016. Oops!)
    //Gradient support
    //----------------
    //		fillRectVGradient	- fills area with vertical gradient
    //		fillRectHGradient	- fills area with horizontal gradient
    //		fillScreenVGradient - fills screen with vertical gradient
    // 	fillScreenHGradient - fills screen with horizontal gradient
    
    //Additional Color Support
    //------------------------
    //		color565toRGB		- converts 565 format 16 bit color to RGB
    //		color565toRGB14		- converts 16 bit 565 format color to 14 bit RGB (2 bits clear for math and sign)
    //		RGB14tocolor565		- converts 14 bit RGB back to 16 bit 565 format color
    
    //Low Memory Bitmap Support
    //-------------------------
    // 		writeRect8BPP - 	write 8 bit per pixel paletted bitmap
    // 		writeRect4BPP - 	write 4 bit per pixel paletted bitmap
    // 		writeRect2BPP - 	write 2 bit per pixel paletted bitmap
    // 		writeRect1BPP - 	write 1 bit per pixel paletted bitmap
    
    //TODO: transparent bitmap writing routines for sprites
    
    //String Pixel Length support
    //---------------------------
    //		strPixelLen			- gets pixel length of given ASCII string
    
    // <\SoftEgg>
    
    #include "ILI9341_t3n.h"
    #include <SPIN.h>
    #include <SPI.h>  
    
    //#define DEBUG_ASYNC_UPDATE  // Enable to print out dma info
    //#define DEBUG_ASYNC_LEDS	// Enable to use digitalWrites to Debug
    #ifdef DEBUG_ASYNC_LEDS
    #define DEBUG_PIN_1 0
    #define DEBUG_PIN_2 1
    #define DEBUG_PIN_3 4
    #endif
    
    #ifdef ENABLE_ILI9341_FRAMEBUFFER
    DMASetting 	ILI9341_t3n::_dmasettings[4];
    DMAChannel 	ILI9341_t3n::_dmatx;
    
    DMAChannel 	_dmatx;
    ILI9341_t3n *ILI9341_t3n::_dmaActiveDisplay = 0;
    volatile uint8_t  	ILI9341_t3n::_dma_state = 0;  // Use pointer to this as a way to get back to object...
    volatile uint32_t	ILI9341_t3n::_dma_frame_count = 0;	// Can return a frame count...
    
    void ILI9341_t3n::dmaInterrupt(void) {
    	//Serial.println("DMA Interrupt");
    	///  digitalWriteFast(1,!digitalReadFast(1));
    #ifdef DEBUG_ASYNC_LEDS
    	digitalWriteFast(DEBUG_PIN_2, HIGH);
    #endif
    	_dma_frame_count++;
    	_dmatx.clearInterrupt();
    
    	// See if we are in continuous mode or not..
    	if ((_dma_state & ILI9341_DMA_CONT) == 0) {
    		// We are in single refresh mode or the user has called cancel so
    		// Lets try to release the CS pin
    		_dmaActiveDisplay->_pspin->waitFifoNotFull();
    		_dmaActiveDisplay->writecommand_last(ILI9341_NOP);
    		_dmaActiveDisplay->endSPITransaction();
    		_dma_state &= ~ILI9341_DMA_ACTIVE;
    		_dmaActiveDisplay = 0;	// We don't have a display active any more... 
    	}
    #ifdef DEBUG_ASYNC_LEDS
    	digitalWriteFast(DEBUG_PIN_2, LOW);
    #endif
    }
    
    #endif
    // Teensy 3.1 can only generate 30 MHz SPI when running at 120 MHz (overclock)
    
    #define WIDTH  ILI9341_TFTWIDTH
    #define HEIGHT ILI9341_TFTHEIGHT
    
    // Constructor when using hardware ILI9241_KINETISK__pspi->  Faster, but must use SPI pins
    // specific to each board type (e.g. 11,13 for Uno, 51,52 for Mega, etc.)
    ILI9341_t3n::ILI9341_t3n(uint8_t cs, uint8_t dc, uint8_t rst, 
    	uint8_t mosi, uint8_t sclk, uint8_t miso, SPINClass *pspin )
    {
    	_cs   = cs;
    	_dc   = dc;
    	_rst  = rst;
    	_mosi = mosi;
    	_sclk = sclk;
    	_miso = miso;
    	_width    = WIDTH;
    	_height   = HEIGHT;
    	_pspin	  = pspin; 
    #ifdef KINETISK	
    	_pkinetisk_spi = _pspin->kinetisk_spi();
    #else
    	_pkinetisl_spi = _pspin->kinetisl_spi();
    #endif		
    
    	rotation  = 0;
    	cursor_y  = cursor_x    = 0;
    	textsize  = 1;
    	textcolor = textbgcolor = 0xFFFF;
    	wrap      = true;
    	font      = NULL;
    	setClipRect();
    	setOrigin();
    
    	// Added to see how much impact actually using non hardware CS pin might be
        _cspinmask = 0;
        _csport = NULL;
    
    	#ifdef ENABLE_ILI9341_FRAMEBUFFER
        _pfbtft = NULL;	
        _use_fbtft = 0;						// Are we in frame buffer mode?
    	_we_allocated_buffer = 0;
        #endif
    
    }
    
    //=======================================================================
    // Add optinal support for using frame buffer to speed up complex outputs
    //=======================================================================
    #define CBALLOC (ILI9341_TFTHEIGHT*ILI9341_TFTWIDTH*2)
    void ILI9341_t3n::setFrameBuffer(uint16_t *frame_buffer) 
    {
    	#ifdef ENABLE_ILI9341_FRAMEBUFFER
    	_pfbtft = frame_buffer;
    	if (_pfbtft != NULL) {
    		memset(_pfbtft, 0, ILI9341_TFTHEIGHT*ILI9341_TFTWIDTH*2);
    	}
    
    	#endif	
    }
    
    uint8_t ILI9341_t3n::useFrameBuffer(boolean b)		// use the frame buffer?  First call will allocate
    {
    	#ifdef ENABLE_ILI9341_FRAMEBUFFER
    	if (b) {
    		// First see if we need to allocate buffer
    		if (_pfbtft == NULL) {
    			_pfbtft = (uint16_t *)malloc(CBALLOC);
    			if (_pfbtft == NULL)
    				return 0;	// failed 
    			memset(_pfbtft, 0, CBALLOC);	
    			_we_allocated_buffer = 1;
    		}
    		_use_fbtft = 1;
    	} else 
    		_use_fbtft = 0;
    
    	return _use_fbtft;	
    	#else
    	return 0;
    	#endif
    }
    
    void ILI9341_t3n::freeFrameBuffer(void)						// explicit call to release the buffer
    {
    	#ifdef ENABLE_ILI9341_FRAMEBUFFER
    	if (_we_allocated_buffer && (_pfbtft != NULL)) {
    		_pfbtft = NULL;
    		_use_fbtft = 0;	// make sure the use is turned off
    		_we_allocated_buffer = 0;
    	}
    	#endif
    }
    void ILI9341_t3n::updateScreen(void)					// call to say update the screen now.
    {
    	// Not sure if better here to check flag or check existence of buffer.
    	// Will go by buffer as maybe can do interesting things?
    	#ifdef ENABLE_ILI9341_FRAMEBUFFER
    	if (_use_fbtft) {
    		beginSPITransaction();
    		if (_standard) {
    			// Doing full window. 
    			setAddr(0, 0, _width-1, _height-1);
    			writecommand_cont(ILI9341_RAMWR);
    
    			// BUGBUG doing as one shot.  Not sure if should or not or do like
    			// main code and break up into transactions...
    			uint16_t *pfbtft_end = &_pfbtft[(ILI9341_TFTWIDTH*ILI9341_TFTHEIGHT)-1];	// setup 
    			uint16_t *pftbft = _pfbtft;
    
    			// Quick write out the data;
    			while (pftbft < pfbtft_end) {
    				writedata16_cont(*pftbft++);
    			}
    			writedata16_last(*pftbft);
    		} else {
    			// setup just to output the clip rectangle area. 
    			setAddr(_displayclipx1, _displayclipy1, _displayclipx2-1, _displayclipy2-1);
    			writecommand_cont(ILI9341_RAMWR);
    
    			// BUGBUG doing as one shot.  Not sure if should or not or do like
    			// main code and break up into transactions...
    			uint16_t * pfbPixel_row = &_pfbtft[ _displayclipy1*_width + _displayclipx1];
    			for (uint16_t y = _displayclipy1; y < _displayclipy2; y++) {
    				uint16_t * pfbPixel = pfbPixel_row;
    				for (uint16_t x = _displayclipx1; x < (_displayclipx2-1); x++) {
    					writedata16_cont(*pfbPixel++);
    				}
    				if (y < (_displayclipy2-1))
    					writedata16_cont(*pfbPixel);
    				else	
    					writedata16_last(*pfbPixel);
    				pfbPixel_row += _width;	// setup for the next row. 
    			}
    		}
    		endSPITransaction();
    	}
    	#endif
    }			 
    
    #ifdef DEBUG_ASYNC_UPDATE
    void dumpDMA_TCD(DMABaseClass *dmabc)
    {
    	Serial.printf("%x %x:", (uint32_t)dmabc, (uint32_t)dmabc->TCD);
    
    	const uint32_t *p = (const uint32_t *)dmabc->TCD;
    
    	for (uint8_t i=0; i<8; i++) {
    		Serial.printf("%x ", *p++);
    	}
    	Serial.println();
    }
    #endif
    
    void	ILI9341_t3n::initDMASettings(void) 
    {
    //	Serial.printf("initDMASettings called %d\n", ili9341_t3n_dma_has_been_init);
    	if (_dma_state) {  // should test for init, but...
    		return;	// we already init this. 
    	}
    
    	//Serial.println("InitDMASettings");
    
    	uint8_t dmaTXevent = _pspin->dmaTXEvent();
    
    	// BUGBUG:: check for -1 as wont work on SPI2 on T3.5
    //	uint16_t *fbtft_start_dma_addr = _pfbtft;
    	uint32_t count_words_write = (CBALLOC/SCREEN_DMA_NUM_SETTINGS)/2; // Note I know the divide will give whole number
    	
    	//Serial.printf("CWW: %d %d %d\n", CBALLOC, SCREEN_DMA_NUM_SETTINGS, count_words_write);
    	// Now lets setup DMA access to this memory... 
    	_dmasettings[0].sourceBuffer(&_pfbtft[1], (count_words_write-1)*2);
    	_dmasettings[0].destination(_pkinetisk_spi->PUSHR);
    
    	// Hack to reset the destination to only output 2 bytes.
    	_dmasettings[0].TCD->ATTR_DST = 1;
    	_dmasettings[0].replaceSettingsOnCompletion(_dmasettings[1]);
    
    	_dmasettings[1].sourceBuffer(&_pfbtft[count_words_write], count_words_write*2);
    	_dmasettings[1].destination(_pkinetisk_spi->PUSHR);
    	_dmasettings[1].TCD->ATTR_DST = 1;
    	_dmasettings[1].replaceSettingsOnCompletion(_dmasettings[2]);
    
    	_dmasettings[2].sourceBuffer(&_pfbtft[count_words_write*2], count_words_write*2);
    	_dmasettings[2].destination(_pkinetisk_spi->PUSHR);
    	_dmasettings[2].TCD->ATTR_DST = 1;
    	_dmasettings[2].replaceSettingsOnCompletion(_dmasettings[3]);
    
    	// Sort of hack - but wrap around to output the first word again. 
    	_dmasettings[3].sourceBuffer(_pfbtft, 2);
    	_dmasettings[3].destination(_pkinetisk_spi->PUSHR);
    	_dmasettings[3].TCD->ATTR_DST = 1;
    	_dmasettings[3].replaceSettingsOnCompletion(_dmasettings[0]);
    
    	// Setup DMA main object
    	//Serial.println("Setup _dmatx");
    	_dmatx.begin(true);
    	_dmatx.triggerAtHardwareEvent(dmaTXevent);
    	_dmatx = _dmasettings[0];
    	_dmatx.attachInterrupt(dmaInterrupt);
    	_dma_state = ILI9341_DMA_INIT;  // Should be first thing set!
    }
    
    
    bool ILI9341_t3n::updateScreenAsync(bool update_cont)					// call to say update the screen now.
    {
    	// Not sure if better here to check flag or check existence of buffer.
    	// Will go by buffer as maybe can do interesting things?
    	// BUGBUG:: only handles full screen so bail on the rest of it...
    	#ifdef ENABLE_ILI9341_FRAMEBUFFER
    	if (!_use_fbtft) return false;
    
    #ifdef DEBUG_ASYNC_LEDS
    	digitalWriteFast(DEBUG_PIN_1, HIGH);
    #endif
    	// Init DMA settings. 
    	initDMASettings();
    
    	// Don't start one if already active.
    	if (_dma_state & ILI9341_DMA_ACTIVE) {
    	#ifdef DEBUG_ASYNC_LEDS
    		digitalWriteFast(DEBUG_PIN_1, LOW);
    	#endif
    		return false;
    	}
    
    	if (update_cont) {
    		// Try to link in #3 into the chain
    		_dmasettings[2].replaceSettingsOnCompletion(_dmasettings[3]);
    		_dmasettings[2].TCD->CSR &= ~(DMA_TCD_CSR_INTMAJOR | DMA_TCD_CSR_DREQ);  // Don't interrupt on this one... 
    		_dmasettings[3].interruptAtCompletion();
    		_dmasettings[3].TCD->CSR &= ~(DMA_TCD_CSR_DREQ);  // Don't disable on this one  
    		_dma_state |= ILI9341_DMA_CONT;
    	} else {
    		// In this case we will only run through once...
    		_dmasettings[2].replaceSettingsOnCompletion(_dmasettings[0]);
    		_dmasettings[2].interruptAtCompletion();
    		_dmasettings[2].disableOnCompletion();
    		_dma_state &= ~ILI9341_DMA_CONT;
    	}
    
    
    #ifdef DEBUG_ASYNC_UPDATE
    	Serial.printf("DMA dump TCDs %d\n", _dmatx.channel);
    	dumpDMA_TCD(&_dmatx);
    	dumpDMA_TCD(&_dmasettings[0]);
    	dumpDMA_TCD(&_dmasettings[1]);
    	dumpDMA_TCD(&_dmasettings[2]);
    	dumpDMA_TCD(&_dmasettings[3]);
    #endif
    	beginSPITransaction();
    
    	// Doing full window. 
    	setAddr(0, 0, _width-1, _height-1);
    	writecommand_cont(ILI9341_RAMWR);
    
    	// Write the first Word out before enter DMA as to setup the proper CS/DC/Continue flaugs
    	writedata16_cont(*_pfbtft);
    	// now lets start up the DMA
    //	volatile uint16_t  biter = _dmatx.TCD->BITER;
    	//DMA_CDNE_CDNE(_dmatx.channel);
    //	_dmatx = _dmasettings[0];
    //	_dmatx.TCD->BITER = biter;
    	_dma_frame_count = 0;  // Set frame count back to zero. 
    	_dmaActiveDisplay = this;
    	_dma_state |= ILI9341_DMA_ACTIVE;
    	_pkinetisk_spi->RSER |= SPI_RSER_TFFF_DIRS |	 SPI_RSER_TFFF_RE;	 // Set DMA Interrupt Request Select and Enable register
    	_pkinetisk_spi->MCR &= ~SPI_MCR_HALT;  //Start transfers.
    	_dmatx.enable();
    #ifdef DEBUG_ASYNC_LEDS
    	digitalWriteFast(DEBUG_PIN_1, LOW);
    #endif
    	return true;
        #else
        return false;     // no frame buffer so will never start... 
    	#endif
    
    }			 
    
    void ILI9341_t3n::endUpdateAsync() {
    	// make sure it is on
    	#ifdef ENABLE_ILI9341_FRAMEBUFFER
    	if (_dma_state & ILI9341_DMA_CONT) {
    		_dma_state &= ~ILI9341_DMA_CONT; // Turn of the continueous mode
    		_dmasettings[3].disableOnCompletion();
    	}
    	#endif
    }
    	
    void ILI9341_t3n::waitUpdateAsyncComplete(void) 
    {
    	#ifdef ENABLE_ILI9341_FRAMEBUFFER
    #ifdef DEBUG_ASYNC_LEDS
    	digitalWriteFast(DEBUG_PIN_3, HIGH);
    #endif
    
    	while ((_dma_state & ILI9341_DMA_ACTIVE)) {
    		// asm volatile("wfi");
    	};
    #ifdef DEBUG_ASYNC_LEDS
    	digitalWriteFast(DEBUG_PIN_3, LOW);
    #endif
    	#endif	
    }
    
    //=======================================================================
    
    
    void ILI9341_t3n::setAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
    {
    	beginSPITransaction();
    	setAddr(x0, y0, x1, y1);
    	writecommand_last(ILI9341_RAMWR); // write to RAM
    	endSPITransaction();
    }
    
    void ILI9341_t3n::pushColor(uint16_t color)
    {
    	beginSPITransaction();
    	writedata16_last(color);
    	endSPITransaction();
    }
    
    void ILI9341_t3n::drawPixel(int16_t x, int16_t y, uint16_t color) {
    	x += _originx;
    	y += _originy;
    	if((x < _displayclipx1) ||(x >= _displayclipx2) || (y < _displayclipy1) || (y >= _displayclipy2)) return;
    
    	#ifdef ENABLE_ILI9341_FRAMEBUFFER
    	if (_use_fbtft) {
    		_pfbtft[y*_width + x] = color;
    
    	} else 
    	#endif
    	{
    		beginSPITransaction();
    		setAddr(x, y, x, y);
    		writecommand_cont(ILI9341_RAMWR);
    		writedata16_last(color);
    		endSPITransaction();
    	}
    }
    
    void ILI9341_t3n::drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color)
    {
    	x+=_originx;
    	y+=_originy;
    	// Rectangular clipping
    	if((x < _displayclipx1) || (x >= _displayclipx2) || (y >= _displayclipy2)) return;
    	if(y < _displayclipy1) { h = h - (_displayclipy1 - y); y = _displayclipy1;}
    	if((y+h-1) >= _displayclipy2) h = _displayclipy2-y;
    	if(h<1) return;
    
    	#ifdef ENABLE_ILI9341_FRAMEBUFFER
    	if (_use_fbtft) {
    		uint16_t * pfbPixel = &_pfbtft[ y*_width + x];
    		while (h--) {
    			*pfbPixel = color;
    			pfbPixel += _width;
    		}
    	} else 
    	#endif
    	{
    		beginSPITransaction();
    		setAddr(x, y, x, y+h-1);
    		writecommand_cont(ILI9341_RAMWR);
    		while (h-- > 1) {
    			writedata16_cont(color);
    		}
    		writedata16_last(color);
    		endSPITransaction();
    	}
    }
    
    void ILI9341_t3n::drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color)
    {
    	x+=_originx;
    	y+=_originy;
    
    	// Rectangular clipping
    	if((y < _displayclipy1) || (x >= _displayclipx2) || (y >= _displayclipy2)) return;
    	if(x<_displayclipx1) { w = w - (_displayclipx1 - x); x = _displayclipx1; }
    	if((x+w-1) >= _displayclipx2)  w = _displayclipx2-x;
    	if (w<1) return;
    
    	#ifdef ENABLE_ILI9341_FRAMEBUFFER
    	if (_use_fbtft) {
    		if ((x&1) || (w&1)) {
    			uint16_t * pfbPixel = &_pfbtft[ y*_width + x];
    			while (w--) {
    				*pfbPixel++ = color;
    			}
    		} else {
    			// X is even and so is w, try 32 bit writes..
    			uint32_t color32 = (color << 16) | color;
    			uint32_t * pfbPixel = (uint32_t*)((uint16_t*)&_pfbtft[ y*_width + x]);
    			while (w) {
    				*pfbPixel++ = color32;
    				w -= 2;
    			}
    		}
    	} else 
    	#endif
    	{
    		beginSPITransaction();
    		setAddr(x, y, x+w-1, y);
    		writecommand_cont(ILI9341_RAMWR);
    		while (w-- > 1) {
    			writedata16_cont(color);
    		}
    		writedata16_last(color);
    		endSPITransaction();
    	}
    }
    
    void ILI9341_t3n::fillScreen(uint16_t color)
    {
    	#ifdef ENABLE_ILI9341_FRAMEBUFFER
    	if (_use_fbtft && _standard) {
    		// Speed up lifted from Franks DMA code... _standard is if no offsets and rects..
    		uint32_t color32 = (color << 16) | color;
    
    		uint32_t *pfbPixel = (uint32_t *)_pfbtft;
    		uint32_t *pfbtft_end = (uint32_t *)((uint16_t *)&_pfbtft[(ILI9341_TFTWIDTH * ILI9341_TFTHEIGHT)]); // setup
    		while (pfbPixel < pfbtft_end) {
    			*pfbPixel++ = color32; *pfbPixel++ = color32; *pfbPixel++ = color32;*pfbPixel++ = color32;
    			*pfbPixel++ = color32; *pfbPixel++ = color32; *pfbPixel++ = color32;*pfbPixel++ = color32;
    			*pfbPixel++ = color32; *pfbPixel++ = color32; *pfbPixel++ = color32;*pfbPixel++ = color32;
    			*pfbPixel++ = color32; *pfbPixel++ = color32; *pfbPixel++ = color32;*pfbPixel++ = color32;
    			*pfbPixel++ = color32; *pfbPixel++ = color32; *pfbPixel++ = color32;*pfbPixel++ = color32;
    			*pfbPixel++ = color32; *pfbPixel++ = color32; *pfbPixel++ = color32;*pfbPixel++ = color32;
    			*pfbPixel++ = color32; *pfbPixel++ = color32; *pfbPixel++ = color32;*pfbPixel++ = color32;
    			*pfbPixel++ = color32; *pfbPixel++ = color32; *pfbPixel++ = color32;*pfbPixel++ = color32;
    		}
    
    	} else 
    	#endif
    	{
    		fillRect(0, 0, _width, _height, color);
    	}
    }
    
    // fill a rectangle
    void ILI9341_t3n::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color)
    {
    	x+=_originx;
    	y+=_originy;
    
    	// Rectangular clipping (drawChar w/big text requires this)
    	if((x >= _displayclipx2) || (y >= _displayclipy2)) return;
    	if (((x+w) <= _displayclipx1) || ((y+h) <= _displayclipy1)) return;
    	if(x < _displayclipx1) {	w -= (_displayclipx1-x); x = _displayclipx1; 	}
    	if(y < _displayclipy1) {	h -= (_displayclipy1 - y); y = _displayclipy1; 	}
    	if((x + w - 1) >= _displayclipx2)  w = _displayclipx2  - x;
    	if((y + h - 1) >= _displayclipy2) h = _displayclipy2 - y;
    
    	#ifdef ENABLE_ILI9341_FRAMEBUFFER
    	if (_use_fbtft) {
    		if ((x&1) || (w&1)) {
    			uint16_t * pfbPixel_row = &_pfbtft[ y*_width + x];
    			for (;h>0; h--) {
    				uint16_t * pfbPixel = pfbPixel_row;
    				for (int i = 0 ;i < w; i++) {
    					*pfbPixel++ = color;
    				}
    				pfbPixel_row += _width;
    			}
    		} else {
    			// Horizontal is even number so try 32 bit writes instead
    			uint32_t color32 = (color << 16) | color;
    			uint32_t * pfbPixel_row = (uint32_t *)((uint16_t*)&_pfbtft[ y*_width + x]);
    			w = w/2;	// only iterate half the times
    			for (;h>0; h--) {
    				uint32_t * pfbPixel = pfbPixel_row;
    				for (int i = 0 ;i < w; i++) {
    					*pfbPixel++ = color32;
    				}
    				pfbPixel_row += (_width/2);
    			}
    		}
    	} else 
    	#endif
    	{
    
    		// TODO: this can result in a very long transaction time
    		// should break this into multiple transactions, even though
    		// it'll cost more overhead, so we don't stall other SPI libs
    		beginSPITransaction();
    		setAddr(x, y, x+w-1, y+h-1);
    		writecommand_cont(ILI9341_RAMWR);
    		for(y=h; y>0; y--) {
    			for(x=w; x>1; x--) {
    				writedata16_cont(color);
    			}
    			writedata16_last(color);
    			if (y > 1 && (y & 1)) {
    				endSPITransaction();
    				beginSPITransaction();
    			}
    		}
    		endSPITransaction();
    	}
    }
    
    // fillRectVGradient	- fills area with vertical gradient
    void ILI9341_t3n::fillRectVGradient(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color1, uint16_t color2)
    {
    	x+=_originx;
    	y+=_originy;
    
    	// Rectangular clipping 
    	if((x >= _displayclipx2) || (y >= _displayclipy2)) return;
    	if(x < _displayclipx1) {	w -= (_displayclipx1-x); x = _displayclipx1; 	}
    	if(y < _displayclipy1) {	h -= (_displayclipy1 - y); y = _displayclipy1; 	}
    	if((x + w - 1) >= _displayclipx2)  w = _displayclipx2  - x;
    	if((y + h - 1) >= _displayclipy2) h = _displayclipy2 - y;
    	
    	int16_t r1, g1, b1, r2, g2, b2, dr, dg, db, r, g, b;
    	color565toRGB14(color1,r1,g1,b1);
    	color565toRGB14(color2,r2,g2,b2);
    	dr=(r2-r1)/h; dg=(g2-g1)/h; db=(b2-b1)/h;
    	r=r1;g=g1;b=b1;	
    
    	#ifdef ENABLE_ILI9341_FRAMEBUFFER
    	if (_use_fbtft) {
    		if ((x&1) || (w&1)) {
    			uint16_t * pfbPixel_row = &_pfbtft[ y*_width + x];
    			for (;h>0; h--) {
    				uint16_t color = RGB14tocolor565(r,g,b);
    				uint16_t * pfbPixel = pfbPixel_row;
    				for (int i = 0 ;i < w; i++) {
    					*pfbPixel++ = color;
    				}
    				r+=dr;g+=dg; b+=db;
    				pfbPixel_row += _width;
    			}
    		} else {
    			// Horizontal is even number so try 32 bit writes instead
    			uint32_t * pfbPixel_row = (uint32_t *)((uint16_t*)&_pfbtft[ y*_width + x]);
    			w = w/2;	// only iterate half the times
    			for (;h>0; h--) {
    				uint32_t * pfbPixel = pfbPixel_row;
    				uint16_t color = RGB14tocolor565(r,g,b);
    				uint32_t color32 = (color << 16) | color;
    				for (int i = 0 ;i < w; i++) {
    					*pfbPixel++ = color32;
    				}
    				pfbPixel_row += (_width/2);
    				r+=dr;g+=dg; b+=db;
    			}
    		}
    	} else 
    	#endif
    	{		
    		beginSPITransaction();
    		setAddr(x, y, x+w-1, y+h-1);
    		writecommand_cont(ILI9341_RAMWR);
    		for(y=h; y>0; y--) {
    			uint16_t color = RGB14tocolor565(r,g,b);
    
    			for(x=w; x>1; x--) {
    				writedata16_cont(color);
    			}
    			writedata16_last(color);
    			if (y > 1 && (y & 1)) {
    				endSPITransaction();
    				beginSPITransaction();
    			}
    			r+=dr;g+=dg; b+=db;
    		}
    		endSPITransaction();
    	}
    }
    
    // fillRectHGradient	- fills area with horizontal gradient
    void ILI9341_t3n::fillRectHGradient(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color1, uint16_t color2)
    {
    	x+=_originx;
    	y+=_originy;
    
    	// Rectangular clipping 
    	if((x >= _displayclipx2) || (y >= _displayclipy2)) return;
    	if(x < _displayclipx1) {	w -= (_displayclipx1-x); x = _displayclipx1; 	}
    	if(y < _displayclipy1) {	h -= (_displayclipy1 - y); y = _displayclipy1; 	}
    	if((x + w - 1) >= _displayclipx2)  w = _displayclipx2  - x;
    	if((y + h - 1) >= _displayclipy2) h = _displayclipy2 - y;
    	
    	int16_t r1, g1, b1, r2, g2, b2, dr, dg, db, r, g, b;
    	uint16_t color;
    	color565toRGB14(color1,r1,g1,b1);
    	color565toRGB14(color2,r2,g2,b2);
    	dr=(r2-r1)/w; dg=(g2-g1)/w; db=(b2-b1)/w;
    	r=r1;g=g1;b=b1;	
    	#ifdef ENABLE_ILI9341_FRAMEBUFFER
    	if (_use_fbtft) {
    		uint16_t * pfbPixel_row = &_pfbtft[ y*_width + x];
    		for (;h>0; h--) {
    			uint16_t * pfbPixel = pfbPixel_row;
    			for (int i = 0 ;i < w; i++) {
    				*pfbPixel++ = RGB14tocolor565(r,g,b);
    				r+=dr;g+=dg; b+=db;
    			}
    			pfbPixel_row += _width;
    			r=r1;g=g1;b=b1;
    		}
    	} else 
    	#endif
    	{
    		beginSPITransaction();
    		setAddr(x, y, x+w-1, y+h-1);
    		writecommand_cont(ILI9341_RAMWR);
    		for(y=h; y>0; y--) {
    			for(x=w; x>1; x--) {
    				color = RGB14tocolor565(r,g,b);
    				writedata16_cont(color);
    				r+=dr;g+=dg; b+=db;
    			}
    			color = RGB14tocolor565(r,g,b);
    			writedata16_last(color);
    			if (y > 1 && (y & 1)) {
    				endSPITransaction();
    				beginSPITransaction();
    			}
    			r=r1;g=g1;b=b1;
    		}
    		endSPITransaction();
    	}
    }
    
    // fillScreenVGradient - fills screen with vertical gradient
    void ILI9341_t3n::fillScreenVGradient(uint16_t color1, uint16_t color2)
    {
    	fillRectVGradient(0,0,_width,_height,color1,color2);
    }
    
    // fillScreenHGradient - fills screen with horizontal gradient
    void ILI9341_t3n::fillScreenHGradient(uint16_t color1, uint16_t color2)
    {
    	fillRectHGradient(0,0,_width,_height,color1,color2);
    }
    
    
    #define MADCTL_MY  0x80
    #define MADCTL_MX  0x40
    #define MADCTL_MV  0x20
    #define MADCTL_ML  0x10
    #define MADCTL_RGB 0x00
    #define MADCTL_BGR 0x08
    #define MADCTL_MH  0x04
    
    void ILI9341_t3n::setRotation(uint8_t m)
    {
    	rotation = m % 4; // can't be higher than 3
    	beginSPITransaction();
    	writecommand_cont(ILI9341_MADCTL);
    	switch (rotation) {
    	case 0:
    		writedata8_last(MADCTL_MX | MADCTL_BGR);
    		_width  = ILI9341_TFTWIDTH;
    		_height = ILI9341_TFTHEIGHT;
    		break;
    	case 1:
    		writedata8_last(MADCTL_MV | MADCTL_BGR);
    		_width  = ILI9341_TFTHEIGHT;
    		_height = ILI9341_TFTWIDTH;
    		break;
    	case 2:
    		writedata8_last(MADCTL_MY | MADCTL_BGR);
    		_width  = ILI9341_TFTWIDTH;
    		_height = ILI9341_TFTHEIGHT;
    		break;
    	case 3:
    		writedata8_last(MADCTL_MX | MADCTL_MY | MADCTL_MV | MADCTL_BGR);
    		_width  = ILI9341_TFTHEIGHT;
    		_height = ILI9341_TFTWIDTH;
    		break;
    	}
    	endSPITransaction();
    	setClipRect();
    	setOrigin();
    	
    	cursor_x = 0;
    	cursor_y = 0;
    }
    
    void ILI9341_t3n::setScroll(uint16_t offset)
    {
    	beginSPITransaction();
    	writecommand_cont(ILI9341_VSCRSADD);
    	writedata16_last(offset);
    	endSPITransaction();
    }
    
    void ILI9341_t3n::invertDisplay(boolean i)
    {
    	beginSPITransaction();
    	writecommand_last(i ? ILI9341_INVON : ILI9341_INVOFF);
    	endSPITransaction();
    }
    
    
    
    
    
    
    
    
    
    
    /*
    uint8_t ILI9341_t3n::readdata(void)
    {
      uint8_t r;
           // Try to work directly with SPI registers...
           // First wait until output queue is empty
            uint16_t wTimeout = 0xffff;
            while (((_pkinetisk_spi->SR) & (15 << 12)) && (--wTimeout)) ; // wait until empty
            
    //       	_pkinetisk_spi->MCR |= SPI_MCR_CLR_RXF; // discard any received data
    //		_pkinetisk_spi->SR = SPI_SR_TCF;
            
            // Transfer a 0 out... 
            writedata8_cont(0);   
            
            // Now wait until completed. 
            wTimeout = 0xffff;
            while (((_pkinetisk_spi->SR) & (15 << 12)) && (--wTimeout)) ; // wait until empty
            r = _pkinetisk_spi->POPR;  // get the received byte... should check for it first...
        return r;
    }
     */
    
    uint8_t ILI9341_t3n::readcommand8(uint8_t c, uint8_t index)
    {
        // Bail if not valid miso
        if (_miso == 0xff) return 0;
    
     #ifdef KINETISK
        uint16_t wTimeout = 0xffff;
        uint8_t r=0;
    
    
        beginSPITransaction();
    	if (_pspin->sizeFIFO() >= 4) {
    	    while (((_pkinetisk_spi->SR) & (15 << 12)) && (--wTimeout)) ; // wait until empty
    
    	    // Make sure the last frame has been sent...
    	    _pkinetisk_spi->SR = SPI_SR_TCF;   // dlear it out;
    	    wTimeout = 0xffff;
    	    while (!((_pkinetisk_spi->SR) & SPI_SR_TCF) && (--wTimeout)) ; // wait until it says the last frame completed
    
    	    // clear out any current received bytes
    	    wTimeout = 0x10;    // should not go more than 4...
    	    while ((((_pkinetisk_spi->SR) >> 4) & 0xf) && (--wTimeout))  {
    	        r = _pkinetisk_spi->POPR;
    	    }
    
    	    //writecommand(0xD9); // sekret command
    		_pkinetisk_spi->PUSHR = 0xD9 | (pcs_command << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT;
    	//	while (((_pkinetisk_spi->SR) & (15 << 12)) > (3 << 12)) ; // wait if FIFO full
    
    	    // writedata(0x10 + index);
    		_pkinetisk_spi->PUSHR = (0x10 + index) | (pcs_data << 16) | SPI_PUSHR_CTAS(0);
    	//	while (((_pkinetisk_spi->SR) & (15 << 12)) > (3 << 12)) ; // wait if FIFO full
    	    // writecommand(c);
    	   	_pkinetisk_spi->PUSHR = c | (pcs_command << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT;
    	//	while (((_pkinetisk_spi->SR) & (15 << 12)) > (3 << 12)) ; // wait if FIFO full
    
    	    // readdata
    		_pkinetisk_spi->PUSHR = 0 | (pcs_data << 16) | SPI_PUSHR_CTAS(0);
    	//	while (((_pkinetisk_spi->SR) & (15 << 12)) > (3 << 12)) ; // wait if FIFO full
    
    	    // Now wait until completed.
    	    wTimeout = 0xffff;
    	    while (((_pkinetisk_spi->SR) & (15 << 12)) && (--wTimeout)) ; // wait until empty
    
    	    // Make sure the last frame has been sent...
    	    _pkinetisk_spi->SR = SPI_SR_TCF;   // dlear it out;
    	    wTimeout = 0xffff;
    	    while (!((_pkinetisk_spi->SR) & SPI_SR_TCF) && (--wTimeout)) ; // wait until it says the last frame completed
    
    	    wTimeout = 0x10;    // should not go more than 4...
    	    // lets get all of the values on the FIFO
    	    while ((((_pkinetisk_spi->SR) >> 4) & 0xf) && (--wTimeout))  {
    	        r = _pkinetisk_spi->POPR;
    	    }
    	} else {
    		while (((_pkinetisk_spi->SR) & (15 << 12)) && (--wTimeout)) ; // wait until empty
    
    	    // Make sure the last frame has been sent...
    	    _pkinetisk_spi->SR = SPI_SR_TCF;   // dlear it out;
    	    wTimeout = 0xffff;
    	    while (!((_pkinetisk_spi->SR) & SPI_SR_TCF) && (--wTimeout)) ; // wait until it says the last frame completed
    
    	    // clear out any current received bytes
    	    wTimeout = 0x10;    // should not go more than 4...
    	    while ((((_pkinetisk_spi->SR) >> 4) & 0xf) && (--wTimeout))  {
    	        r = _pkinetisk_spi->POPR;
    	    }
    
    	    //writecommand(0xD9); // sekret command
    		_pkinetisk_spi->PUSHR = 0xD9 | (pcs_command << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT;
    		while (((_pkinetisk_spi->SR) & (15 << 12)) > (0 << 12)) ; // wait if FIFO full
    
    	    // writedata(0x10 + index);
    		_pkinetisk_spi->PUSHR = (0x10 + index) | (pcs_data << 16) | SPI_PUSHR_CTAS(0);
    		while (((_pkinetisk_spi->SR) & (15 << 12)) > (0 << 12)) ; // wait if FIFO full
    
    		// See if there are any return values to pop 
    	    while (((_pkinetisk_spi->SR) >> 4) & 0xf)  {
    	        r = _pkinetisk_spi->POPR;
    	    }
    
    	    // writecommand(c);
    	   	_pkinetisk_spi->PUSHR = c | (pcs_command << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT;
    		while (((_pkinetisk_spi->SR) & (15 << 12)) > (0 << 12)) ; // wait if FIFO full
    
    		// See if there are any return values to pop 
    	    while (((_pkinetisk_spi->SR) >> 4) & 0xf)  {
    	        r = _pkinetisk_spi->POPR;
    	    }
    
    	    // readdata
    		_pkinetisk_spi->PUSHR = 0 | (pcs_data << 16) | SPI_PUSHR_CTAS(0);
    		while (((_pkinetisk_spi->SR) & (15 << 12)) > (0 << 12)) ; // wait if FIFO full
    
    		// See if there are any return values to pop 
    	    while (((_pkinetisk_spi->SR) >> 4) & 0xf)  {
    	        r = _pkinetisk_spi->POPR;
    	    }
    	    // Now wait until completed.
    	    wTimeout = 0xffff;
    	    while (((_pkinetisk_spi->SR) & (15 << 12)) && (--wTimeout)) ; // wait until empty
    
    	    // Make sure the last frame has been sent...
    	    _pkinetisk_spi->SR = SPI_SR_TCF;   // dlear it out;
    	    wTimeout = 0xffff;
    	    while (!((_pkinetisk_spi->SR) & SPI_SR_TCF) && (--wTimeout)) ; // wait until it says the last frame completed
    
    	    wTimeout = 0x10;    // should not go more than 4...
    	    // lets get all of the values on the FIFO
    	    while ((((_pkinetisk_spi->SR) >> 4) & 0xf) && (--wTimeout))  {
    	        r = _pkinetisk_spi->POPR;
    	    }
    
    	}  
        endSPITransaction();
        return r;  // get the received byte... should check for it first...
    #else
    	beginSPITransaction();
    	writecommand_cont(0xD9);
    	writedata8_cont(0x10 + index);
    
    	writecommand_cont(c);
    	writedata8_cont(0);
    	uint8_t r = waitTransmitCompleteReturnLast();
    	endSPITransaction();
    	return r;
    
    #endif   
    }
    
    // Read Pixel at x,y and get back 16-bit packed color
    #define READ_PIXEL_PUSH_BYTE 0x3f
    uint16_t ILI9341_t3n::readPixel(int16_t x, int16_t y)
    {
    #ifdef KINETISK	
    	//BUGBUG:: Should add some validation of X and Y
    	// Now if we are in buffer mode can return real fast
    	#ifdef ENABLE_ILI9341_FRAMEBUFFER
    	if (_use_fbtft) {
    		x+=_originx;
    		y+=_originy;
    
    		return _pfbtft[y*_width + x] ;
    	}
    	#endif	
    
       if (_miso == 0xff) return 0xffff;	// bail if not valid miso
    
    	// First pass for other SPI busses use readRect to handle the read... 
    	if (_pspin->sizeFIFO() < 4) {
    		uint16_t colors;
    		readRect(x, y, 1, 1, &colors);
    		return colors;
    	}
    
    	uint8_t dummy __attribute__((unused));
    	uint8_t r,g,b;
    
    	beginSPITransaction(ILI9341_SPICLOCK_READ);
    
    	// Update our origin. 
    	x+=_originx;
    	y+=_originy;
    
    	setAddr(x, y, x, y);
    	writecommand_cont(ILI9341_RAMRD); // read from RAM
    	_pspin->waitTransmitComplete();
    
    	// Push 4 bytes over SPI
    	_pkinetisk_spi->PUSHR = 0 | (pcs_data << 16) | SPI_PUSHR_CTAS(0)| SPI_PUSHR_CONT;
    	_pspin->waitFifoEmpty();    // wait for both queues to be empty.
    
    	_pkinetisk_spi->PUSHR = 0 | (pcs_data << 16) | SPI_PUSHR_CTAS(0)| SPI_PUSHR_CONT;
    	_pkinetisk_spi->PUSHR = 0 | (pcs_data << 16) | SPI_PUSHR_CTAS(0)| SPI_PUSHR_CONT;
    	_pkinetisk_spi->PUSHR = 0 | (pcs_data << 16) | SPI_PUSHR_CTAS(0)| SPI_PUSHR_EOQ;
    
    	// Wait for End of Queue
    	while ((_pkinetisk_spi->SR & SPI_SR_EOQF) == 0) ;
    	_pkinetisk_spi->SR = SPI_SR_EOQF;  // make sure it is clear
    
    	// Read Pixel Data
    	dummy = _pkinetisk_spi->POPR;	// Read a DUMMY byte of GRAM
    	r = _pkinetisk_spi->POPR;		// Read a RED byte of GRAM
    	g = _pkinetisk_spi->POPR;		// Read a GREEN byte of GRAM
    	b = _pkinetisk_spi->POPR;		// Read a BLUE byte of GRAM
    
    	endSPITransaction();
    	return color565(r,g,b);
    #else
    	// Kinetisk
    	uint16_t colors;
    	readRect(x, y, 1, 1, &colors);
    	return colors;
    #endif	
    }
    
    // Now lets see if we can read in multiple pixels
    #ifdef KINETISK
    void ILI9341_t3n::readRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t *pcolors)
    {
    	// Use our Origin. 
    	x+=_originx;
    	y+=_originy;
    	//BUGBUG:: Should add some validation of X and Y
    
    	#ifdef ENABLE_ILI9341_FRAMEBUFFER
    	if (_use_fbtft) {
    		uint16_t * pfbPixel_row = &_pfbtft[ y*_width + x];
    		for (;h>0; h--) {
    			uint16_t * pfbPixel = pfbPixel_row;
    			for (int i = 0 ;i < w; i++) {
    				*pcolors++ = *pfbPixel++;
    			}
    			pfbPixel_row += _width;
    		}
    		return;	
    	}
    	#endif	
    
       if (_miso == 0xff) return;		// bail if not valid miso
    
    	uint8_t rgb[3];               // RGB bytes received from the display
    	uint8_t rgbIdx = 0;
    	uint32_t txCount = w * h * 3; // number of bytes we will transmit to the display
    	uint32_t rxCount = txCount;   // number of bytes we will receive back from the display
    
    	beginSPITransaction(ILI9341_SPICLOCK_READ);
    
    	setAddr(x, y, x+w-1, y+h-1);
    	writecommand_cont(ILI9341_RAMRD); // read from RAM
    
    	// transmit a DUMMY byte before the color bytes
    	_pkinetisk_spi->PUSHR = 0 | (pcs_data << 16) | SPI_PUSHR_CTAS(0)| SPI_PUSHR_CONT;
    
    	// skip values returned by the queued up transfers and the current in-flight transfer
    	uint32_t sr = _pkinetisk_spi->SR;
    	uint8_t skipCount = ((sr >> 4) & 0xF) + ((sr >> 12) & 0xF) + 1;
    
    	while (txCount || rxCount) {
    		// transmit another byte if possible
    		if (txCount && ((_pkinetisk_spi->SR & 0xF000) >> 12) < _pspin->sizeFIFO()) {
    			txCount--;
    			if (txCount) {
    				_pkinetisk_spi->PUSHR = READ_PIXEL_PUSH_BYTE | (pcs_data << 16) | SPI_PUSHR_CTAS(0)| SPI_PUSHR_CONT;
    			} else {
    				_pkinetisk_spi->PUSHR = READ_PIXEL_PUSH_BYTE | (pcs_data << 16) | SPI_PUSHR_CTAS(0)| SPI_PUSHR_EOQ;
    			}
    		}
    
    		// receive another byte if possible, and either skip it or store the color
    		if (rxCount && (_pkinetisk_spi->SR & 0xF0)) {
    			rgb[rgbIdx] = _pkinetisk_spi->POPR;
    
    			if (skipCount) {
    				skipCount--;
    			} else {
    				rxCount--;
    				rgbIdx++;
    				if (rgbIdx == 3) {
    					rgbIdx = 0;
    					*pcolors++ = color565(rgb[0], rgb[1], rgb[2]);
    				}
    			}
    		}
    	}
    
    	// wait for End of Queue
    	while ((_pkinetisk_spi->SR & SPI_SR_EOQF) == 0) ;
    	_pkinetisk_spi->SR = SPI_SR_EOQF;  // make sure it is clear
    	endSPITransaction();
    
    }
    #else
    
    // Teensy LC version
    void ILI9341_t3n::readRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t *pcolors)
    {
    	// Use our Origin. 
    	x+=_originx;
    	y+=_originy;
    	//BUGBUG:: Should add some validation of X and Y
    
       if (_miso == 0xff) return;		// bail if not valid miso
    
    	uint8_t rgb[3];               // RGB bytes received from the display
    	uint8_t rgbIdx = 0;
    	uint32_t txCount = w * h * 3; // number of bytes we will transmit to the display
    	uint32_t rxCount = txCount;   // number of bytes we will receive back from the display
    
    	beginSPITransaction(ILI9341_SPICLOCK_READ);
    
    	setAddr(x, y, x+w-1, y+h-1);
    	writecommand_cont(ILI9341_RAMRD); // read from RAM
    
    	// transmit a DUMMY byte before the color bytes
    	writedata8_cont(0);
    
    	// Wait until that one returns, Could do a little better and double buffer but this is easer for now.
    	waitTransmitComplete();
    
    	// Since double buffer setup lets try keeping read/write in sync
    #define RRECT_TIMEOUT 0xffff	
    #undef 	READ_PIXEL_PUSH_BYTE
    #define READ_PIXEL_PUSH_BYTE 0 // try with zero to see... 	
    	uint16_t timeout_countdown = RRECT_TIMEOUT;
    	uint16_t dl_in;
    	// Write out first byte:
    
    	while (!(_pkinetisl_spi->S & SPI_S_SPTEF)) ; // Not worried that this can completely hang?
    	_pkinetisl_spi->DL = READ_PIXEL_PUSH_BYTE;
    
    	while (rxCount && timeout_countdown) {
    		// Now wait until we can output something
    		dl_in = 0xffff;
    		if (rxCount > 1) {
    			while (!(_pkinetisl_spi->S & SPI_S_SPTEF)) ; // Not worried that this can completely hang?
    			if (_pkinetisl_spi->S & SPI_S_SPRF)
    				dl_in = _pkinetisl_spi->DL;  
    			_pkinetisl_spi->DL = READ_PIXEL_PUSH_BYTE;
    		}
    
    		// Now wait until there is a byte available to receive
    		while ((dl_in != 0xffff) && !(_pkinetisl_spi->S & SPI_S_SPRF) && --timeout_countdown) ;
    		if (timeout_countdown) {   // Make sure we did not get here because of timeout 
    			rxCount--;
    			rgb[rgbIdx] = (dl_in != 0xffff)? dl_in : _pkinetisl_spi->DL;
    			rgbIdx++;
    			if (rgbIdx == 3) {
    				rgbIdx = 0;
    				*pcolors++ = color565(rgb[0], rgb[1], rgb[2]);
    			}
    			timeout_countdown = timeout_countdown;
    		}
    	}
    
    	// Debug code. 
    /*	if (timeout_countdown == 0) {
    		Serial.print("RRect Timeout ");
    		Serial.println(rxCount, DEC);
    	} */
    	endSPITransaction();
    }
    #endif		
    
    // Now lets see if we can writemultiple pixels
    void ILI9341_t3n::writeRect(int16_t x, int16_t y, int16_t w, int16_t h, const uint16_t *pcolors)
    {
    
    	x+=_originx;
    	y+=_originy;
    	uint16_t x_clip_left = 0;  // How many entries at start of colors to skip at start of row
    	uint16_t x_clip_right = 0;    // how many color entries to skip at end of row for clipping
    	// Rectangular clipping 
    
    	// See if the whole thing out of bounds...
    	if((x >= _displayclipx2) || (y >= _displayclipy2)) return;
    	if (((x+w) <= _displayclipx1) || ((y+h) <= _displayclipy1)) return;
    
    	// In these cases you can not do simple clipping, as we need to synchronize the colors array with the
    	// We can clip the height as when we get to the last visible we don't have to go any farther. 
    	// also maybe starting y as we will advance the color array. 
     	if(y < _displayclipy1) {
     		int dy = (_displayclipy1 - y);
     		h -= dy; 
     		pcolors += (dy*w); // Advance color array to 
     		y = _displayclipy1; 	
     	}
    
    	if((y + h - 1) >= _displayclipy2) h = _displayclipy2 - y;
    
    	// For X see how many items in color array to skip at start of row and likewise end of row 
    	if(x < _displayclipx1) {
    		x_clip_left = _displayclipx1-x; 
    		w -= x_clip_left; 
    		x = _displayclipx1; 	
    	}
    	if((x + w - 1) >= _displayclipx2) {
    		x_clip_right = w;
    		w = _displayclipx2  - x;
    		x_clip_right -= w; 
    	} 
    
    	#ifdef ENABLE_ILI9341_FRAMEBUFFER
    	if (_use_fbtft) {
    		uint16_t * pfbPixel_row = &_pfbtft[ y*_width + x];
    		for (;h>0; h--) {
    			uint16_t * pfbPixel = pfbPixel_row;
    			pcolors += x_clip_left;
    			for (int i = 0 ;i < w; i++) {
    				*pfbPixel++ = *pcolors++;
    			}
    			pfbPixel_row += _width;
    			pcolors += x_clip_right;
    
    		}
    		return;	
    	}
    	#endif
    
       	beginSPITransaction();
    	setAddr(x, y, x+w-1, y+h-1);
    	writecommand_cont(ILI9341_RAMWR);
    	for(y=h; y>0; y--) {
    		pcolors += x_clip_left;
    		for(x=w; x>1; x--) {
    			writedata16_cont(*pcolors++);
    		}
    		writedata16_last(*pcolors++);
    		pcolors += x_clip_right;
    	}
    	endSPITransaction();
    }
    
    // writeRect8BPP - 	write 8 bit per pixel paletted bitmap
    //					bitmap data in array at pixels, one byte per pixel
    //					color palette data in array at palette
    void ILI9341_t3n::writeRect8BPP(int16_t x, int16_t y, int16_t w, int16_t h, const uint8_t *pixels, const uint16_t * palette )
    {
    	//Serial.printf("\nWR8: %d %d %d %d %x\n", x, y, w, h, (uint32_t)pixels);
    	x+=_originx;
    	y+=_originy;
    
    	uint16_t x_clip_left = 0;  // How many entries at start of colors to skip at start of row
    	uint16_t x_clip_right = 0;    // how many color entries to skip at end of row for clipping
    	// Rectangular clipping 
    
    	// See if the whole thing out of bounds...
    	if((x >= _displayclipx2) || (y >= _displayclipy2)) return;
    	if (((x+w) <= _displayclipx1) || ((y+h) <= _displayclipy1)) return;
    
    	// In these cases you can not do simple clipping, as we need to synchronize the colors array with the
    	// We can clip the height as when we get to the last visible we don't have to go any farther. 
    	// also maybe starting y as we will advance the color array. 
     	if(y < _displayclipy1) {
     		int dy = (_displayclipy1 - y);
     		h -= dy; 
     		pixels += (dy*w); // Advance color array to 
     		y = _displayclipy1; 	
     	}
    
    	if((y + h - 1) >= _displayclipy2) h = _displayclipy2 - y;
    
    	// For X see how many items in color array to skip at start of row and likewise end of row 
    	if(x < _displayclipx1) {
    		x_clip_left = _displayclipx1-x; 
    		w -= x_clip_left; 
    		x = _displayclipx1; 	
    	}
    	if((x + w - 1) >= _displayclipx2) {
    		x_clip_right = w;
    		w = _displayclipx2  - x;
    		x_clip_right -= w; 
    	} 
    	//Serial.printf("WR8C: %d %d %d %d %x- %d %d\n", x, y, w, h, (uint32_t)pixels, x_clip_right, x_clip_left);
    	#ifdef ENABLE_ILI9341_FRAMEBUFFER
    	if (_use_fbtft) {
    		uint16_t * pfbPixel_row = &_pfbtft[ y*_width + x];
    		for (;h>0; h--) {
    			pixels += x_clip_left;
    			uint16_t * pfbPixel = pfbPixel_row;
    			for (int i = 0 ;i < w; i++) {
    				*pfbPixel++ = palette[*pixels++];
    			}
    			pixels += x_clip_right;
    			pfbPixel_row += _width;
    		}
    		return;	
    	}
    	#endif
    
    	beginSPITransaction();
    	setAddr(x, y, x+w-1, y+h-1);
    	writecommand_cont(ILI9341_RAMWR);
    	for(y=h; y>0; y--) {
    		pixels += x_clip_left;
    		//Serial.printf("%x: ", (uint32_t)pixels);
    		for(x=w; x>1; x--) {
    			//Serial.print(*pixels, DEC);
    			writedata16_cont(palette[*pixels++]);
    		}
    		//Serial.println(*pixels, DEC);
    		writedata16_last(palette[*pixels++]);
    		pixels += x_clip_right;
    	}
    	endSPITransaction();
    }
    
    // writeRect4BPP - 	write 4 bit per pixel paletted bitmap
    //					bitmap data in array at pixels, 4 bits per pixel
    //					color palette data in array at palette
    //					width must be at least 2 pixels
    void ILI9341_t3n::writeRect4BPP(int16_t x, int16_t y, int16_t w, int16_t h, const uint8_t *pixels, const uint16_t * palette )
    {
    	// Simply call through our helper
    	writeRectNBPP(x, y, w, h,  4, pixels, palette );
    }
    
    // writeRect2BPP - 	write 2 bit per pixel paletted bitmap
    //					bitmap data in array at pixels, 4 bits per pixel
    //					color palette data in array at palette
    //					width must be at least 4 pixels
    void ILI9341_t3n::writeRect2BPP(int16_t x, int16_t y, int16_t w, int16_t h, const uint8_t *pixels, const uint16_t * palette )
    {
    	// Simply call through our helper
    	writeRectNBPP(x, y, w, h,  2, pixels, palette );
    
    }
    
    ///============================================================================
    // writeRect1BPP - 	write 1 bit per pixel paletted bitmap
    //					bitmap data in array at pixels, 4 bits per pixel
    //					color palette data in array at palette
    //					width must be at least 8 pixels
    void ILI9341_t3n::writeRect1BPP(int16_t x, int16_t y, int16_t w, int16_t h, const uint8_t *pixels, const uint16_t * palette )
    {
    	// Simply call through our helper
    	writeRectNBPP(x, y, w, h,  1, pixels, palette );
    }
    
    
    
    ///============================================================================
    // writeRectNBPP - 	write N(1, 2, 4, 8) bit per pixel paletted bitmap
    //					bitmap data in array at pixels
    //  Currently writeRect1BPP, writeRect2BPP, writeRect4BPP use this to do all of the work. 
    void ILI9341_t3n::writeRectNBPP(int16_t x, int16_t y, int16_t w, int16_t h,  uint8_t bits_per_pixel, 
    		const uint8_t *pixels, const uint16_t * palette )
    {
    	//Serial.printf("\nWR8: %d %d %d %d %x\n", x, y, w, h, (uint32_t)pixels);
    	x+=_originx;
    	y+=_originy;
    	uint8_t pixels_per_byte = 8/ bits_per_pixel;
    	uint16_t count_of_bytes_per_row = (w + pixels_per_byte -1)/pixels_per_byte;		// Round up to handle non multiples
    	uint8_t row_shift_init = 8 - bits_per_pixel;				// We shift down 6 bits by default 
    	uint8_t pixel_bit_mask = (1 << bits_per_pixel) - 1; 		// get mask to use below
    	// Rectangular clipping 
    
    	// See if the whole thing out of bounds...
    	if((x >= _displayclipx2) || (y >= _displayclipy2)) return;
    	if (((x+w) <= _displayclipx1) || ((y+h) <= _displayclipy1)) return;
    
    	// In these cases you can not do simple clipping, as we need to synchronize the colors array with the
    	// We can clip the height as when we get to the last visible we don't have to go any farther. 
    	// also maybe starting y as we will advance the color array. 
    	// Again assume multiple of 8 for width
     	if(y < _displayclipy1) {
     		int dy = (_displayclipy1 - y);
     		h -= dy; 
     		pixels += dy * count_of_bytes_per_row;
     		y = _displayclipy1; 	
     	}
    
    	if((y + h - 1) >= _displayclipy2) h = _displayclipy2 - y;
    
    	// For X see how many items in color array to skip at start of row and likewise end of row 
    	if(x < _displayclipx1) {
    		uint16_t x_clip_left = _displayclipx1-x; 
    		w -= x_clip_left; 
    		x = _displayclipx1; 
    		// Now lets update pixels to the rigth offset and mask
    		uint8_t x_clip_left_bytes_incr = x_clip_left / pixels_per_byte;
    		pixels += x_clip_left_bytes_incr;
    		row_shift_init = 8 - (x_clip_left - (x_clip_left_bytes_incr * pixels_per_byte) + 1) * bits_per_pixel; 	
    	}
    
    	if((x + w - 1) >= _displayclipx2) {
    		w = _displayclipx2  - x;
    	} 
    
    	const uint8_t * pixels_row_start = pixels;  // remember our starting position offset into row
    
    	#ifdef ENABLE_ILI9341_FRAMEBUFFER
    	if (_use_fbtft) {
    		uint16_t * pfbPixel_row = &_pfbtft[ y*_width + x];
    		for (;h>0; h--) {
    			uint16_t * pfbPixel = pfbPixel_row;
    			pixels = pixels_row_start;				// setup for this row
    			uint8_t pixel_shift = row_shift_init;			// Setup mask
    
    			for (int i = 0 ; i < w; i++) {
    				*pfbPixel++ = palette[((*pixels)>>pixel_shift) & pixel_bit_mask];
    				if (!pixel_shift) {
    					pixel_shift = 8 - bits_per_pixel;	//setup next mask
    					pixels++;
    				} else {
    					pixel_shift -= bits_per_pixel;
    				}
    			}
    			pfbPixel_row += _width;
    			pixels_row_start += count_of_bytes_per_row;
    		}
    		return;	
    
    	}
    	#endif
    
    	beginSPITransaction();
    	setAddr(x, y, x+w-1, y+h-1);
    	writecommand_cont(ILI9341_RAMWR);
    	for (;h>0; h--) {
    		pixels = pixels_row_start;				// setup for this row
    		uint8_t pixel_shift = row_shift_init;			// Setup mask
    
    		for (int i = 0 ;i < w; i++) {
    			writedata16_cont(palette[((*pixels)>>pixel_shift) & pixel_bit_mask]);
    			if (!pixel_shift) {
    				pixel_shift = 8 - bits_per_pixel;	//setup next mask
    				pixels++;
    			} else {
    				pixel_shift -= bits_per_pixel;
    			}
    		}
    		pixels_row_start += count_of_bytes_per_row;
    	}
    	writecommand_last(ILI9341_NOP);
    	endSPITransaction();
    }
    
    static const uint8_t init_commands[] = {
    	4, 0xEF, 0x03, 0x80, 0x02,
    	4, 0xCF, 0x00, 0XC1, 0X30,
    	5, 0xED, 0x64, 0x03, 0X12, 0X81,
    	4, 0xE8, 0x85, 0x00, 0x78,
    	6, 0xCB, 0x39, 0x2C, 0x00, 0x34, 0x02,
    	2, 0xF7, 0x20,
    	3, 0xEA, 0x00, 0x00,
    	2, ILI9341_PWCTR1, 0x23, // Power control
    	2, ILI9341_PWCTR2, 0x10, // Power control
    	3, ILI9341_VMCTR1, 0x3e, 0x28, // VCM control
    	2, ILI9341_VMCTR2, 0x86, // VCM control2
    	2, ILI9341_MADCTL, 0x48, // Memory Access Control
    	2, ILI9341_PIXFMT, 0x55,
    	3, ILI9341_FRMCTR1, 0x00, 0x18,
    	4, ILI9341_DFUNCTR, 0x08, 0x82, 0x27, // Display Function Control
    	2, 0xF2, 0x00, // Gamma Function Disable
    	2, ILI9341_GAMMASET, 0x01, // Gamma curve selected
    	16, ILI9341_GMCTRP1, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08,
    		0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00, // Set Gamma
    	16, ILI9341_GMCTRN1, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07,
    		0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F, // Set Gamma
    	0
    };
    
    void ILI9341_t3n::begin(void)
    {
        // verify SPI pins are valid;
    	// allow user to say use current ones...
    #ifdef DEBUG_ASYNC_UPDATE
    	Serial.printf("????? _dmasettings[0] %x %x\n", (uint32_t)&_dmasettings[0], (uint32_t)_dmasettings[0].TCD); Serial.flush();
    #endif
    	if ((_mosi != 255) || (_miso != 255) || (_sclk != 255)) {
    		if (!(_pspin->pinIsMOSI(_mosi)) || !(_pspin->pinIsMISO(_miso)) || !(_pspin->pinIsSCK(_sclk))) {
    			#ifdef SPIN1_OBJECT_CREATED			
    			if (SPIN1.pinIsMOSI(_mosi) && SPIN1.pinIsMISO(_miso) && SPIN1.pinIsSCK(_sclk)) {
    				_pspin = &SPIN1;
    #ifdef KINETISK
    				_pkinetisk_spi = _pspin->kinetisk_spi();
    #else
    				_pkinetisl_spi = _pspin->kinetisl_spi();
    #endif					
    				//Serial.println("ILI9341_t3n: SPIN1 automatically selected");
    			} else {
    				#ifdef SPIN2_OBJECT_CREATED			
    				if (SPIN2.pinIsMOSI(_mosi) && SPIN2.pinIsMISO(_miso) && SPIN2.pinIsSCK(_sclk)) {
    					_pspin = &SPIN2;
    					_pkinetisk_spi = _pspin->kinetisk_spi();
    					//Serial.println("ILI9341_t3n: SPIN2 automatically selected");
    				} else {
    				#endif
    			#endif
    					uint8_t mosi_sck_bad = false;
    					Serial.print("ILI9341_t3n: Error not valid SPI pins:");
    					if(!(_pspin->pinIsMOSI(_mosi)))  {
    						Serial.print(" MOSI");
    						mosi_sck_bad = true;
    					}
    					if (!_pspin->pinIsSCK(_sclk)) {
    						Serial.print(" SCLK");
    						mosi_sck_bad = true;
    					}
    
    					// Maybe allow us to limp with only MISO bad
    					if(!(_pspin->pinIsMISO(_miso))) {
    						Serial.print(" MISO");
    						_miso = 0xff;	// set miso to 255 as flag it is bad
    					}
    					Serial.println();
    					
    					if (mosi_sck_bad) {
    	    				return; // not valid pins...
    					}
    				#ifdef SPIN2_OBJECT_CREATED			
    	    		}
    	    		#endif
    			#ifdef SPIN1_OBJECT_CREATED			
    			}
    			#endif
    
    		}
    		//Serial.printf("MOSI:%d MISO:%d SCK:%d\n\r", _mosi, _miso, _sclk);			
            _pspin->setMOSI(_mosi);
            if (_miso != 0xff) _pspin->setMISO(_miso);
            _pspin->setSCK(_sclk);
    	}
    
    	_pspin->begin();
    #ifdef KINETISK
    	if (_pspin->pinIsChipSelect(_cs, _dc)) {
    		pcs_data = _pspin->setCS(_cs);
    		pcs_command = pcs_data | _pspin->setCS(_dc);
    	} else {
    		// See if at least DC is on chipselect pin, if so try to limp along...
    		if (_pspin->pinIsChipSelect(_dc)) {
    			pcs_data = 0;
    			pcs_command = pcs_data | _pspin->setCS(_dc);
    			pinMode(_cs, OUTPUT);
    			_csport    = portOutputRegister(digitalPinToPort(_cs));
    			_cspinmask = digitalPinToBitMask(_cs);
    			*_csport |= _cspinmask;
    		} else {
    			pcs_data = 0;
    			pcs_command = 0;
      			Serial.println("ILI9341_t3n: Error not DC is not valid hardware CS pin");
    			return;
    		}
    	}
    #else
    	// TLC
    	pcs_data = 0;
    	pcs_command = 0;
    	pinMode(_cs, OUTPUT);
    	_csport    = portOutputRegister(digitalPinToPort(_cs));
    	_cspinmask = digitalPinToBitMask(_cs);
    	*_csport |= _cspinmask;
    	pinMode(_dc, OUTPUT);
    	_dcport    = portOutputRegister(digitalPinToPort(_dc));
    	_dcpinmask = digitalPinToBitMask(_dc);
    	*_dcport |= _dcpinmask;
    	_dcpinAsserted = 0;
    #endif	
    
    	// toggle RST low to reset
    	if (_rst < 255) {
    		pinMode(_rst, OUTPUT);
    		digitalWrite(_rst, HIGH);
    		delay(5);
    		digitalWrite(_rst, LOW);
    		delay(20);
    		digitalWrite(_rst, HIGH);
    		delay(150);
    	}
    /*
    	uint8_t x = readcommand8(ILI9341_RDMODE);
    	Serial.print("\nDisplay Power Mode: 0x"); Serial.println(x, HEX);
    	x = readcommand8(ILI9341_RDMADCTL);
    	Serial.print("\nMADCTL Mode: 0x"); Serial.println(x, HEX);
    	x = readcommand8(ILI9341_RDPIXFMT);
    	Serial.print("\nPixel Format: 0x"); Serial.println(x, HEX);
    	x = readcommand8(ILI9341_RDIMGFMT);
    	Serial.print("\nImage Format: 0x"); Serial.println(x, HEX);
    	x = readcommand8(ILI9341_RDSELFDIAG);
    	Serial.print("\nSelf Diagnostic: 0x"); Serial.println(x, HEX);
    */	
    	beginSPITransaction();
    	const uint8_t *addr = init_commands;
    	while (1) {
    		uint8_t count = *addr++;
    		if (count-- == 0) break;
    		writecommand_cont(*addr++);
    		while (count-- > 0) {
    			writedata8_cont(*addr++);
    		}
    	}
    	writecommand_last(ILI9341_SLPOUT);    // Exit Sleep
    	endSPITransaction();
    	delay(120); 		
    	beginSPITransaction();
    	writecommand_last(ILI9341_DISPON);    // Display on
    	endSPITransaction();
    
    #ifdef DEBUG_ASYNC_LEDS
    	pinMode(DEBUG_PIN_1, OUTPUT);
    	pinMode(DEBUG_PIN_2, OUTPUT);
    	pinMode(DEBUG_PIN_3, OUTPUT);
    #endif
    }
    
    
    
    
    /*
    This is the core graphics library for all our displays, providing a common
    set of graphics primitives (points, lines, circles, etc.).  It needs to be
    paired with a hardware-specific library for each display device we carry
    (to handle the lower-level functions).
    
    Adafruit invests time and resources providing this open source code, please
    support Adafruit & open-source hardware by purchasing products from Adafruit!
    
    Copyright (c) 2013 Adafruit Industries.  All rights reserved.
    
    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are met:
    
    - Redistributions of source code must retain the above copyright notice,
      this list of conditions and the following disclaimer.
    - Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and/or other materials provided with the distribution.
    
    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    POSSIBILITY OF SUCH DAMAGE.
    */
    
    //#include "glcdfont.c"
    extern "C" const unsigned char glcdfont[];
    
    // Draw a circle outline
    void ILI9341_t3n::drawCircle(int16_t x0, int16_t y0, int16_t r,
        uint16_t color) {
      int16_t f = 1 - r;
      int16_t ddF_x = 1;
      int16_t ddF_y = -2 * r;
      int16_t x = 0;
      int16_t y = r;
    
      drawPixel(x0  , y0+r, color);
      drawPixel(x0  , y0-r, color);
      drawPixel(x0+r, y0  , color);
      drawPixel(x0-r, y0  , color);
    
      while (x<y) {
        if (f >= 0) {
          y--;
          ddF_y += 2;
          f += ddF_y;
        }
        x++;
        ddF_x += 2;
        f += ddF_x;
    
        drawPixel(x0 + x, y0 + y, color);
        drawPixel(x0 - x, y0 + y, color);
        drawPixel(x0 + x, y0 - y, color);
        drawPixel(x0 - x, y0 - y, color);
        drawPixel(x0 + y, y0 + x, color);
        drawPixel(x0 - y, y0 + x, color);
        drawPixel(x0 + y, y0 - x, color);
        drawPixel(x0 - y, y0 - x, color);
      }
    }
    
    void ILI9341_t3n::drawCircleHelper( int16_t x0, int16_t y0,
                   int16_t r, uint8_t cornername, uint16_t color) {
      int16_t f     = 1 - r;
      int16_t ddF_x = 1;
      int16_t ddF_y = -2 * r;
      int16_t x     = 0;
      int16_t y     = r;
    
      while (x<y) {
        if (f >= 0) {
          y--;
          ddF_y += 2;
          f     += ddF_y;
        }
        x++;
        ddF_x += 2;
        f     += ddF_x;
        if (cornername & 0x4) {
          drawPixel(x0 + x, y0 + y, color);
          drawPixel(x0 + y, y0 + x, color);
        }
        if (cornername & 0x2) {
          drawPixel(x0 + x, y0 - y, color);
          drawPixel(x0 + y, y0 - x, color);
        }
        if (cornername & 0x8) {
          drawPixel(x0 - y, y0 + x, color);
          drawPixel(x0 - x, y0 + y, color);
        }
        if (cornername & 0x1) {
          drawPixel(x0 - y, y0 - x, color);
          drawPixel(x0 - x, y0 - y, color);
        }
      }
    }
    
    void ILI9341_t3n::fillCircle(int16_t x0, int16_t y0, int16_t r,
    			      uint16_t color) {
      drawFastVLine(x0, y0-r, 2*r+1, color);
      fillCircleHelper(x0, y0, r, 3, 0, color);
    }
    
    // Used to do circles and roundrects
    void ILI9341_t3n::fillCircleHelper(int16_t x0, int16_t y0, int16_t r,
        uint8_t cornername, int16_t delta, uint16_t color) {
    
      int16_t f     = 1 - r;
      int16_t ddF_x = 1;
      int16_t ddF_y = -2 * r;
      int16_t x     = 0;
      int16_t y     = r;
    
      while (x<y) {
        if (f >= 0) {
          y--;
          ddF_y += 2;
          f     += ddF_y;
        }
        x++;
        ddF_x += 2;
        f     += ddF_x;
    
        if (cornername & 0x1) {
          drawFastVLine(x0+x, y0-y, 2*y+1+delta, color);
          drawFastVLine(x0+y, y0-x, 2*x+1+delta, color);
        }
        if (cornername & 0x2) {
          drawFastVLine(x0-x, y0-y, 2*y+1+delta, color);
          drawFastVLine(x0-y, y0-x, 2*x+1+delta, color);
        }
      }
    }
    
    
    // Bresenham's algorithm - thx wikpedia
    void ILI9341_t3n::drawLine(int16_t x0, int16_t y0,
    	int16_t x1, int16_t y1, uint16_t color)
    {
    	if (y0 == y1) {
    		if (x1 > x0) {
    			drawFastHLine(x0, y0, x1 - x0 + 1, color);
    		} else if (x1 < x0) {
    			drawFastHLine(x1, y0, x0 - x1 + 1, color);
    		} else {
    			drawPixel(x0, y0, color);
    		}
    		return;
    	} else if (x0 == x1) {
    		if (y1 > y0) {
    			drawFastVLine(x0, y0, y1 - y0 + 1, color);
    		} else {
    			drawFastVLine(x0, y1, y0 - y1 + 1, color);
    		}
    		return;
    	}
    
    	bool steep = abs(y1 - y0) > abs(x1 - x0);
    	if (steep) {
    		swap(x0, y0);
    		swap(x1, y1);
    	}
    	if (x0 > x1) {
    		swap(x0, x1);
    		swap(y0, y1);
    	}
    
    	int16_t dx, dy;
    	dx = x1 - x0;
    	dy = abs(y1 - y0);
    
    	int16_t err = dx / 2;
    	int16_t ystep;
    
    	if (y0 < y1) {
    		ystep = 1;
    	} else {
    		ystep = -1;
    	}
    
    	beginSPITransaction();
    	int16_t xbegin = x0;
    	if (steep) {
    		for (; x0<=x1; x0++) {
    			err -= dy;
    			if (err < 0) {
    				int16_t len = x0 - xbegin;
    				if (len) {
    					VLine(y0, xbegin, len + 1, color);
    				} else {
    					Pixel(y0, x0, color);
    				}
    				xbegin = x0 + 1;
    				y0 += ystep;
    				err += dx;
    			}
    		}
    		if (x0 > xbegin + 1) {
    			VLine(y0, xbegin, x0 - xbegin, color);
    		}
    
    	} else {
    		for (; x0<=x1; x0++) {
    			err -= dy;
    			if (err < 0) {
    				int16_t len = x0 - xbegin;
    				if (len) {
    					HLine(xbegin, y0, len + 1, color);
    				} else {
    					Pixel(x0, y0, color);
    				}
    				xbegin = x0 + 1;
    				y0 += ystep;
    				err += dx;
    			}
    		}
    		if (x0 > xbegin + 1) {
    			HLine(xbegin, y0, x0 - xbegin, color);
    		}
    	}
    	writecommand_last(ILI9341_NOP);
    	endSPITransaction();
    }
    
    // Draw a rectangle
    void ILI9341_t3n::drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color)
    {
    	#ifdef ENABLE_ILI9341_FRAMEBUFFER
    	if (_use_fbtft) {
    		drawFastHLine(x, y, w, color);
    		drawFastHLine(x, y+h-1, w, color);
    		drawFastVLine(x, y, h, color);
    		drawFastVLine(x+w-1, y, h, color);
    	} else 
    	#endif
    	{
    	    beginSPITransaction();
    		HLine(x, y, w, color);
    		HLine(x, y+h-1, w, color);
    		VLine(x, y, h, color);
    		VLine(x+w-1, y, h, color);
    		writecommand_last(ILI9341_NOP);
    		endSPITransaction();
    	}
    }
    
    // Draw a rounded rectangle
    void ILI9341_t3n::drawRoundRect(int16_t x, int16_t y, int16_t w,
      int16_t h, int16_t r, uint16_t color) {
      // smarter version
      drawFastHLine(x+r  , y    , w-2*r, color); // Top
      drawFastHLine(x+r  , y+h-1, w-2*r, color); // Bottom
      drawFastVLine(x    , y+r  , h-2*r, color); // Left
      drawFastVLine(x+w-1, y+r  , h-2*r, color); // Right
      // draw four corners
      drawCircleHelper(x+r    , y+r    , r, 1, color);
      drawCircleHelper(x+w-r-1, y+r    , r, 2, color);
      drawCircleHelper(x+w-r-1, y+h-r-1, r, 4, color);
      drawCircleHelper(x+r    , y+h-r-1, r, 8, color);
    }
    
    // Fill a rounded rectangle
    void ILI9341_t3n::fillRoundRect(int16_t x, int16_t y, int16_t w,
    				 int16_t h, int16_t r, uint16_t color) {
      // smarter version
      fillRect(x+r, y, w-2*r, h, color);
    
      // draw four corners
      fillCircleHelper(x+w-r-1, y+r, r, 1, h-2*r-1, color);
      fillCircleHelper(x+r    , y+r, r, 2, h-2*r-1, color);
    }
    
    // Draw a triangle
    void ILI9341_t3n::drawTriangle(int16_t x0, int16_t y0,
    				int16_t x1, int16_t y1,
    				int16_t x2, int16_t y2, uint16_t color) {
      drawLine(x0, y0, x1, y1, color);
      drawLine(x1, y1, x2, y2, color);
      drawLine(x2, y2, x0, y0, color);
    }
    
    // Fill a triangle
    void ILI9341_t3n::fillTriangle ( int16_t x0, int16_t y0,
    				  int16_t x1, int16_t y1,
    				  int16_t x2, int16_t y2, uint16_t color) {
    
      int16_t a, b, y, last;
    
      // Sort coordinates by Y order (y2 >= y1 >= y0)
      if (y0 > y1) {
        swap(y0, y1); swap(x0, x1);
      }
      if (y1 > y2) {
        swap(y2, y1); swap(x2, x1);
      }
      if (y0 > y1) {
        swap(y0, y1); swap(x0, x1);
      }
    
      if(y0 == y2) { // Handle awkward all-on-same-line case as its own thing
        a = b = x0;
        if(x1 < a)      a = x1;
        else if(x1 > b) b = x1;
        if(x2 < a)      a = x2;
        else if(x2 > b) b = x2;
        drawFastHLine(a, y0, b-a+1, color);
        return;
      }
    
      int16_t
        dx01 = x1 - x0,
        dy01 = y1 - y0,
        dx02 = x2 - x0,
        dy02 = y2 - y0,
        dx12 = x2 - x1,
        dy12 = y2 - y1,
        sa   = 0,
        sb   = 0;
    
      // For upper part of triangle, find scanline crossings for segments
      // 0-1 and 0-2.  If y1=y2 (flat-bottomed triangle), the scanline y1
      // is included here (and second loop will be skipped, avoiding a /0
      // error there), otherwise scanline y1 is skipped here and handled
      // in the second loop...which also avoids a /0 error here if y0=y1
      // (flat-topped triangle).
      if(y1 == y2) last = y1;   // Include y1 scanline
      else         last = y1-1; // Skip it
    
      for(y=y0; y<=last; y++) {
        a   = x0 + sa / dy01;
        b   = x0 + sb / dy02;
        sa += dx01;
        sb += dx02;
        /* longhand:
        a = x0 + (x1 - x0) * (y - y0) / (y1 - y0);
        b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
        */
        if(a > b) swap(a,b);
        drawFastHLine(a, y, b-a+1, color);
      }
    
      // For lower part of triangle, find scanline crossings for segments
      // 0-2 and 1-2.  This loop is skipped if y1=y2.
      sa = dx12 * (y - y1);
      sb = dx02 * (y - y0);
      for(; y<=y2; y++) {
        a   = x1 + sa / dy12;
        b   = x0 + sb / dy02;
        sa += dx12;
        sb += dx02;
        /* longhand:
        a = x1 + (x2 - x1) * (y - y1) / (y2 - y1);
        b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
        */
        if(a > b) swap(a,b);
        drawFastHLine(a, y, b-a+1, color);
      }
    }
    
    void ILI9341_t3n::drawBitmap(int16_t x, int16_t y,
    			      const uint8_t *bitmap, int16_t w, int16_t h,
    			      uint16_t color) {
    
      int16_t i, j, byteWidth = (w + 7) / 8;
    
      for(j=0; j<h; j++) {
        for(i=0; i<w; i++ ) {
          if(pgm_read_byte(bitmap + j * byteWidth + i / 8) & (128 >> (i & 7))) {
    	drawPixel(x+i, y+j, color);
          }
        }
      }
    }
    
    size_t ILI9341_t3n::write(uint8_t c)
    {
    	if (font) {
    		if (c == '\n') {
    			cursor_y += font->line_space; // Fix linefeed. Added by T.T., SoftEgg
    			cursor_x = 0;
    		} else {
    			drawFontChar(c);
    		}
    	} else {
    		if (c == '\n') {
    			cursor_y += textsize*8;
    			cursor_x  = 0;
    		} else if (c == '\r') {
    			// skip em
    		} else {
    			drawChar(cursor_x, cursor_y, c, textcolor, textbgcolor, textsize);
    			cursor_x += textsize*6;
    			if (wrap && (cursor_x > (_width - textsize*6))) {
    				cursor_y += textsize*8;
    				cursor_x = 0;
    			}
    		}
    	}
    	return 1;
    }
    
    // Draw a character
    void ILI9341_t3n::drawChar(int16_t x, int16_t y, unsigned char c,
    			    uint16_t fgcolor, uint16_t bgcolor, uint8_t size)
    {
    	if((x >= _width)            || // Clip right
    	   (y >= _height)           || // Clip bottom
    	   ((x + 6 * size - 1) < 0) || // Clip left  TODO: is this correct?
    	   ((y + 8 * size - 1) < 0))   // Clip top   TODO: is this correct?
    		return;
    
    	if (fgcolor == bgcolor) {
    		// This transparent approach is only about 20% faster
    		if (size == 1) {
    			uint8_t mask = 0x01;
    			int16_t xoff, yoff;
    			for (yoff=0; yoff < 8; yoff++) {
    				uint8_t line = 0;
    				for (xoff=0; xoff < 5; xoff++) {
    					if (glcdfont[c * 5 + xoff] & mask) line |= 1;
    					line <<= 1;
    				}
    				line >>= 1;
    				xoff = 0;
    				while (line) {
    					if (line == 0x1F) {
    						drawFastHLine(x + xoff, y + yoff, 5, fgcolor);
    						break;
    					} else if (line == 0x1E) {
    						drawFastHLine(x + xoff, y + yoff, 4, fgcolor);
    						break;
    					} else if ((line & 0x1C) == 0x1C) {
    						drawFastHLine(x + xoff, y + yoff, 3, fgcolor);
    						line <<= 4;
    						xoff += 4;
    					} else if ((line & 0x18) == 0x18) {
    						drawFastHLine(x + xoff, y + yoff, 2, fgcolor);
    						line <<= 3;
    						xoff += 3;
    					} else if ((line & 0x10) == 0x10) {
    						drawPixel(x + xoff, y + yoff, fgcolor);
    						line <<= 2;
    						xoff += 2;
    					} else {
    						line <<= 1;
    						xoff += 1;
    					}
    				}
    				mask = mask << 1;
    			}
    		} else {
    			uint8_t mask = 0x01;
    			int16_t xoff, yoff;
    			for (yoff=0; yoff < 8; yoff++) {
    				uint8_t line = 0;
    				for (xoff=0; xoff < 5; xoff++) {
    					if (glcdfont[c * 5 + xoff] & mask) line |= 1;
    					line <<= 1;
    				}
    				line >>= 1;
    				xoff = 0;
    				while (line) {
    					if (line == 0x1F) {
    						fillRect(x + xoff * size, y + yoff * size,
    							5 * size, size, fgcolor);
    						break;
    					} else if (line == 0x1E) {
    						fillRect(x + xoff * size, y + yoff * size,
    							4 * size, size, fgcolor);
    						break;
    					} else if ((line & 0x1C) == 0x1C) {
    						fillRect(x + xoff * size, y + yoff * size,
    							3 * size, size, fgcolor);
    						line <<= 4;
    						xoff += 4;
    					} else if ((line & 0x18) == 0x18) {
    						fillRect(x + xoff * size, y + yoff * size,
    							2 * size, size, fgcolor);
    						line <<= 3;
    						xoff += 3;
    					} else if ((line & 0x10) == 0x10) {
    						fillRect(x + xoff * size, y + yoff * size,
    							size, size, fgcolor);
    						line <<= 2;
    						xoff += 2;
    					} else {
    						line <<= 1;
    						xoff += 1;
    					}
    				}
    				mask = mask << 1;
    			}
    		}
    	} else {
    		// This solid background approach is about 5 time faster
    		uint8_t xc, yc;
    		uint8_t xr, yr;
    		uint8_t mask = 0x01;
    		uint16_t color;
    
    		// We need to offset by the origin.
    		x+=_originx;
    		y+=_originy;
    		int16_t x_char_start = x;  // remember our X where we start outputting...
    
    		if((x >= _displayclipx2)            || // Clip right
    			 (y >= _displayclipy2)           || // Clip bottom
    			 ((x + 6 * size - 1) < _displayclipx1) || // Clip left  TODO: this is not correct
    			 ((y + 8 * size - 1) < _displayclipy1))   // Clip top   TODO: this is not correct
    			return;
    
    
    		#ifdef ENABLE_ILI9341_FRAMEBUFFER
    		if (_use_fbtft) {
    
    			uint16_t * pfbPixel_row = &_pfbtft[ y*_width + x];
    			for (yc=0; (yc < 8) && (y < _displayclipy2); yc++) {
    				for (yr=0; (yr < size) && (y < _displayclipy2); yr++) {
    					x = x_char_start; 		// get our first x position...
    					if (y >= _displayclipy1) {
    						uint16_t * pfbPixel = pfbPixel_row;
    						for (xc=0; xc < 5; xc++) {
    							if (glcdfont[c * 5 + xc] & mask) {
    								color = fgcolor;
    							} else {
    								color = bgcolor;
    							}
    							for (xr=0; xr < size; xr++) {
    								if ((x >= _displayclipx1) && (x < _displayclipx2)) {
    									*pfbPixel = color;
    								}
    								pfbPixel++;
    								x++;
    							}
    						}
    						for (xr=0; xr < size; xr++) {
    							if ((x >= _displayclipx1) && (x < _displayclipx2)) {
    								*pfbPixel = bgcolor;
    							}
    							pfbPixel++;
    							x++;
    						}
    					}
    					pfbPixel_row += _width; // setup pointer to 
    					y++;
    				}
    				mask = mask << 1;
    			}
    
    		} else 
    		#endif
    		{
    			// need to build actual pixel rectangle we will output into.
    			int16_t y_char_top = y;	// remember the y
    			int16_t w =  6 * size;
    			int16_t h = 8 * size;
    
    			if(x < _displayclipx1) {	w -= (_displayclipx1-x); x = _displayclipx1; 	}
    			if((x + w - 1) >= _displayclipx2)  w = _displayclipx2  - x;
    			if(y < _displayclipy1) {	h -= (_displayclipy1 - y); y = _displayclipy1; 	}
    			if((y + h - 1) >= _displayclipy2) h = _displayclipy2 - y;
    
    			beginSPITransaction();
    			setAddr(x, y, x + w -1, y + h - 1);
    
    			y = y_char_top;	// restore the actual y.
    			writecommand_cont(ILI9341_RAMWR);
    			for (yc=0; (yc < 8) && (y < _displayclipy2); yc++) {
    				for (yr=0; (yr < size) && (y < _displayclipy2); yr++) {
    					x = x_char_start; 		// get our first x position...
    					if (y >= _displayclipy1) {
    						for (xc=0; xc < 5; xc++) {
    							if (glcdfont[c * 5 + xc] & mask) {
    								color = fgcolor;
    							} else {
    								color = bgcolor;
    							}
    							for (xr=0; xr < size; xr++) {
    								if ((x >= _displayclipx1) && (x < _displayclipx2)) {
    									writedata16_cont(color);
    								}
    								x++;
    							}
    						}
    						for (xr=0; xr < size; xr++) {
    							if ((x >= _displayclipx1) && (x < _displayclipx2)) {
    								writedata16_cont(bgcolor);
    							}
    							x++;
    						}
    					}
    					y++;
    				}
    				mask = mask << 1;
    			}
    			writecommand_last(ILI9341_NOP);
    			endSPITransaction();
    		}
    	}
    }
    
    static uint32_t fetchbit(const uint8_t *p, uint32_t index)
    {
    	if (p[index >> 3] & (1 << (7 - (index & 7)))) return 1;
    	return 0;
    }
    
    static uint32_t fetchbits_unsigned(const uint8_t *p, uint32_t index, uint32_t required)
    {
    	uint32_t val = 0;
    	do {
    		uint8_t b = p[index >> 3];
    		uint32_t avail = 8 - (index & 7);
    		if (avail <= required) {
    			val <<= avail;
    			val |= b & ((1 << avail) - 1);
    			index += avail;
    			required -= avail;
    		} else {
    			b >>= avail - required;
    			val <<= required;
    			val |= b & ((1 << required) - 1);
    			break;
    		}
    	} while (required);
    	return val;
    }
    
    static uint32_t fetchbits_signed(const uint8_t *p, uint32_t index, uint32_t required)
    {
    	uint32_t val = fetchbits_unsigned(p, index, required);
    	if (val & (1 << (required - 1))) {
    		return (int32_t)val - (1 << required);
    	}
    	return (int32_t)val;
    }
    
    
    void ILI9341_t3n::drawFontChar(unsigned int c)
    {
    	uint32_t bitoffset;
    	const uint8_t *data;
    
    	//Serial.printf("drawFontChar(%c) %d\n", c, c);
    
    	if (c >= font->index1_first && c <= font->index1_last) {
    		bitoffset = c - font->index1_first;
    		bitoffset *= font->bits_index;
    	} else if (c >= font->index2_first && c <= font->index2_last) {
    		bitoffset = c - font->index2_first + font->index1_last - font->index1_first + 1;
    		bitoffset *= font->bits_index;
    	} else if (font->unicode) {
    		return; // TODO: implement sparse unicode
    	} else {
    		return;
    	}
    	//Serial.printf("  index =  %d\n", fetchbits_unsigned(font->index, bitoffset, font->bits_index));
    	data = font->data + fetchbits_unsigned(font->index, bitoffset, font->bits_index);
    
    	uint32_t encoding = fetchbits_unsigned(data, 0, 3);
    	if (encoding != 0) return;
    	uint32_t width = fetchbits_unsigned(data, 3, font->bits_width);
    	bitoffset = font->bits_width + 3;
    	uint32_t height = fetchbits_unsigned(data, bitoffset, font->bits_height);
    	bitoffset += font->bits_height;
    	//Serial.printf("  size =   %d,%d\n", width, height);
    	//Serial.printf("  line space = %d\n", font->line_space);
    
    	int32_t xoffset = fetchbits_signed(data, bitoffset, font->bits_xoffset);
    	bitoffset += font->bits_xoffset;
    	int32_t yoffset = fetchbits_signed(data, bitoffset, font->bits_yoffset);
    	bitoffset += font->bits_yoffset;
    	//Serial.printf("  offset = %d,%d\n", xoffset, yoffset);
    
    	uint32_t delta = fetchbits_unsigned(data, bitoffset, font->bits_delta);
    	bitoffset += font->bits_delta;
    	//Serial.printf("  delta =  %d\n", delta);
    
    	//Serial.printf("  cursor = %d,%d\n", cursor_x, cursor_y);
    
    	 //horizontally, we draw every pixel, or none at all
    	if (cursor_x < 0) cursor_x = 0;
    	int32_t origin_x = cursor_x + xoffset;
    	if (origin_x < 0) {
    		cursor_x -= xoffset;
    		origin_x = 0;
    	}
    	if (origin_x + (int)width > _width) {
    		if (!wrap) return;
    		origin_x = 0;
    		if (xoffset >= 0) {
    			cursor_x = 0;
    		} else {
    			cursor_x = -xoffset;
    		}
    		cursor_y += font->line_space;
    	}
    	if (cursor_y >= _height) return;
    
    	// vertically, the top and/or bottom can be clipped
    	int32_t origin_y = cursor_y + font->cap_height - height - yoffset;
    	//Serial.printf("  origin = %d,%d\n", origin_x, origin_y);
    
    	// TODO: compute top skip and number of lines
    	int32_t linecount = height;
    	//uint32_t loopcount = 0;
    	int32_t y = origin_y;
    	bool opaque = (textbgcolor != textcolor);
    
    
    	// Going to try a fast Opaque method which works similar to drawChar, which is near the speed of writerect
    	if (!opaque) {
    		while (linecount > 0) {
    			//Serial.printf("    linecount = %d\n", linecount);
    			uint32_t n = 1;
    			if (fetchbit(data, bitoffset++) != 0) {
    				n = fetchbits_unsigned(data, bitoffset, 3) + 2;
    				bitoffset += 3;
    			}
    			uint32_t x = 0;
    			do {
    				int32_t xsize = width - x;
    				if (xsize > 32) xsize = 32;
    				uint32_t bits = fetchbits_unsigned(data, bitoffset, xsize);
    				//Serial.printf("    multi line %d %d %x\n", n, x, bits);
    				drawFontBits(opaque, bits, xsize, origin_x + x, y, n);
    				bitoffset += xsize;
    				x += xsize;
    			} while (x < width);
    
    
    			y += n;
    			linecount -= n;
    			//if (++loopcount > 100) {
    				//Serial.println("     abort draw loop");
    				//break;
    			//}
    		}
    	} else {
    		// Now opaque mode... 
    		// Now write out background color for the number of rows above the above the character
    		// figure out bounding rectangle... 
    		// In this mode we need to update to use the offset and bounding rectangles as we are doing it it direct.
    		// also update the Origin 
    		int cursor_x_origin = cursor_x + _originx;
    		int cursor_y_origin = cursor_y + _originy;
    		origin_x += _originx;
    		origin_y += _originy;
    
    
    
    		int start_x = (origin_x < cursor_x_origin) ? origin_x : cursor_x_origin; 	
    		if (start_x < 0) start_x = 0;
    		
    		int start_y = (origin_y < cursor_y_origin) ? origin_y : cursor_y_origin; 
    		if (start_y < 0) start_y = 0;
    		int end_x = cursor_x_origin + delta; 
    		if ((origin_x + (int)width) > end_x)
    			end_x = origin_x + (int)width;
    		if (end_x >= _displayclipx2)  end_x = _displayclipx2;	
    		int end_y = cursor_y_origin + font->line_space; 
    		if ((origin_y + (int)height) > end_y)
    			end_y = origin_y + (int)height;
    		if (end_y >= _displayclipy2) end_y = _displayclipy2;	
    		end_x--;	// setup to last one we draw
    		end_y--;
    		int start_x_min = (start_x >= _displayclipx1) ? start_x : _displayclipx1;
    		int start_y_min = (start_y >= _displayclipy1) ? start_y : _displayclipy1;
    
    		// See if anything is in the display area.
    		if((end_x < _displayclipx1) ||(start_x >= _displayclipx2) || (end_y < _displayclipy1) || (start_y >= _displayclipy2)) {
    			cursor_x += delta;	// could use goto or another indent level...
    		 	return;
    		}
    /*
    		Serial.printf("drawFontChar(%c) %d\n", c, c);
    		Serial.printf("  size =   %d,%d\n", width, height);
    		Serial.printf("  line space = %d\n", font->line_space);
    		Serial.printf("  offset = %d,%d\n", xoffset, yoffset);
    		Serial.printf("  delta =  %d\n", delta);
    		Serial.printf("  cursor = %d,%d\n", cursor_x, cursor_y);
    		Serial.printf("  origin = %d,%d\n", origin_x, origin_y);
    
    		Serial.printf("  Bounding: (%d, %d)-(%d, %d)\n", start_x, start_y, end_x, end_y);
    		Serial.printf("  mins (%d %d),\n", start_x_min, start_y_min);
    */
    		#ifdef ENABLE_ILI9341_FRAMEBUFFER
    		if (_use_fbtft) {
    			uint16_t * pfbPixel_row = &_pfbtft[ start_y*_width + start_x];
    			uint16_t * pfbPixel;
    			int screen_y = start_y;
    			int screen_x;
    
    			while (screen_y < origin_y) {
    				pfbPixel = pfbPixel_row;
    				// only output if this line is within the clipping region.
    				if ((screen_y >= _displayclipy1) && (screen_y < _displayclipy2)) {
    					for (screen_x = start_x; screen_x <= end_x; screen_x++) {
    						if (screen_x >= _displayclipx1) {
    							*pfbPixel = textbgcolor;
    						}
    						pfbPixel++;
    					}
    				}
    				screen_y++;
    				pfbPixel_row += _width;
    			}
    
    			// Now lets process each of the data lines. 
    			screen_y = origin_y;
    
    			while (linecount > 0) {
    				//Serial.printf("    linecount = %d\n", linecount);
    				uint32_t b = fetchbit(data, bitoffset++);
    				uint32_t n;
    				if (b == 0) {
    					//Serial.println("Single");
    					n = 1;
    				} else {
    					//Serial.println("Multi");
    					n = fetchbits_unsigned(data, bitoffset, 3) + 2;
    					bitoffset += 3;
    				}
    				uint32_t bitoffset_row_start = bitoffset;
    				while (n--) {
    					pfbPixel = pfbPixel_row;
    					if ((screen_y >= _displayclipy1) && (screen_y < _displayclipy2)) {
    						bitoffset = bitoffset_row_start;	// we will work through these bits maybe multiple times
    
    						for (screen_x = start_x; screen_x < origin_x; screen_x++) {
    							if (screen_x >= _displayclipx1) {
    								*pfbPixel = textbgcolor;
    							} // make sure not clipped
    							pfbPixel++;
    						}
    					}
    
    					screen_x = origin_x;
    					uint32_t x = 0;
    					do {
    						uint32_t xsize = width - x;
    						if (xsize > 32) xsize = 32;
    						uint32_t bits = fetchbits_unsigned(data, bitoffset, xsize);
    						uint32_t bit_mask = 1 << (xsize-1);
    						//Serial.printf(" %d %d %x %x\n", x, xsize, bits, bit_mask);
    						if ((screen_y >= _displayclipy1) && (screen_y < _displayclipy2)) {
    							while (bit_mask && (screen_x <= end_x)) {
    								if ((screen_x >= _displayclipx1) && (screen_x < _displayclipx2)) {
    									*pfbPixel = (bits & bit_mask) ? textcolor : textbgcolor;
    								}
    								pfbPixel++;	
    								bit_mask = bit_mask >> 1;
    								screen_x++;	// increment our pixel position. 
    							}
    						}
    							bitoffset += xsize;
    						x += xsize;
    					} while (x < width);
    					if ((screen_y >= _displayclipy1) && (screen_y < _displayclipy2)) {
    						// output bg color and right hand side
    						while (screen_x++ <= end_x) {
    							*pfbPixel++ = textbgcolor;
    						}
    					}			 
    		 			screen_y++;
    					pfbPixel_row += _width;
    					linecount--;
    				}
    			}
    
    			// clear below character
    	 		while (screen_y++ <= end_y) {
    				if ((screen_y >= _displayclipy1) && (screen_y < _displayclipy2)) {
    					pfbPixel = pfbPixel_row;
    					for (screen_x = start_x; screen_x <= end_x; screen_x++) {
    						if (screen_x >= _displayclipx1) {
    							*pfbPixel = textbgcolor;
    						}
    						pfbPixel++;
    					}
    				}
    				pfbPixel_row += _width;
    			}
    
    		} else 
    		#endif
    		{
    			beginSPITransaction();
    			//Serial.printf("SetAddr %d %d %d %d\n", start_x_min, start_y_min, end_x, end_y);
    			// output rectangle we are updating... We have already clipped end_x/y, but not yet start_x/y
    			setAddr( start_x_min, start_y_min, end_x, end_y);
    			writecommand_cont(ILI9341_RAMWR);
    			int screen_y = start_y_min;
    			int screen_x;
    			while (screen_y < origin_y) {
    				for (screen_x = start_x_min; screen_x <= end_x; screen_x++) {
    					writedata16_cont(textbgcolor);
    				}
    				screen_y++;
    			}
    
    			// Now lets process each of the data lines. 
    			screen_y = origin_y;
    			while (linecount > 0) {
    				//Serial.printf("    linecount = %d\n", linecount);
    				uint32_t b = fetchbit(data, bitoffset++);
    				uint32_t n;
    				if (b == 0) {
    					//Serial.println("    Single");
    					n = 1;
    				} else {
    					//Serial.println("    Multi");
    					n = fetchbits_unsigned(data, bitoffset, 3) + 2;
    					bitoffset += 3;
    				}
    				uint32_t bitoffset_row_start = bitoffset;
    				while (n--) {
    					// do some clipping here. 
    					bitoffset = bitoffset_row_start;	// we will work through these bits maybe multiple times
    					// We need to handle case where some of the bits may not be visible, but we still need to
    					// read through them
    					//Serial.printf("y:%d  %d %d %d %d\n", screen_y, start_x, origin_x, _displayclipx1, _displayclipx2);
    					if ((screen_y >= _displayclipy1) && (screen_y < _displayclipy2)) {
    						for (screen_x = start_x; screen_x < origin_x; screen_x++) {
    							if ((screen_x >= _displayclipx1) && (screen_x < _displayclipx2)) {
    								//Serial.write('-');
    								writedata16_cont(textbgcolor);
    							}
    						}
    					}	
    					uint32_t x = 0;
    					screen_x = origin_x;
    					do {
    						uint32_t xsize = width - x;
    						if (xsize > 32) xsize = 32;
    						uint32_t bits = fetchbits_unsigned(data, bitoffset, xsize);
    						uint32_t bit_mask = 1 << (xsize-1);
    						//Serial.printf("     %d %d %x %x - ", x, xsize, bits, bit_mask);
    						if ((screen_y >= _displayclipy1) && (screen_y < _displayclipy2)) {
    							while (bit_mask) {
    								if ((screen_x >= _displayclipx1) && (screen_x < _displayclipx2)) {
    									writedata16_cont((bits & bit_mask) ? textcolor : textbgcolor);
    									//Serial.write((bits & bit_mask) ? '*' : '.');
    								}
    								bit_mask = bit_mask >> 1;
    								screen_x++ ; // Current actual screen X
    							}
    							//Serial.println();
    							bitoffset += xsize;
    						}
    						x += xsize;
    					} while (x < width) ;
    					if ((screen_y >= _displayclipy1) && (screen_y < _displayclipy2)) {
    						// output bg color and right hand side
    						while (screen_x++ <= end_x) {
    							writedata16_cont(textbgcolor);
    							//Serial.write('+');
    						}
    						//Serial.println();
    					}
    		 			screen_y++;
    					linecount--;
    				}
    			}
    
    			// clear below character - note reusing xcreen_x for this
    			screen_x = (end_y + 1 - screen_y) * (end_x + 1 - start_x_min); // How many bytes we need to still output
    			//Serial.printf("Clear Below: %d\n", screen_x);
    			while (screen_x-- > 1) {
    				writedata16_cont(textbgcolor);
    			}
    			writedata16_last(textbgcolor);
    			endSPITransaction();
    		}
    
    	}
    	// Increment to setup for the next character.
    	cursor_x += delta;
    
    }
    
    //strPixelLen			- gets pixel length of given ASCII string
    int16_t ILI9341_t3n::strPixelLen(char * str)
    {
    //	//Serial.printf("strPixelLen %s\n", str);
    	if (!str) return(0);
    	uint16_t len=0, maxlen=0;
    	while (*str)
    	{
    		if (*str=='\n')
    		{
    			if ( len > maxlen )
    			{
    				maxlen=len;
    				len=0;
    			}
    		}
    		else
    		{
    			if (!font)
    			{
    				len+=textsize*6;
    			}
    			else
    			{
    
    				uint32_t bitoffset;
    				const uint8_t *data;
    				uint16_t c = *str;
    
    //				//Serial.printf("char %c(%d)\n", c,c);
    
    				if (c >= font->index1_first && c <= font->index1_last) {
    					bitoffset = c - font->index1_first;
    					bitoffset *= font->bits_index;
    				} else if (c >= font->index2_first && c <= font->index2_last) {
    					bitoffset = c - font->index2_first + font->index1_last - font->index1_first + 1;
    					bitoffset *= font->bits_index;
    				} else if (font->unicode) {
    					continue;
    				} else {
    					continue;
    				}
    				//Serial.printf("  index =  %d\n", fetchbits_unsigned(font->index, bitoffset, font->bits_index));
    				data = font->data + fetchbits_unsigned(font->index, bitoffset, font->bits_index);
    
    				uint32_t encoding = fetchbits_unsigned(data, 0, 3);
    				if (encoding != 0) continue;
    //				uint32_t width = fetchbits_unsigned(data, 3, font->bits_width);
    //				//Serial.printf("  width =  %d\n", width);
    				bitoffset = font->bits_width + 3;
    				bitoffset += font->bits_height;
    
    //				int32_t xoffset = fetchbits_signed(data, bitoffset, font->bits_xoffset);
    //				//Serial.printf("  xoffset =  %d\n", xoffset);
    				bitoffset += font->bits_xoffset;
    				bitoffset += font->bits_yoffset;
    
    				uint32_t delta = fetchbits_unsigned(data, bitoffset, font->bits_delta);
    				bitoffset += font->bits_delta;
    //				//Serial.printf("  delta =  %d\n", delta);
    
    				len += delta;//+width-xoffset;
    //				//Serial.printf("  len =  %d\n", len);
    				if ( len > maxlen )
    				{
    					maxlen=len;
    //					//Serial.printf("  maxlen =  %d\n", maxlen);
    				}
    			
    			}
    		}
    		str++;
    	}
    //	//Serial.printf("Return  maxlen =  %d\n", maxlen);
    	return( maxlen );
    }
    void ILI9341_t3n::drawFontBits(bool opaque, uint32_t bits, uint32_t numbits, int32_t x, int32_t y, uint32_t repeat)
    {
    	if (bits == 0) {
    		if (opaque) {
    			fillRect(x, y, numbits, repeat, textbgcolor);
    		}
    	} else {
    		int32_t x1 = x;
    		uint32_t n = numbits;
    		int w;
    		int bgw;
    
    		w = 0;
    		bgw = 0;
    
    		do {
    			n--;
    			if (bits & (1 << n)) {
    				if (bgw>0) {
    					if (opaque) {
    						fillRect(x1 - bgw, y, bgw, repeat, textbgcolor);
    					}
    					bgw=0;
    				}
    				w++;
    			} else {
    				if (w>0) {
    					fillRect(x1 - w, y, w, repeat, textcolor);
    					w = 0;
    				}
    				bgw++;
    			}
    			x1++;
    		} while (n > 0);
    
    		if (w > 0) {
    			fillRect(x1 - w, y, w, repeat, textcolor);
    		}
    
    		if (bgw > 0) {
    			if (opaque) {
    				fillRect(x1 - bgw, y, bgw, repeat, textbgcolor);
    			}
    		}
    	}
    }
    
    void ILI9341_t3n::setCursor(int16_t x, int16_t y) {
    	if (x < 0) x = 0;
    	else if (x >= _width) x = _width - 1;
    	cursor_x = x;
    	if (y < 0) y = 0;
    	else if (y >= _height) y = _height - 1;
    	cursor_y = y;
    }
    void ILI9341_t3n::getCursor(int16_t *x, int16_t *y) {
      *x = cursor_x;
      *y = cursor_y;
    }
    
    void ILI9341_t3n::setTextSize(uint8_t s) {
      textsize = (s > 0) ? s : 1;
    }
    
    uint8_t ILI9341_t3n::getTextSize() {
    	return textsize;
    }
    
    void ILI9341_t3n::setTextColor(uint16_t c) {
      // For 'transparent' background, we'll set the bg
      // to the same as fg instead of using a flag
      textcolor = textbgcolor = c;
    }
    
    void ILI9341_t3n::setTextColor(uint16_t c, uint16_t b) {
      textcolor   = c;
      textbgcolor = b;
    }
    
    void ILI9341_t3n::setTextWrap(boolean w) {
      wrap = w;
    }
    
    boolean ILI9341_t3n::getTextWrap()
    {
    	return wrap;
    }
    
    uint8_t ILI9341_t3n::getRotation(void) {
      return rotation;
    }
    
    void ILI9341_t3n::sleep(bool enable) {
    	beginSPITransaction();
    	if (enable) {
    		writecommand_cont(ILI9341_DISPOFF);		
    		writecommand_last(ILI9341_SLPIN);	
    		  endSPITransaction();
    	} else {
    		writecommand_cont(ILI9341_DISPON);
    		writecommand_last(ILI9341_SLPOUT);
    		endSPITransaction();
    		delay(5);
    	}
    }
    
    void Adafruit_GFX_Button::initButton(ILI9341_t3n *gfx,
    	int16_t x, int16_t y, uint8_t w, uint8_t h,
    	uint16_t outline, uint16_t fill, uint16_t textcolor,
    	const char *label, uint8_t textsize)
    {
    	_x = x;
    	_y = y;
    	_w = w;
    	_h = h;
    	_outlinecolor = outline;
    	_fillcolor = fill;
    	_textcolor = textcolor;
    	_textsize = textsize;
    	_gfx = gfx;
    	strncpy(_label, label, 9);
    	_label[9] = 0;
    }
    
    void Adafruit_GFX_Button::drawButton(bool inverted)
    {
    	uint16_t fill, outline, text;
    
    	if (! inverted) {
    		fill = _fillcolor;
    		outline = _outlinecolor;
    		text = _textcolor;
    	} else {
    		fill =  _textcolor;
    		outline = _outlinecolor;
    		text = _fillcolor;
    	}
    	_gfx->fillRoundRect(_x - (_w/2), _y - (_h/2), _w, _h, min(_w,_h)/4, fill);
    	_gfx->drawRoundRect(_x - (_w/2), _y - (_h/2), _w, _h, min(_w,_h)/4, outline);
    	_gfx->setCursor(_x - strlen(_label)*3*_textsize, _y-4*_textsize);
    	_gfx->setTextColor(text);
    	_gfx->setTextSize(_textsize);
    	_gfx->print(_label);
    }
    
    bool Adafruit_GFX_Button::contains(int16_t x, int16_t y)
    {
    	if ((x < (_x - _w/2)) || (x > (_x + _w/2))) return false;
    	if ((y < (_y - _h/2)) || (y > (_y + _h/2))) return false;
    	return true;
    }

  24. #124
    Senior Member
    Join Date
    Mar 2013
    Posts
    651
    KurtE, seems to be working pretty good, I removed the tft.waitUpdateAsyncComplete(); from the drawtimer loop and it ran all night without locking up. Thank you for creating this library and sticking with it


    Now that its working i'm looking for ways to make it faster. Anyone know of a way to make my second example even faster?

    The below code section takes up to 384uS at 240MHz to complete.
    Code:
          tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
          tft.setTextSize(3);
          tft.setCursor(3, 180);   //cursor set here unless one of the if is true.      
          if(WattHours < 10){
          tft.print("    "); }
          else if(WattHours < 100){
          tft.print("   ");        }
          else if (WattHours < 1000){      
          tft.print("  ");           }
          else if (WattHours < 10000){      
          tft.print(" ");           }          
          tft.print(WattHours, 3);
    This version using fillRect takes up to 220uS at 240MHz to complete. In the not so distant past this was the fastest way to go about it.
    Code:
          tft.setTextColor(ILI9341_WHITE);
          tft.fillRect(3, 180, 162, 24, ILI9341_BLACK);
          tft.setTextSize(3);   
          if(WattHours < 10){
          tft.setCursor(75, 180);       }
          else if(WattHours < 100){
          tft.setCursor(57, 180);       }
          else if (WattHours < 1000){      
          tft.setCursor(39, 180);       }
          else if (WattHours < 10000){      
          tft.setCursor(21, 180);       } 
          else {tft.setCursor(3, 180);  }         
          tft.print(WattHours, 3);

  25. #125
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    5,562
    Couple of things. Some of these may help, some may not.

    Without frame buffer, doing Opaque text was lot faster than transparent so you were helped by using:
    tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);

    FillRect - Now depending on the above, why fill the whole rectangle when you are going to overwrite everything to the right of the new text cursor.

    FillRect - Remember the previous outputs starting setCursor and only when necessary fill rect from the old cursor start to the new cursor spot.
    That is if the new numbers number of digits is >= the previous numbers number of digits, you don't need to do a fill...

Posting Permissions

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