// ===================================================================================== // board_bringup.c - ESP32-P4 / ST7701 / GT911 Hardware-Bringup (JC4880P443C-I-W) // ===================================================================================== // BEWUSST eine .c-Datei (wird als C kompiliert): die esp_lcd-Init-Makros nutzen // Compound-Literals und Out-of-Order-Designated-Initializer, die in C++ (Arduino .cpp) // zu Fehlern fuehren. Der Ablauf ist 1:1 aus dem Hersteller-Beispiel // arduino_examples/lvgl_v9_sw_rotation/lvgl_sw_rotation.c uebernommen - lediglich der // Demo-Aufruf (lv_demo_widgets) entfaellt; das UI wird ueber display_hal.cpp aufgebaut. // // Nur aktiv bei JC_USE_REAL_PANEL == 1. Erwartet die Vendor-BSP-Dateien im Sketch: // pins_config.h, lvgl_port_v9.h/.c, src/lcd/*, src/touch/* // ===================================================================================== #include "config.h" // liefert JC_USE_REAL_PANEL (C-sicher) #if JC_USE_REAL_PANEL #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "driver/gpio.h" #include "driver/ledc.h" #include "driver/i2c_master.h" #include "esp_log.h" #include "esp_cache.h" #include "esp_ldo_regulator.h" #include "esp_lcd_panel_io.h" #include "esp_lcd_panel_ops.h" #include "esp_lcd_mipi_dsi.h" #include "src/touch/esp_lcd_touch_gt911.h" #include "src/lcd/esp_lcd_st7701.h" #include "lvgl_port_v9.h" #define BSP_MIPI_DSI_PHY_PWR_LDO_CHAN (3) #define BSP_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV (2500) #define BSP_LCD_H_RES (480) #define BSP_LCD_V_RES (800) #define BSP_I2C_NUM (I2C_NUM_1) #define BSP_I2C_SDA (GPIO_NUM_7) #define BSP_I2C_SCL (GPIO_NUM_8) #define BSP_LCD_TOUCH_RST (GPIO_NUM_NC) #define BSP_LCD_TOUCH_INT (GPIO_NUM_NC) #define BSP_LCD_RST (GPIO_NUM_5) #define BSP_LCD_BACKLIGHT GPIO_NUM_23 #define LCD_LEDC_CH LEDC_CHANNEL_0 static i2c_master_bus_handle_t s_i2c_handle = NULL; void jc_backlight_init(void) { const ledc_channel_config_t ch = { .gpio_num = BSP_LCD_BACKLIGHT, .speed_mode = LEDC_LOW_SPEED_MODE, .channel = LCD_LEDC_CH, .intr_type = LEDC_INTR_DISABLE, .timer_sel = 1, .duty = 0, .hpoint = 0 }; const ledc_timer_config_t tm = { .speed_mode = LEDC_LOW_SPEED_MODE, .duty_resolution = LEDC_TIMER_10_BIT, .timer_num = 1, .freq_hz = 5000, .clk_cfg = LEDC_AUTO_CLK }; ledc_timer_config(&tm); ledc_channel_config(&ch); } void jc_backlight_set(int percent) { if (percent > 100) percent = 100; if (percent < 0) percent = 0; uint32_t duty = (1023 * percent) / 100; ledc_set_duty(LEDC_LOW_SPEED_MODE, LCD_LEDC_CH, duty); ledc_update_duty(LEDC_LOW_SPEED_MODE, LCD_LEDC_CH); } IRAM_ATTR static bool on_vsync(esp_lcd_panel_handle_t panel, esp_lcd_dpi_panel_event_data_t *edata, void *ctx) { return lvgl_port_notify_lcd_vsync(); } // Bringt Panel + Touch + LVGL-Port hoch (LVGL laeuft danach in eigenem Task). void jc_board_bringup(void) { jc_backlight_init(); i2c_master_bus_config_t i2c_bus_conf = { .clk_source = I2C_CLK_SRC_DEFAULT, .sda_io_num = BSP_I2C_SDA, .scl_io_num = BSP_I2C_SCL, .i2c_port = BSP_I2C_NUM, }; i2c_new_master_bus(&i2c_bus_conf, &s_i2c_handle); static esp_ldo_channel_handle_t phy_pwr_chan = NULL; esp_ldo_channel_config_t ldo_cfg = { .chan_id = BSP_MIPI_DSI_PHY_PWR_LDO_CHAN, .voltage_mv = BSP_MIPI_DSI_PHY_PWR_LDO_VOLTAGE_MV, }; esp_ldo_acquire_channel(&ldo_cfg, &phy_pwr_chan); esp_lcd_dsi_bus_handle_t mipi_dsi_bus; esp_lcd_dsi_bus_config_t bus_config = ST7701_PANEL_BUS_DSI_2CH_CONFIG(); esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus); esp_lcd_panel_io_handle_t io = NULL; esp_lcd_dbi_io_config_t dbi_config = ST7701_PANEL_IO_DBI_CONFIG(); esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &io); esp_lcd_panel_handle_t disp_panel = NULL; esp_lcd_dpi_panel_config_t dpi_config = ST7701_480_360_PANEL_60HZ_DPI_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB565); dpi_config.num_fbs = LVGL_PORT_LCD_BUFFER_NUMS; st7701_vendor_config_t vendor_config = { .mipi_config = { .dsi_bus = mipi_dsi_bus, .dpi_config = &dpi_config, }, .flags = { .use_mipi_interface = 1, }, }; esp_lcd_panel_dev_config_t lcd_dev_config = { .bits_per_pixel = 16, .rgb_ele_order = ESP_LCD_COLOR_SPACE_RGB, .reset_gpio_num = BSP_LCD_RST, .vendor_config = &vendor_config, }; esp_lcd_new_panel_st7701(io, &lcd_dev_config, &disp_panel); esp_lcd_panel_reset(disp_panel); esp_lcd_panel_init(disp_panel); // Alle Framebuffer einmal auf Schwarz loeschen. Im Avoid-Tear-Direct-Mode (mehrere // Buffer) kopiert LVGL pro Frame nur die geaenderten Bereiche in den naechsten Buffer; // bis alle Buffer einmal vollstaendig bemalt sind, wuerde sonst uninitialisierter // Speicher (weisses Rauschen) durchblitzen. Schwarz faellt gegen das dunkle Theme // nicht auf. Cache-Writeback noetig, da die Buffer im PSRAM liegen und der DMA liest. { void *fbs[3] = { NULL, NULL, NULL }; size_t fb_size = (size_t)BSP_LCD_H_RES * BSP_LCD_V_RES * 2; // RGB565 = 2 Byte/Pixel #if LVGL_PORT_LCD_BUFFER_NUMS >= 3 esp_lcd_dpi_panel_get_frame_buffer(disp_panel, 3, &fbs[0], &fbs[1], &fbs[2]); #elif LVGL_PORT_LCD_BUFFER_NUMS == 2 esp_lcd_dpi_panel_get_frame_buffer(disp_panel, 2, &fbs[0], &fbs[1]); #else esp_lcd_dpi_panel_get_frame_buffer(disp_panel, 1, &fbs[0]); #endif for (int i = 0; i < 3; i++) { if (fbs[i]) { memset(fbs[i], 0, fb_size); esp_cache_msync(fbs[i], fb_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); } } } esp_lcd_dpi_panel_event_callbacks_t cbs = { #if LVGL_PORT_AVOID_TEAR_MODE .on_refresh_done = on_vsync, #else .on_color_trans_done = on_vsync, #endif }; esp_lcd_dpi_panel_register_event_callbacks(disp_panel, &cbs, NULL); esp_lcd_panel_io_handle_t tp_io_handle = NULL; esp_lcd_touch_handle_t tp_handle; esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG(); tp_io_config.scl_speed_hz = 100000; esp_lcd_new_panel_io_i2c(s_i2c_handle, &tp_io_config, &tp_io_handle); const esp_lcd_touch_config_t tp_cfg = { .x_max = BSP_LCD_H_RES, .y_max = BSP_LCD_V_RES, .rst_gpio_num = BSP_LCD_TOUCH_RST, .int_gpio_num = BSP_LCD_TOUCH_INT, .levels = { .reset = 0, .interrupt = 0, }, .flags = { .swap_xy = 0, .mirror_x = 0, .mirror_y = 0, }, }; esp_lcd_touch_new_i2c_gt911(tp_io_handle, &tp_cfg, &tp_handle); lvgl_port_interface_t interface = (dpi_config.flags.use_dma2d) ? LVGL_PORT_INTERFACE_MIPI_DSI_DMA : LVGL_PORT_INTERFACE_MIPI_DSI_NO_DMA; lvgl_port_init(disp_panel, tp_handle, interface); } #endif // JC_USE_REAL_PANEL