Teensy 4.0 & ILI9341_t3n

Status
Not open for further replies.

AshPowers

Well-known member
I'm running this display on a T4 that I am using interrupts on for timing events, etc.. When using the tft.updateScreen(); function, the amount of time it takes to process is far too long for the system to work properly. At max, I have pulses coming into an interrupt that are only 0.002s apart.

When not using frame buffering, the system works just fine but there is so much flickering on the screen that it becomes nearly impossible to even read the text I'm displaying.

I also have a chunk of data spewing out of the serial port which does not negatively affect it's operation.. Same info I'm trying to display on the TFT, in fact... Just text, no graphic files at all. I have also tried using the latest ILI9341_T3 library by Paul but it has the same flickering problem and it doesn't have a framebuffer, at least not that I Am aware of..

Here is my code for the setup.. The section where I Am sending a few lines of text to the display are a little below half way through the code...

Hopefully someone can shed some light here...

Code:
#include "SPI.h"
#include "ILI9341_t3n.h"
#include "ili9341_t3n_font_Arial.h"
#include "ili9341_t3n_font_OpenSans.h"
#include "ili9341_t3n_font_ArialBold.h"
#include "ili9341_t3n_font_ComicSansMS.h"

#define TFT_DC 9
#define TFT_CS 10
//#define TFT_RST 8
ILI9341_t3n tft = ILI9341_t3n(TFT_CS, TFT_DC);
#define HRES 320
#define VRES 240

#define REF1 14
#define REF2 15
#define POS1 16

#define MAP 17
#define ETS 7
#define TPS 0

#define IGN1 18
#define IGN2 19
#define IGN3 20
#define IGN4 21
#define IGN5 22
#define IGN6 23

#define INJ1 1
#define INJ2 2
#define INJ3 3
#define INJ4 4
#define INJ5 5
#define INJ6 6
volatile float REFRise;
volatile float REFRiseOld;
volatile float REFFall;
volatile float RPM = 1;
volatile bool CycleInterval = false;
volatile float REFHigh;
volatile bool IGNChk = false;
volatile float FiringTime;
float REFRiseChk;
float DwellTime = 5000;  // in us
long DwellAngle;
float RPMNew;
int CylNo = 0;
int CylNoInj = 0;
float TimePerDegree;
float DTI;
int timingAdvance = 35;
bool IgnWait = false;
bool InjWait = false;
bool Sync = false;
float DTInj;
long Cycles;
float WatchDog1;
float WatchDog2;
volatile int POSDeg = 0;
float InjectionTime;
volatile int ScreenUpdate = 100;
volatile long ScreenTimer1;
volatile long ScreenTimer2;
volatile long ManPres;
volatile long TPSVolt;
volatile int EngTemp = 70;

void setup() {
  Serial.begin(9600);
  tft.begin();
  tft.fillScreen(ILI9341_BLACK);
  tft.setTextColor(ILI9341_YELLOW);
  tft.setRotation(1);
  tft.useFrameBuffer(0);
  pinMode(IGN1, OUTPUT);
  pinMode(IGN2, OUTPUT);
  pinMode(IGN3, OUTPUT);
  pinMode(IGN4, OUTPUT);
  pinMode(IGN5, OUTPUT);
  pinMode(IGN6, OUTPUT);
  pinMode(INJ1, OUTPUT);
  pinMode(INJ2, OUTPUT);
  pinMode(INJ3, OUTPUT);
  pinMode(INJ4, OUTPUT);
  pinMode(INJ5, OUTPUT);
  pinMode(INJ6, OUTPUT);
  pinMode(REF1, INPUT_PULLUP);
  pinMode(REF2, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(REF1), REFRISE, RISING);
  attachInterrupt(digitalPinToInterrupt(REF2), REFFALL, FALLING);
  attachInterrupt(digitalPinToInterrupt(POS1), POSRISE, RISING);
  tft.setTextColor(ILI9341_YELLOW);
  tft.setFont (Arial_18_Bold);
}

void loop() {
  WatchDog1 += 1;
  if (WatchDog1 >= 5000000) {
   Sync = false;
   attachInterrupt(digitalPinToInterrupt(POS1), POSRISE, RISING);
   CycleInterval = false;
   Halt();
  }
  Cycles += 1;
  if (CycleInterval == true) {
    WatchDog1 = 0;
    CylNo += 1;
    if (CylNo == 7) {
      CylNo = 1;
    }
    REFHigh = REFFall - REFRise;
    RPM = 60000000 / (REFRiseOld * 3);
    TimePerDegree = (RPM / 60);
    TimePerDegree *= 360;
    TimePerDegree = (1000000 / TimePerDegree);
    REFHigh /= TimePerDegree;
    DTI = TimePerDegree * (112 - timingAdvance);
    DTI += REFRise;
    IgnWait = true;
    InjWait = true;
    if (Sync == false) {
    if (REFHigh >= 2 && REFHigh <= 10){
       CylNo = 1;
    }
    if (REFHigh >= 43 && REFHigh <= 55){
       CylNo = 2;
    }
    if (REFHigh >= 36 && REFHigh <= 40){
       CylNo = 3;
    }
    if (REFHigh >= 28 && REFHigh <= 34){
       CylNo = 4;
    }
    if (REFHigh >= 21 && REFHigh <= 26){
       CylNo = 5;
    }
    if (REFHigh >= 12 && REFHigh <= 19){
       CylNo = 6;
    }
    Sync = true;
    detachInterrupt(digitalPinToInterrupt(POS1));
    }
    CylNoInj = CylNo - 1;
    if (CylNoInj == 0) {
      CylNoInj = 6;
    }
    InjectionTime = 2234 / RPM;
    DTInj = (InjectionTime * 1000) + micros();
    digitalWrite(CylNoInj, HIGH);
    Serial.print("Engine RPM: ");
    Serial.print(RPM);
    Serial.println();
    Serial.print("Cylinder In Compression Stroke: ");
    Serial.print(CylNo);
    Serial.println();
    Serial.print("Delay Time Interval to next ignition event: ");
    Serial.print(DTI - micros());
    Serial.println(" us");
    Serial.print("Timing Check: ");
    Serial.print(((FiringTime - REFRiseChk) / TimePerDegree) - 112);
    Serial.println(" BTDC");
    Serial.println();
    Serial.print("Cylinder ");
    Serial.print(CylNoInj);
    Serial.println(" is starting fuel injection cycle.");
    Serial.print("Fuel Injector Pulse Duration: ");
    Serial.print(InjectionTime);
    Serial.println(" ms");
    Serial.println();
    Serial.print("REFHigh: ");
    Serial.print(REFHigh);
    Serial.println(" us");
    Serial.println();
    Serial.print("Time Per Degree: ");
    Serial.print(TimePerDegree);
    Serial.println(" us");
    Serial.println(Cycles);
    Serial.println();
    Serial.print("POS Degrees: ");
    Serial.println(POSDeg);
    Serial.println();
    Serial.println();
    Serial.println();
    Serial.println();
    Serial.println();
    CycleInterval = false;
    IGNChk = false;
    tft.setCursor(10,10);
    tft.fillScreen(ILI9341_BLACK);
    //tft.fillRect(55,0,200,120,ILI9341_BLACK);
    tft.print("RPM: ");
    tft.print(RPM);
    tft.setCursor(10, 30);
    tft.print("MAP: ");
    tft.print(ManPres);
    tft.setCursor(10, 50);
    tft.print("TPS: ");
    tft.print(TPSVolt);
    //tft.updateScreen();
  }
  if (IgnWait == true && DTI <= micros()){
    FiringTime = micros();
    digitalWrite((CylNo + 17), HIGH);
    delayMicroseconds(DwellTime / 50);
    digitalWrite((CylNo + 17), LOW);
    REFRiseChk = REFRise;
    IgnWait = false;
    IGNChk = false;
  }
  if (InjWait == true && DTInj <= micros()){
    digitalWrite(CylNoInj, LOW);
    InjWait = false;
  }
}

void Halt() {
  digitalWrite(18, LOW);
  digitalWrite(19, LOW);
  digitalWrite(20, LOW);
  digitalWrite(21, LOW);
  digitalWrite(22, LOW);
  digitalWrite(23, LOW);
  digitalWrite(1, LOW);
  digitalWrite(2, LOW);
  digitalWrite(3, LOW);
  digitalWrite(4, LOW);
  digitalWrite(5, LOW);
  digitalWrite(6, LOW);
}

void REFRISE() {
  REFRiseOld = micros() - REFRise;
  REFRise = micros();
  IGNChk = true;
  WatchDog1 = 0;
}

void REFFALL() {
  REFFall = micros();
  CycleInterval = true;
  WatchDog1 = 0;
  ManPres = analogRead(MAP);
  TPSVolt = analogRead(TPS);
  EngTemp = analogRead(ETS);
}

void POSRISE() {
  POSDeg += 1;
}
 
Sorry, I probably don't have the energy to do a complete review of all of this, but will give a few suggestions on possible solutions, which I hope helps you some.

Frame buffers are a nice thing to use, which can make easy to write quick and dirty code, but you need to remember that tft.updateScreen(), more or less is the same processing wise as tft.fillScreen(), other than instead of sending out a fixed color it simply extracts the color of each pixel from memory to push out through SPI and as such you are going to send about 11 + 32o*240*2 bytes out over SPI, which takes time and the code will wait for all of those bytes to be sent before returning from that call. There are a couple of different approaches that can reduce these timings:
a) setup a clip rectangle around the areas that you updated, before calling the updateScreen. Then the update screen will only send the pixels from that rectangle...
b) use updateScreenAsync (and corresponding functions to detect if still in update... ) which uses DMA (and some interrupts) to do the updates. Note: clip rectangle does not work with this.


Often times things like flicker can be eliminated with (or without frame buffers) by setting up the code differently.
Example with your code:
Code:
    tft.setCursor(10,10);
    tft.fillScreen(ILI9341_BLACK);
    //tft.fillRect(55,0,200,120,ILI9341_BLACK);
    tft.print("RPM: ");
    tft.print(RPM);
    tft.setCursor(10, 30);
    tft.print("MAP: ");
    tft.print(ManPres);
    tft.setCursor(10, 50);
    tft.print("TPS: ");
    tft.print(TPSVolt);
    //tft.updateScreen();
You are guaranteed to have flicker, as first you draw the area of text with black and then you draw the new text, so pixels are on and they are off...

So how to remove/reduce these issues?
a) one obvious thing is only update fields if they have changed. That is if RPM has not changed no need to update it...

b) If your whole display area does not change. Then only output things like: tft.print("RPM: ");
in setup, not every time in the loop. instead compute (maybe by trial and error) what the setCursor should be for the call to tft.print(RPM); and set it and leave the heading alone.

c) Use Opaque text output instead of transparent. Note: when using other fonts than the system font, (i.e. tft.setFont (Arial_18_Bold)) transparent support is not in ILI_9341_t3 library,
I added it to ili9341_t3n... Note: Opaque text output will blank pixels below your actual text (enough that if your text goes to second line they will all be filled), so either things need to be spaced out some
or you can use clipping rectangle (again only in _t3n).

To setup opaque text output: tft.setTextColor(ILI9341_YELLOW, ILI9341_BLACK);
Which will overwrite the old text in one output, so again no flicker.

Note: if you new text is not long enough to cover your old text, than some of it may not be overwritten. again couple ways to handle this.
Quick and dirty works if fields not too tight:

Code:
tft.setCursor(RPM_TFT_X, 10);
tft.print(RPM);
tft.print("  ");
A couple of blanks end will probably cover up old text if extended a bit.

Another approach is to get the position of the text cursor after you do the tft.print and do a fillRect back to black to either end of line area or extent of previous output or ...

Hope that helps some

Kurt
 
Status
Not open for further replies.
Back
Top