#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.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 "esp_err.h"
#include "esp_log.h"
#include "lvgl.h"

static const char *TAG = "example";

#define SW true


#define EXAMPLE_LCD_PIXEL_CLOCK_HZ     (2 * 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_DATA0          6
#define EXAMPLE_PIN_NUM_DATA1          7
#define EXAMPLE_PIN_NUM_DATA2          8
#define EXAMPLE_PIN_NUM_DATA3          9
#define EXAMPLE_PIN_NUM_DATA4          10
#define EXAMPLE_PIN_NUM_DATA5          11
#define EXAMPLE_PIN_NUM_DATA6          12
#define EXAMPLE_PIN_NUM_DATA7          13
#define EXAMPLE_PIN_NUM_DATA8          14
#define EXAMPLE_PIN_NUM_DATA9          15
#define EXAMPLE_PIN_NUM_DATA10         16
#define EXAMPLE_PIN_NUM_DATA11         17
#define EXAMPLE_PIN_NUM_DATA12         18
#define EXAMPLE_PIN_NUM_DATA13         19
#define EXAMPLE_PIN_NUM_DATA14         20
#define EXAMPLE_PIN_NUM_DATA15         21
#define EXAMPLE_PIN_NUM_PCLK           5
#define EXAMPLE_PIN_NUM_CS             3
#define EXAMPLE_PIN_NUM_DC             4
#define EXAMPLE_PIN_NUM_RST            2
#define EXAMPLE_PIN_NUM_BK_LIGHT       1

// The pixel number in horizontal and vertical
#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           16
#define EXAMPLE_LCD_PARAM_BITS         16

#define EXAMPLE_LVGL_TICK_PERIOD_MS    2

// Supported alignment: 16, 32, 64. A higher alignment can enables higher burst transfer size, thus a higher i80 bus throughput.
#define EXAMPLE_PSRAM_DATA_ALIGNMENT   64


// SW START
#define GPIO_OUTPUT_PIN_SEL ((1ULL << EXAMPLE_PIN_NUM_DATA0) | \
                             (1ULL << EXAMPLE_PIN_NUM_DATA1) | \
                             (1ULL << EXAMPLE_PIN_NUM_DATA2) | \
                             (1ULL << EXAMPLE_PIN_NUM_DATA3) | \
                             (1ULL << EXAMPLE_PIN_NUM_DATA4) | \
                             (1ULL << EXAMPLE_PIN_NUM_DATA5) | \
                             (1ULL << EXAMPLE_PIN_NUM_DATA6) | \
                             (1ULL << EXAMPLE_PIN_NUM_DATA7) | \
                             (1ULL << EXAMPLE_PIN_NUM_DATA8) | \
                             (1ULL << EXAMPLE_PIN_NUM_DATA9) | \
                             (1ULL << EXAMPLE_PIN_NUM_DATA10) | \
                             (1ULL << EXAMPLE_PIN_NUM_DATA11) | \
                             (1ULL << EXAMPLE_PIN_NUM_DATA12) | \
                             (1ULL << EXAMPLE_PIN_NUM_DATA13) | \
                             (1ULL << EXAMPLE_PIN_NUM_DATA14) | \
                             (1ULL << EXAMPLE_PIN_NUM_DATA15) | \
                             (1ULL << EXAMPLE_PIN_NUM_PCLK) |  \
                             (1ULL << EXAMPLE_PIN_NUM_CS) |    \
                             (1ULL << EXAMPLE_PIN_NUM_DC) |    \
                             (1ULL << EXAMPLE_PIN_NUM_RST) |   \
                             (1ULL << EXAMPLE_PIN_NUM_BK_LIGHT))

void Lcd_Write_Bus(uint16_t VH) {
    gpio_set_level(EXAMPLE_PIN_NUM_PCLK, 0);

    if (VH & 0x0001 << 0) {
        gpio_set_level(EXAMPLE_PIN_NUM_DATA0, 1);
    } else {
        gpio_set_level(EXAMPLE_PIN_NUM_DATA0, 0);
    }
    if (VH & 0x0001 << 1) {
        gpio_set_level(EXAMPLE_PIN_NUM_DATA1, 1);
    } else {
        gpio_set_level(EXAMPLE_PIN_NUM_DATA1, 0);
    }
    if (VH & 0x0001 << 2) {
        gpio_set_level(EXAMPLE_PIN_NUM_DATA2, 1);
    } else {
        gpio_set_level(EXAMPLE_PIN_NUM_DATA2, 0);
    }
    if (VH & 0x0001 << 3) {
        gpio_set_level(EXAMPLE_PIN_NUM_DATA3, 1);
    } else {
        gpio_set_level(EXAMPLE_PIN_NUM_DATA3, 0);
    }
    if (VH & 0x0001 << 4) {
        gpio_set_level(EXAMPLE_PIN_NUM_DATA4, 1);
    } else {
        gpio_set_level(EXAMPLE_PIN_NUM_DATA4, 0);
    }
    if (VH & 0x0001 << 5) {
        gpio_set_level(EXAMPLE_PIN_NUM_DATA5, 1);
    } else {
        gpio_set_level(EXAMPLE_PIN_NUM_DATA5, 0);
    }
    if (VH & 0x0001 << 6) {
        gpio_set_level(EXAMPLE_PIN_NUM_DATA6, 1);
    } else {
        gpio_set_level(EXAMPLE_PIN_NUM_DATA6, 0);
    }
    if (VH & 0x0001 << 7) {
        gpio_set_level(EXAMPLE_PIN_NUM_DATA7, 1);
    } else {
        gpio_set_level(EXAMPLE_PIN_NUM_DATA7, 0);
    }
    if (VH & 0x0001 << 8) {
        gpio_set_level(EXAMPLE_PIN_NUM_DATA8, 1);
    } else {
        gpio_set_level(EXAMPLE_PIN_NUM_DATA8, 0);
    }
    if (VH & 0x0001 << 9) {
        gpio_set_level(EXAMPLE_PIN_NUM_DATA9, 1);
    } else {
        gpio_set_level(EXAMPLE_PIN_NUM_DATA9, 0);
    }
    if (VH & 0x0001 << 10) {
        gpio_set_level(EXAMPLE_PIN_NUM_DATA10, 1);
    } else {
        gpio_set_level(EXAMPLE_PIN_NUM_DATA10, 0);
    }
    if (VH & 0x0001 << 11)
    {
        gpio_set_level(EXAMPLE_PIN_NUM_DATA11, 1);
    } else {
        gpio_set_level(EXAMPLE_PIN_NUM_DATA11, 0);
    }

    if (VH & 0x0001 << 12) {
        gpio_set_level(EXAMPLE_PIN_NUM_DATA12, 1);
    } else {
        gpio_set_level(EXAMPLE_PIN_NUM_DATA12, 0);
    }

    if (VH & 0x0001 << 13) {
        gpio_set_level(EXAMPLE_PIN_NUM_DATA13, 1);
    } else {
        gpio_set_level(EXAMPLE_PIN_NUM_DATA13, 0);
    }

    if (VH & 0x0001 << 14) {
        gpio_set_level(EXAMPLE_PIN_NUM_DATA14, 1);
    } else {
        gpio_set_level(EXAMPLE_PIN_NUM_DATA14, 0);
    }

    if (VH & 0x0001 << 15) {
        gpio_set_level(EXAMPLE_PIN_NUM_DATA15, 1);
    } else {
        gpio_set_level(EXAMPLE_PIN_NUM_DATA15, 0);
    }

    gpio_set_level(EXAMPLE_PIN_NUM_PCLK, 1);
}

void WriteComm(uint16_t VH) {
    gpio_set_level(EXAMPLE_PIN_NUM_DC, 0);
    Lcd_Write_Bus(VH);
}

void WriteData(uint16_t VH) {
    gpio_set_level(EXAMPLE_PIN_NUM_DC, 1);
    Lcd_Write_Bus(VH);
}

void Address_set(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2) {
    WriteComm(0x2a);
    WriteData(x1 >> 8);
    WriteData(x1);
    WriteData(x2 >> 8);
    WriteData(x2);
    WriteComm(0x2b);
    WriteData(y1 >> 8);
    WriteData(y1);
    WriteData(y2 >> 8);
    WriteData(y2);
    WriteComm(0x2c);
}

void Lcd_Init(void) {
    gpio_config_t io_conf = {};
    io_conf.intr_type = GPIO_INTR_DISABLE;
    io_conf.mode = GPIO_MODE_OUTPUT;
    io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL;
    io_conf.pull_down_en = 0;
    io_conf.pull_up_en = 0;
    gpio_config(&io_conf);

    gpio_set_level(EXAMPLE_PIN_NUM_RST, 0);
    vTaskDelay(pdMS_TO_TICKS(120));
    gpio_set_level(EXAMPLE_PIN_NUM_RST, 1);
    vTaskDelay(pdMS_TO_TICKS(120));

    gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, 1);

    WriteComm(0xB0);
    WriteData(0x04);

    WriteComm(0x36);    
    WriteData(0x00);    

    WriteComm(0x3A);    
    WriteData(0x55);   

    WriteComm(0xB4);    
    WriteData(0x00); 

    WriteComm(0xC0);
    WriteData(0x03);//0013
    WriteData(0xDF);//480
    WriteData(0x40);
    WriteData(0x12);
    WriteData(0x00);
    WriteData(0x01);
    WriteData(0x00);
    WriteData(0x43);

    WriteComm(0xC1);//frame frequency
    WriteData(0x05);//BCn,DIVn[1:0
    WriteData(0x2f);//RTNn[4:0] 
    WriteData(0x08);// BPn[7:0]
    WriteData(0x08);// FPn[7:0]
    WriteData(0x00);

    WriteComm(0xC4);
    WriteData(0x63);
    WriteData(0x00);
    WriteData(0x08);
    WriteData(0x08);

    // WriteComm(0xC8);//Gamma
    // WriteData(0x06);
    // WriteData(0x0c);
    // WriteData(0x16);
    // WriteData(0x24);//26
    // WriteData(0x30);//32 
    // WriteData(0x48);
    // WriteData(0x3d);
    // WriteData(0x28);
    // WriteData(0x20);
    // WriteData(0x14);
    // WriteData(0x0c);
    // WriteData(0x04);
    // WriteData(0x06);
    // WriteData(0x0c);
    // WriteData(0x16);
    // WriteData(0x24);
    // WriteData(0x30);
    // WriteData(0x48);
    // WriteData(0x3d);
    // WriteData(0x28);
    // WriteData(0x20);
    // WriteData(0x14);
    // WriteData(0x0c);
    // WriteData(0x04);

    // WriteComm(0xC9);//Gamma
    // WriteData(0x06);
    // WriteData(0x0c);
    // WriteData(0x16);
    // WriteData(0x24);//26
    // WriteData(0x30);//32 
    // WriteData(0x48);
    // WriteData(0x3d);
    // WriteData(0x28);
    // WriteData(0x20);
    // WriteData(0x14);
    // WriteData(0x0c);
    // WriteData(0x04);
    // WriteData(0x06);
    // WriteData(0x0c);
    // WriteData(0x16);
    // WriteData(0x24);
    // WriteData(0x30);
    // WriteData(0x48);
    // WriteData(0x3d);
    // WriteData(0x28);
    // WriteData(0x20);
    // WriteData(0x14);
    // WriteData(0x0c);
    // WriteData(0x04);

    // WriteComm(0xCA);//Gamma
    // WriteData(0x06);
    // WriteData(0x0c);
    // WriteData(0x16);
    // WriteData(0x24);//26
    // WriteData(0x30);//32 
    // WriteData(0x48);
    // WriteData(0x3d);
    // WriteData(0x28);
    // WriteData(0x20);
    // WriteData(0x14);
    // WriteData(0x0c);
    // WriteData(0x04);
    // WriteData(0x06);
    // WriteData(0x0c);
    // WriteData(0x16);
    // WriteData(0x24);
    // WriteData(0x30);
    // WriteData(0x48);
    // WriteData(0x3d);
    // WriteData(0x28);
    // WriteData(0x20);
    // WriteData(0x14);
    // WriteData(0x0c);
    // WriteData(0x04);

    WriteComm(0xD0);
    WriteData(0x95);
    WriteData(0x06);
    WriteData(0x08);
    WriteData(0x10);
    WriteData(0x3f);

    WriteComm(0xD1);
    WriteData(0x02);
    WriteData(0x28);
    WriteData(0x28);
    WriteData(0x40);

    WriteComm(0xE1);    
    WriteData(0x00);    
    WriteData(0x00);    
    WriteData(0x00);    
    WriteData(0x00);    
    WriteData(0x00);   
    WriteData(0x00);   

    WriteComm(0xE2);    
    WriteData(0x80);    

    WriteComm(0x2A);    
    WriteData(0x00);    
    WriteData(0x00);    
    WriteData(0x01);    
    WriteData(0x3F);    

    WriteComm(0x2B);    
    WriteData(0x00);    
    WriteData(0x00);    
    WriteData(0x01);    
    WriteData(0xDF);    

    WriteComm(0x11);

    vTaskDelay(pdMS_TO_TICKS(120));

    WriteComm(0x29);

    WriteComm(0xC1);//frame frequency
    WriteData(0x05);//BCn,DIVn[1:0
    WriteData(0x2f);//RTNn[4:0] 
    WriteData(0x08);// BPn[7:0]
    WriteData(0x08);// FPn[7:0]
    WriteData(0x00);
    WriteComm(0x20);

    WriteComm(0x36);    
    WriteData(0x68);
}
// SW END


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;

#ifndef SW
    // 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);
#else
    unsigned int i, m, y, x;

    Address_set(offsetx1, offsety1, offsetx2 , offsety2);

    for(y = area->y1; y <= area->y2; y++) {
        for(x = area->x1; x <= area->x2; x++) {
            WriteData( color_map->full );
            color_map++;
        }
    }

    lv_disp_flush_ready(drv);
#endif
}

static void example_increase_lvgl_tick(void *arg)
{
    /* Tell LVGL how many milliseconds has elapsed */
    lv_tick_inc(EXAMPLE_LVGL_TICK_PERIOD_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

#ifdef SW
    Lcd_Init();

    goto lvgl;
#endif

    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));
    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 = {
        .clk_src = LCD_CLK_SRC_DEFAULT,
        .dc_gpio_num = EXAMPLE_PIN_NUM_DC,
        .wr_gpio_num = EXAMPLE_PIN_NUM_PCLK,
        .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,
            EXAMPLE_PIN_NUM_DATA8,
            EXAMPLE_PIN_NUM_DATA9,
            EXAMPLE_PIN_NUM_DATA10,
            EXAMPLE_PIN_NUM_DATA11,
            EXAMPLE_PIN_NUM_DATA12,
            EXAMPLE_PIN_NUM_DATA13,
            EXAMPLE_PIN_NUM_DATA14,
            EXAMPLE_PIN_NUM_DATA15,
        },
        .bus_width = 16,
        .max_transfer_bytes = EXAMPLE_LCD_H_RES * 100 * sizeof(uint16_t),
        .psram_trans_align = EXAMPLE_PSRAM_DATA_ALIGNMENT,
        .sram_trans_align = 4,
    };
    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 = 10,
        .dc_levels = {
            .dc_idle_level = 0,
            .dc_cmd_level = 0,
            .dc_dummy_level = 0,
            .dc_data_level = 1,
        },
        .flags = {
            .swap_color_bytes = !LV_COLOR_16_SWAP, // Swap can be done in LvGL (default) or DMA
        },
        .on_color_trans_done = example_notify_lvgl_flush_ready,
        .user_ctx = &disp_drv,
        .lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS,
        .lcd_param_bits = EXAMPLE_LCD_PARAM_BITS,
    };
    ESP_ERROR_CHECK(esp_lcd_new_panel_io_i80(i80_bus, &io_config, &io_handle));

    esp_lcd_panel_handle_t panel_handle = NULL;

    ESP_LOGI(TAG, "Install LCD driver of ili9341 (st7789 compatible)");
    esp_lcd_panel_dev_config_t panel_config = {
        .reset_gpio_num = EXAMPLE_PIN_NUM_RST,
        .rgb_endian = LCD_RGB_ENDIAN_BGR,
        .bits_per_pixel = 16,
    };
    ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(io_handle, &panel_config, &panel_handle));

    esp_lcd_panel_reset(panel_handle);
    //esp_lcd_panel_init(panel_handle);

    esp_lcd_panel_io_tx_param(io_handle, 0xB0, (uint8_t[]) { 0x04 }, 1);

    // WriteComm(0x36);    
    // WriteData(0x00);

    esp_lcd_panel_io_tx_param(io_handle, 0x36, (uint8_t[]) { 0x00 }, 1);

    esp_lcd_panel_io_tx_param(io_handle, 0x3A, (uint8_t[]) { 0x55 }, 1);

    esp_lcd_panel_io_tx_param(io_handle, 0xB4, (uint8_t[]) { 0x00 }, 1);

    esp_lcd_panel_io_tx_param(io_handle, 0xC0, (uint8_t[]) { 0x03, 0xDF, 0x40, 0x12, 0x00, 0x01, 0x00, 0x43 }, 8);

    esp_lcd_panel_io_tx_param(io_handle, 0xC1, (uint8_t[]) { 0x05, 0x2F, 0x08, 0x08, 0x00 }, 5);

    esp_lcd_panel_io_tx_param(io_handle, 0xC4, (uint8_t[]) { 0x63, 0x00, 0x08, 0x08 }, 4);

    //esp_lcd_panel_io_tx_param(io_handle, 0xC8, (uint8_t[]) {    0x06, 0x0c, 0x16, 0x24, 0x30, 0x48, 0x3d, 0x28, 0x20, 0x14, 0x0c, 0x04,
    //                                                            0x06, 0x0c, 0x16, 0x24, 0x30, 0x48, 0x3d, 0x28, 0x20, 0x14, 0x0c, 0x04 }, 24);

    //esp_lcd_panel_io_tx_param(io_handle, 0xC9, (uint8_t[]) {    0x06, 0x0c, 0x16, 0x24, 0x30, 0x48, 0x3d, 0x28, 0x20, 0x14, 0x0c, 0x04,
    //                                                            0x06, 0x0c, 0x16, 0x24, 0x30, 0x48, 0x3d, 0x28, 0x20, 0x14, 0x0c, 0x04 }, 24);

    //esp_lcd_panel_io_tx_param(io_handle, 0xCA, (uint8_t[]) {    0x06, 0x0c, 0x16, 0x24, 0x30, 0x48, 0x3d, 0x28, 0x20, 0x14, 0x0c, 0x04,
    //                                                            0x06, 0x0c, 0x16, 0x24, 0x30, 0x48, 0x3d, 0x28, 0x20, 0x14, 0x0c, 0x04 }, 24);

    esp_lcd_panel_io_tx_param(io_handle, 0xD0, (uint8_t[]) { 0x95, 0x06, 0x08, 0x10, 0x3F }, 5);

    esp_lcd_panel_io_tx_param(io_handle, 0xD1, (uint8_t[]) { 0x02, 0x28, 0x28, 0x40 }, 4);

    esp_lcd_panel_io_tx_param(io_handle, 0xE1, (uint8_t[]) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 6);

    esp_lcd_panel_io_tx_param(io_handle, 0xE2, (uint8_t[]) { 0x80 }, 1);

    esp_lcd_panel_io_tx_param(io_handle, 0x2A, (uint8_t[]) { 0x00, 0x00, 0x01, 0x3F }, 4);

    esp_lcd_panel_io_tx_param(io_handle, 0x2B, (uint8_t[]) { 0x00, 0x00, 0x01, 0xDF }, 4);
    
    esp_lcd_panel_io_tx_param(io_handle, 0x11, NULL, 0);

    vTaskDelay(pdMS_TO_TICKS(120));

    esp_lcd_panel_io_tx_param(io_handle, 0x29, NULL, 0);

    esp_lcd_panel_io_tx_param(io_handle, 0xC1, (uint8_t[]) { 0x05, 0x2F, 0x08, 0x08, 0x00 }, 5);

    esp_lcd_panel_io_tx_param(io_handle, 0x20, NULL, 0);

    esp_lcd_panel_io_tx_param(io_handle, 0x36, (uint8_t[]) { 0x68 }, 1);

    // 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);

#ifdef SW
lvgl:
#endif

    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 = NULL;
    lv_color_t *buf2 = NULL;
#if CONFIG_EXAMPLE_LCD_I80_COLOR_IN_PSRAM
    buf1 = heap_caps_aligned_alloc(EXAMPLE_PSRAM_DATA_ALIGNMENT, EXAMPLE_LCD_H_RES * 100 * sizeof(lv_color_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
#else
    buf1 = heap_caps_malloc(EXAMPLE_LCD_H_RES * 100 * sizeof(lv_color_t), MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
#endif
    assert(buf1);
#if CONFIG_EXAMPLE_LCD_I80_COLOR_IN_PSRAM
    buf2 = heap_caps_aligned_alloc(EXAMPLE_PSRAM_DATA_ALIGNMENT, EXAMPLE_LCD_H_RES * 100 * sizeof(lv_color_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
#else
    buf2 = heap_caps_malloc(EXAMPLE_LCD_H_RES * 100 * sizeof(lv_color_t), MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
#endif
    assert(buf2);
    ESP_LOGI(TAG, "buf1@%p, buf2@%p", buf1, buf2);
    // initialize LVGL draw buffers
    lv_disp_draw_buf_init(&disp_buf, buf1, buf2, EXAMPLE_LCD_H_RES * 100);

    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.draw_buf = &disp_buf;
#ifndef SW
    disp_drv.user_data = panel_handle;
#endif
    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));

    ESP_LOGI(TAG, "Display LVGL animation");
    example_lvgl_demo_ui(disp);

    while (1) {
        // raise the task priority of LVGL and/or reduce the handler period can improve the performance
        vTaskDelay(pdMS_TO_TICKS(10));
        // The task running lv_timer_handler should have lower priority than that running `lv_tick_inc`
        lv_timer_handler();
    }
}
