Mcufriend_kbv with ili9341 SPI display

Does anybody know how to use mcufriend_kbv library with ILI9341 SPI display ? I did not find any example or any tip about that. Thanks for any help

I forgot to mention: the display is a 4 wire SPI (Din, CS, RS, RST)

@AntonioTesta, your topic has been move to a more suitable location so it can get the attention of those in the know.

Note that there was nothing wrong with where you placed the question but some people only visit the Displays section.

MCUFRIEND_kbv library is for parallel displays.

It is always wise to post a link to the actual display that you have bought. e.g. Ebay sale page.
Note that Chinese Red ILI9341 SPI boards require 3.3V logic.

There are lots of libraries for ILI9341 SPI displays
e.g. Adafruit_ILI9341

Most TFT libraries use Adafruit_GFX methods. So it is fairly easy to port a sketch written for a different library.
Hint. constructor(), begin(), readID() might be different.

The "other" libraries like UTFT or badly-spelled libraries like LCDWIKI require a lot of work.

David.

When using a new peice of hardware with my Arduino, I always check here first :
Arduino Library List

Thanks David. The problem with Adafruit_ILI9341 is that it does not have the readGRAM, exactly the function I most need !!! Any suggestion ? Tks again

Go on. Is it difficult to post a link to your display?

Or describe in words (including any level-shifting)

Then I can give you a definitive reply.

David.

Ooops... sorry. I forgot it in my last post. Here is:

http://www.lcdwiki.com/2.4inch_SPI_Module_ILI9341_SKU:MSP2402

Which Arduino ?
What level shifters ?

It is very straightforward to add a readGRAM() style function to an Adafruit_ILI9341 sketch.

        uint16_t readPixel(int16_t x, int16_t y) {
            uint8_t r, g, b;
            tft.startWrite();
            tft.setAddrWindow(x, y, w, h);  //Adafruit adds 0x2C 
            tft.writeCommand(0x00);         //kill the 0x2C command
            tft.writeCommand(0x2E);         //we need ILI9341_RAMRD
            r = tft.spiRead();              //dummy
            r = tft.spiRead();
            g = tft.spiRead();
            b = tft.spiRead();
            tft.endWrite();
            return tft.color565(r, g, b);    //.kbv
        }

        int16_t  readGRAM(int16_t x, int16_t y, uint16_t *block, int16_t w, int16_t h)
        {
            uint16_t *p;
            for (int row = 0; row < h; row++) {
                p = block + row * w;
                for (int col = 0; col < w; col++) {
                    *p++ = readPixel(x + col, y + row);
                }
            }
        }

Or you can create a class that extends the Adafruit_ILI9341 class with some extra methods. e.g. readID(), readReg(), pushColors(), vertScroll(), ...

Personally, I would write readGRAM() as the primitive function. e.g.

        int16_t  readGRAM(int16_t x, int16_t y, uint16_t *block, int16_t w, int16_t h)
        {
            uint16_t *p;
            uint8_t r, g, b;
            tft.startWrite();
            tft.setAddrWindow(x, y, w, h);  //Adafruit adds 0x2C 
            tft.writeCommand(0x00);         //kill the 0x2C command
            tft.writeCommand(0x2E);         //we need ILI9341_RAMRD
            r = tft.spiRead();              //dummy
            for (int row = 0; row < h; row++) {
                p = block + row * w;
                for (int col = 0; col < w; col++) {
                     r = tft.spiRead();
                     g = tft.spiRead();
                     b = tft.spiRead();
                     *p++ = tft.color565(r, g, b);    //.kbv
                }
            }
            tft.endWrite();
        }

Great... it is cool. I will try. I am using one clone of STM32 bluepill:

operating at 3.3v. Lets see if stm32+SPI tft+Adafruit ILI9341 library works with your suggestion. Thanks a lot

Life is much easier if you had just said "red ILI9341 SPI" with STM32 BluePill.

The above code is untested. Let us know how you get on.

David.

You are right. Living and learning... I tell you the results. Tks

David, unfortunatelly it does not work. It returns random values, usually zeros. I have created a primitive function in the library Adafruit_ILI9341.cpp as following:

int16_t Adafruit_ILI9341::readGRAM(int16_t x, int16_t y, uint16_t *block, int16_t w, int16_t h)
  {
  uint16_t *p;
  uint8_t r, g, b;
  startWrite();
  setAddrWindow(x, y, w, h);  //Adafruit adds 0x2C 
  writeCommand(0x00);         //kill the 0x2C command
  writeCommand(0x2E);         //we need ILI9341_RAMRD
  r = spiRead();              //dummy
  for (int row = 0; row < h; row++) {
    p = block + row * w;
    for (int col = 0; col < w; col++) {
      r = spiRead();
      g = spiRead();
      b = spiRead();
      *p++ = color565(r, g, b);    //.kbv
    }
  }
  endWrite();
}

and in Adafruit_ILI9341.h, under public section:

  int16_t readGRAM(int16_t x, int16_t y, uint16_t *block, int16_t w, int16_t h);

Any idea about what would be wrong ? Thank you so much

Yes, I tried it myself. And it does not work. I will have to investigate.

Most of the Adafruit SPITFT methods are public. So it should be possible to create a user function.

From memory, my personal "GLUE" class worked with earlier Adafruit_ILI9341.

David.

David, I was looking at strange LDCWIKI_SPI.h / .cpp and I realized they use a different method to read the GRAM. I could not test it because there are many errors during the compilation but maybe it has some good clue to fix the problem. According with their Set_Addr_Window command the GRAM is scanned in reverse way. The R24BIT is equal to 1 when controller is ILI9341. If you have a chance please take a look at code bellow. Thanks again.

int16_t LCDWIKI_SPI::Read_GRAM(int16_t x, int16_t y, uint16_t *block, int16_t w, int16_t h)
{
 uint16_t ret, dummy;
 int16_t n = w * h;
 uint8_t r, g, b, tmp;
 Set_Addr_Window(x, y, x + w - 1, y + h - 1);
 while (n > 0){
   CS_ACTIVE;
   writeCmd16(RC);
   setReadDir();
   read8(r);
   while (n){
     if(R24BIT == 1){
       read8(r);
       read8(g);
       read8(b);
       ret = Color_To_565(r, g, b);
     }
       else if(R24BIT == 0){read16(ret);}
     *block++ = ret;
     n--;
   }

//  RD_IDLE;
   CS_IDLE;
   setWriteDir();
 }
 return 0;
}

I have tested the LCDWIKI method above and it also failed... I am afraid this would be a limitation of the ILI9341 IC. If someone had success reading the ILI9341 GRAM please help !!!! Thanks

I strongly recommend that you run library examples e.g. Adafruit_ILI9341, ILI9341_due, TFT_eSPI, ...

It is wise to use exactly the same wiring as suggested in the example.

If you have custom wiring, quote library example by name.
copy-paste any changed lines. e.g. #defines, constuctor(), ...

Libraries like TFT_eSPI have a User_Setup.h file and a sketch to report your configuration. Run the sketch. Copy-paste the report.

This advice applies to most Display problems. i.e. run examples, report your actual wiring, settings, ...

There is no point in writing your own sketch until you have verified that your display hardware is working with the library examples.

If you can't get the examples to work, run any diagnostics, post clear photos of your wiring.
Describe your wiring scheme e.g. TFT_CS = D10 on Uno. white wire color.

Yes, this requires effort on your part.
However you will probably get accurate answers.

David.

@AntonioTesta,

I also had problems with reading GRAM on some controllers. I never found out why.
I used the workaround to address each single pixel. See e.g. in
GxTFT/GxCTRL_ILI9341.cpp at master · ZinggJM/GxTFT · GitHub

#if defined(ILI9341_RAMRD_AUTO_INCREMENT_OK) // not ok on my display

void GxCTRL_ILI9341::readRect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t* data)
{
  uint16_t xe = x + w - 1;
  uint16_t ye = y + h - 1;
  uint32_t num = uint32_t(w) * uint32_t(h);
  IO.startTransaction();
  IO.writeCommand(ILI9341_CASET);  // Column addr set
  IO.writeData(x >> 8);
  IO.writeData(x & 0xFF);  // XSTART
  IO.writeData(xe >> 8);
  IO.writeData(xe & 0xFF); // XEND
  IO.writeCommand(ILI9341_PASET);  // Row addr set
  IO.writeData(y >> 8);
  IO.writeData(y);         // YSTART
  IO.writeData(ye >> 8);
  IO.writeData(ye);        // YEND
  IO.writeCommand(ILI9341_RAMRD);  // read from RAM
  IO.readData(); // dummy
  for (; num > 0; num--)
  {
    uint16_t g = IO.readData();
    uint16_t r = IO.readData();
    uint16_t b = IO.readData();
    *data++ = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3);
  }
  IO.endTransaction();
}

#else

void GxCTRL_ILI9341::readRect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t* data)
{
  uint16_t xe = x + w - 1;
  uint16_t ye = y + h - 1;
  for (uint16_t yy = y; yy <= ye; yy++)
  {
    for (uint16_t xx = x; xx <= xe; xx++)
    {
      IO.startTransaction();
      setWindowAddress(xx, yy, xx, yy);
      IO.writeCommand(ILI9341_RAMRD);  // read from RAM
      IO.readData(); // dummy
      uint16_t g = IO.readData();
      uint16_t r = IO.readData();
      uint16_t b = IO.readData();
      *data++ = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3);
      IO.endTransaction();
    }
  }
}

#endif

But I don't know if I have a SPI display with ILI9341.

Jean-Marc

Added:

Maybe some of these would need be used in my code:

#define ILI9341_RDMODE  0x0A
#define ILI9341_RDMADCTL  0x0B
#define ILI9341_RDPIXFMT  0x0C
#define ILI9341_RDIMGFMT  0x0A
#define ILI9341_RDSELFDIAG  0x0F

Not likely. These are to read settings, I think.

I need to check all my ILI9341 TFTs some day.

@Jean-Marc,

Yes, your readRect() code looks correct for an ILI9341. The ILI9341 always reads GRAM as 3 RGB bytes per pixel. ILI9341_RAMRD_AUTO_INCREMENT_OK should be true on an ILI9341 controller. Some earlier chips did not increment on read. All chips increment on write.

Note that most controllers are much SLOWER for RAMRD than for RAWWR.

You won't notice on the AVR because it is relatively slow. You will see it on STM32, ESP32 etc.

David.

1 Like

All ILI9341 and most modern controllers should auto-increment on read.
Older controllers like ILI9320 read the same GRAM pixel without incrementing the GRAM address.