atanisoft / esp_lcd_ili9488

esp_lcd compatible driver interface for ILI9488 displays
MIT License
16 stars 7 forks source link

About the horizontal screen display problem #16

Open SFNFIH opened 1 week ago

SFNFIH commented 1 week ago

  Hello, your driver successfully drives my LCD screen, but I encountered some problems. The screen will be distorted when switching to landscape mode.The MCU I use is ESP32S3, and the lvgl version is 8.3.

#define EXAMPLE_PIN_NUM_SCLK           14
#define EXAMPLE_PIN_NUM_MOSI           15
#define EXAMPLE_PIN_NUM_MISO           13
#define EXAMPLE_PIN_NUM_LCD_DC         16
#define EXAMPLE_PIN_NUM_LCD_RST        17
#define EXAMPLE_PIN_NUM_LCD_CS         18
#define EXAMPLE_PIN_NUM_BK_LIGHT       2
#define EXAMPLE_PIN_NUM_TOUCH_CS       15

static const int DISPLAY_HORIZONTAL_PIXELS = 320;
static const int DISPLAY_VERTICAL_PIXELS = 480;
static const int DISPLAY_COMMAND_BITS = 8;
static const int DISPLAY_PARAMETER_BITS = 8;
static const unsigned int DISPLAY_REFRESH_HZ = 40000000;
static const int DISPLAY_SPI_QUEUE_LEN = 10;
static const int SPI_MAX_TRANSFER_SIZE = 32768;

static const size_t LV_BUFFER_SIZE = DISPLAY_HORIZONTAL_PIXELS * 25;

static esp_lcd_panel_io_handle_t lcd_io_handle = NULL;
static esp_lcd_panel_handle_t lcd_handle = NULL;
static lv_disp_draw_buf_t lv_disp_buf;
static lv_disp_drv_t lv_disp_drv;
static lv_disp_t *lv_display = NULL;

static const char *TAG = "disp";

static bool 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 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;
    esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map);
}

void disp_init()
{
    ESP_LOGI(TAG, "Initializing SPI bus (MOSI:%d, MISO:%d, CLK:%d)",
             EXAMPLE_PIN_NUM_MOSI, EXAMPLE_PIN_NUM_MISO, EXAMPLE_PIN_NUM_SCLK);
    spi_bus_config_t bus =
    {
        .mosi_io_num = EXAMPLE_PIN_NUM_MOSI,
        .miso_io_num = EXAMPLE_PIN_NUM_MISO,
        .sclk_io_num = EXAMPLE_PIN_NUM_SCLK,
        .quadwp_io_num = GPIO_NUM_NC,
        .quadhd_io_num = GPIO_NUM_NC,
        .data4_io_num = GPIO_NUM_NC,
        .data5_io_num = GPIO_NUM_NC,
        .data6_io_num = GPIO_NUM_NC,
        .data7_io_num = GPIO_NUM_NC,
        .max_transfer_sz = SPI_MAX_TRANSFER_SIZE,
        .flags = SPICOMMON_BUSFLAG_SCLK | SPICOMMON_BUSFLAG_MISO |
                 SPICOMMON_BUSFLAG_MOSI | SPICOMMON_BUSFLAG_MASTER,
        .intr_flags = ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM
    };

    ESP_ERROR_CHECK(spi_bus_initialize(SPI2_HOST, &bus, SPI_DMA_CH_AUTO));

        const esp_lcd_panel_io_spi_config_t io_config = 
    {
        .cs_gpio_num = EXAMPLE_PIN_NUM_LCD_CS,
        .dc_gpio_num = EXAMPLE_PIN_NUM_LCD_DC,
        .spi_mode = 0,
        .pclk_hz = DISPLAY_REFRESH_HZ,
        .trans_queue_depth = DISPLAY_SPI_QUEUE_LEN,
        .on_color_trans_done = notify_lvgl_flush_ready,
        .user_ctx = &lv_disp_drv,
        .lcd_cmd_bits = DISPLAY_COMMAND_BITS,
        .lcd_param_bits = DISPLAY_PARAMETER_BITS,
        .flags =
        {
            .dc_low_on_data = 0,
            .octal_mode = 0,
            .sio_mode = 0,
            .lsb_first = 0,
            .cs_high_active = 0
        }
    };

    const esp_lcd_panel_dev_config_t lcd_config = 
    {
        .reset_gpio_num = EXAMPLE_PIN_NUM_LCD_RST,
        .color_space = LCD_RGB_ELEMENT_ORDER_RGB,
        .bits_per_pixel = 18,
        .flags =
        {
            .reset_active_high = 0
        },
        .vendor_config = NULL
    };

    ESP_ERROR_CHECK(
        esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)SPI2_HOST, &io_config, &lcd_io_handle)); 

    ESP_ERROR_CHECK(esp_lcd_new_panel_ili9488(lcd_io_handle, &lcd_config, LV_BUFFER_SIZE, &lcd_handle));

    ESP_ERROR_CHECK(esp_lcd_panel_reset(lcd_handle));
    ESP_ERROR_CHECK(esp_lcd_panel_init(lcd_handle));
    ESP_ERROR_CHECK(esp_lcd_panel_invert_color(lcd_handle, false));
    ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(lcd_handle, false));
    ESP_ERROR_CHECK(esp_lcd_panel_mirror(lcd_handle, false, false));
    ESP_ERROR_CHECK(esp_lcd_panel_set_gap(lcd_handle, 0, 0));

    ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(lcd_handle, true));
    ESP_LOGI(TAG, "Initializing LVGL");
    lv_init();
    // ESP_LOGI(TAG, "Allocating %zu bytes for LVGL buffer", LV_BUFFER_SIZE * sizeof(lv_color_t));
    // lv_buf_1 = (lv_color_t *)heap_caps_malloc(LV_BUFFER_SIZE * sizeof(lv_color_t), MALLOC_CAP_DMA);
    // ESP_LOGI(TAG, "Creating LVLG display buffer");
    // lv_disp_draw_buf_init(&lv_disp_buf, lv_buf_1, lv_buf_2, LV_BUFFER_SIZE);

    // 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(DISPLAY_HORIZONTAL_PIXELS * 20 * sizeof(lv_color_t), MALLOC_CAP_DMA);
    assert(buf1);
    lv_color_t *buf2 = heap_caps_malloc(DISPLAY_HORIZONTAL_PIXELS * 20 * sizeof(lv_color_t), MALLOC_CAP_DMA);
    assert(buf2);
    // initialize LVGL draw buffers
    lv_disp_draw_buf_init(&lv_disp_buf, buf1, buf2, DISPLAY_HORIZONTAL_PIXELS * 10);

    ESP_LOGI(TAG, "Initializing %dx%d display", DISPLAY_HORIZONTAL_PIXELS, DISPLAY_VERTICAL_PIXELS);
    lv_disp_drv_init(&lv_disp_drv);
    lv_disp_drv.hor_res = DISPLAY_HORIZONTAL_PIXELS;
    lv_disp_drv.ver_res = DISPLAY_VERTICAL_PIXELS;
    lv_disp_drv.flush_cb = lvgl_flush_cb;
    lv_disp_drv.draw_buf = &lv_disp_buf;
    lv_disp_drv.user_data = lcd_handle;
    lv_disp_drv.rotated = LV_DISP_ROT_NONE;
    lv_display = lv_disp_drv_register(&lv_disp_drv);

}

The above is my driver code, which is basically written according to your example。 3D63B1D59FC81D214833042B5C65FE0D 7CF707FBB18128864401E602D4F08FB3

atanisoft commented 1 week ago

I'm not sure what the exact problem might be here, but I'm guessing the data buffers are not being properly accounted for when you press the rotate button on your display. What code executes when that button is pressed? Does it simply update the lv_disp_drv.rotated = LV_DISP_ROT_NONE; value or does it also call the driver to indicate the display is also rotated (via ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(lcd_handle, false));)

SFNFIH commented 1 week ago

`static lv_obj_t meter; static lv_obj_t btn; static lv_disp_rot_t rotation = LV_DISP_ROT_NONE;

static void set_value(void *indic, int32_t v) { lv_meter_set_indicator_end_value(meter, indic, v); }

static void btn_cb(lv_event_t e) { lv_disp_t disp = lv_event_get_user_data(e); rotation++; if (rotation > LV_DISP_ROT_270) { rotation = LV_DISP_ROT_NONE; } lv_disp_set_rotation(disp, rotation); }

void example_lvgl_demo_ui() { lv_obj_t *scr = lv_scr_act(); meter = lv_meter_create(scr); lv_obj_center(meter); lv_obj_set_size(meter, 200, 200);

/*Add a scale first*/
lv_meter_scale_t *scale = lv_meter_add_scale(meter);
lv_meter_set_scale_ticks(meter, scale, 41, 2, 10, lv_palette_main(LV_PALETTE_GREY));
lv_meter_set_scale_major_ticks(meter, scale, 8, 4, 15, lv_color_black(), 10);

lv_meter_indicator_t *indic;

/*Add a blue arc to the start*/
indic = lv_meter_add_arc(meter, scale, 3, lv_palette_main(LV_PALETTE_BLUE), 0);
lv_meter_set_indicator_start_value(meter, indic, 0);
lv_meter_set_indicator_end_value(meter, indic, 20);

/*Make the tick lines blue at the start of the scale*/
indic = lv_meter_add_scale_lines(meter, scale, lv_palette_main(LV_PALETTE_BLUE), lv_palette_main(LV_PALETTE_BLUE), false, 0);
lv_meter_set_indicator_start_value(meter, indic, 0);
lv_meter_set_indicator_end_value(meter, indic, 20);

/*Add a red arc to the end*/
indic = lv_meter_add_arc(meter, scale, 3, lv_palette_main(LV_PALETTE_RED), 0);
lv_meter_set_indicator_start_value(meter, indic, 80);
lv_meter_set_indicator_end_value(meter, indic, 100);

/*Make the tick lines red at the end of the scale*/
indic = lv_meter_add_scale_lines(meter, scale, lv_palette_main(LV_PALETTE_RED), lv_palette_main(LV_PALETTE_RED), false, 0);
lv_meter_set_indicator_start_value(meter, indic, 80);
lv_meter_set_indicator_end_value(meter, indic, 100);

/*Add a needle line indicator*/
indic = lv_meter_add_needle_line(meter, scale, 4, lv_palette_main(LV_PALETTE_GREY), -10);

btn = lv_btn_create(scr);
lv_obj_t * lbl = lv_label_create(btn);
lv_label_set_text_static(lbl, LV_SYMBOL_REFRESH" ROTATE");
lv_obj_align(btn, LV_ALIGN_BOTTOM_LEFT, 30, -30);
/*Button event*/
lv_obj_add_event_cb(btn, btn_cb, LV_EVENT_CLICKED, NULL);

/*Create an animation to set the value*/
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_exec_cb(&a, set_value);
lv_anim_set_var(&a, indic);
lv_anim_set_values(&a, 0, 100);
lv_anim_set_time(&a, 2000);
lv_anim_set_repeat_delay(&a, 100);
lv_anim_set_playback_time(&a, 500);
lv_anim_set_playback_delay(&a, 100);
lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
lv_anim_start(&a);

}` I just called the UI case provided by espressif. Normally, the screen will rotate normally without any distortion.

SFNFIH commented 1 week ago

It seems that there is a problem with the screen's hardware rotation driver code. The software rotation is normal, but the refresh speed is very slow, and it is visible to the naked eye that the lines are refreshed one by one.

atanisoft commented 1 week ago

Thanks for the additional code, based on what you have provided LVGL would be preparing it's rendered image to match the initial state of the hardware and sending it over to the display as-is (a few lines at a time). If you increase the size of the buffer it may help. You might also test with 16-bit mode rather than 18-bit mode. The 18-bit mode requires processing of the pixel data before it can be sent over to the display, this may also add a small delay.

I'd also suggest using esp_lvgl_port if you aren't already.

SFNFIH commented 1 week ago

You are the second person to remind me to use it. I will accept it. Thank you.

SFNFIH commented 1 week ago

`static esp_err_t app_lcd_init(void) { esp_err_t ret = ESP_OK;

/* LCD backlight */
gpio_config_t bk_gpio_config = {
    .mode = GPIO_MODE_OUTPUT,
    .pin_bit_mask = 1ULL << EXAMPLE_LCD_GPIO_BL
};
ESP_ERROR_CHECK(gpio_config(&bk_gpio_config));

/* LCD initialization */
ESP_LOGD(TAG, "Initialize SPI bus");
const spi_bus_config_t buscfg = {
    .sclk_io_num = EXAMPLE_LCD_GPIO_SCLK,
    .mosi_io_num = EXAMPLE_LCD_GPIO_MOSI,
    .miso_io_num = GPIO_NUM_NC,
    .quadwp_io_num = GPIO_NUM_NC,
    .quadhd_io_num = GPIO_NUM_NC,
    .max_transfer_sz = EXAMPLE_LCD_H_RES * EXAMPLE_LCD_DRAW_BUFF_HEIGHT * sizeof(uint16_t),
};
ESP_RETURN_ON_ERROR(spi_bus_initialize(EXAMPLE_LCD_SPI_NUM, &buscfg, SPI_DMA_CH_AUTO), TAG, "SPI init failed");

ESP_LOGD(TAG, "Install panel IO");
const esp_lcd_panel_io_spi_config_t io_config = {
    .dc_gpio_num = EXAMPLE_LCD_GPIO_DC,
    .cs_gpio_num = EXAMPLE_LCD_GPIO_CS,
    .pclk_hz = EXAMPLE_LCD_PIXEL_CLK_HZ,
    .lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS,
    .lcd_param_bits = EXAMPLE_LCD_PARAM_BITS,
    .spi_mode = 0,
    .trans_queue_depth = 10,
};
ESP_GOTO_ON_ERROR(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)EXAMPLE_LCD_SPI_NUM, &io_config, &lcd_io), err, TAG, "New panel IO failed");

ESP_LOGD(TAG, "Install LCD driver");
const esp_lcd_panel_dev_config_t panel_config = {
    .reset_gpio_num = EXAMPLE_LCD_GPIO_RST,
    .color_space = EXAMPLE_LCD_COLOR_SPACE,
    .bits_per_pixel = EXAMPLE_LCD_BITS_PER_PIXEL,
};
ESP_GOTO_ON_ERROR(esp_lcd_new_panel_ili9488(lcd_io, &panel_config, LV_BUFFER_SIZE, &lcd_panel), err, TAG, "New panel failed");

esp_lcd_panel_reset(lcd_panel);
esp_lcd_panel_init(lcd_panel);
esp_lcd_panel_mirror(lcd_panel, true, true);
esp_lcd_panel_disp_on_off(lcd_panel, true);

/* LCD backlight on */
ESP_ERROR_CHECK(gpio_set_level(EXAMPLE_LCD_GPIO_BL, EXAMPLE_LCD_BL_ON_LEVEL));

return ret;

err: if (lcd_panel) { esp_lcd_panel_del(lcd_panel); } if (lcd_io) { esp_lcd_panel_io_del(lcd_io); } spi_bus_free(EXAMPLE_LCD_SPI_NUM); return ret; }

static esp_err_t app_lvgl_init(void) { / Initialize LVGL / const lvgl_port_cfg_t lvgl_cfg = { .task_priority = 4, / LVGL task priority / .task_stack = 4096, / LVGL task stack size / .task_affinity = -1, / LVGL task pinned to core (-1 is no affinity) / .task_max_sleep_ms = 500, / Maximum sleep in LVGL task / .timer_period_ms = 5 / LVGL timer tick period in ms / }; ESP_RETURN_ON_ERROR(lvgl_port_init(&lvgl_cfg), TAG, "LVGL port initialization failed");

/* Add LCD screen */
ESP_LOGD(TAG, "Add LCD screen");
const lvgl_port_display_cfg_t disp_cfg = {
    .io_handle = lcd_io,
    .panel_handle = lcd_panel,
    .buffer_size = EXAMPLE_LCD_H_RES * EXAMPLE_LCD_DRAW_BUFF_HEIGHT,
    .double_buffer = EXAMPLE_LCD_DRAW_BUFF_DOUBLE,
    .hres = EXAMPLE_LCD_H_RES,
    .vres = EXAMPLE_LCD_V_RES,
    .monochrome = false,
    .rotation = {
        .swap_xy = false,
        .mirror_x = true,
        .mirror_y = true,
    },
    .flags = {
        .buff_dma = true,
    }
};
lvgl_disp = lvgl_port_add_disp(&disp_cfg);

return ESP_OK;

}

void app_main(void) { ESP_ERROR_CHECK(app_lcd_init()); ESP_ERROR_CHECK(app_lvgl_init()); lvgl_demo_ui();

ESP_LOGI(TAG, "Display LVGL Meter Widget");
while(1)
{
    vTaskDelay(1000);
}

} ` I'm still a newbie, why is the screen black?

atanisoft commented 1 week ago

I'm not sure off hand why it would be blank, I'd suggest reviewing one of their samples as the only change to their example would be calling to create the display as ili9488.

Gisbert12843 commented 1 week ago

You can try to compare it to this code which is working. LVGL9.1 though and I am using indev drivers which you can simply remove.

header:

#pragma once
#include "wholeinclude.h"

#include "ui.h"

#include "esp_lcd_touch_gt911.h"
#include "esp_lcd_ili9488.h"
#include "touch.h"
#include "helper_functions.h"
#include "driver/i2c_master.h"

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// Please update the following configuration according to your LCD spec //////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// The pixel number in horizontal and vertical
// #define EXAMPLE_LCD_H_RES 320
// #define EXAMPLE_LCD_V_RES 480
#define EXAMPLE_LCD_H_RES 480
#define EXAMPLE_LCD_V_RES 320
// 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 10

#define BUFFER_SIZE EXAMPLE_LCD_H_RES * EXAMPLE_LCD_V_RES / 30

extern void ui_init(lv_obj_t *scr);

// 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
static lv_display_t *disp;

// static lv_color16_t *buf1, *buf2;

static esp_lcd_touch_handle_t tp; // LCD touch handle

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

static void lvgl_flush_cb(lv_display_t *drv, const lv_area_t *area, uint8_t *color_map)
{
    esp_lcd_panel_draw_bitmap((esp_lcd_panel_handle_t)lv_display_get_user_data(drv), area->x1, area->y1, area->x2 + 1, area->y2 + 1, color_map);
    lv_disp_flush_ready(drv);
}

static void lvgl_tick_increment_cb(void *arg)
{
    lv_tick_inc(EXAMPLE_LVGL_TICK_PERIOD_MS);
}

void tft_init(void);

cpp:

#include "tft.h"

// static SemaphoreHandle_t lvgl_mux; // LVGL mutex

#define TAG "LCD"

#define EXAMPLE_LCD_PIXEL_CLOCK_HZ (10 * 1000 * 1000)
#define EXAMPLE_LCD_I80_BUS_WIDTH 8
#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_DATA0 ((gpio_num_t)18)
#define EXAMPLE_PIN_NUM_DATA1 ((gpio_num_t)5)
#define EXAMPLE_PIN_NUM_DATA2 ((gpio_num_t)26)
#define EXAMPLE_PIN_NUM_DATA3 ((gpio_num_t)25)
#define EXAMPLE_PIN_NUM_DATA4 ((gpio_num_t)17)
#define EXAMPLE_PIN_NUM_DATA5 ((gpio_num_t)16)
#define EXAMPLE_PIN_NUM_DATA6 ((gpio_num_t)27)
#define EXAMPLE_PIN_NUM_DATA7 ((gpio_num_t)14)

#define EXAMPLE_PIN_NUM_PCLK ((gpio_num_t)4) // WR
#define EXAMPLE_PIN_NUM_CS ((gpio_num_t)33)
#define EXAMPLE_PIN_NUM_DC ((gpio_num_t)15)
#define EXAMPLE_PIN_NUM_RST ((gpio_num_t)32)
#define EXAMPLE_PIN_NUM_BK_LIGHT ((gpio_num_t)13)

#define EXAMPLE_PIN_NUM_SDA ((gpio_num_t)21)
#define EXAMPLE_PIN_NUM_SCL ((gpio_num_t)22)
#define EXAMPLE_PIN_NUM_INT ((gpio_num_t)19)
#define EXAMPLE_PIN_NUM_CRST ((gpio_num_t)23)
#define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
#define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
#define I2C_MASTER_TIMEOUT_MS 1000

void tft_init(void)
{
    ESP_LOGI(TAG, "Turn off LCD backlight");
    gpio_config_t bk_gpio_config = {
        .pin_bit_mask = 1ULL << EXAMPLE_PIN_NUM_BK_LIGHT,
        .mode = GPIO_MODE_OUTPUT,
        .pull_up_en = GPIO_PULLUP_DISABLE,
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .intr_type = GPIO_INTR_DISABLE};

    ESP_ERROR_CHECK(gpio_config(&bk_gpio_config));
    gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL);

    ESP_LOGI(TAG, "Initialize Intel 8080 bus");
    esp_lcd_i80_bus_handle_t i80_bus = NULL;
    esp_lcd_i80_bus_config_t bus_config = {
        .dc_gpio_num = EXAMPLE_PIN_NUM_DC,
        .wr_gpio_num = EXAMPLE_PIN_NUM_PCLK,
        .clk_src = LCD_CLK_SRC_DEFAULT,
        .data_gpio_nums = {
            EXAMPLE_PIN_NUM_DATA0,
            EXAMPLE_PIN_NUM_DATA1,
            EXAMPLE_PIN_NUM_DATA2,
            EXAMPLE_PIN_NUM_DATA3,
            EXAMPLE_PIN_NUM_DATA4,
            EXAMPLE_PIN_NUM_DATA5,
            EXAMPLE_PIN_NUM_DATA6,
            EXAMPLE_PIN_NUM_DATA7},
        .bus_width = EXAMPLE_LCD_I80_BUS_WIDTH,
        .max_transfer_bytes = BUFFER_SIZE * sizeof(lv_color16_t),
        .psram_trans_align = 64};

    ESP_ERROR_CHECK(esp_lcd_new_i80_bus(&bus_config, &i80_bus));

    esp_lcd_panel_io_handle_t io_handle = NULL;
    esp_lcd_panel_io_i80_config_t io_config = {
        .cs_gpio_num = EXAMPLE_PIN_NUM_CS,
        .pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,
        .trans_queue_depth = 20,
        // .on_color_trans_done = notify_lvgl_flush_ready,
        .user_ctx = disp, // this
        .lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS,
        .lcd_param_bits = EXAMPLE_LCD_PARAM_BITS,
        .dc_levels = {
            .dc_idle_level = 1,
            .dc_cmd_level = 0,
            .dc_dummy_level = 0,
            .dc_data_level = 1},
        .flags = {.cs_active_high = 0, .reverse_color_bits = 0, .swap_color_bytes = 1, .pclk_active_neg = 0, .pclk_idle_low = 0}};

    ESP_ERROR_CHECK(esp_lcd_new_panel_io_i80(i80_bus, &io_config, &io_handle));

    ESP_LOGI(TAG, "Install LCD driver of ili9488");
    esp_lcd_panel_handle_t panel_handle = NULL;
    esp_lcd_panel_dev_config_t panel_config = {
        .reset_gpio_num = EXAMPLE_PIN_NUM_RST,
        .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR,
        .bits_per_pixel = 16};

    ESP_ERROR_CHECK(esp_lcd_new_panel_ili9488(io_handle, &panel_config, BUFFER_SIZE, &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_set_gap(panel_handle, 0, 0));

    //These are here to rotate the screen to landscape mode, dont forget to alter the EXAMPLE_LCD_H_RES and EXAMPLE_LCD_V_RES to your new POV
    ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_handle, true));
    ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, false, true));

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

    helper_functions::delay(200);
    ////////////////////////////////////////////////////
    // I2C INIT
    ESP_LOGI(TAG, "INIT i2c");

    i2c_master_bus_handle_t i2c_bus = NULL;
    i2c_master_bus_config_t i2c_bus_config = {
        .i2c_port = 0,
        .sda_io_num = EXAMPLE_PIN_NUM_SDA,
        .scl_io_num = EXAMPLE_PIN_NUM_SCL,
        .clk_source = I2C_CLK_SRC_DEFAULT,

        .glitch_ignore_cnt = 7,

        .flags{.enable_internal_pullup = true},
    };
    ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_config, &i2c_bus));

    ////////////////////////////////////////////////////////////////////////
    helper_functions::delay(200);

    ESP_LOGI(TAG, "Initialize LVGL library");
    lv_init();

    static lv_color16_t *buf1 = (lv_color16_t *)heap_caps_malloc(BUFFER_SIZE * sizeof(lv_color16_t), MALLOC_CAP_DMA);
    static lv_color16_t *buf2 = (lv_color16_t *)heap_caps_malloc(BUFFER_SIZE * sizeof(lv_color16_t), MALLOC_CAP_DMA);

    ESP_LOGI(TAG, "Buffers initialized");

    disp = lv_display_create(EXAMPLE_LCD_H_RES, EXAMPLE_LCD_V_RES);
    ESP_LOGI(TAG, "Display initialized");

    lv_display_set_buffers(disp, &buf1[0], &buf2[0], BUFFER_SIZE * sizeof(lv_color16_t), LV_DISPLAY_RENDER_MODE_PARTIAL);

    ESP_LOGI(TAG, "buffers set");

    lv_display_set_flush_cb(disp, lvgl_flush_cb);
    lv_display_set_user_data(disp, panel_handle);
    lv_display_set_driver_data(disp, panel_handle);
    lv_display_set_color_format(disp, LV_COLOR_FORMAT_RGB565);

    helper_functions::delay(100);

    ////////////////////////////////////////////////////
    // INIT TOUCH
    ESP_LOGI(TAG, "INIT TOUCH");

    static lv_indev_t *indev_drv_tp = lv_indev_create();

    const esp_lcd_touch_config_t tp_cfg = {
        .x_max = EXAMPLE_LCD_H_RES,
        .y_max = EXAMPLE_LCD_V_RES,
        .rst_gpio_num = GPIO_NUM_NC,
        .int_gpio_num = EXAMPLE_PIN_NUM_INT,
        .levels = {
            .reset = 0,
            .interrupt = 0,
        },
        .flags = {
            //if altering these values do not lead to the expected results,
            // try to alter the values read in your touchpad_read() function
            //(e.g. subtract the hor_max from the x value and multiply by -1)
            .swap_xy = 1,
            .mirror_x = 0,
            .mirror_y = 0,
        },
    };

    esp_lcd_panel_io_handle_t tp_io_handle = NULL;
    esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG();
    tp_io_config.scl_speed_hz = 400000;

    ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c_v2(i2c_bus, &tp_io_config, &tp_io_handle));
    ESP_ERROR_CHECK(esp_lcd_touch_new_i2c_gt911(tp_io_handle, &tp_cfg, &tp));
    assert(tp);

    /* Register a touchpad input device */

    lv_indev_set_type(indev_drv_tp, LV_INDEV_TYPE_POINTER);
    lv_indev_set_read_cb(indev_drv_tp, touchpad_read);
    lv_indev_set_user_data(indev_drv_tp, tp);
    lv_indev_set_display(indev_drv_tp, disp);

    ////////////////////////////////////////////////////

    ESP_LOGI(TAG, "Install LVGL tick timer");
    const esp_timer_create_args_t lvgl_tick_timer_args = {
        .callback = &lvgl_tick_increment_cb,
        .arg = NULL,
        .dispatch_method = ESP_TIMER_TASK,
        .name = "lvgl_tick",
        .skip_unhandled_events = false};
    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));

    ESP_LOGI(TAG, "Display LVGL animation");
    lv_obj_t *scr = lv_disp_get_scr_act(disp);

    ui_init(scr);
}