T4 w/ST7735 1.44" TFT - Lag time >>>

Status
Not open for further replies.

AshPowers

Well-known member
Hi,
I'm running a t4 with the 1.44" ST7735 display via the SPI port. The T4 is being used as a Speed Density Translator in an automotive application to eliminate the MAF and employ a pressure sensor, temp sensor, and engine RPM to produce a mass airflow output signal to the engine management system.

The system without the display works beautifully. However, with the display running, the time it takes for it to update the screen is so long that it interrupts the system from being able to keep up.

I am polling the tach signal line in the code and at each rising edge it calculates the time interval from the previous rising edge. I am NOT using this as an ISR. (This gets an ignition pulse at each ignition event - every 120 degrees on this V6 engine).

So, updating the display is taking so long that it is missing rising edge pulses of the ignition system and throwing everything off.

Doesn't this display have a frame buffer that can be advantaged to significantly lessen the time needed to update the display??? This processor is BLAZING fast and it is a shame that just adding a display puts the entire system at a huge disadvantage..

Here is a video of the setup in operation. The values are being updated at each iteration of the LOOP using simple tft.setCursor, println, commands etc..

https://www.youtube.com/watch?v=kQN4j-iP8Ss
 
Again hard to know what all you are doing, what libraries you are using...

For example are you using ST7735_t3 library? How are you doing updates. Looks like flickering going on, so might be doing the code with erases and then draw new ...

There are many ways of doing things, but hard to say without seeing more specifics of the code.
 
I'm using the ST7735_t3 library here:
https://github.com/PaulStoffregen/ST7735_t3

The videotest example code is what I used.

In setup, I am writing out the top header (AshSPEC and SDT Monitor) as well as the sensor names. I am also calling tft.initR(INITR_144GREENTAB); as this is what the display was happy to work with.

In the loop I am writing the sensor data values at each loop iteration. At the beginning of the loop I am drawing out a fillRect over the area where the sensor values are displayed. The sensor values are populated through the loop as AD conversions are made.

I do not want to share the code here as there are intellectual sensitivities within it. I hope this is understandable and can be worked around to resolve this display issue.

Basically what I need to know is how to minimize the amount of time it takes for the display to be updated. This system is designed to run upwards of 8500RPM, which at one pulse per 120 degrees puts us at 425 cycles per second, or 0.00235s between pulses that MUST be accounted for. I cannot have a display hanging up the process here or it will adversely affect engine fueling and potentially result in catastrophic engine failure.
 
Again always hard to give answers, without more of the details. I totally understand that you don't want to release whole code, as someone might copy it and make their own products...

So again without more details I can only give some generic suggestions, which may or may not help. But at least I know that you are using the ST7735_t3 library and not the Adafruit_st77... library so I know there are some options.

Again if you have a reasonably up to date version of the library. Some of us added the ability to use a logical frame buffer with this display (as well as several others...)
Normally I would try maybe to integrate code with an example given... But... Here is one test sketch I have that we used to test different aspects.
Code:
//#define NEW_THE_DISP
#include <Adafruit_GFX.h>    // Core graphics library
#include <ST7735_t3.h> // Hardware-specific library
#include <ST7789_t3.h> // Hardware-specific library
#include <SPI.h>
#include <st7735_t3_font_Arial.h>
#include <Fonts/FreeMonoBoldOblique12pt7b.h>
#include <Fonts/FreeSerif12pt7b.h>

#if defined(__IMXRT1052__) || defined(__IMXRT1062__)  // Teensy 4.x
//#define USE_SPI1
//#define USE_SPI2
//#define NON_SPI_PINS
#ifdef USE_SPI2
#define TFT_SCLK 37  // 
//#define TFT_MISO 34
#define TFT_MOSI 35  // MOSI can also use pin 7
#define TFT_CS   -1//36  // CS & DC can use pins 2, 6, 9, 10, 15, 20, 21, 22, 23
#define TFT_DC   38  //  but certain pairs must NOT be used: 2+10, 6+9, 20+23, 21+22
#define TFT_RST   39  // RST can use any pin
#elif defined(USE_SPI1)
#define TFT_SCLK 27  // SCLK can also use pin 14
#define TFT_MOSI 26  // MOSI can also use pin 7
#define TFT_CS   0  // CS & DC can use pins 2, 6, 9, 10, 15, 20, 21, 22, 23
#define TFT_DC    2  //  but certain pairs must NOT be used: 2+10, 6+9, 20+23, 21+22
#define TFT_RST   3  // RST can use any pin

#elif defined(NON_SPI_PINS)
#define TFT_SCLK 7  // SCLK can also use pin 14
#define TFT_MOSI 8  // MOSI can also use pin 7
#define TFT_CS   10  // CS & DC can use pins 2, 6, 9, 10, 15, 20, 21, 22, 23
#define TFT_DC    9  //  but certain pairs must NOT be used: 2+10, 6+9, 20+23, 21+22
#define TFT_RST   20  // RST can use any pin
#else
#define TFT_SCLK 13  // SCLK can also use pin 14
#define TFT_MOSI 11  // MOSI can also use pin 7
#define TFT_CS   10  // CS & DC can use pins 2, 6, 9, 10, 15, 20, 21, 22, 23
#define TFT_DC    9  //  but certain pairs must NOT be used: 2+10, 6+9, 20+23, 21+22
#define TFT_RST   8  // RST can use any pin
#endif
#define SD_CS     4  // CS for SD card, can use any pin
#else
//--------------------------------------------
// T3.X
//#define USE_SPI1
//#define USE_SPI2
#define USE_SPI1_NOT_HARDWARE_CSDC
#if defined(USE_SPI1_NOT_HARDWARE_CSDC)
#define TFT_SCLK  20   // SCLK can also use pin 14
#define TFT_MOSI  21  // MOSI can also use pin 7
#define TFT_CS    2  // CS & DC can use pins 2, 6, 9, 10, 15, 20, 21, 22, 23
#define TFT_DC    3  //  but certain pairs must NOT be used: 2+10, 6+9, 20+23, 21+22
#define TFT_RST   8  // RST can use any pin
#define SD_CS     4  // CS for SD card, can use any pin

#elif defined(USE_SPI1)
#define TFT_SCLK 32  // T3.5/T3.6
#define TFT_MOSI 0   // 
#define TFT_CS   30  // random digital pin
#define TFT_DC   31   // Only Hardware CS pin on SPI1
#define TFT_RST  29  // RST can use any pin
#elif defined(USE_SPI2)
#define TFT_SCLK 46  // T3.5/T3.6
#define TFT_MOSI 44   // 
#define TFT_CS   54  // Hardware CS on SPI2
#define TFT_DC   55   // Hardware CS pin on SPI2
#else
#define TFT_SCLK 13  // SCLK can also use pin 14
#define TFT_MOSI 11  // MOSI can also use pin 7
#define TFT_CS   10  // CS & DC can use pins 2, 6, 9, 10, 15, 20, 21, 22, 23
#define TFT_DC    9  //  but certain pairs must NOT be used: 2+10, 6+9, 20+23, 21+22
#define TFT_RST   8  // RST can use any pin
#endif
#define SD_CS     4  // CS for SD card, can use any pin
#endif
//#define TFT_CS2   7
#define TFT_BLK 7


#if defined(NEW_THE_DISP)
ST7735_t3 *ptft = nullptr;
#else
// Option 1: use any pins but a little slower
#if defined(USE_SPI1)
//ST7789_t3 tft = ST7789_t3(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);
ST7735_t3 tft = ST7735_t3(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);
#elif defined(USE_SPI2)
ST7789_t3 tft = ST7789_t3(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);
#else
ST7789_t3 tft = ST7789_t3(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);
//ST7735_t3 tft = ST7735_t3(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);
#endif

ST7789_t3 *ptft = &tft;
#endif
// Option 2: must use the hardware SPI pins
// (for UNO thats sclk = 13 and sid = 11) and pin 10 must be
// an output. This is much faster - also required if you want
// to use the microSD card (see the image drawing example)
//ST7735_t3 tft = ST7735_t3(TFT_CS, TFT_DC, TFT_RST);
float p = 3.1415926;

uint8_t use_fb = 0;

void setup(void) {
#ifdef TFT_BLK
  pinMode(TFT_BLK, OUTPUT);
  digitalWrite(TFT_BLK, HIGH);
#endif
  pinMode(SD_CS, INPUT_PULLUP);  // don't touch the SD card
  pinMode(TFT_CS, INPUT_PULLUP);

  Serial.begin(9600);
  Serial.print("hello!");

#if defined(NEW_THE_DISP)
  ptft = new ST7735_t3(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);
#endif
  Serial.printf("CS:%d DC:%d MOSI:%d SCLK:%d RST:%d\n", TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);
  // Use this initializer if you're using a 1.8" TFT
  //ptft->initR(INITR_BLACKTAB);
  // Use this initializer (uncomment) if you're using a 1.44" TFT
  //ptft->initR(INITR_GREENTAB);
#if defined(USE_SPI1)
  ptft->initR(INITR_144GREENTAB);
//  ptft->init(240,240) ;  // use for ILI9488
#elif defined(USE_SPI2)
  ptft->init(240,240, SPI_MODE2) ;  // CS challenged 
#else
  ptft->init(240,240, SPI_MODE2) ;  // use for ILI9488
//  ptft->initR(INITR_144GREENTAB);
//  ptft->initR(INITR_144GREENTAB_OFFSET);
//    ptft->initR(INITR_MINI160x80);
  //ptft->setRowColStart(0,0);
#endif
  use_fb = 1;
  if (![COLOR="#FF0000"]ptft->useFrameBuffer(true)[/COLOR]) { // lets try using a frame buffer. 
    Serial.println("\n***** Use Frame Buffer Failed *****\n");
    use_fb = 0;
  }
  Serial.printf("init CS:%d DC:%d MOSI:%d SCLK:%d RST:%d\n ", TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);
  Serial.printf("Row Start:%d  Col Start: %d\n", ptft->rowStart(), ptft->colStart());
}

uint8_t rotation = 0;
void drawTestScreen(uint16_t background_color) {
  ptft->fillScreen(background_color);
  ptft->fillRect(ptft->width() / 2 - 32, 20, 64, ptft->height() - 40, ST7735_GREEN);
  ptft->fillRect(ptft->width() / 4 - 16, ptft->height() / 4 - 16, 32, 32, ST7735_BLUE);
  ptft->fillRect(ptft->width() * 3 / 4 - 16, ptft->height() * 3 / 4 - 16, 32, 32, ST7735_WHITE);
  ptft->fillRect(ptft->width() * 3 / 4 - 16, ptft->height() / 4 - 16, 32, 32, ST7735_BLACK);
  ptft->fillRect(0, 0, 8, 8, ST7735_BLACK);
  ptft->fillRect(ptft->width() - 8, ptft->height() - 8, 8, 8, ST7735_WHITE);
  ptft->drawPixel(0, 0, ST7735_YELLOW);
  ptft->drawPixel(1, 1, ST7735_RED);
  ptft->drawPixel(2, 2, ST7735_BLUE);
  ptft->setCursor(0, ptft->height() / 2 - 4);
  ptft->printf("R:%d W:%d H:%d", rotation, ptft->width(), ptft->height());
  ptft->drawPixel(ptft->width() - 1, ptft->height() - 1, ST7735_YELLOW);
  ptft->drawPixel(ptft->width() - 2, ptft->height() - 2, ST7735_RED);
  ptft->drawPixel(ptft->width() - 3, ptft->height() - 3, ST7735_BLUE);
}
void printTextSizes(const char *sz) {
  Serial.printf("%s(%d,%d): SPL:%u ", sz, tft.getCursorX(), tft.getCursorY(), tft.strPixelLen(sz));
  int16_t x, y;
  uint16_t w, h;
  tft.getTextBounds(sz, tft.getCursorX(), tft.getCursorY(), &x, &y, &w, &h);
  Serial.printf(" Rect(%d, %d, %u %u)\n", x, y, w, h);  
}

void drawTextScreen(bool fOpaque) {
  Serial.println("Enter drawTextScreen"); Serial.flush();
  tft.fillScreen(ST77XX_RED);
  Serial.println("After Fill screen"); Serial.flush();
  //tft.setFont();
  tft.setCursor(0, 5);
  tft.setFont();
  Serial.println("After Set font"); Serial.flush();
  printTextSizes("SysFont");
  Serial.println("After print Text sizes"); Serial.flush();
  tft.println("SysFont");
    
  if (fOpaque)
    tft.setTextColor(ST77XX_WHITE, ST77XX_BLACK );
  else
    tft.setTextColor(ST77XX_WHITE);
  tft.setFont(Arial_28);
  printTextSizes("Arial");
  tft.print("Arial");
  tft.setFont(Arial_20);
  printTextSizes("0123456789");
  tft.println("0123456789");
  tft.setFont(&FreeMonoBoldOblique12pt7b);
  printTextSizes("AdaFruit");
  tft.println("AdaFruit");
  tft.setFont(&FreeSerif12pt7b);
  printTextSizes("FreeSan12");
  tft.println("FreeSan12");

  tft.setCursor(30, 120);
//  tft.drawRect(30, 120, 60, 20, ST7735_GREEN);
  int16_t x, y;
  uint16_t w, h;
  tft.getTextBounds("FreeSans", tft.getCursorX(), tft.getCursorY(), &x, &y, &w, &h);
  tft.drawRect(x, y, w, h, ST7735_GREEN);
  tft.print("FreeSans");
  tft.setFont();
  tft.setCursor(30, 150);
//  tft.drawRect(30, 150, 60, 20, ST7735_GREEN);
  tft.getTextBounds("System", tft.getCursorX(), tft.getCursorY(), &x, &y, &w, &h);
  tft.drawRect(x, y, w, h, ST7735_GREEN);
  tft.print("System");
  tft.setCursor(30, 180);
  tft.setFont(&FreeSerif12pt7b);
//  tft.drawRect(30, 180, 60, 20, ST7735_GREEN);
  tft.getTextBounds("FreeSans", tft.getCursorX(), tft.getCursorY(), &x, &y, &w, &h);
  tft.drawRect(x, y, w, h, ST7735_GREEN);
  tft.print("FreeSans");
  Serial.println("After last print"); Serial.flush();
  [COLOR="#FF0000"]tft.updateScreen()[/COLOR];
  Serial.println("Exit drawTextScreen"); Serial.flush();
}

void loop() {
  ptft->setRotation(rotation);
  Serial.printf("Set Rotation: %d width: %d height: %d\n", rotation, ptft->width(), ptft->height());
  rotation = (rotation + 1) & 0x3;
  elapsedMillis timer;
  drawTestScreen(ST7735_RED);
  [COLOR="#FF0000"]ptft->updateScreen();[/COLOR]
  // large block of text
  //delay(2500);
  Serial.println("Hit any key to continue");
  uint8_t loffset = 0;
  for (;;) {
    while (!Serial.available()) ;
    char ch = Serial.read();
    while (Serial.read() != -1) ;
    
    if (ch == '.') {
      ptft->drawRect(loffset, loffset, ptft->width() - 2 * loffset, ptft->height() - 2 * loffset, ST7735_GREEN);
      ptft->updateScreen();
      loffset++;
    } else if (ch == 'a') {
      drawTestScreen(ST7735_YELLOW);
      elapsedMillis ema = 0;
     [COLOR="#FF0000"] ptft->updateScreenAsync();[/COLOR]
      Serial.println("After Async Update");
      [COLOR="#FF0000"]while (ptft->asyncUpdateActive()) ;[/COLOR]
      Serial.print("Async completed ");
      Serial.println((int)ema, DEC);
    } else if (ch == 'c') {
      testContinuousUpdate();
    } else if (ch == 'o') {
      drawTextScreen(1);
    } else if (ch == 't') {
      drawTextScreen(0);
    } else if (ch == 'f') {
      // Toggle frame buffer or not frame buffer...
      if (use_fb) {
        use_fb = 0;
        tft.useFrameBuffer(false);
        Serial.println("*** Turned off frame buffer ***");
      } else {
        use_fb = 0;
        tft.useFrameBuffer(true);
        Serial.println("*** Turned on frame buffer ***");
      }
    } else {
      break;
    }
  }
}


void testContinuousUpdate() {
  elapsedMillis emu = 0;
  Serial.println("Start Continuous update test");
  ptft->fillScreen(ST7735_RED);
  ptft->updateScreenAsync(true);
  Serial.println("After updateScreenAsync");
  while(ptft->frameCount() < 10) {
   if (emu > 500) {
    emu = 0;
    Serial.printf("Frame count: %d\n", ptft->frameCount());
   }
  }
  ptft->fillScreen(ST7735_GREEN);
  while(ptft->frameCount() < 20) ;
  ptft->fillScreen(ST7735_BLUE);
  while(ptft->frameCount() < 30) ;
  ptft->fillScreen(ST7735_RED);
  ptft->drawRect(0,0, ptft->width(), ptft->height(), ST7735_BLUE);
  ptft->setCursor(10, ptft->height() / 2 - 4);
  ptft->printf("R:%d W:%d H:%d", rotation, ptft->width(), ptft->height());
  while(ptft->frameCount() < 35) ;
  Serial.println("Finished all frames");
  ptft->endUpdateAsync();
  Serial.println("After call to endUpdateAsync");
  while (ptft->asyncUpdateActive());  
  Serial.println("Test completed");
}
With this there are newer methods associated with frame buffer. Some of them are in RED in listing. Things like useFrameBuffer
You can setup to use your own memory desired by using the setFrameBuffer method.

When you are using frame buffer. All of the graphic primitives will write to memory.
Only when you do something like call: tft.updateScreen();
Will it then update the screen. This will hold up again until that call completes. More or less the same time frame as a fillScreen takes.

Alternatively you can use: updateScreenAsync() and it will start up a DMA transfer of the screen buffer to the display and return more or less immediately. You can query to see if the update has completed by calling asyncUpdateActive or you can do a wait for it to complete: waitUpdateAsyncComplete

Often times when I see output like you showed it is because of code like:
Code:
void outputMyStuff() {
    tft.fillScreen(black);
    <set up text positions and colors...
    tft.print("My new values");
... 
}
This is sort of slow AND perceived to be slow as it is flashing... So again without seeing your code, I would almost never do a fillScreen or large fillRect to remove the old stuff.
I would also try to only output the Header data once ...

Instead I would look to using Opaque text. That is text that is setup to output both the forground color of the text pixels, but also the background color specifid for thos pixels that aren't set to the font output. Example: setTextColor(RED, BLACK); ... sets forground to RED and background to black.
There may be some tricky stuff with the background color depending on what type of font you use. That is sometimes it will extend a long ways below the text (size to start of next row of text). Which may not be what you want. So we do have the ability to set Clipping rectangle to say only allow stuff to go in this region...

With Opaque text, I will often then Output the new text for a field. Then query the CursorX position, and the fill in with the background color to the end of the field, or if I saved away how far the previous text output went, to that position... That way if new text is shorter then previous text, it erases that part. And again as each pixel is only output once it is both faster and now flash.

Hope one of these approaches helps.

Good luck
 
Hi Kurt!

I *just* managed to get this all working and just saw your message posted, LOL.

I found that example and tweaked it for my setup and it works wonderfully. The updateScreenAsync(); is only taking 6 microseconds to perform. :) Now time to spruce it up with some nicer fonts. :)

https://www.youtube.com/watch?v=zLnmUw00CWk
 
Status
Not open for further replies.
Back
Top