chore(repo): keep only current main snapshot

History squashed to one root commit to permanently drop old branch history and objects.
This commit is contained in:
admin
2026-02-28 16:41:12 +08:00
commit 8df2095acf
89 changed files with 21634 additions and 0 deletions

View File

@@ -0,0 +1,904 @@
#include "display_lcd_module.h"
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "esp_check.h"
#include "esp_heap_caps.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "lvgl.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 200
#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
#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;
lv_disp_draw_buf_t lv_draw_buf;
lv_disp_drv_t lv_drv;
lv_disp_t *lv_disp;
lv_obj_t *preview_img;
lv_color_t *draw_buf_1;
lv_color_t *draw_buf_2;
uint16_t *preview_pixels;
lv_img_dsc_t preview_dsc;
bool lvgl_inited;
bool lvgl_ready;
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 void *lcd_alloc_fb(size_t bytes)
{
void *buf = heap_caps_malloc(bytes, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
if (buf == NULL) {
buf = heap_caps_malloc(bytes, MALLOC_CAP_8BIT);
}
return buf;
}
static void lcd_free_lvgl_buffers(void)
{
if (s_lcd.draw_buf_1 != NULL) {
heap_caps_free(s_lcd.draw_buf_1);
s_lcd.draw_buf_1 = NULL;
}
if (s_lcd.draw_buf_2 != NULL) {
heap_caps_free(s_lcd.draw_buf_2);
s_lcd.draw_buf_2 = NULL;
}
}
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;
}
/*
* Direct-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_lvgl_flush_cb(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_map)
{
st7789p3_handle_t panel = (st7789p3_handle_t)disp_drv->user_data;
if (panel == NULL || area == NULL || color_map == NULL) {
lv_disp_flush_ready(disp_drv);
return;
}
if (area->x1 < 0 || area->y1 < 0 || area->x2 < area->x1 || area->y2 < area->y1) {
lv_disp_flush_ready(disp_drv);
return;
}
esp_err_t err = st7789p3_draw_bitmap(panel,
(uint16_t)area->x1,
(uint16_t)area->y1,
(uint16_t)(area->x2 + 1),
(uint16_t)(area->y2 + 1),
(const uint16_t *)color_map);
if (err != ESP_OK) {
ESP_LOGW(TAG, "lvgl flush failed: %s", esp_err_to_name(err));
}
lv_disp_flush_ready(disp_drv);
}
static esp_err_t lcd_lvgl_init_locked(void)
{
if (s_lcd.lvgl_ready) {
return ESP_OK;
}
if (!s_lcd.lvgl_inited) {
lv_init();
s_lcd.lvgl_inited = true;
}
const size_t draw_buf_pixels = LCD_PANEL_H_RES * CONFIG_TQ_SCREEN_DRAW_LINES;
const size_t draw_buf_bytes = draw_buf_pixels * sizeof(lv_color_t);
s_lcd.draw_buf_1 = (lv_color_t *)lcd_alloc_fb(draw_buf_bytes);
if (s_lcd.draw_buf_1 == NULL) {
return ESP_ERR_NO_MEM;
}
s_lcd.draw_buf_2 = (lv_color_t *)lcd_alloc_fb(draw_buf_bytes);
if (s_lcd.draw_buf_2 == NULL) {
lcd_free_lvgl_buffers();
return ESP_ERR_NO_MEM;
}
lv_disp_draw_buf_init(&s_lcd.lv_draw_buf, s_lcd.draw_buf_1, s_lcd.draw_buf_2, draw_buf_pixels);
lv_disp_drv_init(&s_lcd.lv_drv);
s_lcd.lv_drv.hor_res = LCD_PANEL_H_RES;
s_lcd.lv_drv.ver_res = LCD_PANEL_V_RES;
s_lcd.lv_drv.flush_cb = lcd_lvgl_flush_cb;
s_lcd.lv_drv.draw_buf = &s_lcd.lv_draw_buf;
s_lcd.lv_drv.user_data = s_lcd.panel;
s_lcd.lv_disp = lv_disp_drv_register(&s_lcd.lv_drv);
if (s_lcd.lv_disp == NULL) {
lcd_free_lvgl_buffers();
ESP_LOGE(TAG, "lvgl display register failed");
return ESP_FAIL;
}
lv_obj_t *screen = lv_scr_act();
lv_obj_set_style_bg_color(screen, lv_color_black(), LV_PART_MAIN);
lv_obj_set_style_bg_opa(screen, LV_OPA_COVER, LV_PART_MAIN);
lv_obj_clear_flag(screen, LV_OBJ_FLAG_SCROLLABLE);
s_lcd.preview_img = lv_img_create(screen);
lv_obj_add_flag(s_lcd.preview_img, LV_OBJ_FLAG_HIDDEN);
lv_obj_center(s_lcd.preview_img);
s_lcd.lvgl_ready = true;
lv_refr_now(s_lcd.lv_disp);
return ESP_OK;
}
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,
lv_color_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_dsc.header.w;
uint16_t preview_h = s_lcd.preview_dsc.header.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, (uint16_t)bg_color.full);
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.lv_disp != NULL) {
lv_disp_remove(s_lcd.lv_disp);
s_lcd.lv_disp = NULL;
}
if (s_lcd.preview_pixels != NULL) {
heap_caps_free(s_lcd.preview_pixels);
s_lcd.preview_pixels = NULL;
}
lcd_free_lvgl_buffers();
s_lcd.preview_img = NULL;
s_lcd.lvgl_ready = false;
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;
}
if (!lcd_lock_take(LCD_LOCK_TIMEOUT_MS)) {
lcd_module_deinit();
return ESP_ERR_TIMEOUT;
}
err = lcd_lvgl_init_locked();
lcd_lock_give();
if (err != ESP_OK) {
lcd_module_deinit();
return err;
}
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_dsc.header.w;
uint16_t prev_h = s_lcd.preview_dsc.header.h;
bool clear_before_draw = (s_lcd.preview_pixels == NULL ||
s_lcd.preview_dsc.header.w != preview_w ||
s_lcd.preview_dsc.header.h != preview_h);
s_lcd.preview_pixels = preview_pixels;
memset(&s_lcd.preview_dsc, 0, sizeof(s_lcd.preview_dsc));
s_lcd.preview_dsc.header.always_zero = 0;
s_lcd.preview_dsc.header.w = preview_w;
s_lcd.preview_dsc.header.h = preview_h;
s_lcd.preview_dsc.header.cf = LV_IMG_CF_TRUE_COLOR;
s_lcd.preview_dsc.data_size = (uint32_t)((size_t)preview_w * preview_h * sizeof(uint16_t));
s_lcd.preview_dsc.data = (const uint8_t *)s_lcd.preview_pixels;
esp_err_t draw_err = lcd_apply_preview_locked(prev_pixels,
prev_w,
prev_h,
clear_before_draw,
lv_color_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_dsc.header.w;
uint16_t prev_h = s_lcd.preview_dsc.header.h;
bool clear_before_draw = (s_lcd.preview_pixels == NULL ||
s_lcd.preview_dsc.header.w != preview_w ||
s_lcd.preview_dsc.header.h != preview_h);
s_lcd.preview_pixels = preview_pixels;
memset(&s_lcd.preview_dsc, 0, sizeof(s_lcd.preview_dsc));
s_lcd.preview_dsc.header.always_zero = 0;
s_lcd.preview_dsc.header.w = preview_w;
s_lcd.preview_dsc.header.h = preview_h;
s_lcd.preview_dsc.header.cf = LV_IMG_CF_TRUE_COLOR;
s_lcd.preview_dsc.data_size = (uint32_t)((size_t)preview_w * preview_h * sizeof(uint16_t));
s_lcd.preview_dsc.data = (const uint8_t *)s_lcd.preview_pixels;
esp_err_t draw_err = lcd_apply_preview_locked(prev_pixels,
prev_w,
prev_h,
clear_before_draw,
lv_color_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_start_demo(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 *demo_pixels = NULL;
uint16_t demo_w = 0;
uint16_t demo_h = 0;
char decode_err[96] = {0};
esp_err_t decode_rc = platform_lcd_decode_demo_png(&demo_pixels,
&demo_w,
&demo_h,
decode_err,
sizeof(decode_err));
if (decode_rc != ESP_OK) {
ESP_LOGW(TAG, "demo png 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_dsc.header.w;
uint16_t prev_h = s_lcd.preview_dsc.header.h;
bool clear_before_draw = (s_lcd.preview_pixels == NULL ||
s_lcd.preview_dsc.header.w != demo_w ||
s_lcd.preview_dsc.header.h != demo_h);
s_lcd.preview_pixels = demo_pixels;
memset(&s_lcd.preview_dsc, 0, sizeof(s_lcd.preview_dsc));
s_lcd.preview_dsc.header.always_zero = 0;
s_lcd.preview_dsc.header.w = demo_w;
s_lcd.preview_dsc.header.h = demo_h;
s_lcd.preview_dsc.header.cf = LV_IMG_CF_TRUE_COLOR;
s_lcd.preview_dsc.data_size = (uint32_t)((size_t)demo_w * demo_h * sizeof(uint16_t));
s_lcd.preview_dsc.data = (const uint8_t *)s_lcd.preview_pixels;
esp_err_t draw_err = lcd_apply_preview_locked(prev_pixels,
prev_w,
prev_h,
clear_before_draw,
lv_color_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, "demo draw failed: rc=0x%x msg=%s", (unsigned)draw_err, decode_err);
return draw_err;
}
return ESP_OK;
#endif
}