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

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...

It never hurts to reevaluate something in case of not well known tricks, since library's change there is always a chance something will get improved or broken if we aren't lucky.

Without the frame buffer FillRect is slower then printing values to the screen. On another project I blank the text using the previous value set to the background and then print the new value. It was faster then FillRect and I don't recall how fast Opaque was. The side effect was it caused flickering of the values since the screen would update between blanking and redraws.
With your Framebuffer DMA that's not an issue since the screen wont get anything until your ready to update the whole screen.

Using the Framebuffer DMA @ 240Mhz see the red text below for time breakdowns.
Code:
float WattHours = 10000.000;   //with defines.
//Loop
      tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
      tft.setTextSize(3);
      tft.setCursor(3, 180);
      tft.print(WattHours, 3);    [COLOR="#FF0000"]//This takes 380uS to print a value with 8 digits and 1 decimal.[/COLOR]
                                  [COLOR="#FF0000"]//Each digit adds 40uS to the print.[/COLOR]
                                  [COLOR="#FF0000"]//And it actually takes 42uS to print a single space and 84uS to print two spaces and so on.[/COLOR]

Code:
float WattHours = 10000.000;   //with defines.
//Loop
      tft.setTextColor(ILI9341_WHITE);
      tft.fillRect(3, 180, 162, 24, ILI9341_BLACK);   [COLOR="#FF0000"] //This takes 84uS to turn the entire selected area black (3888 Pixels).[/COLOR]
      tft.setTextSize(3);
      tft.setCursor(3, 180);
      tft.print(WattHours, 3);     [COLOR="#FF0000"]//This takes 142uS to print a value with 8 digits and 1 decimal.[/COLOR]
                                   [COLOR="#FF0000"]//About 120ish uS for 7digits and 1 decimal.[/COLOR]
                                   [COLOR="#FF0000"]//105 - 110uS for 6digits and 1 decimal.[/COLOR]

In both above cases I'm printing a 9 character value, I consider this a worst case for the project I'm working on ATM. Its actually the largest value I plan to display at any time and I may set it up to go over to KWH after a curtain value is reached. With a max +/-220uS update time I'm thinking I can safely run a 3KHz loop on my 300W CC Dummy Load Project. My goal was 5KHz but that would require a separate MCU to run the screen.
 
Its me again :/ Sorry to bug you KurtE
Been trying to get touch going with the DMA Frame Buffer Async, I moved TIRQ to 3 and TCS to 6. It appears as if having the touch and LCD on the same SPI may not work or at the least needs some TLC. For the most part the IRQ does not appear to get setup properly, if I touch my multimeter to the resistor I have on the pin it causes the IRQ line to drop enough to get the Teensy to see it but it seems to collide with DMA and usually takes around 600mS to get going again. I pulled out my scope and for some reason I do not think the Touch IC got setup. If I touch the screen nothing happens there is no firing and nothing appears on the screen. If I touch the IRQ resistor with my meter I see the voltage drop on my scope and serial data appears. I even checked the CLK speed and it was around 1.8-1.9Mhz, which is in spec.

Supposedly the Touch IRQ is supposed to work in the background but I see no examples and there is very little to it in both the .h and .cpp of the XPT library. And what is there is over my head.
It also looks like the XPT library was never made to be used with anything but SPI0.

Code:
//Using KurtE's Async DMA ILI9341 library.  240Mhz high speed test.
//Enabled Touch Screen



#include "SPI.h"
#include <ILI9341_t3n.h>
#include <XPT2046_Touchscreen.h>


#define F_BUS 120000000
#define ILI9341_SPICLOCK 60000000 


#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  6   //Touch CS
#define TIRQ_PIN  3  //Touch Interupt pin

float WattHours = 100.000;

//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





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.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(ILI9341_SPICLOCK);
  Serial.println(" MHZ");

 
  tft.waitUpdateAsyncComplete();
  tft.println("Connected!");
  tft.updateScreenAsync();  


    delay(5000); 

}

 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;
 byte touchme = 0;


void loop(void) {

if(debugmenu == 0){
  tft.waitUpdateAsyncComplete() ;
  tft.fillScreen(ILI9341_BLACK);  
  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 && debugmenu == 1){
  
  drawtimer = 0;  //reset drawtimer
//  drawtime = 0;   
 
//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
  
    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;
printFloat6Char2Dec(3, 180, 3, WattHours);
drawtimelast = drawtime;

  /*    
drawtime = 0;      
      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);   
drawtimelast = drawtime;

*/
      tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
      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(" ");


     tft.updateScreenAsync();
//  drawtimelast = drawtime;
      WattHours = WattHours + 0.1;
      touchme = 0; 
 }







  if (ts.touched() && drawtimer > 80 && drawtimer < 90 && touchme != 1) {
    touchtime = 0;
    tft.waitUpdateAsyncComplete();

    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;
      touchme = 1;
  }



  
}



void printFloat5Char2Dec(uint16_t posx, uint16_t posy, uint8_t Textsize, float value){   
    
    
      tft.setTextColor(ILI9341_WHITE);
      tft.fillRect(posx, posy, (Textsize*6*6), (Textsize*8), ILI9341_BLACK);
      tft.setTextSize(Textsize);   
      if(value < 10){
      tft.setCursor(Textsize * 12 + posx, posy);       }
      else if(value < 100){
      tft.setCursor(Textsize * 6 + posx, posy);       }
      else {tft.setCursor(posx, posy);  }        
      tft.print(value, 2);  
      
      }
      
void printFloat6Char2Dec(uint16_t posx, uint16_t posy, uint8_t Textsize, float value){   
    
    
      tft.setTextColor(ILI9341_WHITE);
      tft.fillRect(posx, posy, (Textsize*6*7), (Textsize*8), ILI9341_BLACK);
      tft.setTextSize(Textsize);   
      if(value < 10){
      tft.setCursor(Textsize * 18 + posx, posy);       }
      else if(value < 100){
      tft.setCursor(Textsize * 12 + posx, posy);       }
      else if (value < 1000){      
      tft.setCursor(Textsize * 6 + posx, posy);       }
      else {tft.setCursor(posx, posy);  }        
      tft.print(value, 2);  
      
      }



Here is the output. All of these values were produced with my multimeter touching the IRQpin with my finger on the screen.
Code:
ILI9341 Test!
F_CPU 240000000 MHZ
F_BUS 120000000 MHZ
SPICLOCK 60000000 MHZ
Pressure = 0, x = 0, y = 0, time = 580183uS
Pressure = 0, x = 0, y = 0, time = 62400uS
Pressure = 2389, x = 2447, y = 2724, time = 1uS
Pressure = 2882, x = 2494, y = 1880, time = 1uS
Pressure = 3034, x = 2464, y = 1877, time = 582583uS
Pressure = 3009, x = 2481, y = 1884, time = 482255uS
Pressure = 3062, x = 2462, y = 1883, time = 582926uS
Pressure = 3079, x = 2467, y = 1863, time = 582599uS
Pressure = 3091, x = 2691, y = 2103, time = 1uS
Pressure = 2952, x = 2705, y = 2131, time = 582583uS
Pressure = 2802, x = 2662, y = 2010, time = 583254uS
Pressure = 3114, x = 2624, y = 2062, time = 582918uS
Pressure = 2937, x = 2566, y = 1839, time = 582674uS
Pressure = 2937, x = 2595, y = 1793, time = 583262uS
Pressure = 0, x = 0, y = 0, time = 582892uS
 
Hi again,

I have not played very much with the TIRQ stuff. But here is my understanding with looking at the code.
If you pass in the pass in a pin as to interrupt pin to the constructor. The begin method (XPT202046)
Will simply set that Pin to INPUT and will attach that pin to an Interrupt:
Code:
	if (255 != tirqPin) {
		pinMode( tirqPin, INPUT );
		attachInterrupt(digitalPinToInterrupt(tirqPin), isrPin, FALLING);
		isrPinptr = this;
	}
This interrupt function just sets a flag:
Code:
void isrPin( void )
{
	XPT2046_Touchscreen *o = isrPinptr;
	o->isrWake = true;
}

Now all of the query methods, call the update method...

The update method starts off with:
Code:
void XPT2046_Touchscreen::update()
{
	int16_t data[6];

	if (!isrWake) return;
	uint32_t now = millis();
	if (now - msraw < MSEC_THRESHOLD) return;
...
Note: the member variable isrWake is int as TRUE and only gets set false if you are using the ISR pin (and the Z value gets as such as to say no longer pressed)...
Otherwise it goes off and dos the SPI transaction to get the information... When update is short circuited, the query functions will then simply return the previous data...

So you lose very little when you don't use the ISR pin with this library... I thought the library should have a query method to get the logical state of isrWake, which you can use to
completely short circuit calling stuff. But probably not an issue as when it is not set in theory the code should then punt out at the touched returning false...

Again not sure if that helps you...
 
Thanks KurtE, that makes it more clear, looks like I was assuming the touch examples did something with the interrupt enabled, but not really. Calling ts.Touched() like I was in the if statement was actually sending a Zpressure request to the XPT, which would trip up the DMA transfer.

I just removed the IRQ pin and made the following change and its running fine now, once I start making my main program I will just have to make sure I don't call Touched until I'm sure the DMA is done. Added some notes since notes make everything better :)

Code:
//dont allow touch functions to get in DMA's way or things get messy
  if (drawtimer > 90 && drawtimer < 95 && touchme != 1) {   //takes about 30mS to transfer the buffer at 60MHz SPI and about 100uS to do a full touch read
    touchtime = 0;
    tft.waitUpdateAsyncComplete();  //last chance to avoid DMA transfer falling on its face
    if(ts.touched()){               //This actually requests pressure data from the XPT, 2MHz SPI starts hear!
    TS_Point p = ts.getPoint();     //If Touched returned a value greater then the threshold then this gets the x and y cords.
    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");       
  }
      touchme = 1;
  }

EDIT
Bah Spoke too soon, i'm now getting random continuous garbage sometimes. Not sure what causes it.

Code:
Pressure = 4064, x = 3044, y = 1567, time = 84uS
Pressure = 9218, x = 2665, y = 1757, time = 85uS
Pressure = 9216, x = 1996, y = 2053, time = 84uS
Pressure = 9216, x = 2472, y = 1813, time = 84uS
Pressure = 9216, x = 3495, y = 1178, time = 85uS
Pressure = 4064, x = 2712, y = 1807, time = 84uS
Pressure = 9216, x = 2543, y = 1826, time = 84uS
Pressure = 4064, x = 2645, y = 1763, time = 84uS
Pressure = 9216, x = 1957, y = 1037, time = 84uS
Pressure = 9216, x = 3120, y = 1382, time = 84uS
Pressure = 9216, x = 3919, y = 934, time = 85uS
Pressure = 4064, x = 3024, y = 1574, time = 84uS
Pressure = 9216, x = 2536, y = 1826, time = 84uS
Pressure = 4064, x = 2009, y = 2141, time = 85uS
Pressure = 4064, x = 2992, y = 1614, time = 84uS
Pressure = 9216, x = 2627, y = 1791, time = 85uS
Pressure = 8193, x = 2672, y = 1748, time = 85uS

EDIT2
Looks like its wiring related, if I move the wires on the breadboard it starts and stops. So that's a thing :/
 
Last edited:
I touched on this in my prior post #111 on 10/2:
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.

So indeed as written - the IRQ stops polling when NO touch - but after IRQ indicates touch - it then proceeds to poll the SPI for where it is.

As noted - It would be a nice extension to add a function that simply returns TRUE when isrWake is TRUE- then the display DMA could be shut off and touch data received.
 
I agree, it would be very helpful to just have a flag that says the screen was touched and let the code check the XPT when its convenient instead of instantly.
The thing I noticed was that my screen was not pulling the IRQ Pin low ever. So either it was not being set correctly after the previous read or the touch x and y are out of spec for the chip on my screen.

Also we really need a way to change F_BUS and ILI9341_SPICLOCK from inside our code, instead of having to change the .h files :rolleyes:

The defines I was placing in my code only changed the value that Serial printed, not the actual value that is used :mad:
 
Earlier I assumed that the xpt... library was an adafruit library, but it is one by Paul: https://github.com/PaulStoffregen/XPT2046_Touchscreen
There is a read me file up there with a little more information about how it works.

so: it would be easy to add simple public method to the header file like: bool getISRWake() {return isrWake;}
Not sure about name - Too many style guides for names... I would tend to call it isrWake... And rename member variable to something like isrWake_
but...

F_BUS (kinetis.h) - Beyond my scope - This needs to be done global... Wish Arduino (or at least Teensy) had a standardized way to do per sketch settings...
But I at one point wondered if it could maybe be added to boards.txt as an option... And maybe only set in Kinetis.h if it was not already defined...

As for ILI9341_SPICLOCK, would not be hard... Simply change: the inline function:
Code:
	void beginSPITransaction(uint32_t clock = ILI9341_SPICLOCK) __attribute__((always_inline)) {
		_pspin->beginTransaction(SPISettings(clock, MSBFIRST, SPI_MODE0));
		if (_csport)
			*_csport  &= ~_cspinmask;
	}
Create a member variable like: _spi_write_speed and init it to ILI9341_SPICLOCK. Then create a member function: spiWriteSpeed(uint32_t write_speed) which sets this value...
But it may cause additional code/time to work... That is the SPISettings() code checks if the baud is a builtin constant and then inlines the code that than optimizes out all of the code except what the constant translated into.

But again this would not necessarily be the actual SPI speed. That is governed by the SPI registers and F_BUS.. Finds the first match where your clock speed required is >=
FBUS/2, FBUS/3, 4, 5, 6, 8, 10, 12, 16, 20, ... There are a few other options which the code does not currently do for possible more exact matches, which involve using the use double speed and then divide...

As to get the actual SPI speed, not sure if there is any simple query function for it... You would need to reverse the code to look at F_BUS and then calculate the divisor used and do the divide...
 
Indeed Paul wrote XPT2046_Touchscreen and I added the TIRQ portion. I threw out that idea knowing it could be done - but indeed the naming and style was up in the air: tirqEvent() tirqWake()? Since it only works when TIRQ is provided - otherwise always true.

FrankB has his DEFS.h file process that can change F_CPU and F_BUS by UNDEF on a sketch basis. There is a thread for that - does it work for this?
 
I just looked at the code Perhaps this change fits in since touched() is currently used to detect TOUCH without interrupt reference and no point data is returned, I propose this added interface to do the same for "tirq".

A call to tirqTouched() will return TRUE when the IRQ pin change was recorded and touch MAY be active. As noted when called for non_TIRQ case it will always return true. But in no case will this code actually perform any SPI data transfers:

I:\arduino-1.8.4\hardware\teensy\avr\libraries\XPT2046_Touchscreen\XPT2046_Touchscreen.cpp [line 69] ADD::
Code:
bool XPT2046_Touchscreen::tirqTouched()
{
	return (isrWake);
}

and this public member for I:\arduino-1.8.4\hardware\teensy\avr\libraries\XPT2046_Touchscreen\XPT2046_Touchscreen.h [line 47] ADD::
Code:
	bool tirqTouched();

I'll see if I can for in Win GitHub and make the changes to current PJRC code and offer a PULL request.

@Donziboy2 - can you make these changes and try it on your system?
 
Done - untested: Return IRQ touch state without SPI xfer

<edit>: did do a test compile with this edit to : libraries\XPT2046_Touchscreen\examples\TouchTest\TouchTest.ino:
Code:
void loop() {
  [B][U]if (ts.tirqTouched()) [/U][/B]{
    TS_Point p = ts.getPoint();
    Serial.print("Pressure = ");
    Serial.print(p.z);
    Serial.print(", x = ");
    Serial.print(p.x);
    Serial.print(", y = ");
    Serial.print(p.y);
    delay(30);
    Serial.println();
  }
}

caveat: the TIRQ might set a touch - that might not be present at the time when the getPoint call is made.

"I:\arduino-1.8.5\hardware\teensy/../tools/stdout_redirect" "C:\Users\Tim\AppData\Local\Temp\arduino_build_937956/TouchTest.ino.sym" "I:\arduino-1.8.5\hardware\teensy/../tools/arm/bin/arm-none-eabi-objdump" -t -C "C:\Users\Tim\AppData\Local\Temp\arduino_build_937956/TouchTest.ino.elf"
Using library XPT2046_Touchscreen at version 1.2 in folder: I:\arduino-1.8.5\hardware\teensy\avr\libraries\XPT2046_Touchscreen
 
Last edited:
@KurtE and @defragster,
Thank you both for the information and ideas. I may play with the Screen in the next few days. I got the screen running and moved onto battling the ADC's, from what I can tell even sending requests to the XPT causes large spikes of noise to be induced onto my ADC readings, which I have been trying to determine how much of it was induced by my setup and how much of it is induced on the Teensy. After removing the screen wires from the Teensy a fair amount is still induced on the ADC and I wont be able to get around it without going external or changing my sample size to some god awful large number.

The things I still want to play with on the ILI9341 are that above 30Mhz SPI I noticed a slight flicker of the screen, not sure at this time what was causing it. I may order a few more screens and see if its persistent. I also want to play with Touch IRQ.
 
Donziboy2 - if you test my code and find it to work in your DMA use case that might be enough to get Paul to add it to the library.

Seems it should work as expected without any big bloat hit or problems when not used.

As a lightweight value check you can call it anytime - then stop DMA - get touch data and react as desired and restart DMA updates. Not having the screen update during touch almost makes sense when user feedback is desired.
 
Donziboy2/KurtE: Paul merged the XPT2046_Touchscreen::tirqTouched() from the PULL request above. Please give it a try. It will let Display update run full speed and calling that will indicate when there has been a display touch to switch modes and look for SPI Touch input.
 
Thanks defragster,

Will have to try it out. Probably when I get back to the well monitor program. In my cases things are probably not quite as system taxing as Donziboy2s stuff, When I was doing it earlier I simply did not process touch display while the screen update was in progress...

The other thing I always meant to do was to potentially have the graphic functions remember a bounding rectangle for those areas that have been updated and only update that... I know have the ability to set clip rectangle and only update those areas, but don't remember if that works with Async... One of these days will get back to it...
 
It looks like a lot of good work is going on here. I've been trying to use ILI9341_t3n and am running into some problems. Hoping one of you could steer me in the right direction.

I've been using ILI9341_t3 on a T3.2 and it's working great but want to also use the same code on a Teensy LC but the _t3 library doesn't support it. I got the latest (I think) _t3n and SPIN but am getting errors ('class SPINClass' has no member named 'kinetisl_spi') on either T3.2 or LC. I read through this thread and it wasn't immediately obvious what I should be using. My guess is it's probably pretty obvious to you. If I have to I'll dig through the code but hopefully there's a simple "use these files" answer.
 
Ah, figured it out. Using save-before-td1.37. Works great on both LC and T3.2, Thanks for doing that work!

By the way, I'm using TD1.37
 
Thanks for all the work on this library KurtE!

I've got one of these 2.4" displays and the 320x240 resolution is a bit hard to see on such a small screen when text isn't larger. So I was thinking I'd just have a smaller framebuffer 160x120 and output it to the screen scaled up 2x. That would also same a bunch of RAM (38k instead of 153k). Here is a diff of what I changed in the Async-Support branch:

Code:
diff --git a/ILI9341_t3n.h b/ILI9341_t3n.h
index 590d113..1755981 100644
--- a/ILI9341_t3n.h
+++ b/ILI9341_t3n.h
@@ -77,8 +77,8 @@

 #endif

-#define ILI9341_TFTWIDTH  240
-#define ILI9341_TFTHEIGHT 320
+#define ILI9341_TFTWIDTH  120
+#define ILI9341_TFTHEIGHT 160

 #define ILI9341_NOP     0x00
 #define ILI9341_SWRESET 0x01

diff --git a/ILI9341_t3n.cpp b/ILI9341_t3n.cpp
index 28add13..38bf77c 100644
--- a/ILI9341_t3n.cpp
+++ b/ILI9341_t3n.cpp
@@ -195,7 +195,7 @@ void ILI9341_t3n::updateScreen(void)					// call to say update the screen now.
     beginSPITransaction();
     if (_standard) {
       // Doing full window.
-      setAddr(0, 0, _width-1, _height-1);
+      setAddr(0, 0, _width*2-1, _height*2-1);
       writecommand_cont(ILI9341_RAMWR);

       // BUGBUG doing as one shot.  Not sure if should or not or do like
@@ -203,11 +204,20 @@ void ILI9341_t3n::updateScreen(void)					// call to say update the screen now.
       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++);
-      }
+      // 2x Scaling
+      uint8_t r = 0, c = 0;
+      while (pftbft <= pfbtft_end) {
+        writedata16_cont(*pftbft);
+        writedata16_cont(*pftbft++);
+        c++;
+        if (c==_width) {
+          c=0;
+          r++;
+          if (r%2!=0)
+            pftbft -= _width;
+        }
+      }
       writedata16_last(*pftbft);
     } else {
       // setup just to output the clip rectangle area.
       setAddr(_displayclipx1, _displayclipy1, _displayclipx2-1, _displayclipy2-1);

It works pretty good as is. I get about 32 frames per second when I run the teensy 3.6 at 240mhz and using #define ILI9341_SPICLOCK 120000000.

Is this possible using the DMA approach? When I'm using your DMA code I get about 40 fps at 320x240. I've been looking through DMAChannel.h trying to understand it but still have a ways to go.
 
Last edited:
Sounds interesting, but sorry, I don't know DMA well enough to be able to be able to setup a DMA transfer that for each row will repeat each write twice and then repeat each row twice. There might be something you can do with the minor loops setup, but... Maybe some other DMA experts?

Other options for larger easier to read text: Use a larger font ;) :lol: But that does not save you memory.

Awhile ago I was wondering about trying to make a version that uses less memory with Frame buffer. So maybe it would be more viable on other boards as well.

Couple of approaches I thought of included:

a) Be able to create a frame buffer that lets say is 1/4 size. Then have API to say, now back region 0, 1, 2 or 3. Run through your output functions that clip to the current region and then update that region... I punted on this as I did not think it would work overly well.

b) like a) but instead of setup for regions, my update code would set the clipping and frame to a specific region of the screen I wanted to update. Like when a value to changes for some logical meter I am displaying, set the frame/clip to this region and update the display... I do think this has some merit...

c) Most of the time, when I actually use the display, I use limited colors. So thought it would be interesting to have a version where instead of 16 bit colors I pass in probably 8 bit color indexes and have a color table. Code is already in place to do writeRect8 or writeRect4... So just have the higher level primitives fill in the FB for the number of bits we are using to back the display...

Would not be too hard to make this work for non DMA case. But not sure if it is possible to do DMA, where instead of the normal
S ---> D type transfer, we do S -- (Use S as index in Table to grab value --- > D
 
Thanks for the great ideas Kurt! I am already doing option C and using 8bit colors. Actually only 16 colors so far. I may make a generalized version of the 8bit approach for others looking to save ram.
I looked a bit through the K66 manual and it seems like DMA is best used with contiguous blocks of memory. The scatter/gather terminology got me excited for a minute. It reminded me of scatter and gather functions in MPI (parallel computing on clusters). From what I understand the DMA scatter/gather is chaining multiple operations.

My best bet is probably to make the rest of my code as non-blocking as possible. I removed the I2C calls from my main loop and the frame rate jumped up to 42 fps. I'll look at the i2c_t3 library and see if I can make that happen in the background.
 
Now with the current Beta and having the T3.5 with 256k of memory, I thought I would try to see if I can make the Frame Buffer code work on it.

As expected no problem using normal updates.

But now trying to get DMA updates to work on it, which turns out to be a little more challenging.

Currently I am only trying to get SPI to work. We will see about SPI1 and SPI2, as these only have one DMA source for either TX or RX, which in our case may be fine as we are only doing TX...

However there are still issues...

The first one is, that I ran into was: When it started to output, it was only doing 8 bit writes.... Luckily I remember running into this when I was working on SPI async Transfer code. Did not find the issue in current code base, and then remembered that we did not include the aynch version of transfer16 into the SPI library. Luckily I kept a copy of that code sitting around.

Then found in the code where it appears like the DMA SPI code on the T3.5 ignored that I was trying to use CTAR1 and not CTAR0... So in the SPI library I had the code update CTAR0 to be 16 bits during the dma transfer... Did that here again and that worked.

However the CS pin was not left asserted. Not sure if any way that I can have it do it directly or maybe in this case I might have to check to see if we are using hardware CS pin and if so hack the CS pin, change it to be in Mode(0) and assert it and when the DMA transfers are complete revert it back...

Also I don't think I am getting a full transfer. The DMA transfer is done in 3 chunks as it is larger than can fit in one chunk... Maybe the replace the descriptor is working differently on 3.5. Might need to tell it to continue... Back to debugging and checking details in manuals again...
 
In case anyone is interested: I finally merged the Aynch branch into Master, such that by default you now have Async (DMA) support for T3.6.

Currently in another branch: T3.5-Frame-buffer-support, I have a first pass where Frame buffer is supported by the "New" T3.5 with more memory. I also now have a version that I believe works with dma on SPI. As I mentioned in the previous post, I had to do a few different things to make this work:
a) The first time you try to do the Async update, if it was using hardware CS pin, it acts like it was not a hardware CS pin and instead converts over to using the port/mask approach. This was needed as the DMA on SPI was not using the high word of the PUSHR and I was losing the CS

b) Was having issues with scatter/gather support, so I changed it up to work like I have in SPI library, where it only uses the main Dma channels stuff. It also defines an RX channel as well where it throws the retrieved data into bitbucket. I use the RX channel, with Interrupt and end, where it checks to see if full screen has been output, if not it resets the output to continue to the next block. Takes 3 blocks to output full screen. For continuous update mode, when it output the 3rd block it resets to start to output the first block again...

Currently the differences here between 3.5 and 3.6 are #if code, so 3.6 code should not be modified by this.

Again this currently only works for SPI on the T3.5. Currently not on SPI1 or SPI2. More issues here: There is not a separate TX and RX DMA source on SPI1/2 only one which can be either but not both... Fixed this in SPI library by having the RX trigger TX... Will migrate this in as well. The other issue is with this level update. The max transfer is I believe 511 instead of 32767, so will need to change from taking 3 packets to maybe 160 packets (could do as little as 151 if I add code in to handle last packet not full size...)
 
Frame buffer support for the Teensy 3.5 has now been merged into the master branch.

It now works with SPI1 and SPI2 as well as SPI... Again this assumes the latest beta Teensyduino for to enable the 256kb of memory on the 3.5 board.

Now back to my normal diversions.
 
Hi Kurt,
I really like this library! I haven't used an ILI9341 library with dma before, and this is amazing! The screen refresh is instant! No more flashing when updating things I love it.

So far I've only used your FB_and_clip_tests example. I'm having trouble getting this to work with my own code due to library conflicts. If i try to use this library in a project where I previously had used ILI9341_t3, I get a bunch of compiler complaints about redefinitions. Here's the compiler output below, I can post the complete verbose output if that helps.

I'd like to know how to fix this problem, I've it encountered before, library conflicts. What are the steps to take to fix it or figure out how to fix it? Thanks so much for any help you can provide!!


Compiling sketch...
Using previously compiled file: /tmp/arduino_build_554105/sketch/font_Roboto-Bold.c.o
"/home/john/Arduino IDE/arduino-1.8.5/hardware/teensy/../tools/arm/bin/arm-none-eabi-g++" -c -O2 -g -Wall -ffunction-sections -fdata-sections -nostdlib -MMD -fno-exceptions -felide-constructors -std=gnu++14 -fno-rtti -mthumb -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -fsingle-precision-constant -D__MK64FX512__ -DTEENSYDUINO=141 -DARDUINO=10805 -DF_CPU=120000000 -DUSB_SERIAL -DLAYOUT_US_ENGLISH "-I/home/john/Arduino IDE/arduino-1.8.5/hardware/teensy/avr/cores/teensy3" "-I/home/john/Arduino/libraries/ILI9341_t3n" "-I/home/john/Arduino IDE/arduino-1.8.5/hardware/teensy/avr/libraries/SPI" "-I/home/john/Arduino/libraries/SPIN" "-I/home/john/Arduino IDE/arduino-1.8.5/hardware/teensy/avr/libraries/ILI9341_t3" "/tmp/arduino_build_554105/sketch/SLIP_RING_5-25-18.ino.cpp" -o "/tmp/arduino_build_554105/sketch/SLIP_RING_5-25-18.ino.cpp.o"
In file included from /tmp/arduino_build_554105/sketch/Fonts/font_DroidSans_Bold.h:4:0,
from /home/john/Arduino/SLIP_RING_5-25-18/SLIP_RING_5-25-18.ino:18:
/home/john/Arduino IDE/arduino-1.8.5/hardware/teensy/avr/libraries/ILI9341_t3/ILI9341_t3.h:166:3: error: conflicting declaration 'typedef struct ILI9341_t3_font_t ILI9341_t3_font_t'
} ILI9341_t3_font_t;
^
In file included from /home/john/Arduino/SLIP_RING_5-25-18/SLIP_RING_5-25-18.ino:16:0:
/home/john/Arduino/libraries/ILI9341_t3n/ILI9341_t3n.h:184:3: note: previous declaration as 'typedef struct ILI9341_t3_font_t ILI9341_t3_font_t'
} ILI9341_t3_font_t;
^
In file included from /tmp/arduino_build_554105/sketch/Fonts/font_DroidSans_Bold.h:4:0,
from /home/john/Arduino/SLIP_RING_5-25-18/SLIP_RING_5-25-18.ino:18:
/home/john/Arduino IDE/arduino-1.8.5/hardware/teensy/avr/libraries/ILI9341_t3/ILI9341_t3.h:404:7: error: redefinition of 'class Adafruit_GFX_Button'
class Adafruit_GFX_Button {
^
In file included from /home/john/Arduino/SLIP_RING_5-25-18/SLIP_RING_5-25-18.ino:16:0:
/home/john/Arduino/libraries/ILI9341_t3n/ILI9341_t3n.h:669:7: error: previous definition of 'class Adafruit_GFX_Button'
class Adafruit_GFX_Button {
^
Using library ILI9341_t3n at version 1.0 in folder: /home/john/Arduino/libraries/ILI9341_t3n
Using library SPI at version 1.0 in folder: /home/john/Arduino IDE/arduino-1.8.5/hardware/teensy/avr/libraries/SPI
Using library SPIN in folder: /home/john/Arduino/libraries/SPIN (legacy)
Using library ILI9341_t3 at version 1.0 in folder: /home/john/Arduino IDE/arduino-1.8.5/hardware/teensy/avr/libraries/ILI9341_t3
Error compiling for board Teensy 3.5.
 
From the error it's obvious you are trying to use fonts for Paul S. highly-optimized-ILI9341_t3 library -> font_DroidSans_Bold.h <-
inside your sketch you have this:
#include "font_DroidSans_Bold.h".
obviously you need to change this to:
#include "ili9341_t3n_font_DroidSans_Bold.h"
but it's not that easy as you also need the fonts properly installed in the ( ILI9341_t3n ) library folder.

Unfortunately ( ILI9341_t3n ) library only comes with two fonts:
ili9341_t3n_font_Arial.c
ili9341_t3n_font_Arial.h
-------------------------------
ili9341_t3n_font_ArialBold.c
ili9341_t3n_font_ArialBold.h


Get all the fonts for Highly optimized ILI9341_t3 here: https://github.com/PaulStoffregen/ILI9341_t3
EDIT here: https://github.com/PaulStoffregen/ILI9341_fonts
Now you will need to edit the fonts file names and also all the references inside the font files correctly.

I do have ili9341_t3n_font_DroidSans.c and ili9341_t3n_font_DroidSans.h but not the Bold version, here:
 

Attachments

  • ILI9341_t3n.zip
    102.3 KB · Views: 84
Last edited:
Hi Chris,
Thank you very much for the response. You solved my problem. I have a question for you though- why is it that the fonts must be in the ILI9341_t3n folder instead of the same folder that holds my .ino sketch? I dont completely understand the order of linking things together. For instance if the ili9341_t3n_font_DroidSans.c file was in my sketch folder, I would get an undefined reference error from the compiler. But if its in the ILI9341_t3n folder everything works fine. Why is that?
 
Back
Top