ILI9341 + ESP IDF with LVGL

I need help, I’m trying to make this display work with ESP-IDF and LVGL but it doesn’t work.

Does anyone have an idea what it could be?

I found a lib that makes TFT work perfectly:

A lib é essa: GitHub - nopnop2002/esp-idf-ili9340: SPI TFT and XPT2046 touch screen controller driver for esp-idf

So much so that the examples work correctly, but when I use LVGL something goes wrong.

The working code:

void f_setupDisplay() {
        ESP_LOGW(TAG, "Setup Display Init");
        TFT_t dev;
        spi_master_init(&dev, CONFIG_MOSI_GPIO, CONFIG_SCLK_GPIO, CONFIG_TFT_CS_GPIO, CONFIG_DC_GPIO, 
            CONFIG_RESET_GPIO, CONFIG_BL_GPIO, -1, -1, -1, -1, -1);
        lcdInit(&dev, 0x9341, CONFIG_WIDTH, CONFIG_HEIGHT, CONFIG_OFFSETX, CONFIG_OFFSETY);

        TriangleTest(&dev, CONFIG_WIDTH, CONFIG_HEIGHT);

        // lv_init();
        // static lv_disp_draw_buf_t disp_buf;
        // static lv_color_t buf1[CONFIG_WIDTH * 30];
        // static lv_color_t buf2[CONFIG_WIDTH * 30];
        // lv_disp_draw_buf_init(&disp_buf, buf1, buf2, CONFIG_WIDTH * 10);


        // static lv_disp_drv_t disp_drv;
        // lv_disp_drv_init(&disp_drv);
        // disp_drv.hor_res = CONFIG_WIDTH;
        // disp_drv.ver_res = CONFIG_HEIGHT;
        // disp_drv.flush_cb = lvgl_flush_cb;
        // disp_drv.draw_buf = &disp_buf;
        // disp_drv.user_data = &dev;                    // Passa o handle do dispositivo para o LVGL
        // lv_disp_drv_register(&disp_drv);

        // xTaskCreate(lvgl_tick_task, "LVGL Tick", 1024, NULL, tskIDLE_PRIORITY, NULL);

        // lv_obj_t *label = lv_label_create(lv_scr_act());
        // lv_label_set_text(label, "Hello, World!");
        // lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
       

        // while (1) {
        //      lv_timer_handler(); // Processa as tarefas da LVGL
        //      vTaskDelay(pdMS_TO_TICKS(10)); // Aguarda 10ms para liberar a CPU
        // }
}

image

Don’t working code:

void f_setupDisplay() {
        ESP_LOGW(TAG, "Setup Display Init");
        TFT_t dev;
        spi_master_init(&dev, CONFIG_MOSI_GPIO, CONFIG_SCLK_GPIO, CONFIG_TFT_CS_GPIO, CONFIG_DC_GPIO, 
            CONFIG_RESET_GPIO, CONFIG_BL_GPIO, -1, -1, -1, -1, -1);
        lcdInit(&dev, 0x9341, CONFIG_WIDTH, CONFIG_HEIGHT, CONFIG_OFFSETX, CONFIG_OFFSETY);

//        TriangleTest(&dev, CONFIG_WIDTH, CONFIG_HEIGHT);

        lv_init();
        static lv_disp_draw_buf_t disp_buf;
        static lv_color_t buf1[CONFIG_WIDTH * 30];
        static lv_color_t buf2[CONFIG_WIDTH * 30];
        lv_disp_draw_buf_init(&disp_buf, buf1, buf2, CONFIG_WIDTH * 10);


        static lv_disp_drv_t disp_drv;
        lv_disp_drv_init(&disp_drv);
        disp_drv.hor_res = CONFIG_WIDTH;
        disp_drv.ver_res = CONFIG_HEIGHT;
        disp_drv.flush_cb = lvgl_flush_cb;
        disp_drv.draw_buf = &disp_buf;
        disp_drv.user_data = &dev;          //This associated the driver TFT
        lv_disp_drv_register(&disp_drv);

        xTaskCreate(lvgl_tick_task, "LVGL Tick", 1024, NULL, tskIDLE_PRIORITY, NULL);

        lv_obj_t *label = lv_label_create(lv_scr_act());
        lv_label_set_text(label, "Hello, World!");
        lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
       

        while (1) {
             lv_timer_handler(); // Crash the device ? Why ??
             vTaskDelay(pdMS_TO_TICKS(10));
        }
}

the crash print:

Please help me ? Any idea ? Another approach?

That it works with TFT SPI ILI9341 and LVGL?

I solved this code:

/*
 * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: CC0-1.0
 */

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_timer.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_ops.h"
#include "driver/gpio.h"
#include "driver/spi_master.h"
#include "esp_err.h"
#include "esp_log.h"
#include "lvgl.h"

#if CONFIG_EXAMPLE_LCD_CONTROLLER_ILI9341
#include "esp_lcd_ili9341.h"
#elif CONFIG_EXAMPLE_LCD_CONTROLLER_GC9A01
#include "esp_lcd_gc9a01.h"
#endif

static const char *TAG = "example";

// Using SPI2 in the example
#define LCD_HOST  SPI2_HOST

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// Please update the following configuration according to your LCD spec //////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define EXAMPLE_LCD_PIXEL_CLOCK_HZ     (20 * 1000 * 1000)
#define EXAMPLE_LCD_BK_LIGHT_ON_LEVEL  1
#define EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL !EXAMPLE_LCD_BK_LIGHT_ON_LEVEL
#define EXAMPLE_PIN_NUM_SCLK           14
#define EXAMPLE_PIN_NUM_MOSI           13
#define EXAMPLE_PIN_NUM_MISO           12
#define EXAMPLE_PIN_NUM_LCD_DC         2
#define EXAMPLE_PIN_NUM_LCD_RST        -1
#define EXAMPLE_PIN_NUM_LCD_CS         15
#define EXAMPLE_PIN_NUM_BK_LIGHT       27

// The pixel number in horizontal and vertical
#if CONFIG_EXAMPLE_LCD_CONTROLLER_ILI9341
#define EXAMPLE_LCD_H_RES              240
#define EXAMPLE_LCD_V_RES              320
#elif CONFIG_EXAMPLE_LCD_CONTROLLER_GC9A01
#define EXAMPLE_LCD_H_RES              240
#define EXAMPLE_LCD_V_RES              240
#endif
// Bit number used to represent command and parameter
#define EXAMPLE_LCD_CMD_BITS           8
#define EXAMPLE_LCD_PARAM_BITS         8

#define EXAMPLE_LVGL_TICK_PERIOD_MS    2
#define EXAMPLE_LVGL_TASK_MAX_DELAY_MS 500
#define EXAMPLE_LVGL_TASK_MIN_DELAY_MS 1
#define EXAMPLE_LVGL_TASK_STACK_SIZE   (4 * 1024)
#define EXAMPLE_LVGL_TASK_PRIORITY     2

static SemaphoreHandle_t lvgl_mux = NULL;

extern void example_lvgl_demo_ui(lv_disp_t *disp);

static bool example_notify_lvgl_flush_ready(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx)
{
    lv_disp_drv_t *disp_driver = (lv_disp_drv_t *)user_ctx;
    lv_disp_flush_ready(disp_driver);
    return false;
}

static void example_lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map)
{
    esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data;
    int offsetx1 = area->x1;
    int offsetx2 = area->x2;
    int offsety1 = area->y1;
    int offsety2 = area->y2;
    // copy a buffer's content to a specific area of the display
    esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map);
}

/* Rotate display when rotated screen in LVGL. Called when driver parameters are updated. */
static void example_lvgl_port_update_callback(lv_disp_drv_t *drv)
{
    esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data;

    switch (drv->rotated) {
    case LV_DISP_ROT_NONE:
        // Rotate LCD display
        esp_lcd_panel_swap_xy(panel_handle, false);
        esp_lcd_panel_mirror(panel_handle, true, false);
        break;
    case LV_DISP_ROT_90:
        // Rotate LCD display
        esp_lcd_panel_swap_xy(panel_handle, true);
        esp_lcd_panel_mirror(panel_handle, true, true);
        break;
    case LV_DISP_ROT_180:
        // Rotate LCD display
        esp_lcd_panel_swap_xy(panel_handle, false);
        esp_lcd_panel_mirror(panel_handle, false, true);
        break;
    case LV_DISP_ROT_270:
        // Rotate LCD display
        esp_lcd_panel_swap_xy(panel_handle, true);
        esp_lcd_panel_mirror(panel_handle, false, false);
        break;
    }
}

static void example_increase_lvgl_tick(void *arg)
{
    /* Tell LVGL how many milliseconds has elapsed */
    lv_tick_inc(EXAMPLE_LVGL_TICK_PERIOD_MS);
}

bool example_lvgl_lock(int timeout_ms)
{
    // Convert timeout in milliseconds to FreeRTOS ticks
    // If `timeout_ms` is set to -1, the program will block until the condition is met
    const TickType_t timeout_ticks = (timeout_ms == -1) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
    return xSemaphoreTakeRecursive(lvgl_mux, timeout_ticks) == pdTRUE;
}

void example_lvgl_unlock(void)
{
    xSemaphoreGiveRecursive(lvgl_mux);
}

static void example_lvgl_port_task(void *arg)
{
    ESP_LOGI(TAG, "Starting LVGL task");
    uint32_t task_delay_ms = EXAMPLE_LVGL_TASK_MAX_DELAY_MS;
    while (1) {
        // Lock the mutex due to the LVGL APIs are not thread-safe
        if (example_lvgl_lock(-1)) {
            task_delay_ms = lv_timer_handler();
            // Release the mutex
            example_lvgl_unlock();
        }
        if (task_delay_ms > EXAMPLE_LVGL_TASK_MAX_DELAY_MS) {
            task_delay_ms = EXAMPLE_LVGL_TASK_MAX_DELAY_MS;
        } else if (task_delay_ms < EXAMPLE_LVGL_TASK_MIN_DELAY_MS) {
            task_delay_ms = EXAMPLE_LVGL_TASK_MIN_DELAY_MS;
        }
        vTaskDelay(pdMS_TO_TICKS(task_delay_ms));
    }
}

void app_main(void)
{
    static lv_disp_draw_buf_t disp_buf; // contains internal graphic buffer(s) called draw buffer(s)
    static lv_disp_drv_t disp_drv;      // contains callback functions

    ESP_LOGI(TAG, "Turn off LCD backlight");
    gpio_config_t bk_gpio_config = {
        .mode = GPIO_MODE_OUTPUT,
        .pin_bit_mask = 1ULL << EXAMPLE_PIN_NUM_BK_LIGHT
    };
    ESP_ERROR_CHECK(gpio_config(&bk_gpio_config));

    ESP_LOGI(TAG, "Initialize SPI bus");
    spi_bus_config_t buscfg = {
        .sclk_io_num = EXAMPLE_PIN_NUM_SCLK,
        .mosi_io_num = EXAMPLE_PIN_NUM_MOSI,
        .miso_io_num = EXAMPLE_PIN_NUM_MISO,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
        .max_transfer_sz = EXAMPLE_LCD_H_RES * 80 * sizeof(uint16_t),
    };
    ESP_ERROR_CHECK(spi_bus_initialize(LCD_HOST, &buscfg, SPI_DMA_CH_AUTO));

    ESP_LOGI(TAG, "Install panel IO");
    esp_lcd_panel_io_handle_t io_handle = NULL;
    esp_lcd_panel_io_spi_config_t io_config = {
        .dc_gpio_num = EXAMPLE_PIN_NUM_LCD_DC,
        .cs_gpio_num = EXAMPLE_PIN_NUM_LCD_CS,
        .pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,
        .lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS,
        .lcd_param_bits = EXAMPLE_LCD_PARAM_BITS,
        .spi_mode = 0,
        .trans_queue_depth = 10,
        .on_color_trans_done = example_notify_lvgl_flush_ready,
        .user_ctx = &disp_drv,
    };
    // Attach the LCD to the SPI bus
    ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_HOST, &io_config, &io_handle));

    esp_lcd_panel_handle_t panel_handle = NULL;
    esp_lcd_panel_dev_config_t panel_config = {
        .reset_gpio_num = EXAMPLE_PIN_NUM_LCD_RST,
        .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR,
        .bits_per_pixel = 16,
    };
#if CONFIG_EXAMPLE_LCD_CONTROLLER_ILI9341
    ESP_LOGI(TAG, "Install ILI9341 panel driver");
    ESP_ERROR_CHECK(esp_lcd_new_panel_ili9341(io_handle, &panel_config, &panel_handle));
#elif CONFIG_EXAMPLE_LCD_CONTROLLER_GC9A01
    ESP_LOGI(TAG, "Install GC9A01 panel driver");
    ESP_ERROR_CHECK(esp_lcd_new_panel_gc9a01(io_handle, &panel_config, &panel_handle));
#endif

    ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
    ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
#if CONFIG_EXAMPLE_LCD_CONTROLLER_GC9A01
    ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, true));
#endif
    ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, true, false));

    // user can flush pre-defined pattern to the screen before we turn on the screen or backlight
    ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true));

    ESP_LOGI(TAG, "Turn on LCD backlight");
    gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, EXAMPLE_LCD_BK_LIGHT_ON_LEVEL);

    ESP_LOGI(TAG, "Initialize LVGL library");
    lv_init();
    // alloc draw buffers used by LVGL
    // it's recommended to choose the size of the draw buffer(s) to be at least 1/10 screen sized
    lv_color_t *buf1 = heap_caps_malloc(EXAMPLE_LCD_H_RES * 20 * sizeof(lv_color_t), MALLOC_CAP_DMA);
    assert(buf1);
    lv_color_t *buf2 = heap_caps_malloc(EXAMPLE_LCD_H_RES * 20 * sizeof(lv_color_t), MALLOC_CAP_DMA);
    assert(buf2);
    // initialize LVGL draw buffers
    lv_disp_draw_buf_init(&disp_buf, buf1, buf2, EXAMPLE_LCD_H_RES * 20);

    ESP_LOGI(TAG, "Register display driver to LVGL");
    lv_disp_drv_init(&disp_drv);
    disp_drv.hor_res = EXAMPLE_LCD_H_RES;
    disp_drv.ver_res = EXAMPLE_LCD_V_RES;
    disp_drv.flush_cb = example_lvgl_flush_cb;
    disp_drv.drv_update_cb = example_lvgl_port_update_callback;
    disp_drv.draw_buf = &disp_buf;
    disp_drv.user_data = panel_handle;
    lv_disp_t *disp = lv_disp_drv_register(&disp_drv);

    ESP_LOGI(TAG, "Install LVGL tick timer");
    // Tick interface for LVGL (using esp_timer to generate 2ms periodic event)
    const esp_timer_create_args_t lvgl_tick_timer_args = {
        .callback = &example_increase_lvgl_tick,
        .name = "lvgl_tick"
    };
    esp_timer_handle_t lvgl_tick_timer = NULL;
    ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));
    ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, EXAMPLE_LVGL_TICK_PERIOD_MS * 1000));

    lvgl_mux = xSemaphoreCreateRecursiveMutex();
    assert(lvgl_mux);
    ESP_LOGI(TAG, "Create LVGL task");
    xTaskCreate(example_lvgl_port_task, "LVGL", EXAMPLE_LVGL_TASK_STACK_SIZE, NULL, EXAMPLE_LVGL_TASK_PRIORITY, NULL);

    ESP_LOGI(TAG, "Display LVGL Meter Widget");
    // Lock the mutex due to the LVGL APIs are not thread-safe
    if (example_lvgl_lock(-1)) {
        example_lvgl_demo_ui(disp);
        // Release the mutex
        example_lvgl_unlock();
    }
}

and ESP COMPONENTS:

dependencies:
  atanisoft/esp_lcd_touch_xpt2046: "^1.0.4"
  idf: ">=4.4"
  lvgl/lvgl: "~8.3.0"
  esp_lcd_ili9341: "^1.0"
  esp_lcd_gc9a01: "^1.0"
  esp_lcd_touch_stmpe610: "^1.0"

image

Is don’t touch

This above code is more short code.

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_timer.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_ops.h"
#include "driver/gpio.h"
#include "driver/spi_master.h"
#include "esp_err.h"
#include "esp_log.h"
#include "lvgl.h"
#include "esp_lcd_ili9341.h"

static const char *TAG = "example";
#define LCD_HOST  SPI2_HOST
#define EXAMPLE_LCD_PIXEL_CLOCK_HZ     (20 * 1000 * 1000)
#define EXAMPLE_LCD_BK_LIGHT_ON_LEVEL  1
#define EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL !EXAMPLE_LCD_BK_LIGHT_ON_LEVEL
#define EXAMPLE_PIN_NUM_SCLK           14
#define EXAMPLE_PIN_NUM_MOSI           13
#define EXAMPLE_PIN_NUM_MISO           12
#define EXAMPLE_PIN_NUM_LCD_DC         2
#define EXAMPLE_PIN_NUM_LCD_RST        -1
#define EXAMPLE_PIN_NUM_LCD_CS         15
#define EXAMPLE_PIN_NUM_BK_LIGHT       27
#define EXAMPLE_LCD_H_RES              240
#define EXAMPLE_LCD_V_RES              320

#define EXAMPLE_LCD_CMD_BITS           8
#define EXAMPLE_LCD_PARAM_BITS         8

#define EXAMPLE_LVGL_TICK_PERIOD_MS    2
#define EXAMPLE_LVGL_TASK_MAX_DELAY_MS 500
#define EXAMPLE_LVGL_TASK_MIN_DELAY_MS 1
#define EXAMPLE_LVGL_TASK_STACK_SIZE   (4 * 1024)
#define EXAMPLE_LVGL_TASK_PRIORITY     2

static SemaphoreHandle_t lvgl_mux = NULL;
static bool flush_ready_cb(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx){
        lv_disp_drv_t *disp_driver = (lv_disp_drv_t *)user_ctx;
        lv_disp_flush_ready(disp_driver);
        return false;
}

static void flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map){
        esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data;
        int offsetx1 = area->x1;
        int offsetx2 = area->x2;
        int offsety1 = area->y1;
        int offsety2 = area->y2;
        esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map);
}

void app_main(void){
        static lv_disp_draw_buf_t disp_buf; // contains internal graphic buffer(s) called draw buffer(s)
        static lv_disp_drv_t disp_drv;      // contains callback functions
        ESP_LOGI(TAG, "Turn off LCD backlight");
        gpio_config_t bk_gpio_config = {
                .mode = GPIO_MODE_OUTPUT,
                .pin_bit_mask = 1ULL << EXAMPLE_PIN_NUM_BK_LIGHT
        };
        gpio_config(&bk_gpio_config);
        spi_bus_config_t buscfg = {
                .sclk_io_num = EXAMPLE_PIN_NUM_SCLK,
                .mosi_io_num = EXAMPLE_PIN_NUM_MOSI,
                .miso_io_num = EXAMPLE_PIN_NUM_MISO,
                .quadwp_io_num = -1,
                .quadhd_io_num = -1,
                .max_transfer_sz = EXAMPLE_LCD_H_RES * 80 * sizeof(uint16_t),
        };
        ESP_ERROR_CHECK(spi_bus_initialize(LCD_HOST, &buscfg, SPI_DMA_CH_AUTO));
        esp_lcd_panel_io_handle_t io_handle = NULL;
        esp_lcd_panel_io_spi_config_t io_config = {
                .dc_gpio_num = EXAMPLE_PIN_NUM_LCD_DC,
                .cs_gpio_num = EXAMPLE_PIN_NUM_LCD_CS,
                .pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,
                .lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS,
                .lcd_param_bits = EXAMPLE_LCD_PARAM_BITS,
                .spi_mode = 0,
                .trans_queue_depth = 10,
                .on_color_trans_done = flush_ready_cb,
                .user_ctx = &disp_drv,
        };
        esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_HOST, &io_config, &io_handle);
        esp_lcd_panel_handle_t panel_handle = NULL;
        esp_lcd_panel_dev_config_t panel_config = {
                .reset_gpio_num = EXAMPLE_PIN_NUM_LCD_RST,
                .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR,
                .bits_per_pixel = 16,
        };
        ESP_LOGI(TAG, "Install ILI9341 panel driver");
        esp_lcd_new_panel_ili9341(io_handle, &panel_config, &panel_handle);
        esp_lcd_panel_reset(panel_handle);
        esp_lcd_panel_init(panel_handle);
        esp_lcd_panel_mirror(panel_handle, true, false);
        esp_lcd_panel_disp_on_off(panel_handle, true);// user can flush pre-defined pattern to the screen before we turn on the screen or backlight
        gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, EXAMPLE_LCD_BK_LIGHT_ON_LEVEL);
        lv_init();
        lv_color_t *buf1 = heap_caps_malloc(EXAMPLE_LCD_H_RES * 20 * sizeof(lv_color_t), MALLOC_CAP_DMA);
        assert(buf1);
        lv_color_t *buf2 = heap_caps_malloc(EXAMPLE_LCD_H_RES * 20 * sizeof(lv_color_t), MALLOC_CAP_DMA);
        assert(buf2);
        lv_disp_draw_buf_init(&disp_buf, buf1, buf2, EXAMPLE_LCD_H_RES * 20);
        lv_disp_drv_init(&disp_drv);
        disp_drv.hor_res = EXAMPLE_LCD_H_RES;
        disp_drv.ver_res = EXAMPLE_LCD_V_RES;
        disp_drv.flush_cb = flush_cb;
        disp_drv.draw_buf = &disp_buf;
        disp_drv.user_data = panel_handle;
        lv_disp_t *disp = lv_disp_drv_register(&disp_drv);
        esp_timer_handle_t lvgl_tick_timer = NULL;
        esp_timer_start_periodic(lvgl_tick_timer, EXAMPLE_LVGL_TICK_PERIOD_MS * 1000);
        lvgl_mux = xSemaphoreCreateRecursiveMutex();
        assert(lvgl_mux);
        ESP_LOGI(TAG, "Create LVGL task");
        lv_obj_t *label = lv_label_create(lv_scr_act());
        lv_label_set_text(label, "Hello, LVGL!");lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
        lv_timer_handler();

}

and

yml - component

dependencies:
  atanisoft/esp_lcd_touch_xpt2046: "^1.0.4"
  idf: ">=4.4"
  lvgl/lvgl: "~8.4.0"
  esp_lcd_ili9341: "^1.0"

image

I need the help the code above is not working with xpt2046 touch driver.

In practice I need code that works on esp idf 5 with lvgl 8.4 and works on the ILI9341 driver and Touch Driver XPT2046.

I’ve been struggling for days and can’t find an example that works correctly.

@kisvegabor @kdschlosser - Please help me

I try the code, with the library for colegue:

/*
 * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: CC0-1.0
 */

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_timer.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_ops.h"
#include "driver/gpio.h"
#include "driver/spi_master.h"
#include "esp_err.h"
#include "esp_log.h"
#include "lvgl.h"

#if CONFIG_EXAMPLE_LCD_CONTROLLER_ILI9341
#include "esp_lcd_ili9341.h"
#elif CONFIG_EXAMPLE_LCD_CONTROLLER_GC9A01
#include "esp_lcd_gc9a01.h"
#endif

#if CONFIG_EXAMPLE_LCD_TOUCH_CONTROLLER_XPT2046
#include "esp_lcd_touch_XPT2046.h"
#endif

static const char *TAG = "example";

// Using SPI2 in the example
#define LCD_HOST  SPI2_HOST

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// Please update the following configuration according to your LCD spec //////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define EXAMPLE_LCD_PIXEL_CLOCK_HZ     (20 * 1000 * 1000)
#define EXAMPLE_LCD_BK_LIGHT_ON_LEVEL  1
#define EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL !EXAMPLE_LCD_BK_LIGHT_ON_LEVEL
#define EXAMPLE_PIN_NUM_SCLK           14
#define EXAMPLE_PIN_NUM_MOSI           13
#define EXAMPLE_PIN_NUM_MISO           12
#define EXAMPLE_PIN_NUM_LCD_DC         2
#define EXAMPLE_PIN_NUM_LCD_RST        -1
#define EXAMPLE_PIN_NUM_LCD_CS         15
#define EXAMPLE_PIN_NUM_BK_LIGHT       27
#define EXAMPLE_PIN_NUM_TOUCH_CS       33

// The pixel number in horizontal and vertical
#if CONFIG_EXAMPLE_LCD_CONTROLLER_ILI9341
#define EXAMPLE_LCD_H_RES              240
#define EXAMPLE_LCD_V_RES              320
#elif CONFIG_EXAMPLE_LCD_CONTROLLER_GC9A01
#define EXAMPLE_LCD_H_RES              240
#define EXAMPLE_LCD_V_RES              240
#endif
// Bit number used to represent command and parameter
#define EXAMPLE_LCD_CMD_BITS           8
#define EXAMPLE_LCD_PARAM_BITS         8

#define EXAMPLE_LVGL_TICK_PERIOD_MS    2
#define EXAMPLE_LVGL_TASK_MAX_DELAY_MS 500
#define EXAMPLE_LVGL_TASK_MIN_DELAY_MS 1
#define EXAMPLE_LVGL_TASK_STACK_SIZE   (4 * 1024)
#define EXAMPLE_LVGL_TASK_PRIORITY     2

static SemaphoreHandle_t lvgl_mux = NULL;

#if CONFIG_EXAMPLE_LCD_TOUCH_ENABLED
esp_lcd_touch_handle_t tp = NULL;
#endif

extern void example_lvgl_demo_ui(lv_disp_t *disp);

static bool example_notify_lvgl_flush_ready(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx)
{
    lv_disp_drv_t *disp_driver = (lv_disp_drv_t *)user_ctx;
    lv_disp_flush_ready(disp_driver);
    return false;
}

static void example_lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map)
{
    esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data;
    int offsetx1 = area->x1;
    int offsetx2 = area->x2;
    int offsety1 = area->y1;
    int offsety2 = area->y2;
    // copy a buffer's content to a specific area of the display
    esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map);
}

/* Rotate display and touch, when rotated screen in LVGL. Called when driver parameters are updated. */
static void example_lvgl_port_update_callback(lv_disp_drv_t *drv)
{
    esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data;

    switch (drv->rotated) {
    case LV_DISP_ROT_NONE:
        // Rotate LCD display
        esp_lcd_panel_swap_xy(panel_handle, false);
        esp_lcd_panel_mirror(panel_handle, true, false);
#if CONFIG_EXAMPLE_LCD_TOUCH_ENABLED
        // Rotate LCD touch
        esp_lcd_touch_set_mirror_y(tp, false);
        esp_lcd_touch_set_mirror_x(tp, false);
#endif
        break;
    case LV_DISP_ROT_90:
        // Rotate LCD display
        esp_lcd_panel_swap_xy(panel_handle, true);
        esp_lcd_panel_mirror(panel_handle, true, true);
#if CONFIG_EXAMPLE_LCD_TOUCH_ENABLED
        // Rotate LCD touch
        esp_lcd_touch_set_mirror_y(tp, false);
        esp_lcd_touch_set_mirror_x(tp, false);
#endif
        break;
    case LV_DISP_ROT_180:
        // Rotate LCD display
        esp_lcd_panel_swap_xy(panel_handle, false);
        esp_lcd_panel_mirror(panel_handle, false, true);
#if CONFIG_EXAMPLE_LCD_TOUCH_ENABLED
        // Rotate LCD touch
        esp_lcd_touch_set_mirror_y(tp, false);
        esp_lcd_touch_set_mirror_x(tp, false);
#endif
        break;
    case LV_DISP_ROT_270:
        // Rotate LCD display
        esp_lcd_panel_swap_xy(panel_handle, true);
        esp_lcd_panel_mirror(panel_handle, false, false);
#if CONFIG_EXAMPLE_LCD_TOUCH_ENABLED
        // Rotate LCD touch
        esp_lcd_touch_set_mirror_y(tp, false);
        esp_lcd_touch_set_mirror_x(tp, false);
#endif
        break;
    }
}

#if CONFIG_EXAMPLE_LCD_TOUCH_ENABLED
static void example_lvgl_touch_cb(lv_indev_drv_t * drv, lv_indev_data_t * data)
{
    uint16_t touchpad_x[1] = {0};
    uint16_t touchpad_y[1] = {0};
    uint8_t touchpad_cnt = 0;

    /* Read touch controller data */
    esp_lcd_touch_read_data(drv->user_data);

    /* Get coordinates */
    bool touchpad_pressed = esp_lcd_touch_get_coordinates(drv->user_data, touchpad_x, touchpad_y, NULL, &touchpad_cnt, 1);

    if (touchpad_pressed && touchpad_cnt > 0) {
        data->point.x = touchpad_x[0];
        data->point.y = touchpad_y[0];
        data->state = LV_INDEV_STATE_PRESSED;
    } else {
        data->state = LV_INDEV_STATE_RELEASED;
    }
}
#endif

static void example_increase_lvgl_tick(void *arg)
{
    /* Tell LVGL how many milliseconds has elapsed */
    lv_tick_inc(EXAMPLE_LVGL_TICK_PERIOD_MS);
}

bool example_lvgl_lock(int timeout_ms)
{
    // Convert timeout in milliseconds to FreeRTOS ticks
    // If `timeout_ms` is set to -1, the program will block until the condition is met
    const TickType_t timeout_ticks = (timeout_ms == -1) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
    return xSemaphoreTakeRecursive(lvgl_mux, timeout_ticks) == pdTRUE;
}

void example_lvgl_unlock(void)
{
    xSemaphoreGiveRecursive(lvgl_mux);
}

static void example_lvgl_port_task(void *arg)
{
    ESP_LOGI(TAG, "Starting LVGL task");
    uint32_t task_delay_ms = EXAMPLE_LVGL_TASK_MAX_DELAY_MS;
    while (1) {
        // Lock the mutex due to the LVGL APIs are not thread-safe
        if (example_lvgl_lock(-1)) {
            task_delay_ms = lv_timer_handler();
            // Release the mutex
            example_lvgl_unlock();
        }
        if (task_delay_ms > EXAMPLE_LVGL_TASK_MAX_DELAY_MS) {
            task_delay_ms = EXAMPLE_LVGL_TASK_MAX_DELAY_MS;
        } else if (task_delay_ms < EXAMPLE_LVGL_TASK_MIN_DELAY_MS) {
            task_delay_ms = EXAMPLE_LVGL_TASK_MIN_DELAY_MS;
        }
        vTaskDelay(pdMS_TO_TICKS(task_delay_ms));
    }
}

void app_main(void)
{
    static lv_disp_draw_buf_t disp_buf; // contains internal graphic buffer(s) called draw buffer(s)
    static lv_disp_drv_t disp_drv;      // contains callback functions

    ESP_LOGI(TAG, "Turn off LCD backlight");
    gpio_config_t bk_gpio_config = {
        .mode = GPIO_MODE_OUTPUT,
        .pin_bit_mask = 1ULL << EXAMPLE_PIN_NUM_BK_LIGHT
    };
    ESP_ERROR_CHECK(gpio_config(&bk_gpio_config));

    ESP_LOGI(TAG, "Initialize SPI bus");
    spi_bus_config_t buscfg = {
        .sclk_io_num = EXAMPLE_PIN_NUM_SCLK,
        .mosi_io_num = EXAMPLE_PIN_NUM_MOSI,
        .miso_io_num = EXAMPLE_PIN_NUM_MISO,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
        .max_transfer_sz = EXAMPLE_LCD_H_RES * 80 * sizeof(uint16_t),
    };
    ESP_ERROR_CHECK(spi_bus_initialize(LCD_HOST, &buscfg, SPI_DMA_CH_AUTO));

    ESP_LOGI(TAG, "Install panel IO");
    esp_lcd_panel_io_handle_t io_handle = NULL;
    esp_lcd_panel_io_spi_config_t io_config = {
        .dc_gpio_num = EXAMPLE_PIN_NUM_LCD_DC,
        .cs_gpio_num = EXAMPLE_PIN_NUM_LCD_CS,
        .pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,
        .lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS,
        .lcd_param_bits = EXAMPLE_LCD_PARAM_BITS,
        .spi_mode = 0,
        .trans_queue_depth = 10,
        .on_color_trans_done = example_notify_lvgl_flush_ready,
        .user_ctx = &disp_drv,
    };
    // Attach the LCD to the SPI bus
    ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_HOST, &io_config, &io_handle));

    esp_lcd_panel_handle_t panel_handle = NULL;
    esp_lcd_panel_dev_config_t panel_config = {
        .reset_gpio_num = EXAMPLE_PIN_NUM_LCD_RST,
        .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR,
        .bits_per_pixel = 16,
    };
#if CONFIG_EXAMPLE_LCD_CONTROLLER_ILI9341
    ESP_LOGI(TAG, "Install ILI9341 panel driver");
    ESP_ERROR_CHECK(esp_lcd_new_panel_ili9341(io_handle, &panel_config, &panel_handle));
#elif CONFIG_EXAMPLE_LCD_CONTROLLER_GC9A01
    ESP_LOGI(TAG, "Install GC9A01 panel driver");
    ESP_ERROR_CHECK(esp_lcd_new_panel_gc9a01(io_handle, &panel_config, &panel_handle));
#endif

    ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
    ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
#if CONFIG_EXAMPLE_LCD_CONTROLLER_GC9A01
    ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, true));
#endif
    ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, true, false));

    // user can flush pre-defined pattern to the screen before we turn on the screen or backlight
    ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true));

#if CONFIG_EXAMPLE_LCD_TOUCH_ENABLED
    esp_lcd_panel_io_handle_t tp_io_handle = NULL;
    esp_lcd_panel_io_spi_config_t tp_io_config = ESP_LCD_TOUCH_IO_SPI_XPT2046_CONFIG(EXAMPLE_PIN_NUM_TOUCH_CS);
    // Attach the TOUCH to the SPI bus
    ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_HOST, &tp_io_config, &tp_io_handle));

    esp_lcd_touch_config_t tp_cfg = {
        .x_max = EXAMPLE_LCD_H_RES,
        .y_max = EXAMPLE_LCD_V_RES,
        .rst_gpio_num = -1,
        .int_gpio_num = -1,
        .flags = {
            .swap_xy = 0,
            .mirror_x = 0,
            .mirror_y = 0,
        },
    };

#if CONFIG_EXAMPLE_LCD_TOUCH_CONTROLLER_XPT2046
    ESP_LOGI(TAG, "Initialize touch controller XPT2046");
    ESP_ERROR_CHECK(esp_lcd_touch_new_spi_xpt2046(tp_io_handle, &tp_cfg, &tp));
#endif // CONFIG_EXAMPLE_LCD_TOUCH_CONTROLLER_XPT2046
#endif // CONFIG_EXAMPLE_LCD_TOUCH_ENABLED

    ESP_LOGI(TAG, "Turn on LCD backlight");
    gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, EXAMPLE_LCD_BK_LIGHT_ON_LEVEL);

    ESP_LOGI(TAG, "Initialize LVGL library");
    lv_init();
    // alloc draw buffers used by LVGL
    // it's recommended to choose the size of the draw buffer(s) to be at least 1/10 screen sized
    lv_color_t *buf1 = heap_caps_malloc(EXAMPLE_LCD_H_RES * 20 * sizeof(lv_color_t), MALLOC_CAP_DMA);
    assert(buf1);
    lv_color_t *buf2 = heap_caps_malloc(EXAMPLE_LCD_H_RES * 20 * sizeof(lv_color_t), MALLOC_CAP_DMA);
    assert(buf2);
    // initialize LVGL draw buffers
    lv_disp_draw_buf_init(&disp_buf, buf1, buf2, EXAMPLE_LCD_H_RES * 20);

    ESP_LOGI(TAG, "Register display driver to LVGL");
    lv_disp_drv_init(&disp_drv);
    disp_drv.hor_res = EXAMPLE_LCD_H_RES;
    disp_drv.ver_res = EXAMPLE_LCD_V_RES;
    disp_drv.flush_cb = example_lvgl_flush_cb;
    disp_drv.drv_update_cb = example_lvgl_port_update_callback;
    disp_drv.draw_buf = &disp_buf;
    disp_drv.user_data = panel_handle;
    lv_disp_t *disp = lv_disp_drv_register(&disp_drv);

    ESP_LOGI(TAG, "Install LVGL tick timer");
    // Tick interface for LVGL (using esp_timer to generate 2ms periodic event)
    const esp_timer_create_args_t lvgl_tick_timer_args = {
        .callback = &example_increase_lvgl_tick,
        .name = "lvgl_tick"
    };
    esp_timer_handle_t lvgl_tick_timer = NULL;
    ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));
    ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, EXAMPLE_LVGL_TICK_PERIOD_MS * 1000));

#if CONFIG_EXAMPLE_LCD_TOUCH_ENABLED
    static lv_indev_drv_t indev_drv;    // Input device driver (Touch)
    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_POINTER;
    indev_drv.disp = disp;
    indev_drv.read_cb = example_lvgl_touch_cb;
    indev_drv.user_data = tp;

    lv_indev_drv_register(&indev_drv);
#endif

    lvgl_mux = xSemaphoreCreateRecursiveMutex();
    assert(lvgl_mux);
    ESP_LOGI(TAG, "Create LVGL task");
    xTaskCreate(example_lvgl_port_task, "LVGL", EXAMPLE_LVGL_TASK_STACK_SIZE, NULL, EXAMPLE_LVGL_TASK_PRIORITY, NULL);

    ESP_LOGI(TAG, "Display LVGL Meter Widget");
    // Lock the mutex due to the LVGL APIs are not thread-safe
    if (example_lvgl_lock(-1)) {
        example_lvgl_demo_ui(disp);
        // Release the mutex
        example_lvgl_unlock();
    }
}

Is don’t work!

It seems a lot of ESP support libs are involved (e.g. esp_lcd_panel) so it feel more like an ESP specific question. The UI team of ESP is very supportive and know LVGL very well. I suggest opening an issue here.

The add this components:

Buts was:

I haven’t had any success yet.

Is there any way you can help me?

I found a library that works perfectly, Display and Touch.

But I don’t know how to apply this touch driver to LVGL.

This library is work but was is not LVGL aplyed.

This library is work ILI9341 but was is not input driver XPT2046

I need the library aplyed with LVGL and ILI9341 and XPT2046

I solved, but SPI_HOST is SAME TOUCH AND LCD

Link for the project:

esp32-2432S028(ILI9341+XPT2046).zip (71.8 KB)

I use the code for repository:

And modify this touch.c and hardware files:

#pragma once

#define LCD_H_RES          240
#define LCD_V_RES          320
#define LCD_BITS_PIXEL     16
#define LCD_BUF_LINES      30
#define LCD_DOUBLE_BUFFER  1
#define LCD_DRAWBUF_SIZE   (LCD_H_RES * LCD_BUF_LINES)

//#ifdef CYD_ILI9341
#define LCD_MIRROR_X       (true)
#define LCD_MIRROR_Y       (false)
// #else
// #define LCD_MIRROR_X       (false)
// #define LCD_MIRROR_Y       (false)
// #endif

#define LCD_PIXEL_CLOCK_HZ (40 * 1000 * 1000)
#define LCD_CMD_BITS       (8)
#define LCD_PARAM_BITS     (8)
#define LCD_SPI_HOST       SPI2_HOST
#define LCD_SPI_CLK        (gpio_num_t) GPIO_NUM_14
#define LCD_SPI_MOSI       (gpio_num_t) GPIO_NUM_13
#define LCD_SPI_MISO       (gpio_num_t) GPIO_NUM_12
#define LCD_DC             (gpio_num_t) GPIO_NUM_2
#define LCD_CS             (gpio_num_t) GPIO_NUM_15
#define LCD_RESET          (gpio_num_t) GPIO_NUM_4
#define LCD_BUSY           (gpio_num_t) GPIO_NUM_NC

#define LCD_BACKLIGHT      (gpio_num_t) GPIO_NUM_27
#define LCD_BACKLIGHT_LEDC_CH  (1)

#define TOUCH_X_RES_MIN 0
#define TOUCH_X_RES_MAX 240
#define TOUCH_Y_RES_MIN 0
#define TOUCH_Y_RES_MAX 320
/*
#define TOUCH_CLOCK_HZ ESP_LCD_TOUCH_SPI_CLOCK_HZ
#define TOUCH_SPI      SPI3_HOST
#define TOUCH_SPI_CLK  (gpio_num_t) GPIO_NUM_25
#define TOUCH_SPI_MOSI (gpio_num_t) GPIO_NUM_32
#define TOUCH_SPI_MISO (gpio_num_t) GPIO_NUM_39
#define TOUCH_CS       (gpio_num_t) GPIO_NUM_33
#define TOUCH_DC       (gpio_num_t) GPIO_NUM_NC
#define TOUCH_RST      (gpio_num_t) GPIO_NUM_NC
#define TOUCH_IRQ      (gpio_num_t) GPIO_NUM_NC // GPIO_NUM_36, XPT driver is working better (for me) without IRQ 
*/


#define TOUCH_CLOCK_HZ ESP_LCD_TOUCH_SPI_CLOCK_HZ
#define TOUCH_SPI      SPI3_HOST
// #define TOUCH_SPI_CLK  (gpio_num_t) GPIO_NUM_NC
// #define TOUCH_SPI_MOSI (gpio_num_t) GPIO_NUM_NC
// #define TOUCH_SPI_MISO (gpio_num_t) GPIO_NUM_NC
#define TOUCH_CS       (gpio_num_t) GPIO_NUM_33
#define TOUCH_DC       (gpio_num_t) GPIO_NUM_NC
#define TOUCH_RST      (gpio_num_t) GPIO_NUM_NC
#define TOUCH_IRQ      (gpio_num_t) GPIO_NUM_NC /* GPIO_NUM_36, XPT driver is working better (for me) without IRQ */



#define TOUCH_MIRROR_X (true)
#define TOUCH_MIRROR_Y (false)

and

Touch:

#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/semphr.h>
#include <esp_log.h>
#include <esp_err.h>
#include <esp_timer.h>
#include <esp_lcd_touch.h>
#include <esp_lcd_touch_xpt2046.h>
#include <driver/spi_master.h>
#include <driver/gpio.h>

#include "hardware.h"
#include "touch.h"

static uint16_t map(uint16_t n, uint16_t in_min, uint16_t in_max, uint16_t out_min, uint16_t out_max){
    uint16_t value = (n - in_min) * (out_max - out_min) / (in_max - in_min);
    return (value < out_min) ? out_min : ((value > out_max) ? out_max : value);
}

static void process_coordinates(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num){
    *x = map(*x, TOUCH_X_RES_MIN, TOUCH_X_RES_MAX, 0, LCD_H_RES);
    *y = map(*y, TOUCH_Y_RES_MIN, TOUCH_Y_RES_MAX, 0, LCD_V_RES);
}

esp_err_t touch_init(esp_lcd_touch_handle_t *tp){
        esp_lcd_panel_io_handle_t tp_io_handle = NULL;
        const esp_lcd_panel_io_spi_config_t tp_io_config = { .cs_gpio_num = TOUCH_CS,
                            .dc_gpio_num = TOUCH_DC,
                            .spi_mode = 0,
                            .pclk_hz = TOUCH_CLOCK_HZ,
                            .trans_queue_depth = 3,
                            .on_color_trans_done = NULL,
                            .user_ctx = NULL,
                            .lcd_cmd_bits = 8,
                            .lcd_param_bits = 8,
                            .flags = { .dc_low_on_data = 0, .octal_mode = 0, .sio_mode = 0, .lsb_first = 0, .cs_high_active = 0 } };
        esp_lcd_touch_config_t tp_cfg = {.x_max = LCD_H_RES,
                            .y_max = LCD_V_RES,
                            .rst_gpio_num = TOUCH_RST,
                            .int_gpio_num = TOUCH_IRQ,
                            .levels = {.reset = 0, .interrupt = 0},
                            .flags =
                                {
                                    .swap_xy = false,
                                    .mirror_x = TOUCH_MIRROR_X,
                                    .mirror_y = TOUCH_MIRROR_Y
                                },
                            .process_coordinates = process_coordinates,
                            .interrupt_callback = NULL};

        ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_SPI_HOST, &tp_io_config, &tp_io_handle));
        ESP_ERROR_CHECK(esp_lcd_touch_new_spi_xpt2046(tp_io_handle, &tp_cfg, tp));

        return ESP_OK;
}

I’m having trouble getting this functional example to work perfectly in LVGL 8.4

Because this example is ready for LVGL 9.0

Can I use squarelinestudio to generate codes for LVGL 9.0?

Is this peaceful?

@kisvegabor

It’s great that you have found a solution. I’m sorry that I couldn’t help much. :frowning:

According to SquareLine’s announcements now they support LVGL v9. Note that we consider SquareLine as a 3rd party UI editor provider and we have no influance on its behaviour, roadmap, etc.

1 Like

The problem is that Squareline Studio is very expensive. Although it has a good free option, the paid version was far from the reality of my company.

I solved,

The solutions code:

lcd.c

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_timer.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_ops.h"
#include "driver/gpio.h"
#include "driver/spi_master.h"
#include "esp_err.h"
#include "esp_log.h"
#include "lvgl.h"
#include "nvs_flash.h"
#include "esp_lcd_ili9341.h"
#include "esp_lcd_touch_xpt2046.h"
#include "touch.h"
#include "hardware.h"
#include "lcd.h"
#include "ui.h"

static const char *TAG = "LCD:";
esp_lcd_touch_handle_t tp_Handle;
static SemaphoreHandle_t lvgl_mux = NULL;


static bool flush_ready_cb(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx){
        lv_disp_drv_t *disp_driver = (lv_disp_drv_t *)user_ctx;
        lv_disp_flush_ready(disp_driver);
        return false;
}

static void flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map){
        esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data;
        int offsetx1 = area->x1;
        int offsetx2 = area->x2;
        int offsety1 = area->y1;
        int offsety2 = area->y2;
        esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map);
}

static void touch_cb(lv_indev_drv_t * drv, lv_indev_data_t * data){
        uint16_t touchpad_x[1] = {0};
        uint16_t touchpad_y[1] = {0};
        uint8_t touchpad_cnt = 0;
        esp_lcd_touch_read_data(drv->user_data);
        bool touchpad_pressed = esp_lcd_touch_get_coordinates(drv->user_data, touchpad_x, touchpad_y, NULL, &touchpad_cnt, 1);
        if (touchpad_pressed && touchpad_cnt > 0) {
            data->point.x = touchpad_x[0];
            data->point.y = touchpad_y[0];
            data->state = LV_INDEV_STATE_PRESSED;
        } else {
            data->state = LV_INDEV_STATE_RELEASED;
        }
}
static void example_increase_lvgl_tick(void *arg){
        lv_tick_inc(EXAMPLE_LVGL_TICK_PERIOD_MS);
}

bool example_lvgl_lock(int timeout_ms){
        const TickType_t timeout_ticks = (timeout_ms == -1) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
        return xSemaphoreTakeRecursive(lvgl_mux, timeout_ticks) == pdTRUE;
}
void example_lvgl_unlock(void){
    xSemaphoreGiveRecursive(lvgl_mux);
}

static void port_task(void *arg){
        ESP_LOGI(TAG, "Starting LVGL task");
        uint32_t task_delay_ms = EXAMPLE_LVGL_TASK_MAX_DELAY_MS;
        while (1) {
            if (example_lvgl_lock(-1)) {
                task_delay_ms = lv_timer_handler();
                example_lvgl_unlock();
            }
            if (task_delay_ms > EXAMPLE_LVGL_TASK_MAX_DELAY_MS) {
                task_delay_ms = EXAMPLE_LVGL_TASK_MAX_DELAY_MS;
            } else if (task_delay_ms < EXAMPLE_LVGL_TASK_MIN_DELAY_MS) {
                task_delay_ms = EXAMPLE_LVGL_TASK_MIN_DELAY_MS;
            }
            vTaskDelay(pdMS_TO_TICKS(task_delay_ms));
        }
}

void f_setupDisplay(){
            static lv_disp_draw_buf_t disp_buf; // contains internal graphic buffer(s) called draw buffer(s)
            static lv_disp_drv_t disp_drv;      // contains callback functions
            ESP_LOGI(TAG, "Turn off LCD backlight");
            gpio_config_t bk_gpio_config = {.mode = GPIO_MODE_OUTPUT,.pin_bit_mask = 1ULL << EXAMPLE_PIN_NUM_BK_LIGHT};
            ESP_ERROR_CHECK(gpio_config(&bk_gpio_config));
            ESP_LOGI(TAG, "Initialize SPI bus");
            spi_bus_config_t buscfg = {
                    .sclk_io_num = EXAMPLE_PIN_NUM_SCLK,
                    .mosi_io_num = EXAMPLE_PIN_NUM_MOSI,
                    .miso_io_num = EXAMPLE_PIN_NUM_MISO,
                    .quadwp_io_num = -1,
                    .quadhd_io_num = -1,
                    .max_transfer_sz = CONFIG_LCD_HRES * 80 * sizeof(uint16_t),
            };
            ESP_ERROR_CHECK(spi_bus_initialize(LCD_HOST, &buscfg, SPI_DMA_CH_AUTO));
            ESP_LOGI(TAG, "Install panel IO");
            esp_lcd_panel_io_handle_t io_handle = NULL;
            esp_lcd_panel_io_spi_config_t io_config = {
                    .dc_gpio_num = EXAMPLE_PIN_NUM_LCD_DC,
                    .cs_gpio_num = EXAMPLE_PIN_NUM_LCD_CS,
                    .pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,
                    .lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS,
                    .lcd_param_bits = EXAMPLE_LCD_PARAM_BITS,
                    .spi_mode = 0,
                    .trans_queue_depth = 10,
                    .on_color_trans_done = flush_ready_cb,
                    .user_ctx = &disp_drv,
            };
            ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_HOST, &io_config, &io_handle));
            esp_lcd_panel_handle_t panel_handle = NULL;
            esp_lcd_panel_dev_config_t panel_config = {
                    .reset_gpio_num = EXAMPLE_PIN_NUM_LCD_RST,
                    .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR,
                    .bits_per_pixel = 16,
            };
            ESP_LOGI(TAG, "Install ILI9341 panel driver");
            ESP_ERROR_CHECK(esp_lcd_new_panel_ili9341(io_handle, &panel_config, &panel_handle));
            ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
            ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
            ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, true, false));
            ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true));
            tp_Handle = InitTouchPanel();
            ESP_LOGI(TAG, "Turn on LCD backlight");
            gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, EXAMPLE_LCD_BK_LIGHT_ON_LEVEL);
            ESP_LOGI(TAG, "Initialize LVGL library");
            lv_init();
            // alloc draw buffers used by LVGL
            // it's recommended to choose the size of the draw buffer(s) to be at least 1/10 screen sized
            lv_color_t *buf1 = heap_caps_malloc(CONFIG_LCD_HRES * 20 * sizeof(lv_color_t), MALLOC_CAP_DMA);
            assert(buf1);
            lv_color_t *buf2 = heap_caps_malloc(CONFIG_LCD_HRES * 20 * sizeof(lv_color_t), MALLOC_CAP_DMA);
            assert(buf2);
            lv_disp_draw_buf_init(&disp_buf, buf1, buf2, CONFIG_LCD_HRES * 20);
            ESP_LOGI(TAG, "Register display driver to LVGL");
            lv_disp_drv_init(&disp_drv);
            disp_drv.hor_res = CONFIG_LCD_HRES;
            disp_drv.ver_res = CONFIG_LCD_VRES;
            disp_drv.flush_cb = flush_cb;
            disp_drv.draw_buf = &disp_buf;
            disp_drv.user_data = panel_handle;
            lv_disp_t *disp = lv_disp_drv_register(&disp_drv);
            ESP_LOGI(TAG, "Install LVGL tick timer");
            const esp_timer_create_args_t lvgl_tick_timer_args = {.callback = &example_increase_lvgl_tick,.name = "lvgl_tick"};
            esp_timer_handle_t lvgl_tick_timer = NULL;
            ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));
            ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, EXAMPLE_LVGL_TICK_PERIOD_MS * 1000));
            static lv_indev_drv_t indev_drv;    // Input device driver (Touch)
            lv_indev_drv_init(&indev_drv);
            indev_drv.type = LV_INDEV_TYPE_POINTER;
            indev_drv.disp = disp;
            indev_drv.read_cb = touch_cb;
            indev_drv.user_data = tp_Handle;
            lv_indev_drv_register(&indev_drv);
            lvgl_mux = xSemaphoreCreateRecursiveMutex();
            assert(lvgl_mux);
            ESP_LOGI(TAG, "Create LVGL task");

            xTaskCreate(port_task, "LVGL", EXAMPLE_LVGL_TASK_STACK_SIZE, NULL, EXAMPLE_LVGL_TASK_PRIORITY, NULL);

            ui_init();
            

}

touch.c

#include <stdio.h>
#include <stdlib.h>
#include "esp_log.h"
#include "driver/spi_master.h"
#include "esp_lcd_panel_ops.h"
#include "esp_lcd_touch_xpt2046.h"
#include "touch.h"
#include "hardware.h"
#include "nvs_flash.h"

#define TAG "TouchPanel"

static touch_calibration_t touch_calibration = {0, CONFIG_LCD_HRES, 0, CONFIG_LCD_VRES};
esp_lcd_touch_handle_t InitTouchPanel(void);

void calibrate_touch_screen(esp_lcd_touch_handle_t tp) {
        uint16_t x[1], y[1], strength[1];
        uint8_t point_num = 0;
        uint16_t x_sum = 0, y_sum = 0;
        const int calibration_points = 5;
        uint16_t x_min, y_min, x_max, y_max;  // Declaração de x_min, y_min, x_max, e y_max
        ESP_LOGI(TAG, "Calibrating touch screen...");
        ESP_LOGI(TAG, "Touch the top-left corner %d times", calibration_points);
        x_sum = 0;
        y_sum = 0;
        for (int i = 0; i < calibration_points; i++) {
            point_num = 0;
            while (point_num == 0) {
                esp_lcd_touch_read_data(tp);
                if (esp_lcd_touch_get_coordinates(tp, x, y, strength, &point_num, 1)) {
                    x_sum += x[0];
                    y_sum += y[0];
                    ESP_LOGI(TAG, "Sample %d: X: %" PRIu16 " Y: %" PRIu16, i + 1, x[0], y[0]);
                }
                vTaskDelay(pdMS_TO_TICKS(100));
            }
            vTaskDelay(pdMS_TO_TICKS(500));  // Pequena pausa para o próximo toque
        }
        x_min = x_sum / calibration_points;
        y_min = y_sum / calibration_points;
        ESP_LOGI(TAG, "Averaged top-left X: %" PRIu16 " Y: %" PRIu16, x_min, y_min);
        ESP_LOGI(TAG, "Touch the top-right corner %d times", calibration_points);
        x_sum = 0;
        y_sum = 0;
        for (int i = 0; i < calibration_points; i++) {
            point_num = 0;
            while (point_num == 0) {
                esp_lcd_touch_read_data(tp);
                if (esp_lcd_touch_get_coordinates(tp, x, y, strength, &point_num, 1)) {
                    x_sum += x[0];
                    y_sum += y[0];
                    ESP_LOGI(TAG, "Sample %d: X: %" PRIu16 " Y: %" PRIu16, i + 1, x[0], y[0]);
                }
                vTaskDelay(pdMS_TO_TICKS(100));
            }
            vTaskDelay(pdMS_TO_TICKS(500));
        }
        x_max = x_sum / calibration_points;
        ESP_LOGI(TAG, "Averaged top-right X: %" PRIu16 " Y: %" PRIu16, x_max, y_min);
        ESP_LOGI(TAG, "Touch the bottom-left corner %d times", calibration_points);
        x_sum = 0;
        y_sum = 0;
        for (int i = 0; i < calibration_points; i++) {
            point_num = 0;
            while (point_num == 0) {
                esp_lcd_touch_read_data(tp);
                if (esp_lcd_touch_get_coordinates(tp, x, y, strength, &point_num, 1)) {
                    x_sum += x[0];
                    y_sum += y[0];
                    ESP_LOGI(TAG, "Sample %d: X: %" PRIu16 " Y: %" PRIu16, i + 1, x[0], y[0]);
                }
                vTaskDelay(pdMS_TO_TICKS(100));
            }
            vTaskDelay(pdMS_TO_TICKS(500));
        }
        y_max = y_sum / calibration_points;  // Corrigido: y_max agora é declarado corretamente
        ESP_LOGI(TAG, "Averaged bottom-left X: %" PRIu16 " Y: %" PRIu16, x_min, y_max);
        ESP_LOGI(TAG, "Touch the bottom-right corner %d times", calibration_points);
        x_sum = 0;
        y_sum = 0;
        for (int i = 0; i < calibration_points; i++) {
            point_num = 0;
            while (point_num == 0) {
                esp_lcd_touch_read_data(tp);
                if (esp_lcd_touch_get_coordinates(tp, x, y, strength, &point_num, 1)) {
                    x_sum += x[0];
                    y_sum += y[0];
                    ESP_LOGI(TAG, "Sample %d: X: %" PRIu16 " Y: %" PRIu16, i + 1, x[0], y[0]);
                }
                vTaskDelay(pdMS_TO_TICKS(100));
            }
            vTaskDelay(pdMS_TO_TICKS(500));
        }
        ESP_LOGI(TAG, "Averaged bottom-right X: %" PRIu16 " Y: %" PRIu16, x_max, y_max);
        touch_calibration.x_min = x_min;
        touch_calibration.y_min = y_min;
        touch_calibration.x_max = x_max;
        touch_calibration.y_max = y_max;
        ESP_LOGI(TAG, "Touch screen calibration complete.");
}



uint16_t map(uint16_t n, uint16_t in_min, uint16_t in_max, uint16_t out_min, uint16_t out_max){
        uint16_t value = (n - in_min) * (out_max - out_min) / (in_max - in_min);
        return (value < out_min) ? out_min : ((value > out_max) ? out_max : value);
}

void process_coordinates(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num) {
        *x = map(*x, touch_calibration.x_min, touch_calibration.x_max, 0, CONFIG_LCD_HRES);
        *y = map(*y, touch_calibration.y_min, touch_calibration.y_max, 0, CONFIG_LCD_VRES);
}


esp_lcd_touch_handle_t InitTouchPanel(void){
        esp_lcd_panel_io_handle_t tp_io_handle = NULL;
        static esp_lcd_touch_handle_t tp = NULL;

        const esp_lcd_panel_io_spi_config_t tp_io_config = { .cs_gpio_num = CONFIG_TOUCH_CS,
            .dc_gpio_num = CONFIG_TOUCH_DC,
            .spi_mode = 0,
            .pclk_hz = CONFIG_TOUCH_CLOCK_HZ,
            .trans_queue_depth = 3,
            .on_color_trans_done = NULL,
            .user_ctx = NULL,
            .lcd_cmd_bits = 8,
            .lcd_param_bits = 8,
            .flags = { .dc_low_on_data = 0, .octal_mode = 0, .sio_mode = 0, .lsb_first = 0, .cs_high_active = 0 } };
        esp_lcd_touch_config_t tp_cfg = {.x_max = CONFIG_LCD_HRES,
                                    .y_max = CONFIG_LCD_VRES,
                                    .rst_gpio_num = CONFIG_TOUCH_RST,
                                    .int_gpio_num = CONFIG_TOUCH_IRQ,
                                    .levels = {.reset = 0, .interrupt = 0},
                                    .flags ={
                                            .swap_xy = 0,
                                            .mirror_x = 0,
                                            .mirror_y = 0,
                                        },
                                    .process_coordinates = process_coordinates,
                                    .interrupt_callback = NULL};
        ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)CONFIG_TOUCH_SPI, &tp_io_config, &tp_io_handle));
        ESP_ERROR_CHECK(esp_lcd_touch_new_spi_xpt2046(tp_io_handle, &tp_cfg, &tp));
        ESP_LOGI(TAG, "Touch Inicializado");

        InitTouchPanelWithCalibration(tp);

        return tp;
}

void InitTouchPanelWithCalibration(esp_lcd_touch_handle_t tp) {
        if (!load_calibration_data(&touch_calibration)) {
            ESP_LOGI(TAG, "Calibrating touch screen...");
            calibrate_touch_screen(tp);
            save_calibration_data(&touch_calibration);
        } else {
            ESP_LOGI(TAG, "Calibration data loaded from NVS");
        }
}

void save_calibration_data(touch_calibration_t *calibration) {
        nvs_handle_t nvs_handle;
        esp_err_t err = nvs_open("storage", NVS_READWRITE, &nvs_handle);
        if (err != ESP_OK) {
            ESP_LOGE(TAG, "Error opening NVS handle!");
            return;
        }

        err = nvs_set_u16(nvs_handle, "x_min", calibration->x_min);
        if (err != ESP_OK) ESP_LOGE(TAG, "Failed to save x_min");

        err = nvs_set_u16(nvs_handle, "x_max", calibration->x_max);
        if (err != ESP_OK) ESP_LOGE(TAG, "Failed to save x_max");

        err = nvs_set_u16(nvs_handle, "y_min", calibration->y_min);
        if (err != ESP_OK) ESP_LOGE(TAG, "Failed to save y_min");

        err = nvs_set_u16(nvs_handle, "y_max", calibration->y_max);
        if (err != ESP_OK) ESP_LOGE(TAG, "Failed to save y_max");

        err = nvs_commit(nvs_handle);
        if (err != ESP_OK) {
            ESP_LOGE(TAG, "Failed to commit changes!");
        }

        nvs_close(nvs_handle);
}

bool load_calibration_data(touch_calibration_t *calibration) {
        nvs_handle_t nvs_handle;
        esp_err_t err = nvs_open("storage", NVS_READONLY, &nvs_handle);
        if (err != ESP_OK) {
            ESP_LOGE(TAG, "Error opening NVS handle!");
            return false;
        }

        err = nvs_get_u16(nvs_handle, "x_min", &calibration->x_min);
        if (err != ESP_OK) {
            ESP_LOGE(TAG, "Failed to load x_min");
            nvs_close(nvs_handle);
            return false;
        }

        err = nvs_get_u16(nvs_handle, "x_max", &calibration->x_max);
        if (err != ESP_OK) {
            ESP_LOGE(TAG, "Failed to load x_max");
            nvs_close(nvs_handle);
            return false;
        }

        err = nvs_get_u16(nvs_handle, "y_min", &calibration->y_min);
        if (err != ESP_OK) {
            ESP_LOGE(TAG, "Failed to load y_min");
            nvs_close(nvs_handle);
            return false;
        }

        err = nvs_get_u16(nvs_handle, "y_max", &calibration->y_max);
        if (err != ESP_OK) {
            ESP_LOGE(TAG, "Failed to load y_max");
            nvs_close(nvs_handle);
            return false;
        }

        nvs_close(nvs_handle);
        return true;
}

hardware.h

#pragma once

#define CONFIG_TOUCH_CLOCK_HZ ESP_LCD_TOUCH_SPI_CLOCK_HZ
#define CONFIG_TOUCH_SPI      SPI2_HOST
#define CONFIG_TOUCH_CS       (gpio_num_t) GPIO_NUM_33
#define CONFIG_TOUCH_DC       (gpio_num_t) GPIO_NUM_NC
#define CONFIG_TOUCH_RST      (gpio_num_t) GPIO_NUM_NC
#define CONFIG_TOUCH_IRQ      (gpio_num_t) GPIO_NUM_36 /* GPIO_NUM_36 */

// #define TOUCH_Y_RES_MIN 15
// #define TOUCH_Y_RES_MAX 218

#define CONFIG_LCD_HRES     240
#define CONFIG_LCD_VRES     320

//---------------------------------------------------------------------------------------------------

#define LCD_HOST  SPI2_HOST
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// Please update the following configuration according to your LCD spec //////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define EXAMPLE_LCD_PIXEL_CLOCK_HZ     (20 * 1000 * 1000)
#define EXAMPLE_LCD_BK_LIGHT_ON_LEVEL  1
#define EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL !EXAMPLE_LCD_BK_LIGHT_ON_LEVEL
#define EXAMPLE_PIN_NUM_SCLK           14
#define EXAMPLE_PIN_NUM_MOSI           13
#define EXAMPLE_PIN_NUM_MISO           12
#define EXAMPLE_PIN_NUM_LCD_DC         2
#define EXAMPLE_PIN_NUM_LCD_RST        -1
#define EXAMPLE_PIN_NUM_LCD_CS         15
#define EXAMPLE_PIN_NUM_BK_LIGHT       27
#define EXAMPLE_PIN_NUM_TOUCH_CS       33

#define EXAMPLE_LCD_CMD_BITS           8
#define EXAMPLE_LCD_PARAM_BITS         8

#define EXAMPLE_LVGL_TICK_PERIOD_MS    2
#define EXAMPLE_LVGL_TASK_MAX_DELAY_MS 500
#define EXAMPLE_LVGL_TASK_MIN_DELAY_MS 1
#define EXAMPLE_LVGL_TASK_STACK_SIZE   (4 * 1024)
#define EXAMPLE_LVGL_TASK_PRIORITY     2


and

idf_component.yml

dependencies:
  idf: ">=4.4"
  lvgl/lvgl: "~8.3.0"
  esp_lcd_ili9341: "^1.0"
  esp_lcd_gc9a01: "^1.0"
  esp_lcd_touch_stmpe610: "^1.0"
  atanisoft/esp_lcd_touch_xpt2046: "~1.0.1"