Adafruit ILI9341 & HW SPI

What MCU/Processor/Board and compiler are you using?

I´m using ESP32 with Arduino IDE.

What LVGL version are you using?

I´m using LVGL library and LVGL examples (form Library manager) versin 7.10.0

What do you want to achieve?

I would like to use ILI9341 LCD with HW SPI.

What have you tried so far?

I can run it with SW SPI, but LCD is very slo.

Code to reproduce

Add a code snippet which can run in the simulator. It should contain only the relevant code that compiles without errors when separated from your main code base.

The code block(s) should be formatted like:

#include <lvgl.h>
#include <lv_examples.h>
#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"

#define _cs   5  // goes to TFT CS
#define _dc   21  // goes to TFT DC
#define _mosi 23  // goes to TFT MOSI
#define _sclk 18  // goes to TFT SCK/CLK
#define _rst  -1   // goes to TFT RESET
#define _miso 19

// If using the breakout, change pins as desired
//Adafruit_ILI9341 tft = Adafruit_ILI9341(_cs, _dc, _mosi, _sclk, _rst, _miso); //SW works
Adafruit_ILI9341 tft = Adafruit_ILI9341(_cs, _dc, _rst); //HW does not work

static lv_disp_buf_t disp_buf;
static lv_color_t buf[LV_HOR_RES_MAX * 10];

#if USE_LV_LOG != 0
/* Serial debugging */
void my_print(lv_log_level_t level, const char * file, uint32_t line, const char * dsc)
{

  Serial.printf("%s@%d->%s\r\n", file, line, dsc);
  Serial.flush();
}
#endif

/* Display flushing */
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
  uint32_t w = (area->x2 - area->x1 + 1);
  uint32_t h = (area->y2 - area->y1 + 1);
  uint32_t wh = w * h; //for Adafruit library

  tft.startWrite();
  tft.setAddrWindow(area->x1, area->y1, w, h);
  while (wh--) tft.pushColor(color_p++->full); //for Adafruit library
  tft.endWrite();

  lv_disp_flush_ready(disp);
}

void setup()
{
  Serial.begin(115200); /* prepare for possible serial debug */

  lv_init();

#if USE_LV_LOG != 0
  lv_log_register_print_cb(my_print); /* register print function for debugging */
#endif

  tft.begin(); /* TFT init */
  tft.setRotation(1); /* Landscape orientation */

  lv_disp_buf_init(&disp_buf, buf, NULL, LV_HOR_RES_MAX * 10);

  /*Initialize the display*/
  lv_disp_drv_t disp_drv;
  lv_disp_drv_init(&disp_drv);
  disp_drv.hor_res = 320;
  disp_drv.ver_res = 240;
  disp_drv.flush_cb = my_disp_flush;
  disp_drv.buffer = &disp_buf;
  lv_disp_drv_register(&disp_drv);

  /* Try an example from the lv_examples repository
     https://github.com/lvgl/lv_examples */
  //lv_demo_widgets();
  lv_demo_stress();
}

void loop()
{
  lv_task_handler(); /* let the GUI do its work */
  delay(5);
}

When I upload code with call for HW SPI, I get just this at serial monitor and it´s look to me as that main loop is not working.

image
If I upload code with call for SW SPI, screen is working but framerate is very slow.

Have anyone idea what I could try to make it work with HW SPI?

Many thanks for help.

Try with TFT_eSPI library, set the pins of the display in the User_Setup.h, this sketch works with your display:

#include <lvgl.h>
#include <Ticker.h>
#include <TFT_eSPI.h>

TFT_eSPI tft = TFT_eSPI(); /* TFT instance*/
uint16_t calData[5] = { x, x, x, x, x }; //your tft cal


#define LVGL_TICK_PERIOD 30
Ticker tick; /* timer for interrupt handler */

static lv_disp_buf_t disp_buf;
static lv_color_t buf[LV_HOR_RES_MAX * 10];

int screenWidth = 320;
int screenHeight = 240;

#if USE_LV_LOG != 0
/* Serial debugging */
void my_print(lv_log_level_t level, const char * file, uint32_t line, const char * dsc)
{

    Serial.printf("%s@%d->%s\r\n", file, line, dsc);
    Serial.flush();
}
#endif

/* Display flushing */
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
    uint32_t w = (area->x2 - area->x1 + 1);
    uint32_t h = (area->y2 - area->y1 + 1);

    tft.startWrite();
    tft.setAddrWindow(area->x1, area->y1, w, h);
    tft.pushColors(&color_p->full, w * h, true);
    tft.endWrite();

    lv_disp_flush_ready(disp);
}



bool my_touchpad_read(lv_indev_drv_t * indev_driver, lv_indev_data_t * data)
{
    uint16_t touchX, touchY;

    bool touched = tft.getTouch(&touchX, &touchY, 600);

    if(!touched)
    {
      data->state = LV_INDEV_STATE_REL;
      return false;
    }
    else
    {
      data->state = LV_INDEV_STATE_PR;
    }

    if(touchX>screenWidth || touchY > screenHeight)
    {
      Serial.println("Y or y outside of expected parameters..");
      Serial.print("y:");
      Serial.print(touchX);
      Serial.print(" x:");
      Serial.print(touchY);
    }
    else
    {
      /*Set the coordinates*/
      data->point.x = touchX;
      data->point.y = touchY;
  
      Serial.print("Data x");
      Serial.println(touchX);
      
      Serial.print("Data y");
      Serial.println(touchY);

    }

    return false; /*Return `false` because we are not buffering and no more data to read*/
}

static void lv_tick_handler(void)
{
  lv_tick_inc(LVGL_TICK_PERIOD);
}

void setup()
{

    lv_init();

    
#if USE_LV_LOG != 0
    lv_log_register_print_cb(my_print); /* register print function for debugging */
#endif

    tft.begin(); /* TFT init */
    tft.setRotation(3); /* Landscape orientation */

    tft.setTouch(calData);
    screenWidth  = tft.width();
    screenHeight = tft.height();
  
    lv_disp_buf_init(&disp_buf, buf, NULL, LV_HOR_RES_MAX * 10);

    /*Initialize the display*/
    lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);
    disp_drv.hor_res = 320;
    disp_drv.ver_res = 240;
    disp_drv.flush_cb = my_disp_flush;
    disp_drv.buffer = &disp_buf;
    lv_disp_drv_register(&disp_drv);

    lv_indev_drv_t indev_drv;
    lv_indev_drv_init(&indev_drv);             /*Descriptor of a input device driver*/
    indev_drv.type = LV_INDEV_TYPE_POINTER;    /*Touch pad is a pointer-like device*/
    indev_drv.read_cb = my_touchpad_read;      /*Set your driver function*/
    lv_indev_drv_register(&indev_drv);         /*Finally register the driver*/

  /*Initialize the graphics library's tick*/
  tick.attach_ms(LVGL_TICK_PERIOD, lv_tick_handler);
    

}

void loop()
{
    lv_task_handler ();
    delay(5);
    
}
1 Like

Thx, but I do not want to use TFT_eSPI :wink: I know that this woudl work with it, but that is not goal of this. I want to make it work with library from Adafruit, as it is easy to setup and less confusing for begginers than TFT_eSPI.

It’s easier if you follow the steps of the official documentation.

Documentation

Regards.

Where we would be if we everytime choose just the easier path? :wink: Is there any problem to try something else?

1 Like

Don’t fall in Reinvent the wheel

Regards

Ada fruit libraries and SPI are picky when it comes to the pins they are using. Are you a 1000% sure you connecting to the pins adafruit recommends and you are using that?

  1. I had little issue using TFT_espi because I found it easer, much better examples!
  2. I don’t always like the Adafruit libraries because sometimes they have to much magic in them, or they are to much geared towards the Arduino way of working (no thread usage in mind, using delays(xx) , incorrect initialisation of hardware… I don’t blame them though, for the community they to very good work.
2 Likes
  1. It is working with SW SPI so pins are OK, otherwise it would not work at all. I did make it work (TFT_eSPi), but if you want to use it with different board and screens, editing config outside of sketch every time is not good workflow (at least for me). I use same pins, same Adafruit library(HW SPI setup) in other project and there is no issue with library or pins.

  2. Yes, they are not perfect, but they do work (mostly) and they are easy to setup. One think which hold me back to use LVGL is that it is not easy to read for most of the beginners and that will (as well as me in the past) scare them and they will not use this library. I think it is bit sad that this awesome library can be used by much less people than other ones over the net. I´m not complaining, but I´m trying to find way to show that this library can be used with libraries which you already know.

I´m not programmer but I do not think that problem is with Adafruit library (otherwise would not work in SW SPI setup) but there will be something with HW SPI and LVGL, but that is just my guess.

SPI is slow than 16 bit parallel and even slower than 8 bits parallel, and it’s even slower when it’s uses software SPI than hardware SPI. Your code suggests that you are using software SPI:

Adafruit_ILI9341 tft = Adafruit_ILI9341(_cs, _dc, _rst); //HW does not work

If you have an oscilloscope, look at the SPI clk output and see what kind of bit transfer frequency you get.

1 Like

As you can see from the log, there is probably other issue than SPI speed. How could speed of SPI stop main loop when using HW SPI in sketch?

I return to this issue again and by logging on which line I´m a did find place where code stop:

image

Problem is with code at line 407 (in this picture):

Can anyone help me with this please?

Does it reach your flush_cb?

Yes

image

Solved by @ats3788 here :heart: