#include "esp_log.h" #include "esp_lcd_panel_io.h" #include "esp_lcd_panel_ops.h" #include "esp_lcd_panel_vendor.h" #include "esp_lcd_panel_st7789.h" #include "driver/spi_master.h" #include "hw_pins.h" #include "display_manager.h" static const char *TAG = "display"; #define LCD_HOST HW_SPI_HOST #define LCD_PIXEL_CLOCK_HZ (8 * 1000 * 1000) #define LCD_CMD_BITS 8 #define LCD_PARAM_BITS 8 static esp_lcd_panel_handle_t s_panel = NULL; static lv_disp_t *s_disp = NULL; esp_err_t display_manager_init(void) { if (s_disp != NULL) { return ESP_OK; } /* native SPI bus init directly in display manager */ spi_bus_config_t buscfg = { .sclk_io_num = HW_SPI_SCK_GPIO, .mosi_io_num = HW_SPI_MOSI_GPIO, .miso_io_num = HW_SPI_MISO_GPIO, .quadwp_io_num = -1, .quadhd_io_num = -1, .max_transfer_sz = 240 * 80 * sizeof(uint16_t), }; esp_err_t ret = spi_bus_initialize(LCD_HOST, &buscfg, SPI_DMA_CH_AUTO); if (ret != ESP_OK && ret != ESP_ERR_INVALID_STATE) { ESP_LOGE(TAG, "spi_bus_initialize failed: %s", esp_err_to_name(ret)); return ret; } /* 1. Initialize ST7789 panel via esp_lcd */ esp_lcd_panel_io_handle_t io = NULL; esp_lcd_panel_io_spi_config_t io_config = { .dc_gpio_num = HW_LCD_DC_GPIO, .cs_gpio_num = HW_LCD_CS_GPIO, .pclk_hz = LCD_PIXEL_CLOCK_HZ, .lcd_cmd_bits = LCD_CMD_BITS, .lcd_param_bits = LCD_PARAM_BITS, .spi_mode = 0, .trans_queue_depth = 10, }; ret = esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_HOST, &io_config, &io); if (ret != ESP_OK) { ESP_LOGE(TAG, "panel io init failed: %s", esp_err_to_name(ret)); return ret; } esp_lcd_panel_dev_config_t panel_config = { .reset_gpio_num = HW_LCD_RST_GPIO, .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, .bits_per_pixel = 16, }; ret = esp_lcd_new_panel_st7789(io, &panel_config, &s_panel); if (ret != ESP_OK) { ESP_LOGE(TAG, "st7789 panel create failed: %s", esp_err_to_name(ret)); return ret; } ESP_ERROR_CHECK(esp_lcd_panel_reset(s_panel)); ESP_ERROR_CHECK(esp_lcd_panel_init(s_panel)); ESP_ERROR_CHECK(esp_lcd_panel_invert_color(s_panel, true)); ESP_ERROR_CHECK(esp_lcd_panel_mirror(s_panel, true, true)); ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(s_panel, true)); /* 2. Initialize LVGL port on Core 1 */ lvgl_port_cfg_t port_cfg = ESP_LVGL_PORT_INIT_CONFIG(); port_cfg.task_affinity = 1; ret = lvgl_port_init(&port_cfg); if (ret != ESP_OK) { ESP_LOGE(TAG, "lvgl port init failed: %s", esp_err_to_name(ret)); return ret; } /* 20 lines buffer (240 * 20 * 2 = 9.6 KB) - lean SRAM footprint */ lvgl_port_display_cfg_t disp_cfg = { .io_handle = io, .panel_handle = s_panel, .buffer_size = DISP_W * 20, .double_buffer = false, .hres = DISP_W, .vres = DISP_H, .monochrome = false, .rotation = { .swap_xy = false, .mirror_x = true, .mirror_y = true, }, .flags = { .buff_dma = true, }, }; s_disp = lvgl_port_add_disp(&disp_cfg); if (s_disp == NULL) { ESP_LOGE(TAG, "lvgl_port_add_disp failed"); return ESP_FAIL; } ESP_LOGI(TAG, "ST7789 %dx%d with LVGL ready (CS=%d DC=%d RST=%d)", DISP_W, DISP_H, HW_LCD_CS_GPIO, HW_LCD_DC_GPIO, HW_LCD_RST_GPIO); return ESP_OK; } lv_disp_t *display_get_disp(void) { return s_disp; } static bool s_sleeping = false; esp_err_t display_set_sleep(bool sleep) { if (s_panel == NULL) { return ESP_ERR_INVALID_STATE; } if (s_sleeping == sleep) { return ESP_OK; } s_sleeping = sleep; ESP_LOGI(TAG, "Display %s", sleep ? "sleep (off)" : "wake (on)"); return esp_lcd_panel_disp_on_off(s_panel, !sleep); } bool display_is_sleeping(void) { return s_sleeping; }