ILI9341 display: Recommended way to rewrite an area with data?

clinker8

Well-known member
I thought I had a way to overwrite the screen with constantly updated data, but isn't working that well. I have the output of a digital read out head (DRO) being output to my ILI9341. Just got it connected up. Some parts of the rectangle have remnants of previous readings. The way I did it, has worked for other sections of my code, so I am puzzled. However, I'm not sure if what I am doing is the "right way" or even close to the recommended way. Here's a copy of the function I use to update the X DRO.
Code:
void updateX()
{
  if (Xval != oldXval)
  {
    uint16_t x1, y1, w, h;
    oldXval = Xval;
    String newstr = "XXXXXXXX";
    tft.setFont(Arial_18);
    tft.setTextColor(ILI9341_WHITE);
    tft.getTextBounds(newstr, cxgX, cygX, &x1, &y1, &w, &h);
    tft.fillRect(x1, y1, w, h, thisGREY);
    tft.setTextDatum(BL_DATUM);
    tft.drawFloat( Xval, 4, cxgX, cygX );
    Serial.printf("Xval = %+7f4\n", Xval);
  }
}
I am using the ILI934_t3n library. thisGREY is just some background color. cxgX and cygX are the coordinates on the screen for the number to be displayed and are uint16_t. Xval is updated by @luni's EncoderTool. I use this basic code to update my RPM of my lathe, and I have not noticed the artifacts, like I see with this code.

Probably doing it wrong - I'm not a very good C or C++ coder. Is there a better way of doing something like this? Here's a picture of the sort of artifacts that I am seeing.
I have seen it on both the X and Z DRO displays, but not the RPM. You are seeing my first PCB made with KiCAD. It is for an Electronic Lead Screw for my lathe. It cuts threads in metric and imperial and feeds in both units. That part is working well. So far I have not had to make any changes to the PCB. I am now adding in the DRO inputs. The X DRO wires are coming in at the top right connector. The DRO display is not calibrated - I will have to determine the calibration coefficient at a later time. But it would be helpful to be able to read that displayed number.

Thanks for any insights.
 

Attachments

  • PXL_20220929_220025599_1080.jpg
    PXL_20220929_220025599_1080.jpg
    279.6 KB · Views: 57
I did find something that helped. I used BL_DATUM above, but I should have used TL_DATUM. That helped some. I also discovered that if I used Arial_18 or the Arial bold 18 (whatever it is called), the getTextBounds fails. It returns the wrong value. When I changed to Arial_16 for the DRO output, the getTextBounds worked as expected. The failure resulted in the grey box overwriting the buttons on the left side of the screen - at the same y level as the text. Perhaps it was an underflow problem?

Got the DRO roughly calibrated. At least it matches the cross-slide dial on the lathe, if I take up the backlash of the screw. Bit by bit, it is getting closer.
 
I didn't really look at your issue at depth but I would think you would want to erase the bounds of the old string instead of the new string. So something like:

Code:
void updateX()
{
static uint16_t x1, y1, w, h;

  if (Xval != oldXval)
  {

    oldXval = Xval;
    String newstr = "XXXXXXXX";
    tft.setFont(Arial_18);
    tft.setTextColor(ILI9341_WHITE);

    tft.fillRect(x1, y1, w, h, thisGREY);
    tft.getTextBounds(newstr, cxgX, cygX, &x1, &y1, &w, &h);

    tft.setTextDatum(BL_DATUM);
    tft.drawFloat( Xval, 4, cxgX, cygX );
    Serial.printf("Xval = %+7f4\n", Xval);
  }
}
 
I usually do something like this a few different ways, depending on how lazy I am.

First, I typically use Opaque text output instead of Transparent.
That is: tft. setTextColor(ILI9341_WHITE, thisGREY);

Two reasons for this:
1) it draws faster - opaque outputs all of it in one rectangle update, transparent works by having to address each pixel
2) It overwrites the previous data within its new text output.

Downside - depending on fault it may output farther down than you want (line spacing). You can handle this by setting a clip boundary.

Now assuming now wrapping to next line and the like, and room between two fields and the like, sometimes I get lazy and simply output a few extra blanks, to
hopefully overwrite previous if new text is shorter:

Code:
tft.drawFloat( Xval, 4, cxgX, cygX );
tft.write("   ");
If I want to be a little more sophisticated, I erase everything to the right of new output to the end of logical field.

Or slightly better, remember where the last text ended and if our new text does not extend as far, then erase from current end to previous end.
For example, I do that in the USBHost example keyboard_viewer. Slightly edited here to remove #if for ILI9341_t3 library (showing for _t3n)

Code:
uint16_t cursor_last_key_end = 0;

void ShowLastKeyChange(const char *sz, bool press) {
  tft.setTextColor(press ? YELLOW : RED, BLACK);
  tft.setCursor(COL_LAST_MSG, ROW_LAST_MSG);
  tft.print(sz);
  uint16_t cursor_x = tft.getCursorX();
  if (cursor_x < cursor_last_key_end) {
    tft.fillRect(cursor_x, ROW_LAST_MSG, cursor_last_key_end - cursor_x + 1, ROW_SIZE, BLACK);
  }
  cursor_last_key_end = cursor_x;
}

The interesting part here is the stuff dealing with tft.getCursorX();
 
I'm not sure this will help, but if you are trying to update text on a screen and do not want the text to flicker, have a look at this lib. The vid show this FlickerFreePrint in action. Basically what the lib does is paint over only changed data to hide it then it redraws updated text. It will work with any font, especially ones where char widths vary. Float, doubles, int, char are supported but not strings.

For example

Old text
427.428

New text
428.351

the 42 will not be redrawn, but the 7.428 will be redrawn in background color on top of the old text, then the 8.351 will be drawn in your normal font color.

https://www.youtube.com/watch?v=uMqTAoCCCX8

https://github.com/KrisKasprzak/FlickerFreePrint

Hope this helps
 
Thanks everyone. I will look into all of your helpful suggestions.

While I am here on display topics, is there a way to have the text (float) be right justified? More importantly, I want the decimal point to be in a fixed position. Sometimes the sign of the number changes, and the whole number shifts. This can be really distracting if this happens when machining. A shift in a decimal point usually signifies something bad is happening, or about to happen!

I output the number as xxx.xxxx. Alternatively, is there a way to output the sign? So the number is +0.0124, or -10.2840, so the decimal point stays in the correct place? I know you can do this in C formatting, aka sprintf but can it be done with the ILI9341 display? My attempts at this so far have failed.
 
I output the number as xxx.xxxx. Alternatively, is there a way to output the sign? So the number is +0.0124, or -10.2840, so the decimal point stays in the correct place? I know you can do this in C formatting, aka sprintf but can it be done with the ILI9341 display? My attempts at this so far have failed.
How about a separate field for the +/- sign. Change the sign if the value goes from negative to positive, or vice versa, and then just print out the absolute value of the number.
 
I'm not sure this will help, but if you are trying to update text on a screen and do not want the text to flicker, have a look at this lib. The vid show this FlickerFreePrint in action. Basically what the lib does is paint over only changed data to hide it then it redraws updated text. It will work with any font, especially ones where char widths vary. Float, doubles, int, char are supported but not strings.
Quick note: The code I showed above to use Opaque text should not flicker. Every bit should be updated exactly once.

And if you have memory for Frame Buffer you can also write the code to pretty stupid and avoid flicker...
For example, to update the field area, you could simply set clip rectangle around the field. do a fillRect() back to the background color, draw the new text and then do an updateScreen...


Thanks everyone. I will look into all of your helpful suggestions.

While I am here on display topics, is there a way to have the text (float) be right justified? More importantly, I want the decimal point to be in a fixed position. Sometimes the sign of the number changes, and the whole number shifts. This can be really distracting if this happens when machining. A shift in a decimal point usually signifies something bad is happening, or about to happen!

I output the number as xxx.xxxx. Alternatively, is there a way to output the sign? So the number is +0.0124, or -10.2840, so the decimal point stays in the correct place? I know you can do this in C formatting, aka sprintf but can it be done with the ILI9341 display? My attempts at this so far have failed.

Sorry I have never played or used drawFloat (or the other draw numbers stuff in the library)

Note: The libraries are derived from the Print class, so you can use any of the functions you can use with things that use print, like:
tft.printf() or tft.print() tft.println() tft.write()


So you can do things like:
Code:
    tft.setCursor(cxgX, cygX); 
    if (xVal >= 0) tft.write('+');
    tft.print(Xval, 4);

Note: the functions like drawFloat do allow you to right justify, by using one of the other DATUM, like BR_DATUM, but then you run into the issues of erasing the stuff that was not updated to left
of the new text.

You can do all of that manually yourself, by for example using sprintf your value to a string.
You can then use getTextBounds function with that string to measure the bounding rectangle of that text.
And then, you can choose how you want it justified and then compute the rectangles of area that may need to be filled with the background color.

Hope that makes sense
 
How about a separate field for the +/- sign. Change the sign if the value goes from negative to positive, or vice versa, and then just print out the absolute value of the number.

Well that's simple enough! Thanks. Don't know why I didn't think of that, it is so obvious, one you have pointed it out!
 
Also alternative to print +- is to use different color, like black for plus and red for minus
 
Back
Top