#include "St7796i8080Display.h" #include #include #include #include #include #include #include #include static const auto LOGGER = tt::Logger("St7796i8080Display"); static St7796i8080Display* g_display_instance = nullptr; St7796i8080Display::St7796i8080Display(const Configuration& config) : configuration(config), lock(std::make_shared()) { // Validate configuration if (!configuration.isValid()) { LOGGER.error("Invalid configuration: resolution must be set"); return; } } bool St7796i8080Display::createI80Bus() { LOGGER.info("Creating I80 bus"); // Create I80 bus configuration esp_lcd_i80_bus_config_t bus_cfg = { .dc_gpio_num = configuration.dcPin, .wr_gpio_num = configuration.wrPin, .clk_src = LCD_CLK_SRC_PLL160M, .data_gpio_nums = { configuration.dataPins[0], configuration.dataPins[1], configuration.dataPins[2], configuration.dataPins[3], configuration.dataPins[4], configuration.dataPins[5], configuration.dataPins[6], configuration.dataPins[7], }, .bus_width = configuration.busWidth, .max_transfer_bytes = configuration.bufferSize * sizeof(uint16_t), .psram_trans_align = 64, .sram_trans_align = 4 }; if (esp_lcd_new_i80_bus(&bus_cfg, &i80BusHandle) != ESP_OK) { LOGGER.error("Failed to create I80 bus"); return false; } return true; } bool St7796i8080Display::createPanelIO() { LOGGER.info("Creating panel IO"); // Create panel IO esp_lcd_panel_io_i80_config_t io_cfg = { .cs_gpio_num = configuration.csPin, .pclk_hz = configuration.pixelClockFrequency, .trans_queue_depth = configuration.transactionQueueDepth, .on_color_trans_done = nullptr, .user_ctx = nullptr, .lcd_cmd_bits = configuration.lcdCmdBits, .lcd_param_bits = configuration.lcdParamBits, .dc_levels = { .dc_idle_level = configuration.dcLevels.dcIdleLevel, .dc_cmd_level = configuration.dcLevels.dcCmdLevel, .dc_dummy_level = configuration.dcLevels.dcDummyLevel, .dc_data_level = configuration.dcLevels.dcDataLevel, }, .flags = { .cs_active_high = configuration.flags.csActiveHigh, .reverse_color_bits = configuration.flags.reverseColorBits, .swap_color_bytes = configuration.flags.swapColorBytes, .pclk_active_neg = configuration.flags.pclkActiveNeg, .pclk_idle_low = configuration.flags.pclkIdleLow } }; if (esp_lcd_new_panel_io_i80(i80BusHandle, &io_cfg, &ioHandle) != ESP_OK) { LOGGER.error("Failed to create panel"); return false; } return true; } bool St7796i8080Display::createPanel() { LOGGER.info("Configuring panel"); // Create ST7796 panel esp_lcd_panel_dev_config_t panel_config = { .reset_gpio_num = configuration.resetPin, .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR, .data_endian = LCD_RGB_DATA_ENDIAN_LITTLE, .bits_per_pixel = 16, .flags = { .reset_active_high = false }, .vendor_config = nullptr }; if (esp_lcd_new_panel_st7796(ioHandle, &panel_config, &panelHandle) != ESP_OK) { LOGGER.error("Failed to create panel"); return false; } // Reset panel if (esp_lcd_panel_reset(panelHandle) != ESP_OK) { LOGGER.error("Failed to reset panel"); return false; } // Initialize panel if (esp_lcd_panel_init(panelHandle) != ESP_OK) { LOGGER.error("Failed to init panel"); return false; } // Set swap XY if (esp_lcd_panel_swap_xy(panelHandle, configuration.swapXY) != ESP_OK) { LOGGER.error("Failed to swap XY "); return false; } // Set mirror if (esp_lcd_panel_mirror(panelHandle, configuration.mirrorX, configuration.mirrorY) != ESP_OK) { LOGGER.error("Failed to set panel to mirror"); return false; } // Set inversion if (esp_lcd_panel_invert_color(panelHandle, configuration.invertColor) != ESP_OK) { LOGGER.error("Failed to set panel to invert"); return false; } // Turn on display if (esp_lcd_panel_disp_on_off(panelHandle, true) != ESP_OK) { LOGGER.error("Failed to turn display on"); return false; } return true; } bool St7796i8080Display::start() { LOGGER.info("Initializing I8080 ST7796 Display hardware..."); // Calculate buffer size if needed configuration.calculateBufferSize(); // Allocate buffer size_t buffer_size = configuration.bufferSize * LV_COLOR_FORMAT_GET_SIZE(LV_COLOR_FORMAT_RGB565); displayBuffer = (uint8_t*)heap_caps_malloc(buffer_size, MALLOC_CAP_DMA); if (!displayBuffer) { LOGGER.error("Failed to allocate display buffer"); return false; } // Create I80 bus if (!createI80Bus()) { stop(); return false; } // Create panel IO if (!createPanelIO()) { stop(); return false; } // Create panel if (!createPanel()) { stop(); return false; } LOGGER.info("Display hardware initialized"); return true; } bool St7796i8080Display::stop() { // Turn off display if (panelHandle) { esp_lcd_panel_disp_on_off(panelHandle, false); } // Destroy in reverse order: panel, IO, bus if (panelHandle) { esp_lcd_panel_del(panelHandle); panelHandle = nullptr; } if (ioHandle) { esp_lcd_panel_io_del(ioHandle); ioHandle = nullptr; } if (i80BusHandle) { esp_lcd_del_i80_bus(i80BusHandle); i80BusHandle = nullptr; } // Free buffer 1 if (displayBuffer) { heap_caps_free(displayBuffer); displayBuffer = nullptr; } // Turn off backlight if (configuration.backlightDutyFunction) { configuration.backlightDutyFunction(0); } return true; } bool St7796i8080Display::startLvgl() { LOGGER.info("Initializing LVGL for ST7796 display"); // Don't reinitialize hardware if it's already done if (!ioHandle) { LOGGER.info("Hardware not initialized, calling start()"); if (!start()) { LOGGER.error("Hardware initialization failed"); return false; } } else { LOGGER.info("Hardware already initialized, skipping"); } // Create LVGL display using lvgl_port lvgl_port_display_cfg_t display_cfg = { .io_handle = ioHandle, .panel_handle = panelHandle, .control_handle = nullptr, .buffer_size = configuration.bufferSize, .double_buffer = false, .trans_size = 0, .hres = configuration.horizontalResolution, .vres = configuration.verticalResolution, .monochrome = false, .rotation = { .swap_xy = configuration.swapXY, .mirror_x = configuration.mirrorX, .mirror_y = configuration.mirrorY, }, .color_format = LV_COLOR_FORMAT_RGB565, .flags = { .buff_dma = true, .buff_spiram = false, .sw_rotate = false, .swap_bytes = false, .full_refresh = false, .direct_mode = false } }; // Create the LVGL display lvglDisplay = lvgl_port_add_disp(&display_cfg); if (!lvglDisplay) { LOGGER.error("Failed to create LVGL display"); return false; } g_display_instance = this; LOGGER.info("LVGL display created successfully"); return true; } bool St7796i8080Display::stopLvgl() { if (lvglDisplay) { lvgl_port_remove_disp(lvglDisplay); lvglDisplay = nullptr; } return true; }