Refreshing/overwriting display (1.8" TFT) - flashes

This more of a generic question really. Im using a 1.8" TFT display with the adafruit libraries.

If i want to display a variable on the same screen position, i see the values overwriting each other, so to display them properly I write a background coloured rectangle over the display spot between updates. This has the side effect that the variable seems to strobe/flash when you look at it.

has anyone else encountered this.... and found a solution? (reducing the update frequency isnt an option).

Moderator edit: reference to company selling counterfeit product removed

(reducing the update frequency isnt an option)

What is the update frequency?
Sounds like your current solution of writing something to clear the spot, before you write what you want new, is already reducing the update frequency.
Explain why you can't reduce the update frequency if you want the display to look better, or else why your faster update frequency is better.
thanks, Jack

Currently its updating ~0.5 secs. I want the user to see the stuff in realtime, hence why dropping the frequency isnt an option (it makes little difference anyway it still 'flashes' whenever you make a change.

Incidentally if you dont clear before you write to the spot, you get both the old and new characters merged.

My question was more oriented along 'is this an inherent characteristic of these displays, or is there some technique to resolve this'.

Sorry, I do not have experience with that display. So I will back away now.

Clearly this relates to the time delay between writing your blanking block, and re-writing the variable and then writing the blanking again. Of course you do not re-write the variable if it is unchanged.

Is there some facet of your code which prolongs this delay? Surely you are not writing the blank then evaluating the variable but have the screen data already formatted before blanking.

It sounds as if you are "or-ing" the image data to the frame buffer rather than fully over-writing it. Surely this should not be necessary, perhaps you are using the wrong library function.

So listing the dumb mistake options;

  1. Extra delays/not having data ready. Nope.
  2. Wrong library function. I dont think so, its tft.print() from the adafruit library and sadly you do seem to need to delete the previous contents first.

You might notice I omitted the 'not writing data if it hasnt changed'. This would be a shameless attempt to gloss over a dumb mistake :wink:

Cheers for the pointer

Without knowing the size of the area to be updated (or the board & hence available RAM), I don't know just how practical this would be for you.

But, why not write your own code to do double-buffering?
That is to say, keep some memory set-aside for this area. Assuming 16bit mode, you'll need 2 bytes per pixel. Just initialize the memory with your background colour, then use a (self-written) function that will 'print' the data to the memory buffer, rather than the display.

Once done, just set the update window to be the area of concern, then simply blit the memory buffer.

Assuming you'd like to reserve 1024 bytes, you'll have space for 512 pixels. This would be 23 x 22 pixels, 10 x 51 and any other combination whose product didn't exceed 512.

Of course(?), this approach would be the most suitable for drawing text over animated, complex or calculated backgrounds - drawing text over the top of a plasma, xor-pattern, fractal all come to mind.
If on the other hand, you've got a single solid-colour background, as I suspect you have - then you could just add a new function to the class. If you have a close look at the code, you'll see.

You may find this excerpt of interest:

//Adafruit_GFX.cpp - line 390
void Adafruit_GFX::drawChar(int16_t x, int16_t y, unsigned char c,
			    uint16_t color, uint16_t bg, uint8_t size) {

  if((x >= _width)            || // Clip right
     (y >= _height)           || // Clip bottom
     ((x + 6 * size - 1) < 0) || // Clip left
     ((y + 8 * size - 1) < 0))   // Clip top
    return;

  for (int8_t i=0; i<6; i++ ) {
    uint8_t line;
    if (i == 5) 
      line = 0x0;
    else 
      line = pgm_read_byte(font+(c*5)+i);
    for (int8_t j = 0; j<8; j++) {
      if (line & 0x1) {
        if (size == 1) // default size
          drawPixel(x+i, y+j, color);
        else {  // big size
          fillRect(x+(i*size), y+(j*size), size, size, color);
        } 
      } else if (bg != color) {
        if (size == 1) // default size
          drawPixel(x+i, y+j, bg);
        else {  // big size
          fillRect(x+i*size, y+j*size, size, size, bg);
        }
      }
      line >>= 1;
    }
  }
}

A quick scan of the code suggests to me that the print functions call this one internally.
So, when print is preceded by a call to the below snippet I'd expect to see both the text colour and background set.

// line 439 of Adafruit_GFX.cpp
void Adafruit_GFX::setTextColor(uint16_t c, uint16_t b)

A friend has my screen at the moment, so I'm not in a position to play around. I'd give the setTextColor function a go before print and see what happens.

Well thats a bath moment... (eureka!)....thanks VERY much

I read all the example sketch, all of adafruit_gfx.h and all of adafruit_ST7735.h. I should've known the answer would lie in the one bit I didnt read!

The bad news is my liver suffered while trying to work it out, the good news is i can slim my code down now :slight_smile:

Though it is unlikely to completely eliminate the flashing effect, I'd suggest erasing text a little differently.

To overpaint with a rectangle you have to calculate coordinates and then, in effect, paint a series of lines. You can remove text by changing text color to your background color and then writing the same text again. You can, for example, put the printing routine in a Print_Or_Erase procedure to which you pass your text and a flag for "print" or "erase". As the following sketch (snipped out of a much larger program) illustrates, doing this is a little faster (just ca. 2%) than erasing by filling a rectangle, and it may therefore be a bit less visually distracting if the print and erase don't follow each other as fast as they do in this sketch.

#include  <avr/pgmspace.h> // PROGMEM (flash) handling
#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library
#include <SPI.h>

#define cs   10
#define dc   9
#define rst  8  // you can also connect this to the Arduino reset

Adafruit_ST7735 tft = Adafruit_ST7735(cs, dc, rst);
float p = 3.1415926;

#define Landscape 3
#define Background ST7735_WHITE
#define charwidth 6 // from AdaFruit GFX
#define charheight 8 // from AdaFruit GFX

void setup(void) {
  Serial.begin(115200);
  Serial.print("hello!");

  tft.initR(INITR_BLACKTAB);   // initialize a ST7735S chip, black tab
  tft.fillScreen(Background);
  tft.setRotation (Landscape);
  int CtrX = tft.width()/2;
  int CtrY = tft.height()/2;
  byte txtsize = 2;
  tft.setTextWrap(false);
  tft.setTextSize(txtsize);
  uint32_t time = millis ();
  
  for (byte i=1; i<= 10; i++){
    tft.setTextColor(ST7735_BLACK);
    tft.setCursor(CtrX-(9*charwidth*txtsize)/2,3);
    tft.println("CONTACTOR");
    tft.setCursor(CtrX-(5*charwidth*txtsize)/2,3+charheight*txtsize);
    tft.println("FAULT");
    tft.setTextColor(Background);
    tft.setCursor(CtrX-(9*charwidth*txtsize)/2,3);
    tft.println("CONTACTOR");
    tft.setCursor(CtrX-(5*charwidth*txtsize)/2,3+charheight*txtsize);
    tft.println("FAULT");
  }
  time = millis() - time;
  Serial.print ("Elapsed time for 10 'erase by overprint' = ");
  Serial.println (time);
  Serial.println ();
  tft.setTextColor(ST7735_BLACK);
  tft.setCursor(CtrX-(9*charwidth*txtsize)/2,3);
  tft.println("PRINTOVER");
  tft.setCursor(CtrX-(4*charwidth*txtsize)/2,3+charheight*txtsize);
  tft.println("DONE");
  delay (3000);

  tft.setTextColor(Background);
  tft.setCursor(CtrX-(9*charwidth*txtsize)/2,3);
  tft.println("CONTACTOR");
  tft.setCursor(CtrX-(4*charwidth*txtsize)/2,3+charheight*txtsize);
  tft.println("DONE");
  time = millis ();
  for (byte i=1; i<= 10; i++){
    tft.setTextColor(ST7735_BLACK);
    tft.setCursor(CtrX-(9*charwidth*txtsize)/2,3);
    tft.println("CONCTACTOR");
    tft.setCursor(CtrX-(4*charwidth*txtsize)/2,3+charheight*txtsize);
    tft.println("FAULT");
    tft.fillRect (CtrX-(9*charwidth*txtsize)/2, 3, 10*charwidth*txtsize, 2*charheight*txtsize, Background);
  }
  time = millis() - time;
  Serial.print ("Elapsed time for 10 'erase by fillRect' = ");
  Serial.println (time);
  tft.setTextColor(ST7735_BLACK);
  tft.setCursor(CtrX-(8*charwidth*txtsize)/2,3);
  tft.println("fillRect");
  tft.setCursor(CtrX-(4*charwidth*txtsize)/2,3+charheight*txtsize);
  tft.println("DONE");
} // end setup

void loop() {
}

Ciao,
Lenny

@scrumfled: A pleasure, you're welcome. Heh heh heh.
Glad to hear the code and liver are both looking towards brighter futures!

And y'all too...
See my post about your problem and fix:
http://forum.arduino.cc/index.php?topic=177021.0

I had the same issues with a 1.8" SPI Adafruit display and the Adafruit library where you can't print over a character because it doesn't clear the print location first.
My solution was to move all static printing, Labels and other artwork that gets printed once to setup and print blank spaces at as many places as I had to print over with new data, I did it as part of the print function I wrote to display new data..
Seconds and minutes from the clock and the last digits of Temperature and RH usually.
It's a 'cute' display and real easy to use but for the size and that annoying function of Adafruits Library... I scrapped it long ago.
Nick Gammon found a way to speed up the display writing by changing the SPI clock divider in the lib files as I remember... I think I tried it for a noticeable change but that was a year ago too..

Doc

Folks,

Y'all making same mistake (for what you are trying to do) - providing single color argument.

If you carefully read API notes you will notice that tft.setTextColor can take either one or two arguments. If single argument provided - it will print text color with clear background (junk pixels will accumulate). If two arguments provided - it will fill background of each character with color of second argument.

Nice and simple. No need for any blank space overwrite before new print.

Keep in mind that Adafruit library draws every single pixel separately. It will take more time to print with background color specified than without.

Enjoy!

SuperLAVA is absolutely correct with one big IF.

Specifying text background color works to remove previous text IF the new text occupies exactly the same position as the old. If that is not the case, pixels outside that "box" will not be removed. In my application, I am printing two horizontally centered lines of variable length. If I overprint with longer text, OK, I could just use the two-parameter form. If I overprint with shorter text, however, this alone will not work.

Ciao,
Lenny

So - do not overprint with shorter text.

Problem solved.


I'm not joking.

This is nothing new, the same situation for more than 43 years. Displaying a progress bar - which seems to be the case here - required a CR without LF and re-writing the whole line, or more complex backspace-space-backspace sequences and such. Unless you write a character or blank, the previous data will show. If you want to write over part of a line, but make all of the previous line disappear, just write blanks over the part you do not want.

It may - or may not - be slower this way, but it works reliably and easily.

enhzflep:
If on the other hand, you've got a single solid-colour background, as I suspect you have - then you could just add a new function to the class. If you have a close look at the code, you'll see.

You may find this excerpt of interest:

//Adafruit_GFX.cpp - line 390

void Adafruit_GFX::drawChar(int16_t x, int16_t y, unsigned char c,
   uint16_t color, uint16_t bg, uint8_t size) {

if((x >= _width)            || // Clip right
    (y >= _height)           || // Clip bottom
    ((x + 6 * size - 1) < 0) || // Clip left
    ((y + 8 * size - 1) < 0))   // Clip top
   return;

for (int8_t i=0; i<6; i++ ) {
   uint8_t line;
   if (i == 5)
     line = 0x0;
   else
     line = pgm_read_byte(font+(c5)+i);
   for (int8_t j = 0; j<8; j++) {
     if (line & 0x1) {
       if (size == 1) // default size
         drawPixel(x+i, y+j, color);
       else {  // big size
         fillRect(x+(i
size), y+(jsize), size, size, color);
       }
     } else if (bg != color) {
       if (size == 1) // default size
         drawPixel(x+i, y+j, bg);
       else {  // big size
         fillRect(x+i
size, y+j*size, size, size, bg);
       }
     }
     line >>= 1;
   }
 }
}




A _quick_ scan of the code suggests to me that the print functions call this one internally. 
So, when print is preceded by a call to the below snippet I'd expect to see both the text colour and background set.


// line 439 of Adafruit_GFX.cpp
void Adafruit_GFX::setTextColor(uint16_t c, uint16_t b)

Enzhflep answered this question ages ago in the thread, guys.

print old data with inverted colors before print new data with right colors

enhzflep:
So, when print is preceded by a call to the below snippet I'd expect to see both the text colour and background set.

// line 439 of Adafruit_GFX.cpp

void Adafruit_GFX::setTextColor(uint16_t c, uint16_t b)

This. Is. Awesome. Thank you so much, i was looking for double buffering my text but you saved me heaps of programming.

Hi guys,

I tried the technique with using setTextColor with two arguments. It worked, but itroduced a significant delay to my code (~2s). Anyone else experienced that?

Or know how to get rid of it?

Keep in mind that Adafruit library draws every single pixel separately. It will take more time to print with background color specified than without.

There are faster ways to do things. Quite honestly, it does not matter on these small displays.

Most times, you write transparently on a fresh background.
You might want to update some numbers in a small window. So you print with setColor(foreground, background) in this small window.

Alternatively, you always use transparent. And fillRect() the small window with the background before updating it.

David.