Using ILI9341

Status
Not open for further replies.

AllenCherry

Active member
Anyone who can help please:

I am using the ILI9341 display to draw 6 concentric circles, and I want to have 'cross hair' moving around over the circles.
The problem I am experiencing is that the 'cross hair' is erasing the circle, and to re-draw the circles each time is spoiling the 'smoothness' of the display update. The code is as follows:

#include "SPI.h"
#include "ILI9488_t3.h"
#define TFT_RST 8
#define TFT_DC 9
#define TFT_CS 10

ILI9488_t3 tft = ILI9488_t3(&SPI, TFT_CS, TFT_DC, TFT_RST);

void setup()
{
tft.begin();
tft.fillScreen(ILI9488_BLACK);
tft.setOrigin(0,0);

//draw the 6 circles on the screen

tft.drawCircle(119, 160, 119, (ILI9488_WHITE));
tft.drawCircle(119, 160, 100, (ILI9488_WHITE));
tft.drawCircle(119, 160, 80, (ILI9488_WHITE));
tft.drawCircle(119, 160, 60, (ILI9488_WHITE));
tft.drawCircle(119, 160, 40, (ILI9488_WHITE));
tft.drawCircle(119, 160, 20, (ILI9488_WHITE));
}

void loop()
{
//draw the + and move it across and up the screen

for (int x = 50; x <= 255; x++)
{
for(int y = 50; y <= 255;y++)
{

tft.drawFastVLine(x, y-10,20,(ILI9488_WHITE));
tft.drawFastHLine(x-10, y,20,(ILI9488_WHITE));
delay(3);

//erase the cross by painting it the same as the background colour

tft.drawFastVLine(x, y-10,20,(ILI9488_BLACK));
tft.drawFastHLine(x-10, y,20,(ILI9488_BLACK));

//have to re-draw the circles - this slows everything down

tft.drawCircle(119, 160, 119, (ILI9488_WHITE));
tft.drawCircle(119, 160, 100, (ILI9488_WHITE));
tft.drawCircle(119, 160, 80, (ILI9488_WHITE));
tft.drawCircle(119, 160, 60, (ILI9488_WHITE));
tft.drawCircle(119, 160, 40, (ILI9488_WHITE));
tft.drawCircle(119, 160, 20, (ILI9488_WHITE));

}
}
}

Is there anyway to have the 'cross hair' moving across the background circles without erasing them when the cross hair is moved to a new position?

Thanks for any help,

Allen
 
Which Teensy are you using?
And which display?

You mention ILI9341 in header, but you are using the ILI9488_t3 library?
 
Quick and Dirty version, using Frame buffer (assuming your Teensy has enough memory...)

I updated it to ILI9341_t3n as I had an ILI9341 setup, would need to setup ILI9488. But then most things from my ili9341_t3n is in the other library (github\kurte)


The quick and dirty writes everything every time, BUT I set a clip rectangle to the union of the two rectangles for previous cursor and new cursor...
Code:
#include "SPI.h"
#include <ILI9341_t3n.h>
#define TFT_RST 8
#define TFT_DC 9
#define TFT_CS 10

ILI9341_t3n tft = ILI9341_t3n(TFT_CS, TFT_DC, TFT_RST);

void setup()
{
  tft.begin();
  tft.fillScreen(ILI9341_BLACK);
  tft.setOrigin(0, 0);

  //draw the 6 circles on the screen

  tft.drawCircle(119, 160, 119, (ILI9341_WHITE));
  tft.drawCircle(119, 160, 100, (ILI9341_WHITE));
  tft.drawCircle(119, 160, 80, (ILI9341_WHITE));
  tft.drawCircle(119, 160, 60, (ILI9341_WHITE));
  tft.drawCircle(119, 160, 40, (ILI9341_WHITE));
  tft.drawCircle(119, 160, 20, (ILI9341_WHITE));
  tft.useFrameBuffer(true);
}

void loop()
{
  //draw the + and move it across and up the screen
  int last_x = -1;
  int last_y = -1;
  int min_x, min_y, w, h;

  for (int x = 50; x <= 255; x++)
  {
    for (int y = 50; y <= 255; y++)
    {
      if (x < last_x) {
        min_x = x - 10;
        w = (last_x + 10) - min_x;
      } else {
        min_x = last_x - 10;
        w = (x + 10) - min_x;
      }
      if (y <= last_y) {
        min_y = y - 10;
        h = (last_y + 10) - min_y;
      } else {
        min_y = last_y - 10;
        h = (y + 10) - min_y;
      }
      tft.setClipRect(min_x, min_y, w + 1, h + 1);
      
      last_x = x;
      last_y = y;
      //erase the cross by painting it the same as the background colour
      tft.fillScreen(ILI9341_BLACK);

      //draw the 6 circles on the screen

      tft.drawCircle(119, 160, 119, (ILI9341_WHITE));
      tft.drawCircle(119, 160, 100, (ILI9341_WHITE));
      tft.drawCircle(119, 160, 80, (ILI9341_WHITE));
      tft.drawCircle(119, 160, 60, (ILI9341_WHITE));
      tft.drawCircle(119, 160, 40, (ILI9341_WHITE));
      tft.drawCircle(119, 160, 20, (ILI9341_WHITE));

      tft.drawFastVLine(x, y - 10, 20, (ILI9341_RED));
      tft.drawFastHLine(x - 10, y, 20, (ILI9341_RED));
      tft.updateScreen();
      delay(2);
    }
  }
}

Another method, that can work, is to have more smarts drawing the cursor.

That is you can use a function like:
i
Code:
nt cursor_x = -1;
int cursor_y;
uint16_t cursor_rect[400];

void update_cursor(int x, int y) {
    if ((x == cursor_x) && (y == cursor_y)) return;  // same one
    if (cursor_x >= 0) {
        // restore last cursor
        tft.writeRect(cursor_x-10, cursor_y-10, 20, 20, cursor_rect);
    }
    
    // remember the pixels that were under us... 
    tft.readRect(x-10, y-10, 20, 20);

    // Write the new cursor
    tft.drawFastVLine(x, y - 10, 20, (ILI9341_RED));
    tft.drawFastHLine(x - 10, y, 20, (ILI9341_RED));
    cursor_x = x;
    cursor_y = y;
}

Assuming you can actually read from the display, this can work with or without frame buffer. More likely to work with ILI9341 than 9488 without frame buffer as this requires us to read data from the actual display over SPI...
With frame buffer, it is just copying memory around. Also without frame buffer will get flash as it draws cursor, restores pixels and draws new one..

BUT: this could be written more intelligent. You might need a larger save buffer, and then figure out which pixels change with new location and which don't and only update those pixels that actually change.

Hope this helps some
 
If you want higher framerate, one thing you can do is render the circles to an "overlay" buffer and then use DMA to overwrite the framebuffer with it at the beginning of every frame. Following this, draw only the reticle. In this way, the reticle acts sort of like a sprite, in that it's very computationally cheap to draw and the circles never need to be redrawn.
 
KurtE,

Thanks for your reply - I am using a Teensy 4.1, the exact display is the Adafruit 2.8" LCD break-out board marked as ILI9341, and yes, I am using the ILI9488_t3 library. Is this the best library fir the board?

Thanks,

Allen
 
Pilot,

Thanks for the suggestions - this is my first attempt at using a display, please can you point me in the right direction for some information on the use of 'overlays' and DMA?

Many thanks,

Allen
 
KurtE,

Many thanks for the guidance and the code example - it certainly has achieved what I was looking for!

Really appreciate your help,

Allen
 
You actually don't have to use DMA, but it would be a little faster, and you'd have a few clock cycles to do other stuff while you wait for the transfer to complete.

I don't think the ILI library has a concept of a framebuffer. What you would do is allocate an unsigned int[] array long enough to hold the screen as overlay[]. At bootup, you write the circles to this array, either by directly setting the pixels (free code to do this should be easy to find online) or by using the ILI's graphics functions and then writing the whole screen to overlay[]. The former method is faster and won't have any visible artifacts when it boots.

At the end of this process, you have a clean copy of the circles that you can use to re-draw whatever portion of the screen.

You can then pick from two different strategies:
  • Allocate another int unsigned array called frameBuffer[]. At the beginning of each frame, copy overlay[] to frameBuffer[], either programmatically or using DMA. (I would start with doing it programmatically, just to get it working, and use DMA if you want it to go faster or make use of the transfer time to do other things.) After it's finished copying, set the pixels in frameBuffer[] necessary to create the lines. If they are all horizontal, vertical, or exactly 45 degrees, this will be exceedingly simple, otherwise you can find some code for the Bresenham algorithm to draw lines of whatever slope you want. After you have drawn the reticle to frameBuffer[], use writeRect8BPP() to transfer the framebuffer to the display. To make this go faster, you can only transfer the pixels that have changed during the previous and current frames.
  • Write reticle-drawing functions that can either draw or un-draw the reticle. There is no framebuffer in this case; instead, all pixels are sent directly to the display. The draw function is obvious; it can use the ILI library's drawing functions. The un-draw function does the same thing; but instead of setting the pixels to white (or whatever) it sets them to what they are in overlay[]. This should be a little faster than using a framebuffer (assuming the case where you are ONLY drawing the one reticle, and nothing much more complex than that) because you are transferring far fewer pixels over SPI. If the scene becomes much more complex, it will eventually make more sense to use a framebuffer.
 
Actually ILI9341_t3n library has concept of frame buffer..

As shown in the quick and dirty code I wrote above.

Note: the above I could have written: tft.updateScreenAsync() instead of tft.updateScreen and it would do it using DMA. BUT so far the DMA code updates whole screen... Whereas the non-dma looks at things like clip rectangle and only updates those parts within the rectangle... So in this case can be faster as it is simply updating the pixels in the union rectangle of the current cursor and previous cursor.

But again you could write the code a 100 different ways. Example, the ILI9341_t3n library allows you to set the frame buffer to some different memory. You can use it to do double buffer if you want So again with T4.1 you could setup one buffer to start the Async update, tell system to switch buffers, output the next page and then update, and switch back to first...

Or you can write code that saves the region of memory and use knowledge of your drawing code, that figure out for the bounding rectangles which pixel should be used to draw... Not complicated but...
 
Hi KurtE, Hi Pilot,

My sincere thanks to you both for you help - I really appreciate it. This is my first foray into working with a display, and your guidance has helped a lot.
I hope that I can ask for more adviceshould I hit a problem!

Allen
 
Status
Not open for further replies.
Back
Top