/* * SPDX-FileCopyrightText: 2026 User * SPDX-License-Identifier: Apache-2.0 */ #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.h" #include "driver/gpio.h" #include "driver/i2c_master.h" #include "esp_log.h" #include "esp_heap_caps.h" #include "esp_lcd_panel_ops.h" #include "esp_lcd_panel_io.h" #include "esp_lcd_mipi_dsi.h" #include "esp_ldo_regulator.h" #include "esp_lcd_touch_gt911.h" #include "lvgl.h" #include "esp_lcd_jd9365_10_1.h" #define TAG "JD9365_LVGL" // Display config #define LCD_H_RES 800 #define LCD_V_RES 1280 #define LCD_BIT_PER_PIXEL 16 // RGB565 for better performance #define LCD_RST_GPIO 27 #define LCD_BK_GPIO 26 #define LCD_BK_ON_LEVEL 1 // MIPI DSI config #define MIPI_DSI_LANE_NUM 2 #define MIPI_DSI_PHY_LDO_CHAN 3 #define MIPI_DSI_PHY_LDO_VOLTAGE_MV 2500 // Touch config (GT911) #define TOUCH_I2C_SDA 7 #define TOUCH_I2C_SCL 8 #define TOUCH_INT_GPIO 3 #define TOUCH_RST_GPIO 2 #define TOUCH_I2C_FREQ_HZ 400000 static esp_lcd_panel_handle_t panel_handle = NULL; static esp_lcd_touch_handle_t touch_handle = NULL; static lv_display_t *display = NULL; // ================= Callbacks =================== static bool on_color_trans_done(esp_lcd_panel_handle_t panel, esp_lcd_dpi_panel_event_data_t *event_data, void *user_ctx) { BaseType_t high_task_awoken = pdFALSE; if (display) { lv_display_flush_ready(display); } return high_task_awoken == pdTRUE; } // ================= Flush Callback =================== static void lvgl_flush_cb(lv_display_t *disp, const lv_area_t *area, uint8_t *color_p) { // Warte bis vorheriger Transfer fertig ist esp_lcd_panel_draw_bitmap(panel_handle, area->x1, area->y1, area->x2 + 1, area->y2 + 1, color_p); } static void lvgl_touch_cb(lv_indev_t *indev, lv_indev_data_t *data) { uint16_t x[1], y[1]; uint8_t touch_cnt = 0; esp_lcd_touch_read_data(touch_handle); if (esp_lcd_touch_get_coordinates(touch_handle, x, y, NULL, &touch_cnt, 1) && touch_cnt > 0) { data->point.x = x[0]; data->point.y = y[0]; data->state = LV_INDEV_STATE_PRESSED; } else { data->state = LV_INDEV_STATE_RELEASED; } } // ================= LCD init =================== static void lcd_init(void) { gpio_config_t bk_gpio = { .mode = GPIO_MODE_OUTPUT, .pin_bit_mask = 1ULL << LCD_BK_GPIO }; gpio_config(&bk_gpio); gpio_set_level(LCD_BK_GPIO, LCD_BK_ON_LEVEL); esp_ldo_channel_handle_t ldo_mipi = NULL; esp_ldo_channel_config_t ldo_cfg = { .chan_id = MIPI_DSI_PHY_LDO_CHAN, .voltage_mv = MIPI_DSI_PHY_LDO_VOLTAGE_MV }; esp_ldo_acquire_channel(&ldo_cfg, &ldo_mipi); esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL; esp_lcd_dsi_bus_config_t bus_cfg = JD9365_PANEL_BUS_DSI_2CH_CONFIG(); esp_lcd_new_dsi_bus(&bus_cfg, &mipi_dsi_bus); esp_lcd_panel_io_handle_t mipi_dbi_io = NULL; esp_lcd_dbi_io_config_t io_cfg = JD9365_PANEL_IO_DBI_CONFIG(); esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &io_cfg, &mipi_dbi_io); esp_lcd_dpi_panel_config_t dpi_cfg = JD9365_800_1280_PANEL_60HZ_DPI_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB565); jd9365_vendor_config_t vendor_cfg = { .flags = { .use_mipi_interface = 1 }, .mipi_config = { .dsi_bus = mipi_dsi_bus, .dpi_config = &dpi_cfg, .lane_num = MIPI_DSI_LANE_NUM } }; esp_lcd_panel_dev_config_t panel_cfg = { .reset_gpio_num = LCD_RST_GPIO, .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, .bits_per_pixel = LCD_BIT_PER_PIXEL, .vendor_config = &vendor_cfg }; esp_lcd_new_panel_jd9365(mipi_dbi_io, &panel_cfg, &panel_handle); esp_lcd_panel_reset(panel_handle); esp_lcd_panel_init(panel_handle); esp_lcd_panel_disp_on_off(panel_handle, true); esp_lcd_dpi_panel_event_callbacks_t cbs = { .on_color_trans_done = on_color_trans_done }; esp_lcd_dpi_panel_register_event_callbacks(panel_handle, &cbs, NULL); } // ================= Touch init =================== static void touch_init(void) { i2c_master_bus_config_t i2c_bus_cfg = { .i2c_port = I2C_NUM_0, .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, }; i2c_master_bus_handle_t i2c_bus = NULL; ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &i2c_bus)); esp_lcd_panel_io_handle_t touch_io = NULL; esp_lcd_panel_io_i2c_config_t touch_io_cfg = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG(); touch_io_cfg.scl_speed_hz = TOUCH_I2C_FREQ_HZ; ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c(i2c_bus, &touch_io_cfg, &touch_io)); esp_lcd_touch_config_t touch_cfg = { .x_max = LCD_H_RES, .y_max = LCD_V_RES, .rst_gpio_num = TOUCH_RST_GPIO, .int_gpio_num = TOUCH_INT_GPIO, .levels = { .reset = 0, .interrupt = 0 }, .flags = { .swap_xy = 0, .mirror_x = 0, .mirror_y = 0 }, }; ESP_ERROR_CHECK(esp_lcd_touch_new_i2c_gt911(touch_io, &touch_cfg, &touch_handle)); } // ================= LVGL init =================== static void lvgl_init(void) { lv_init(); lv_display_t *disp = lv_display_create(LCD_H_RES, LCD_V_RES); display = disp; lv_display_set_color_format(disp, LV_COLOR_FORMAT_RGB565); // Full-screen double buffer für bessere Performance mit DPI Panel size_t buf_size = LCD_H_RES * 200 * sizeof(uint16_t); uint8_t *buf1 = heap_caps_aligned_alloc(64, buf_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA); uint8_t *buf2 = heap_caps_aligned_alloc(64, buf_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA); if (!buf1 || !buf2) { ESP_LOGE(TAG, "Failed to allocate buffers!"); return; } lv_display_set_buffers(disp, buf1, buf2, buf_size, LV_DISPLAY_RENDER_MODE_PARTIAL); lv_display_set_flush_cb(disp, lvgl_flush_cb); lv_display_set_default(disp); lv_indev_t *indev = lv_indev_create(); lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER); lv_indev_set_read_cb(indev, lvgl_touch_cb); lv_indev_set_display(indev, disp); } // ================= UI =================== static void create_ui(void) { lv_obj_t *tileview = lv_tileview_create(lv_screen_active()); // Page 1 - Blue lv_obj_t *tile1 = lv_tileview_add_tile(tileview, 0, 0, LV_DIR_BOTTOM); lv_obj_set_style_bg_color(tile1, lv_color_hex(0x2196F3), 0); lv_obj_t *label1 = lv_label_create(tile1); lv_label_set_text(label1, "Seite 1\n\nSwipe nach oben"); lv_obj_set_style_text_color(label1, lv_color_white(), 0); lv_obj_set_style_text_font(label1, &lv_font_montserrat_28, 0); lv_obj_center(label1); // Page 2 - Green lv_obj_t *tile2 = lv_tileview_add_tile(tileview, 0, 1, LV_DIR_TOP); lv_obj_set_style_bg_color(tile2, lv_color_hex(0x4CAF50), 0); lv_obj_t *label2 = lv_label_create(tile2); lv_label_set_text(label2, "Seite 2\n\nSwipe nach unten"); lv_obj_set_style_text_color(label2, lv_color_white(), 0); lv_obj_set_style_text_font(label2, &lv_font_montserrat_28, 0); lv_obj_center(label2); } // ================= Main =================== void app_main(void) { ESP_LOGI(TAG, "Starting JD9365 + LVGL 9 (Portrait RGB565)"); lcd_init(); touch_init(); lvgl_init(); create_ui(); while (1) { lv_tick_inc(10); lv_task_handler(); vTaskDelay(pdMS_TO_TICKS(10)); } }