#include "display_lcd_module.h" #include #include #include #include #include #include "esp_check.h" #include "esp_heap_caps.h" #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "sdkconfig.h" #include "display_lcd_decode_image.h" #include "display_st7789p3.h" static const char *TAG = "lcd_module"; #define ST7789_SPI_HOST SPI2_HOST #define LCD_PREVIEW_TIMEOUT_MS_DEFAULT 3000 #define LCD_LOCK_TIMEOUT_MS 1000 #define LCD_DEFAULT_DRAW_LINES 20 #define LCD_PREVIEW_FADE_DURATION_MS 150 #define LCD_PREVIEW_FADE_STEPS 3 #define LCD_RGB565_BLACK 0x0000u #define LCD_RGB565_WHITE 0xFFFFu #ifndef CONFIG_TQ_SCREEN_PIXEL_CLOCK_HZ #define CONFIG_TQ_SCREEN_PIXEL_CLOCK_HZ (10 * 1000 * 1000) #endif #ifndef CONFIG_TQ_SCREEN_H_RES #define CONFIG_TQ_SCREEN_H_RES 320 #endif #ifndef CONFIG_TQ_SCREEN_V_RES #define CONFIG_TQ_SCREEN_V_RES 240 #endif #ifndef CONFIG_TQ_SCREEN_SPI_MODE #define CONFIG_TQ_SCREEN_SPI_MODE 0 #endif #ifndef CONFIG_TQ_SCREEN_DRAW_LINES #define CONFIG_TQ_SCREEN_DRAW_LINES LCD_DEFAULT_DRAW_LINES #endif #ifndef CONFIG_TQ_SCREEN_COLOR_ORDER_BGR #define CONFIG_TQ_SCREEN_COLOR_ORDER_BGR 0 #endif #ifndef CONFIG_TQ_SCREEN_BACKLIGHT_ACTIVE_HIGH #define CONFIG_TQ_SCREEN_BACKLIGHT_ACTIVE_HIGH 1 #endif #ifndef CONFIG_TQ_SCREEN_MIRROR_X #define CONFIG_TQ_SCREEN_MIRROR_X 1 #endif #ifndef CONFIG_TQ_SCREEN_MIRROR_Y #define CONFIG_TQ_SCREEN_MIRROR_Y 0 #endif #ifndef CONFIG_TQ_SCREEN_SWAP_XY #define CONFIG_TQ_SCREEN_SWAP_XY 1 #endif #ifndef CONFIG_TQ_SCREEN_X_GAP #define CONFIG_TQ_SCREEN_X_GAP 0 #endif #ifndef CONFIG_TQ_SCREEN_Y_GAP #define CONFIG_TQ_SCREEN_Y_GAP 0 #endif #ifndef CONFIG_TQ_SCREEN_INVERT_COLOR #define CONFIG_TQ_SCREEN_INVERT_COLOR 1 #endif #ifndef CONFIG_TQ_SCREEN_SWAP_COLOR_BYTES #define CONFIG_TQ_SCREEN_SWAP_COLOR_BYTES 1 #endif #ifndef CONFIG_TQ_SCREEN_RESET_PIN #define CONFIG_TQ_SCREEN_RESET_PIN -1 #endif #ifndef CONFIG_TQ_SCREEN_BACKLIGHT_PIN #define CONFIG_TQ_SCREEN_BACKLIGHT_PIN -1 #endif #define LCD_PANEL_H_RES ((uint16_t)CONFIG_TQ_SCREEN_H_RES) #define LCD_PANEL_V_RES ((uint16_t)CONFIG_TQ_SCREEN_V_RES) #if CONFIG_TQ_SCREEN_BACKLIGHT_ACTIVE_HIGH #define ST7789_BACKLIGHT_ON_LEVEL 1 #else #define ST7789_BACKLIGHT_ON_LEVEL 0 #endif typedef struct { st7789p3_handle_t panel; SemaphoreHandle_t lock; uint16_t *preview_pixels; uint16_t preview_w; uint16_t preview_h; bool initialized; bool shared_spi_dirty; } lcd_state_t; static lcd_state_t s_lcd; void lcd_module_mark_shared_spi_dirty(void) { s_lcd.shared_spi_dirty = true; } static int resolve_reset_pin(void) { if (CONFIG_TQ_SCREEN_RESET_PIN >= 0) { return CONFIG_TQ_SCREEN_RESET_PIN; } return CONFIG_TQ_SCREEN_BACKLIGHT_PIN; } static uint8_t lcd_build_madctl(void) { uint8_t madctl = 0; #if CONFIG_TQ_SCREEN_MIRROR_X madctl |= ST7789P3_MADCTL_MX; #endif #if CONFIG_TQ_SCREEN_MIRROR_Y madctl |= ST7789P3_MADCTL_MY; #endif #if CONFIG_TQ_SCREEN_SWAP_XY madctl |= ST7789P3_MADCTL_MV; #endif #if CONFIG_TQ_SCREEN_COLOR_ORDER_BGR madctl |= ST7789P3_MADCTL_BGR; #endif return madctl; } static void lcd_fill_err(char *err, size_t err_len, const char *msg) { if (err != NULL && err_len > 0) { snprintf(err, err_len, "%s", msg != NULL ? msg : "unknown"); } } static bool lcd_lock_take(uint32_t timeout_ms) { if (s_lcd.lock == NULL) { return false; } TickType_t ticks = pdMS_TO_TICKS(timeout_ms); if (timeout_ms > 0 && ticks == 0) { ticks = 1; } return xSemaphoreTake(s_lcd.lock, ticks) == pdTRUE; } static void lcd_lock_give(void) { if (s_lcd.lock != NULL) { xSemaphoreGive(s_lcd.lock); } } static esp_err_t lcd_recover_panel_for_shared_spi(void) { if (!s_lcd.initialized || s_lcd.panel == NULL) { return lcd_module_init(); } if (!s_lcd.shared_spi_dirty) { return ESP_OK; } /* * Printer path temporarily takes over shared MOSI/CLK as GPIO. * Recover LCD SPI routing only when printer touched shared pins. */ esp_err_t recover_rc = st7789p3_recover_spi_bus(s_lcd.panel); if (recover_rc == ESP_OK) { s_lcd.shared_spi_dirty = false; return ESP_OK; } ESP_LOGW(TAG, "fast SPI recover failed: rc=0x%x, fallback to full panel reinit", (unsigned)recover_rc); lcd_module_deinit(); return lcd_module_init(); } static void lcd_calc_preview_size(uint16_t src_w, uint16_t src_h, uint16_t *out_w, uint16_t *out_h) { if (src_w == 0 || src_h == 0 || out_w == NULL || out_h == NULL) { return; } if (src_w <= LCD_PANEL_H_RES && src_h <= LCD_PANEL_V_RES) { *out_w = src_w; *out_h = src_h; return; } if ((uint64_t)src_w * LCD_PANEL_V_RES > (uint64_t)src_h * LCD_PANEL_H_RES) { *out_w = LCD_PANEL_H_RES; *out_h = (uint16_t)(((uint32_t)src_h * LCD_PANEL_H_RES) / src_w); } else { *out_h = LCD_PANEL_V_RES; *out_w = (uint16_t)(((uint32_t)src_w * LCD_PANEL_V_RES) / src_h); } if (*out_w == 0) { *out_w = 1; } if (*out_h == 0) { *out_h = 1; } } static esp_err_t lcd_render_raster_to_rgb565(const uint8_t *raster, size_t raster_len, uint16_t src_w, uint16_t src_h, uint16_t **out_pixels, uint16_t *out_w, uint16_t *out_h, char *err, size_t err_len) { if (raster == NULL || src_w == 0 || src_h == 0 || out_pixels == NULL || out_w == NULL || out_h == NULL) { lcd_fill_err(err, err_len, "invalid raster"); return ESP_ERR_INVALID_ARG; } size_t bytes_per_row = ((size_t)src_w + 7u) / 8u; size_t needed_len = bytes_per_row * src_h; if (raster_len < needed_len) { lcd_fill_err(err, err_len, "raster length mismatch"); return ESP_ERR_INVALID_SIZE; } lcd_calc_preview_size(src_w, src_h, out_w, out_h); size_t pixel_count = (size_t)(*out_w) * (size_t)(*out_h); if (pixel_count == 0 || pixel_count > SIZE_MAX / sizeof(uint16_t)) { lcd_fill_err(err, err_len, "preview size invalid"); return ESP_ERR_INVALID_SIZE; } uint16_t *pixels = (uint16_t *)heap_caps_malloc(pixel_count * sizeof(uint16_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); if (pixels == NULL) { pixels = (uint16_t *)heap_caps_malloc(pixel_count * sizeof(uint16_t), MALLOC_CAP_8BIT); } if (pixels == NULL) { lcd_fill_err(err, err_len, "no memory"); return ESP_ERR_NO_MEM; } for (uint16_t y = 0; y < *out_h; ++y) { uint16_t src_y = (uint16_t)(((uint32_t)y * src_h) / (*out_h)); size_t row_base = (size_t)src_y * bytes_per_row; size_t out_row = (size_t)y * (*out_w); for (uint16_t x = 0; x < *out_w; ++x) { uint16_t src_x = (uint16_t)(((uint32_t)x * src_w) / (*out_w)); size_t byte_idx = row_base + (src_x / 8u); uint8_t bit = (uint8_t)(7u - (src_x % 8u)); bool is_black = (raster[byte_idx] & (uint8_t)(1u << bit)) != 0; pixels[out_row + x] = is_black ? 0x0000 : 0xFFFF; } } *out_pixels = pixels; return ESP_OK; } static esp_err_t lcd_scale_rgb565_to_preview(const uint16_t *src_pixels, uint16_t src_w, uint16_t src_h, uint16_t **out_pixels, uint16_t *out_w, uint16_t *out_h, char *err, size_t err_len) { if (src_pixels == NULL || src_w == 0 || src_h == 0 || out_pixels == NULL || out_w == NULL || out_h == NULL) { lcd_fill_err(err, err_len, "invalid rgb565 image"); return ESP_ERR_INVALID_ARG; } *out_pixels = NULL; *out_w = 0; *out_h = 0; lcd_calc_preview_size(src_w, src_h, out_w, out_h); size_t pixel_count = (size_t)(*out_w) * (size_t)(*out_h); if (pixel_count == 0 || pixel_count > SIZE_MAX / sizeof(uint16_t)) { lcd_fill_err(err, err_len, "preview size invalid"); return ESP_ERR_INVALID_SIZE; } uint16_t *preview = (uint16_t *)heap_caps_malloc(pixel_count * sizeof(uint16_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); if (preview == NULL) { preview = (uint16_t *)heap_caps_malloc(pixel_count * sizeof(uint16_t), MALLOC_CAP_8BIT); } if (preview == NULL) { lcd_fill_err(err, err_len, "no memory"); return ESP_ERR_NO_MEM; } if (*out_w == src_w && *out_h == src_h) { memcpy(preview, src_pixels, pixel_count * sizeof(uint16_t)); *out_pixels = preview; return ESP_OK; } for (uint16_t y = 0; y < *out_h; ++y) { uint16_t src_y = (uint16_t)(((uint32_t)y * src_h) / (*out_h)); size_t src_row = (size_t)src_y * src_w; size_t dst_row = (size_t)y * (*out_w); for (uint16_t x = 0; x < *out_w; ++x) { uint16_t src_x = (uint16_t)(((uint32_t)x * src_w) / (*out_w)); preview[dst_row + x] = src_pixels[src_row + src_x]; } } *out_pixels = preview; return ESP_OK; } static uint16_t lcd_blend_rgb565(uint16_t from, uint16_t to, uint8_t alpha_255) { uint32_t inv = (uint32_t)(255u - alpha_255); uint32_t fr = (from >> 11) & 0x1Fu; uint32_t fg = (from >> 5) & 0x3Fu; uint32_t fb = from & 0x1Fu; uint32_t tr = (to >> 11) & 0x1Fu; uint32_t tg = (to >> 5) & 0x3Fu; uint32_t tb = to & 0x1Fu; uint32_t r = (fr * inv + tr * alpha_255 + 127u) / 255u; uint32_t g = (fg * inv + tg * alpha_255 + 127u) / 255u; uint32_t b = (fb * inv + tb * alpha_255 + 127u) / 255u; return (uint16_t)((r << 11) | (g << 5) | b); } static esp_err_t lcd_draw_preview_pixels_locked(const uint16_t *pixels, uint16_t preview_w, uint16_t preview_h, char *err, size_t err_len) { if (s_lcd.panel == NULL || pixels == NULL) { lcd_fill_err(err, err_len, "display panel not ready"); return ESP_ERR_INVALID_STATE; } if (preview_w == 0 || preview_h == 0 || preview_w > LCD_PANEL_H_RES || preview_h > LCD_PANEL_V_RES) { lcd_fill_err(err, err_len, "invalid preview size"); return ESP_ERR_INVALID_SIZE; } uint16_t x = (uint16_t)((LCD_PANEL_H_RES - preview_w) / 2u); uint16_t y = (uint16_t)((LCD_PANEL_V_RES - preview_h) / 2u); esp_err_t draw_rc = st7789p3_draw_bitmap(s_lcd.panel, x, y, (uint16_t)(x + preview_w), (uint16_t)(y + preview_h), pixels); if (draw_rc != ESP_OK) { lcd_fill_err(err, err_len, "display draw failed"); return draw_rc; } return ESP_OK; } static esp_err_t lcd_apply_preview_locked(const uint16_t *prev_pixels, uint16_t prev_w, uint16_t prev_h, bool clear_before_draw, uint16_t bg_color, char *err, size_t err_len) { if (s_lcd.panel == NULL || s_lcd.preview_pixels == NULL) { lcd_fill_err(err, err_len, "display panel not ready"); return ESP_ERR_INVALID_STATE; } uint16_t preview_w = s_lcd.preview_w; uint16_t preview_h = s_lcd.preview_h; if (preview_w == 0 || preview_h == 0 || preview_w > LCD_PANEL_H_RES || preview_h > LCD_PANEL_V_RES) { lcd_fill_err(err, err_len, "invalid preview size"); return ESP_ERR_INVALID_SIZE; } if (clear_before_draw) { esp_err_t clear_rc = st7789p3_fill_color(s_lcd.panel, bg_color); if (clear_rc != ESP_OK) { lcd_fill_err(err, err_len, "display clear failed"); return clear_rc; } esp_err_t draw_rc = lcd_draw_preview_pixels_locked(s_lcd.preview_pixels, preview_w, preview_h, err, err_len); if (draw_rc == ESP_OK) { ESP_LOGI(TAG, "preview drawn directly: size=%ux%u", (unsigned)preview_w, (unsigned)preview_h); } return draw_rc; } bool can_fade = (prev_pixels != NULL && prev_w == preview_w && prev_h == preview_h); if (!can_fade) { esp_err_t draw_rc = lcd_draw_preview_pixels_locked(s_lcd.preview_pixels, preview_w, preview_h, err, err_len); if (draw_rc == ESP_OK) { ESP_LOGI(TAG, "preview drawn directly: size=%ux%u", (unsigned)preview_w, (unsigned)preview_h); } return draw_rc; } size_t pixel_count = (size_t)preview_w * (size_t)preview_h; uint16_t *fade_pixels = (uint16_t *)heap_caps_malloc(pixel_count * sizeof(uint16_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); if (fade_pixels == NULL) { fade_pixels = (uint16_t *)heap_caps_malloc(pixel_count * sizeof(uint16_t), MALLOC_CAP_8BIT); } if (fade_pixels == NULL) { // Fallback to direct draw when memory is tight. esp_err_t draw_rc = lcd_draw_preview_pixels_locked(s_lcd.preview_pixels, preview_w, preview_h, err, err_len); if (draw_rc == ESP_OK) { ESP_LOGI(TAG, "preview drawn directly (fade fallback): size=%ux%u", (unsigned)preview_w, (unsigned)preview_h); } return draw_rc; } const uint32_t frame_count = LCD_PREVIEW_FADE_STEPS + 1u; uint32_t frame_delay_ms = LCD_PREVIEW_FADE_DURATION_MS / frame_count; if (frame_delay_ms == 0) { frame_delay_ms = 1; } for (uint32_t step = 1; step <= LCD_PREVIEW_FADE_STEPS; ++step) { uint8_t alpha = (uint8_t)((step * 255u) / frame_count); for (size_t i = 0; i < pixel_count; ++i) { fade_pixels[i] = lcd_blend_rgb565(prev_pixels[i], s_lcd.preview_pixels[i], alpha); } esp_err_t draw_rc = lcd_draw_preview_pixels_locked(fade_pixels, preview_w, preview_h, err, err_len); if (draw_rc != ESP_OK) { heap_caps_free(fade_pixels); return draw_rc; } vTaskDelay(pdMS_TO_TICKS(frame_delay_ms)); } heap_caps_free(fade_pixels); esp_err_t draw_rc = lcd_draw_preview_pixels_locked(s_lcd.preview_pixels, preview_w, preview_h, err, err_len); if (draw_rc == ESP_OK) { ESP_LOGI(TAG, "preview drawn with fade: size=%ux%u fade_ms=%u", (unsigned)preview_w, (unsigned)preview_h, (unsigned)LCD_PREVIEW_FADE_DURATION_MS); } return draw_rc; } void lcd_module_deinit(void) { #if CONFIG_TQ_SCREEN_ENABLE if (s_lcd.lock != NULL) { if (xSemaphoreTake(s_lcd.lock, pdMS_TO_TICKS(1000)) != pdTRUE) { return; } } if (s_lcd.preview_pixels != NULL) { heap_caps_free(s_lcd.preview_pixels); s_lcd.preview_pixels = NULL; } s_lcd.preview_w = 0; s_lcd.preview_h = 0; if (s_lcd.lock != NULL) { xSemaphoreGive(s_lcd.lock); vSemaphoreDelete(s_lcd.lock); s_lcd.lock = NULL; } if (s_lcd.panel != NULL) { (void)st7789p3_del(s_lcd.panel); s_lcd.panel = NULL; } s_lcd.initialized = false; #endif } esp_err_t lcd_module_init(void) { #if !CONFIG_TQ_SCREEN_ENABLE return ESP_ERR_NOT_SUPPORTED; #else if (s_lcd.initialized) { return ESP_OK; } st7789p3_config_t lcd_cfg = ST7789P3_CONFIG_DEFAULT(); lcd_cfg.spi_host = ST7789_SPI_HOST; lcd_cfg.pin_miso = CONFIG_TQ_PRINTER_MISO_PIN; lcd_cfg.pin_mosi = CONFIG_TQ_SPI_SHARED_MOSI_PIN; lcd_cfg.pin_sclk = CONFIG_TQ_SPI_SHARED_CLK_PIN; lcd_cfg.pin_cs = CONFIG_TQ_SCREEN_CS_PIN; lcd_cfg.pin_dc = CONFIG_TQ_SCREEN_DC_PIN; lcd_cfg.pin_rst = resolve_reset_pin(); lcd_cfg.pin_bckl = CONFIG_TQ_SCREEN_BACKLIGHT_PIN; lcd_cfg.backlight_on_level = ST7789_BACKLIGHT_ON_LEVEL; lcd_cfg.pclk_hz = CONFIG_TQ_SCREEN_PIXEL_CLOCK_HZ; lcd_cfg.spi_mode = CONFIG_TQ_SCREEN_SPI_MODE; lcd_cfg.h_res = LCD_PANEL_H_RES; lcd_cfg.v_res = LCD_PANEL_V_RES; lcd_cfg.x_gap = (uint16_t)CONFIG_TQ_SCREEN_X_GAP; lcd_cfg.y_gap = (uint16_t)CONFIG_TQ_SCREEN_Y_GAP; lcd_cfg.madctl_val = lcd_build_madctl(); lcd_cfg.invert_color = CONFIG_TQ_SCREEN_INVERT_COLOR; lcd_cfg.swap_color_bytes = CONFIG_TQ_SCREEN_SWAP_COLOR_BYTES; lcd_cfg.max_transfer_sz = CONFIG_TQ_SCREEN_DRAW_LINES * LCD_PANEL_H_RES * sizeof(uint16_t) + 8; esp_err_t err = st7789p3_new_panel(&lcd_cfg, &s_lcd.panel); if (err != ESP_OK) { return err; } err = st7789p3_init(s_lcd.panel); if (err != ESP_OK) { lcd_module_deinit(); return err; } s_lcd.lock = xSemaphoreCreateMutex(); if (s_lcd.lock == NULL) { lcd_module_deinit(); return ESP_ERR_NO_MEM; } s_lcd.initialized = true; s_lcd.shared_spi_dirty = false; ESP_LOGI(TAG, "st7789p3 ready: spi_host=%d mosi=%d clk=%d miso=%d dc=%d cs=%d rst=%d bk=%d pclk=%u mode=%u res=%ux%u madctl=0x%02x gap=%u,%u invert=%d swap_bytes=%d", ST7789_SPI_HOST, CONFIG_TQ_SPI_SHARED_MOSI_PIN, CONFIG_TQ_SPI_SHARED_CLK_PIN, CONFIG_TQ_PRINTER_MISO_PIN, CONFIG_TQ_SCREEN_DC_PIN, CONFIG_TQ_SCREEN_CS_PIN, lcd_cfg.pin_rst, CONFIG_TQ_SCREEN_BACKLIGHT_PIN, (unsigned)lcd_cfg.pclk_hz, (unsigned)lcd_cfg.spi_mode, (unsigned)lcd_cfg.h_res, (unsigned)lcd_cfg.v_res, (unsigned)lcd_cfg.madctl_val, (unsigned)lcd_cfg.x_gap, (unsigned)lcd_cfg.y_gap, lcd_cfg.invert_color ? 1 : 0, lcd_cfg.swap_color_bytes ? 1 : 0); return ESP_OK; #endif } esp_err_t lcd_module_show_raster_1bpp(const uint8_t *raster, size_t raster_len, uint16_t width, uint16_t height, uint32_t timeout_ms, char *err, size_t err_len) { #if !CONFIG_TQ_SCREEN_ENABLE return ESP_ERR_NOT_SUPPORTED; #else ESP_RETURN_ON_ERROR(lcd_recover_panel_for_shared_spi(), TAG, "display init failed"); if (timeout_ms == 0) { timeout_ms = LCD_PREVIEW_TIMEOUT_MS_DEFAULT; } uint16_t *preview_pixels = NULL; uint16_t preview_w = 0; uint16_t preview_h = 0; esp_err_t convert_err = lcd_render_raster_to_rgb565(raster, raster_len, width, height, &preview_pixels, &preview_w, &preview_h, err, err_len); if (convert_err != ESP_OK) { return convert_err; } if (!lcd_lock_take(timeout_ms)) { heap_caps_free(preview_pixels); lcd_fill_err(err, err_len, "display lock timeout"); return ESP_ERR_TIMEOUT; } uint16_t *prev_pixels = s_lcd.preview_pixels; uint16_t prev_w = s_lcd.preview_w; uint16_t prev_h = s_lcd.preview_h; bool clear_before_draw = (s_lcd.preview_pixels == NULL || s_lcd.preview_w != preview_w || s_lcd.preview_h != preview_h); s_lcd.preview_pixels = preview_pixels; s_lcd.preview_w = preview_w; s_lcd.preview_h = preview_h; esp_err_t draw_err = lcd_apply_preview_locked(prev_pixels, prev_w, prev_h, clear_before_draw, LCD_RGB565_WHITE, err, err_len); if (prev_pixels != NULL) { heap_caps_free(prev_pixels); } lcd_lock_give(); return draw_err; #endif } esp_err_t lcd_module_show_png(const uint8_t *png, size_t png_len, uint32_t timeout_ms, char *err, size_t err_len) { #if !CONFIG_TQ_SCREEN_ENABLE return ESP_ERR_NOT_SUPPORTED; #else ESP_RETURN_ON_ERROR(lcd_recover_panel_for_shared_spi(), TAG, "display init failed"); if (timeout_ms == 0) { timeout_ms = LCD_PREVIEW_TIMEOUT_MS_DEFAULT; } uint16_t *src_pixels = NULL; uint16_t src_w = 0; uint16_t src_h = 0; esp_err_t decode_err = platform_lcd_decode_png_rgb565(png, png_len, &src_pixels, &src_w, &src_h, err, err_len); if (decode_err != ESP_OK) { return decode_err; } uint16_t *preview_pixels = NULL; uint16_t preview_w = 0; uint16_t preview_h = 0; esp_err_t scale_err = lcd_scale_rgb565_to_preview(src_pixels, src_w, src_h, &preview_pixels, &preview_w, &preview_h, err, err_len); heap_caps_free(src_pixels); if (scale_err != ESP_OK) { return scale_err; } if (!lcd_lock_take(timeout_ms)) { heap_caps_free(preview_pixels); lcd_fill_err(err, err_len, "display lock timeout"); return ESP_ERR_TIMEOUT; } uint16_t *prev_pixels = s_lcd.preview_pixels; uint16_t prev_w = s_lcd.preview_w; uint16_t prev_h = s_lcd.preview_h; bool clear_before_draw = (s_lcd.preview_pixels == NULL || s_lcd.preview_w != preview_w || s_lcd.preview_h != preview_h); s_lcd.preview_pixels = preview_pixels; s_lcd.preview_w = preview_w; s_lcd.preview_h = preview_h; esp_err_t draw_err = lcd_apply_preview_locked(prev_pixels, prev_w, prev_h, clear_before_draw, LCD_RGB565_WHITE, err, err_len); if (prev_pixels != NULL) { heap_caps_free(prev_pixels); } lcd_lock_give(); return draw_err; #endif } esp_err_t lcd_module_show_boot_logo(void) { #if !CONFIG_TQ_SCREEN_ENABLE return ESP_ERR_NOT_SUPPORTED; #else ESP_RETURN_ON_ERROR(lcd_recover_panel_for_shared_spi(), TAG, "display init failed"); if (!lcd_lock_take(LCD_LOCK_TIMEOUT_MS)) { return ESP_ERR_TIMEOUT; } uint16_t *logo_pixels = NULL; uint16_t logo_w = 0; uint16_t logo_h = 0; char decode_err[96] = {0}; esp_err_t decode_rc = platform_lcd_decode_logo_png(&logo_pixels, &logo_w, &logo_h, decode_err, sizeof(decode_err)); if (decode_rc != ESP_OK) { ESP_LOGW(TAG, "boot logo decode failed: rc=0x%x msg=%s", (unsigned)decode_rc, decode_err); lcd_lock_give(); return decode_rc; } uint16_t *prev_pixels = s_lcd.preview_pixels; uint16_t prev_w = s_lcd.preview_w; uint16_t prev_h = s_lcd.preview_h; bool clear_before_draw = (s_lcd.preview_pixels == NULL || s_lcd.preview_w != logo_w || s_lcd.preview_h != logo_h); s_lcd.preview_pixels = logo_pixels; s_lcd.preview_w = logo_w; s_lcd.preview_h = logo_h; esp_err_t draw_err = lcd_apply_preview_locked(prev_pixels, prev_w, prev_h, clear_before_draw, LCD_RGB565_BLACK, decode_err, sizeof(decode_err)); if (prev_pixels != NULL) { heap_caps_free(prev_pixels); } lcd_lock_give(); if (draw_err != ESP_OK) { ESP_LOGW(TAG, "boot logo draw failed: rc=0x%x msg=%s", (unsigned)draw_err, decode_err); return draw_err; } return ESP_OK; #endif }