#include "lcd/lcd.h" #include "driver/gpio.h" #include "driver/i2c_master.h" #include "esp_check.h" #include "esp_idf_version.h" #include "esp_lcd_panel_io.h" #include "esp_lcd_panel_ops.h" #include "esp_lcd_panel_rgb.h" #include "esp_lcd_touch_gt911.h" #include "esp_log.h" #include "esp_lvgl_port.h" #define LCD_DATA_WIDTH (16) #define LCD_RGB_BUFFER_NUMS (2) #define LCD_DRAW_BUFF_HEIGHT (100) #define LCD_LVGL_DIRECT_MODE (0) #define LCD_LVGL_AVOID_TEAR (0) #define LCD_RGB_BOUNCE_BUFFER_MODE (1) #define LCD_RGB_BOUNCE_BUFFER_HEIGHT (10) #define LCD_LVGL_ROTATION (LV_DISPLAY_ROTATION_90) #define LCD_GPIO_VSYNC (GPIO_NUM_41) #define LCD_GPIO_HSYNC (GPIO_NUM_39) #define LCD_GPIO_DE (GPIO_NUM_40) #define LCD_GPIO_PCLK (GPIO_NUM_0) #define LCD_GPIO_DISP (GPIO_NUM_NC) #define LCD_GPIO_BACKLIGHT (GPIO_NUM_2) #define LCD_BACKLIGHT_ON_LEVEL (1) #define LCD_GPIO_DATA0 (GPIO_NUM_8) #define LCD_GPIO_DATA1 (GPIO_NUM_3) #define LCD_GPIO_DATA2 (GPIO_NUM_46) #define LCD_GPIO_DATA3 (GPIO_NUM_9) #define LCD_GPIO_DATA4 (GPIO_NUM_1) #define LCD_GPIO_DATA5 (GPIO_NUM_5) #define LCD_GPIO_DATA6 (GPIO_NUM_6) #define LCD_GPIO_DATA7 (GPIO_NUM_7) #define LCD_GPIO_DATA8 (GPIO_NUM_15) #define LCD_GPIO_DATA9 (GPIO_NUM_16) #define LCD_GPIO_DATA10 (GPIO_NUM_4) #define LCD_GPIO_DATA11 (GPIO_NUM_45) #define LCD_GPIO_DATA12 (GPIO_NUM_48) #define LCD_GPIO_DATA13 (GPIO_NUM_47) #define LCD_GPIO_DATA14 (GPIO_NUM_21) #define LCD_GPIO_DATA15 (GPIO_NUM_14) #define TOUCH_I2C_NUM (1) #define TOUCH_I2C_SCL (GPIO_NUM_20) #define TOUCH_I2C_SDA (GPIO_NUM_19) #define TOUCH_RST (GPIO_NUM_NC) #define TOUCH_INT (GPIO_NUM_NC) #define LCD_PANEL_RGB_TIMING() \ { \ .pclk_hz = 12 * 1000 * 1000, \ .h_res = LCD_H_RES, \ .v_res = LCD_V_RES, \ .hsync_pulse_width = 4, \ .hsync_front_porch = 8, \ .hsync_back_porch = 43, \ .vsync_pulse_width = 4, \ .vsync_front_porch = 8, \ .vsync_back_porch = 12, \ .flags.pclk_active_neg = true, \ } static const char *TAG = "lcd"; static esp_lcd_panel_handle_t lcd_panel; static esp_lcd_touch_handle_t touch_handle; static lv_display_t *lvgl_display; static lv_indev_t *lvgl_touch_indev; static esp_err_t init_backlight(void){ const gpio_config_t backlight_config = { .mode = GPIO_MODE_OUTPUT, .pin_bit_mask = 1ULL << LCD_GPIO_BACKLIGHT, }; ESP_RETURN_ON_ERROR(gpio_config(&backlight_config), TAG, "Backlight GPIO config failed"); ESP_RETURN_ON_ERROR(gpio_set_level(LCD_GPIO_BACKLIGHT, LCD_BACKLIGHT_ON_LEVEL), TAG, "Backlight GPIO set failed"); ESP_LOGI(TAG, "Backlight enabled on GPIO%d", LCD_GPIO_BACKLIGHT); return ESP_OK; } static esp_err_t probe_gt911_address(i2c_master_bus_handle_t i2c_handle, uint32_t *dev_addr){ const uint8_t addresses[] = { ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS, ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS_BACKUP, }; for(size_t address_index = 0; address_index < sizeof(addresses) / sizeof(addresses[0]); address_index++){ const uint8_t address = addresses[address_index]; const esp_err_t probe_ret = i2c_master_probe(i2c_handle, address, 100); if(probe_ret == ESP_OK){ *dev_addr = address; ESP_LOGI(TAG, "GT911 found at I2C address 0x%02x", address); return ESP_OK; } ESP_LOGW(TAG, "GT911 not found at I2C address 0x%02x: %s", address, esp_err_to_name(probe_ret)); } return ESP_ERR_NOT_FOUND; } static esp_err_t init_rgb_panel(void){ ESP_LOGI(TAG, "Initialize RGB panel"); const esp_lcd_rgb_panel_config_t panel_config = { .clk_src = LCD_CLK_SRC_PLL160M, #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0) .psram_trans_align = 64, #else .dma_burst_size = 64, #endif .data_width = LCD_DATA_WIDTH, #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0) .in_color_format = LCD_COLOR_FMT_RGB565, #else .bits_per_pixel = 16, #endif .de_gpio_num = LCD_GPIO_DE, .pclk_gpio_num = LCD_GPIO_PCLK, .vsync_gpio_num = LCD_GPIO_VSYNC, .hsync_gpio_num = LCD_GPIO_HSYNC, .disp_gpio_num = LCD_GPIO_DISP, .data_gpio_nums = { LCD_GPIO_DATA0, LCD_GPIO_DATA1, LCD_GPIO_DATA2, LCD_GPIO_DATA3, LCD_GPIO_DATA4, LCD_GPIO_DATA5, LCD_GPIO_DATA6, LCD_GPIO_DATA7, LCD_GPIO_DATA8, LCD_GPIO_DATA9, LCD_GPIO_DATA10, LCD_GPIO_DATA11, LCD_GPIO_DATA12, LCD_GPIO_DATA13, LCD_GPIO_DATA14, LCD_GPIO_DATA15, }, .timings = LCD_PANEL_RGB_TIMING(), .flags.fb_in_psram = true, .num_fbs = LCD_RGB_BUFFER_NUMS, #if LCD_RGB_BOUNCE_BUFFER_MODE .bounce_buffer_size_px = LCD_H_RES * LCD_RGB_BOUNCE_BUFFER_HEIGHT, #endif }; ESP_RETURN_ON_ERROR(esp_lcd_new_rgb_panel(&panel_config, &lcd_panel), TAG, "RGB panel create failed"); ESP_RETURN_ON_ERROR(esp_lcd_panel_init(lcd_panel), TAG, "LCD panel init failed"); return init_backlight(); } static esp_err_t init_touch(void){ ESP_LOGI(TAG, "Initialize GT911 touch"); i2c_master_bus_handle_t i2c_handle = NULL; const i2c_master_bus_config_t i2c_config = { .i2c_port = TOUCH_I2C_NUM, .sda_io_num = TOUCH_I2C_SDA, .scl_io_num = TOUCH_I2C_SCL, .clk_source = I2C_CLK_SRC_DEFAULT, .glitch_ignore_cnt = 7, .flags = { .enable_internal_pullup = true, }, }; ESP_RETURN_ON_ERROR(i2c_new_master_bus(&i2c_config, &i2c_handle), TAG, "I2C bus init failed"); esp_lcd_panel_io_i2c_config_t touch_io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG(); ESP_RETURN_ON_ERROR(probe_gt911_address(i2c_handle, &touch_io_config.dev_addr), TAG, "GT911 I2C probe failed"); esp_lcd_touch_io_gt911_config_t gt911_config = { .dev_addr = touch_io_config.dev_addr, }; const esp_lcd_touch_config_t touch_config = { .x_max = LCD_H_RES, .y_max = LCD_V_RES, .rst_gpio_num = TOUCH_RST, .int_gpio_num = TOUCH_INT, .levels = { .reset = 0, .interrupt = 0, }, .flags = { .swap_xy = false, .mirror_x = false, .mirror_y = false, }, .driver_data = >911_config, }; esp_lcd_panel_io_handle_t touch_io_handle = NULL; ESP_RETURN_ON_ERROR(esp_lcd_new_panel_io_i2c(i2c_handle, &touch_io_config, &touch_io_handle), TAG, "Touch IO init failed"); ESP_RETURN_ON_ERROR(esp_lcd_touch_new_i2c_gt911(touch_io_handle, &touch_config, &touch_handle), TAG, "GT911 driver init failed"); ESP_LOGI(TAG, "GT911 touch ready"); return ESP_OK; } static esp_err_t init_lvgl(void){ ESP_LOGI(TAG, "Initialize LVGL port"); lvgl_port_cfg_t lvgl_config = ESP_LVGL_PORT_INIT_CONFIG(); lvgl_config.task_stack = 16 * 1024; ESP_RETURN_ON_ERROR(lvgl_port_init(&lvgl_config), TAG, "LVGL port init failed"); uint32_t buffer_size = LCD_H_RES * LCD_DRAW_BUFF_HEIGHT; #if LCD_LVGL_DIRECT_MODE buffer_size = LCD_H_RES * LCD_V_RES; #endif const lvgl_port_display_cfg_t display_config = { .panel_handle = lcd_panel, .buffer_size = buffer_size, .double_buffer = false, .hres = LCD_H_RES, .vres = LCD_V_RES, .monochrome = false, #if LVGL_VERSION_MAJOR >= 9 .color_format = LV_COLOR_FORMAT_RGB565, #endif .rotation = { .swap_xy = false, .mirror_x = false, .mirror_y = false, }, .flags = { .buff_dma = false, .buff_spiram = false, .sw_rotate = true, #if LVGL_VERSION_MAJOR >= 9 .swap_bytes = false, #endif #if LCD_LVGL_DIRECT_MODE .direct_mode = true, #endif }, }; const lvgl_port_display_rgb_cfg_t rgb_config = { .flags = { .bb_mode = true, #if LCD_LVGL_AVOID_TEAR .avoid_tearing = true, #endif }, }; ESP_LOGI(TAG, "Add LVGL RGB display"); lvgl_display = lvgl_port_add_disp_rgb(&display_config, &rgb_config); ESP_RETURN_ON_FALSE(lvgl_display, ESP_FAIL, TAG, "LVGL display add failed"); lv_display_set_rotation(lvgl_display, LCD_LVGL_ROTATION); ESP_LOGI(TAG, "LVGL display rotation set to %d", LCD_LVGL_ROTATION); if(touch_handle){ const lvgl_port_touch_cfg_t touch_config = { .disp = lvgl_display, .handle = touch_handle, }; lvgl_touch_indev = lvgl_port_add_touch(&touch_config); ESP_RETURN_ON_FALSE(lvgl_touch_indev, ESP_FAIL, TAG, "LVGL touch add failed"); ESP_LOGI(TAG, "LVGL touch input ready"); } else ESP_LOGW(TAG, "LVGL touch input disabled"); ESP_LOGI(TAG, "LVGL ready"); return ESP_OK; } esp_err_t lcd_init(void){ ESP_RETURN_ON_ERROR(init_rgb_panel(), TAG, "LCD panel init failed"); const esp_err_t touch_ret = init_touch(); if(touch_ret != ESP_OK) ESP_LOGW(TAG, "Continuing without touch: %s", esp_err_to_name(touch_ret)); return init_lvgl(); } lv_display_t *lcd_get_display(void){ return lvgl_display; }