feat(lcd): migrate driver to platform and replace jpg demo with png
This commit is contained in:
@@ -5,6 +5,9 @@ idf_component_register(
|
||||
"src/ble_printer_client.c"
|
||||
"src/direct_thermal_printer.c"
|
||||
"src/display_st7789.c"
|
||||
"src/display_lcd_module.c"
|
||||
"src/display_lcd_decode_image.c"
|
||||
"src/display_st7789p3.c"
|
||||
"src/voice_audio.c"
|
||||
"src/runtime_policy.c"
|
||||
"src/runtime_diagnostics.c"
|
||||
@@ -21,8 +24,13 @@ idf_component_register(
|
||||
nvs_flash
|
||||
mbedtls
|
||||
esp_adc
|
||||
esp_lcd
|
||||
esp_driver_gpio
|
||||
esp_driver_spi
|
||||
lvgl
|
||||
espressif__libpng
|
||||
esp_codec_dev
|
||||
esp_audio_codec
|
||||
driver
|
||||
EMBED_FILES
|
||||
"assets/lcd_demo.png"
|
||||
)
|
||||
|
||||
BIN
components/platform/assets/lcd_demo.png
Normal file
BIN
components/platform/assets/lcd_demo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 183 KiB |
@@ -92,6 +92,13 @@ esp_err_t platform_direct_printer_gap_move(uint32_t timeout_ms, char *err, size_
|
||||
// ---------- display_st7789 ----------
|
||||
esp_err_t platform_display_init(void);
|
||||
esp_err_t platform_display_show_test_pattern(void);
|
||||
esp_err_t platform_display_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);
|
||||
void platform_display_deinit(void);
|
||||
|
||||
// ---------- voice_audio ----------
|
||||
|
||||
12
components/platform/internal/display_lcd_decode_image.h
Normal file
12
components/platform/internal/display_lcd_decode_image.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "esp_err.h"
|
||||
|
||||
esp_err_t platform_lcd_decode_demo_png(uint16_t **out_pixels,
|
||||
uint16_t *out_width,
|
||||
uint16_t *out_height,
|
||||
char *err,
|
||||
size_t err_len);
|
||||
25
components/platform/internal/display_lcd_module.h
Normal file
25
components/platform/internal/display_lcd_module.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
esp_err_t lcd_module_init(void);
|
||||
esp_err_t lcd_module_start_demo(void);
|
||||
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);
|
||||
void lcd_module_deinit(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
92
components/platform/internal/display_st7789p3.h
Normal file
92
components/platform/internal/display_st7789p3.h
Normal file
@@ -0,0 +1,92 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "driver/spi_master.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct st7789p3_panel_t *st7789p3_handle_t;
|
||||
|
||||
#define ST7789P3_MADCTL_MY 0x80
|
||||
#define ST7789P3_MADCTL_MX 0x40
|
||||
#define ST7789P3_MADCTL_MV 0x20
|
||||
#define ST7789P3_MADCTL_ML 0x10
|
||||
#define ST7789P3_MADCTL_BGR 0x08
|
||||
#define ST7789P3_MADCTL_MH 0x04
|
||||
|
||||
typedef struct {
|
||||
spi_host_device_t spi_host;
|
||||
int pin_mosi;
|
||||
int pin_miso;
|
||||
int pin_sclk;
|
||||
int pin_cs;
|
||||
int pin_dc;
|
||||
int pin_rst;
|
||||
int pin_bckl;
|
||||
uint32_t pclk_hz;
|
||||
uint8_t spi_mode;
|
||||
int queue_size;
|
||||
size_t max_transfer_sz;
|
||||
uint16_t h_res;
|
||||
uint16_t v_res;
|
||||
uint8_t madctl_val;
|
||||
bool invert_color;
|
||||
bool swap_color_bytes;
|
||||
int backlight_on_level;
|
||||
bool init_spi_bus;
|
||||
} st7789p3_config_t;
|
||||
|
||||
#define ST7789P3_CONFIG_DEFAULT() \
|
||||
{ \
|
||||
.spi_host = SPI2_HOST, \
|
||||
.pin_mosi = -1, \
|
||||
.pin_miso = -1, \
|
||||
.pin_sclk = -1, \
|
||||
.pin_cs = -1, \
|
||||
.pin_dc = -1, \
|
||||
.pin_rst = -1, \
|
||||
.pin_bckl = -1, \
|
||||
.pclk_hz = 10 * 1000 * 1000, \
|
||||
.spi_mode = 0, \
|
||||
.queue_size = 7, \
|
||||
.max_transfer_sz = 0, \
|
||||
.h_res = 320, \
|
||||
.v_res = 240, \
|
||||
.madctl_val = ST7789P3_MADCTL_MX | ST7789P3_MADCTL_MV, \
|
||||
.invert_color = false, \
|
||||
.swap_color_bytes = false, \
|
||||
.backlight_on_level = 1, \
|
||||
.init_spi_bus = true, \
|
||||
}
|
||||
|
||||
esp_err_t st7789p3_new_panel(const st7789p3_config_t *config, st7789p3_handle_t *ret_panel);
|
||||
esp_err_t st7789p3_del(st7789p3_handle_t panel);
|
||||
|
||||
esp_err_t st7789p3_reset(st7789p3_handle_t panel);
|
||||
esp_err_t st7789p3_init(st7789p3_handle_t panel);
|
||||
|
||||
esp_err_t st7789p3_set_backlight(st7789p3_handle_t panel, bool on);
|
||||
esp_err_t st7789p3_set_madctl(st7789p3_handle_t panel, uint8_t madctl_val);
|
||||
esp_err_t st7789p3_set_invert(st7789p3_handle_t panel, bool invert_color);
|
||||
esp_err_t st7789p3_set_swap_color_bytes(st7789p3_handle_t panel, bool swap_color_bytes);
|
||||
|
||||
esp_err_t st7789p3_set_window(
|
||||
st7789p3_handle_t panel, uint16_t x_start, uint16_t y_start, uint16_t x_end, uint16_t y_end);
|
||||
esp_err_t st7789p3_draw_bitmap(st7789p3_handle_t panel,
|
||||
uint16_t x_start,
|
||||
uint16_t y_start,
|
||||
uint16_t x_end,
|
||||
uint16_t y_end,
|
||||
const uint16_t *color_data);
|
||||
|
||||
esp_err_t st7789p3_fill_color(st7789p3_handle_t panel, uint16_t color);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
113
components/platform/src/display_lcd_decode_image.c
Normal file
113
components/platform/src/display_lcd_decode_image.c
Normal file
@@ -0,0 +1,113 @@
|
||||
#include "display_lcd_decode_image.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "esp_check.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_log.h"
|
||||
#include "lvgl.h"
|
||||
#include "png.h"
|
||||
|
||||
static const char *TAG = "lcd_png_dec";
|
||||
|
||||
extern const uint8_t lcd_demo_png_start[] asm("_binary_lcd_demo_png_start");
|
||||
extern const uint8_t lcd_demo_png_end[] asm("_binary_lcd_demo_png_end");
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t platform_lcd_decode_demo_png(uint16_t **out_pixels,
|
||||
uint16_t *out_width,
|
||||
uint16_t *out_height,
|
||||
char *err,
|
||||
size_t err_len)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
ESP_RETURN_ON_FALSE(out_pixels != NULL && out_width != NULL && out_height != NULL,
|
||||
ESP_ERR_INVALID_ARG,
|
||||
TAG,
|
||||
"invalid output args");
|
||||
*out_pixels = NULL;
|
||||
*out_width = 0;
|
||||
*out_height = 0;
|
||||
|
||||
const size_t png_len = (size_t)(lcd_demo_png_end - lcd_demo_png_start);
|
||||
ESP_RETURN_ON_FALSE(png_len > 0, ESP_ERR_INVALID_SIZE, TAG, "empty png data");
|
||||
|
||||
png_image image;
|
||||
memset(&image, 0, sizeof(image));
|
||||
image.version = PNG_IMAGE_VERSION;
|
||||
|
||||
ESP_RETURN_ON_FALSE(png_image_begin_read_from_memory(&image, lcd_demo_png_start, png_len),
|
||||
ESP_FAIL,
|
||||
TAG,
|
||||
"png header decode failed: %s",
|
||||
image.message);
|
||||
ESP_GOTO_ON_FALSE(image.width > 0 && image.height > 0, ESP_ERR_INVALID_SIZE, fail, TAG, "invalid png size");
|
||||
ESP_GOTO_ON_FALSE(image.width <= UINT16_MAX && image.height <= UINT16_MAX,
|
||||
ESP_ERR_INVALID_SIZE,
|
||||
fail,
|
||||
TAG,
|
||||
"png dimensions too large");
|
||||
ESP_GOTO_ON_FALSE(image.width <= SIZE_MAX / image.height, ESP_ERR_INVALID_SIZE, fail, TAG, "png too large");
|
||||
|
||||
image.format = PNG_FORMAT_RGB;
|
||||
png_alloc_size_t rgb_size = PNG_IMAGE_SIZE(image);
|
||||
ESP_GOTO_ON_FALSE(rgb_size > 0, ESP_ERR_INVALID_SIZE, fail, TAG, "png output size invalid");
|
||||
|
||||
uint8_t *rgb = (uint8_t *)heap_caps_malloc((size_t)rgb_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
if (rgb == NULL) {
|
||||
rgb = (uint8_t *)heap_caps_malloc((size_t)rgb_size, MALLOC_CAP_8BIT);
|
||||
}
|
||||
ESP_GOTO_ON_FALSE(rgb != NULL, ESP_ERR_NO_MEM, fail, TAG, "alloc rgb buffer failed");
|
||||
|
||||
png_alloc_size_t stride = PNG_IMAGE_ROW_STRIDE(image);
|
||||
if (!png_image_finish_read(&image, NULL, rgb, stride, NULL)) {
|
||||
const char *png_err = image.message[0] != '\0' ? image.message : "png decode failed";
|
||||
lcd_fill_err(err, err_len, png_err);
|
||||
heap_caps_free(rgb);
|
||||
png_image_free(&image);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
png_image_free(&image);
|
||||
|
||||
size_t pixel_count = (size_t)image.width * (size_t)image.height;
|
||||
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, "alloc rgb565 buffer failed");
|
||||
heap_caps_free(rgb);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < pixel_count; ++i) {
|
||||
const size_t base = i * 3u;
|
||||
lv_color_t color = lv_color_make(rgb[base], rgb[base + 1u], rgb[base + 2u]);
|
||||
pixels[i] = (uint16_t)color.full;
|
||||
}
|
||||
heap_caps_free(rgb);
|
||||
|
||||
*out_pixels = pixels;
|
||||
*out_width = (uint16_t)image.width;
|
||||
*out_height = (uint16_t)image.height;
|
||||
return ESP_OK;
|
||||
|
||||
fail:
|
||||
lcd_fill_err(err, err_len, "invalid png");
|
||||
png_image_free(&image);
|
||||
if (ret == ESP_OK) {
|
||||
ret = ESP_FAIL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
513
components/platform/src/display_lcd_module.c
Normal file
513
components/platform/src/display_lcd_module.c
Normal file
@@ -0,0 +1,513 @@
|
||||
#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_PANEL_H_RES 320
|
||||
#define LCD_PANEL_V_RES 240
|
||||
#define LCD_PREVIEW_TIMEOUT_MS_DEFAULT 200
|
||||
#define LCD_LOCK_TIMEOUT_MS 1000
|
||||
#define LCD_DRAW_LINES 20
|
||||
|
||||
#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_DRAW_LINES
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_SCREEN_BACKLIGHT_ACTIVE_HIGH
|
||||
#define CONFIG_TQ_SCREEN_BACKLIGHT_ACTIVE_HIGH 1
|
||||
#endif
|
||||
|
||||
#if CONFIG_TQ_SCREEN_BACKLIGHT_ACTIVE_HIGH
|
||||
#define ST7789_BACKLIGHT_ON_LEVEL 1
|
||||
#else
|
||||
#define ST7789_BACKLIGHT_ON_LEVEL 0
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
bool bgr;
|
||||
bool swap_bytes;
|
||||
bool invert;
|
||||
} lcd_color_mode_t;
|
||||
|
||||
// Locked profile chosen from calibration (equivalent to MODE 6).
|
||||
static const lcd_color_mode_t s_default_color_mode = {
|
||||
.bgr = false,
|
||||
.swap_bytes = true,
|
||||
.invert = true,
|
||||
};
|
||||
|
||||
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;
|
||||
} lcd_state_t;
|
||||
|
||||
static lcd_state_t s_lcd;
|
||||
|
||||
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 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 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;
|
||||
}
|
||||
|
||||
void lcd_module_deinit(void)
|
||||
{
|
||||
#if CONFIG_TQ_SCREEN_ENABLE
|
||||
if (s_lcd.lock != NULL) {
|
||||
(void)xSemaphoreTake(s_lcd.lock, portMAX_DELAY);
|
||||
}
|
||||
|
||||
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.madctl_val = ST7789P3_MADCTL_MX | ST7789P3_MADCTL_MV;
|
||||
if (s_default_color_mode.bgr) {
|
||||
lcd_cfg.madctl_val |= ST7789P3_MADCTL_BGR;
|
||||
}
|
||||
lcd_cfg.invert_color = s_default_color_mode.invert;
|
||||
lcd_cfg.swap_color_bytes = s_default_color_mode.swap_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;
|
||||
ESP_LOGI(TAG,
|
||||
"st7789p3 ready: spi_host=%d mosi=%d clk=%d miso=%d dc=%d cs=%d rst=%d bk=%d pclk=%u",
|
||||
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);
|
||||
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_module_init(), 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;
|
||||
}
|
||||
|
||||
if (s_lcd.preview_pixels != NULL) {
|
||||
heap_caps_free(s_lcd.preview_pixels);
|
||||
}
|
||||
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;
|
||||
|
||||
lv_obj_t *screen = lv_scr_act();
|
||||
lv_obj_set_style_bg_color(screen, lv_color_white(), LV_PART_MAIN);
|
||||
lv_obj_set_style_bg_opa(screen, LV_OPA_COVER, LV_PART_MAIN);
|
||||
lv_img_set_src(s_lcd.preview_img, &s_lcd.preview_dsc);
|
||||
lv_obj_clear_flag(s_lcd.preview_img, LV_OBJ_FLAG_HIDDEN);
|
||||
lv_obj_center(s_lcd.preview_img);
|
||||
lv_refr_now(s_lcd.lv_disp);
|
||||
|
||||
lcd_lock_give();
|
||||
return ESP_OK;
|
||||
#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_module_init(), 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;
|
||||
}
|
||||
|
||||
if (s_lcd.preview_pixels != NULL) {
|
||||
heap_caps_free(s_lcd.preview_pixels);
|
||||
}
|
||||
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;
|
||||
|
||||
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_img_set_src(s_lcd.preview_img, &s_lcd.preview_dsc);
|
||||
lv_obj_clear_flag(s_lcd.preview_img, LV_OBJ_FLAG_HIDDEN);
|
||||
lv_obj_center(s_lcd.preview_img);
|
||||
lv_refr_now(s_lcd.lv_disp);
|
||||
lcd_lock_give();
|
||||
return ESP_OK;
|
||||
#endif
|
||||
}
|
||||
@@ -1,317 +1,29 @@
|
||||
#include "platform.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "display_lcd_module.h"
|
||||
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "esp_lcd_panel_ops.h"
|
||||
#include "esp_lcd_panel_vendor.h"
|
||||
#include "esp_log.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
static const char *TAG = "display_st7789";
|
||||
|
||||
#define ST7789_SPI_HOST SPI2_HOST
|
||||
#define ST7789_COLOR_BYTES_PER_PIXEL 2
|
||||
|
||||
#ifndef CONFIG_TQ_SCREEN_SPI_MODE
|
||||
#define CONFIG_TQ_SCREEN_SPI_MODE 0
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_SCREEN_BACKLIGHT_ACTIVE_HIGH
|
||||
#define CONFIG_TQ_SCREEN_BACKLIGHT_ACTIVE_HIGH 1
|
||||
#endif
|
||||
|
||||
#if CONFIG_TQ_SCREEN_BACKLIGHT_ACTIVE_HIGH
|
||||
#define ST7789_BACKLIGHT_ON_LEVEL 1
|
||||
#define ST7789_BACKLIGHT_OFF_LEVEL 0
|
||||
#else
|
||||
#define ST7789_BACKLIGHT_ON_LEVEL 0
|
||||
#define ST7789_BACKLIGHT_OFF_LEVEL 1
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
esp_lcd_panel_io_handle_t io_handle;
|
||||
esp_lcd_panel_handle_t panel_handle;
|
||||
bool bus_owned;
|
||||
bool initialized;
|
||||
} st7789_display_state_t;
|
||||
|
||||
static st7789_display_state_t s_display;
|
||||
|
||||
#if CONFIG_TQ_SCREEN_COLOR_ORDER_BGR
|
||||
#define ST7789_COLOR_ORDER LCD_RGB_ELEMENT_ORDER_BGR
|
||||
#else
|
||||
#define ST7789_COLOR_ORDER LCD_RGB_ELEMENT_ORDER_RGB
|
||||
#endif
|
||||
|
||||
#if CONFIG_TQ_SCREEN_SWAP_XY
|
||||
#define ST7789_SWAP_XY true
|
||||
#else
|
||||
#define ST7789_SWAP_XY false
|
||||
#endif
|
||||
|
||||
#if CONFIG_TQ_SCREEN_MIRROR_X
|
||||
#define ST7789_MIRROR_X true
|
||||
#else
|
||||
#define ST7789_MIRROR_X false
|
||||
#endif
|
||||
|
||||
#if CONFIG_TQ_SCREEN_MIRROR_Y
|
||||
#define ST7789_MIRROR_Y true
|
||||
#else
|
||||
#define ST7789_MIRROR_Y false
|
||||
#endif
|
||||
|
||||
static uint16_t rgb565(uint8_t r, uint8_t g, uint8_t b) {
|
||||
return (uint16_t)(((uint16_t)(r & 0xF8) << 8) |
|
||||
((uint16_t)(g & 0xFC) << 3) |
|
||||
((uint16_t)(b & 0xF8) >> 3));
|
||||
void platform_display_deinit(void)
|
||||
{
|
||||
lcd_module_deinit();
|
||||
}
|
||||
|
||||
static uint16_t test_pattern_color(int x, int y, int h_res, int v_res) {
|
||||
int top = v_res / 3;
|
||||
int middle = (v_res * 2) / 3;
|
||||
|
||||
if (y < top) {
|
||||
uint8_t r = (uint8_t)((x * 255) / (h_res - 1));
|
||||
uint8_t g = (uint8_t)(255 - r);
|
||||
return rgb565(r, g, 48);
|
||||
}
|
||||
|
||||
if (y < middle) {
|
||||
uint8_t gray = (uint8_t)(((y - top) * 255) / ((middle - top) > 1 ? (middle - top - 1) : 1));
|
||||
return rgb565(gray, gray, gray);
|
||||
}
|
||||
|
||||
bool checker = (((x / 16) + (y / 16)) & 1) != 0;
|
||||
return checker ? rgb565(250, 250, 250) : rgb565(8, 8, 8);
|
||||
esp_err_t platform_display_init(void)
|
||||
{
|
||||
return lcd_module_init();
|
||||
}
|
||||
|
||||
static void fill_test_pattern_chunk(uint16_t *buf,
|
||||
int y_start,
|
||||
int lines,
|
||||
int h_res,
|
||||
int v_res) {
|
||||
for (int row = 0; row < lines; ++row) {
|
||||
int y = y_start + row;
|
||||
for (int x = 0; x < h_res; ++x) {
|
||||
uint16_t color = test_pattern_color(x, y, h_res, v_res);
|
||||
|
||||
if (x == 0 || y == 0 || x == (h_res - 1) || y == (v_res - 1)) {
|
||||
color = rgb565(255, 0, 0);
|
||||
} else if (x == (h_res / 2) || y == (v_res / 2)) {
|
||||
color = rgb565(255, 255, 255);
|
||||
}
|
||||
|
||||
buf[row * h_res + x] = color;
|
||||
}
|
||||
}
|
||||
esp_err_t platform_display_show_test_pattern(void)
|
||||
{
|
||||
return lcd_module_start_demo();
|
||||
}
|
||||
|
||||
void platform_display_deinit(void) {
|
||||
#if CONFIG_TQ_SCREEN_ENABLE
|
||||
if (CONFIG_TQ_SCREEN_BACKLIGHT_PIN >= 0) {
|
||||
(void)gpio_set_level(CONFIG_TQ_SCREEN_BACKLIGHT_PIN, ST7789_BACKLIGHT_OFF_LEVEL);
|
||||
}
|
||||
|
||||
if (s_display.panel_handle != NULL) {
|
||||
(void)esp_lcd_panel_del(s_display.panel_handle);
|
||||
s_display.panel_handle = NULL;
|
||||
}
|
||||
if (s_display.io_handle != NULL) {
|
||||
(void)esp_lcd_panel_io_del(s_display.io_handle);
|
||||
s_display.io_handle = NULL;
|
||||
}
|
||||
if (s_display.bus_owned) {
|
||||
(void)spi_bus_free(ST7789_SPI_HOST);
|
||||
s_display.bus_owned = false;
|
||||
}
|
||||
s_display.initialized = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
esp_err_t platform_display_init(void) {
|
||||
#if !CONFIG_TQ_SCREEN_ENABLE
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#else
|
||||
if (s_display.initialized) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
if (CONFIG_TQ_SCREEN_BACKLIGHT_PIN >= 0) {
|
||||
ESP_RETURN_ON_ERROR(gpio_set_level(CONFIG_TQ_SCREEN_BACKLIGHT_PIN, ST7789_BACKLIGHT_OFF_LEVEL),
|
||||
TAG,
|
||||
"set backlight off failed");
|
||||
}
|
||||
|
||||
const size_t max_transfer_size = (size_t)CONFIG_TQ_SCREEN_H_RES *
|
||||
(size_t)CONFIG_TQ_SCREEN_DRAW_LINES *
|
||||
ST7789_COLOR_BYTES_PER_PIXEL + 8;
|
||||
|
||||
spi_bus_config_t bus_cfg = {
|
||||
.sclk_io_num = CONFIG_TQ_SPI_SHARED_CLK_PIN,
|
||||
.mosi_io_num = CONFIG_TQ_SPI_SHARED_MOSI_PIN,
|
||||
.miso_io_num = -1,
|
||||
.quadwp_io_num = -1,
|
||||
.quadhd_io_num = -1,
|
||||
.max_transfer_sz = (int)max_transfer_size,
|
||||
};
|
||||
|
||||
esp_err_t err = spi_bus_initialize(ST7789_SPI_HOST, &bus_cfg, SPI_DMA_CH_AUTO);
|
||||
if (err == ESP_OK) {
|
||||
s_display.bus_owned = true;
|
||||
} else if (err == ESP_ERR_INVALID_STATE) {
|
||||
s_display.bus_owned = false;
|
||||
ESP_LOGW(TAG, "SPI host already initialized, reuse host=%d", ST7789_SPI_HOST);
|
||||
} else {
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_lcd_panel_io_spi_config_t io_cfg = {
|
||||
.cs_gpio_num = CONFIG_TQ_SCREEN_CS_PIN,
|
||||
.dc_gpio_num = CONFIG_TQ_SCREEN_DC_PIN,
|
||||
.spi_mode = CONFIG_TQ_SCREEN_SPI_MODE,
|
||||
.pclk_hz = CONFIG_TQ_SCREEN_PIXEL_CLOCK_HZ,
|
||||
.trans_queue_depth = 1,
|
||||
.lcd_cmd_bits = 8,
|
||||
.lcd_param_bits = 8,
|
||||
};
|
||||
err = esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)ST7789_SPI_HOST, &io_cfg, &s_display.io_handle);
|
||||
if (err != ESP_OK) {
|
||||
platform_display_deinit();
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_lcd_panel_dev_config_t panel_cfg = {
|
||||
.reset_gpio_num = CONFIG_TQ_SCREEN_RESET_PIN,
|
||||
.rgb_ele_order = ST7789_COLOR_ORDER,
|
||||
.bits_per_pixel = 16,
|
||||
};
|
||||
err = esp_lcd_new_panel_st7789(s_display.io_handle, &panel_cfg, &s_display.panel_handle);
|
||||
if (err != ESP_OK) {
|
||||
platform_display_deinit();
|
||||
return err;
|
||||
}
|
||||
|
||||
err = esp_lcd_panel_reset(s_display.panel_handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "panel reset failed: %s", esp_err_to_name(err));
|
||||
platform_display_deinit();
|
||||
return err;
|
||||
}
|
||||
err = esp_lcd_panel_init(s_display.panel_handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "panel init failed: %s", esp_err_to_name(err));
|
||||
platform_display_deinit();
|
||||
return err;
|
||||
}
|
||||
err = esp_lcd_panel_swap_xy(s_display.panel_handle, ST7789_SWAP_XY);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "swap xy failed: %s", esp_err_to_name(err));
|
||||
platform_display_deinit();
|
||||
return err;
|
||||
}
|
||||
err = esp_lcd_panel_mirror(s_display.panel_handle, ST7789_MIRROR_X, ST7789_MIRROR_Y);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "mirror failed: %s", esp_err_to_name(err));
|
||||
platform_display_deinit();
|
||||
return err;
|
||||
}
|
||||
err = esp_lcd_panel_set_gap(s_display.panel_handle, CONFIG_TQ_SCREEN_X_GAP, CONFIG_TQ_SCREEN_Y_GAP);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "set gap failed: %s", esp_err_to_name(err));
|
||||
platform_display_deinit();
|
||||
return err;
|
||||
}
|
||||
err = esp_lcd_panel_invert_color(s_display.panel_handle, false);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "invert control failed: %s", esp_err_to_name(err));
|
||||
platform_display_deinit();
|
||||
return err;
|
||||
}
|
||||
err = esp_lcd_panel_disp_on_off(s_display.panel_handle, true);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "display on failed: %s", esp_err_to_name(err));
|
||||
platform_display_deinit();
|
||||
return err;
|
||||
}
|
||||
|
||||
if (CONFIG_TQ_SCREEN_BACKLIGHT_PIN >= 0) {
|
||||
err = gpio_set_level(CONFIG_TQ_SCREEN_BACKLIGHT_PIN, ST7789_BACKLIGHT_ON_LEVEL);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "set backlight on failed: %s", esp_err_to_name(err));
|
||||
platform_display_deinit();
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
s_display.initialized = true;
|
||||
ESP_LOGI(TAG,
|
||||
"st7789 ready: spi_host=%d sclk=%d mosi=%d dc=%d cs=%d rst=%d bk=%d bk_on=%d spi_mode=%d size=%dx%d pclk=%u",
|
||||
ST7789_SPI_HOST,
|
||||
CONFIG_TQ_SPI_SHARED_CLK_PIN,
|
||||
CONFIG_TQ_SPI_SHARED_MOSI_PIN,
|
||||
CONFIG_TQ_SCREEN_DC_PIN,
|
||||
CONFIG_TQ_SCREEN_CS_PIN,
|
||||
CONFIG_TQ_SCREEN_RESET_PIN,
|
||||
CONFIG_TQ_SCREEN_BACKLIGHT_PIN,
|
||||
ST7789_BACKLIGHT_ON_LEVEL,
|
||||
CONFIG_TQ_SCREEN_SPI_MODE,
|
||||
CONFIG_TQ_SCREEN_H_RES,
|
||||
CONFIG_TQ_SCREEN_V_RES,
|
||||
(unsigned)CONFIG_TQ_SCREEN_PIXEL_CLOCK_HZ);
|
||||
return ESP_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
esp_err_t platform_display_show_test_pattern(void) {
|
||||
#if !CONFIG_TQ_SCREEN_ENABLE
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#else
|
||||
ESP_RETURN_ON_ERROR(platform_display_init(), TAG, "display init failed");
|
||||
|
||||
const int h_res = CONFIG_TQ_SCREEN_H_RES;
|
||||
const int v_res = CONFIG_TQ_SCREEN_V_RES;
|
||||
const int draw_lines = CONFIG_TQ_SCREEN_DRAW_LINES > 0 ? CONFIG_TQ_SCREEN_DRAW_LINES : 1;
|
||||
const size_t buf_pixels = (size_t)h_res * (size_t)draw_lines;
|
||||
const size_t buf_size = buf_pixels * ST7789_COLOR_BYTES_PER_PIXEL;
|
||||
|
||||
uint16_t *draw_buf = (uint16_t *)heap_caps_malloc(buf_size, MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
|
||||
if (draw_buf == NULL) {
|
||||
draw_buf = (uint16_t *)heap_caps_malloc(buf_size, MALLOC_CAP_8BIT);
|
||||
}
|
||||
ESP_RETURN_ON_FALSE(draw_buf != NULL, ESP_ERR_NO_MEM, TAG, "alloc draw buffer failed");
|
||||
|
||||
ESP_LOGI(TAG, "draw test pattern start");
|
||||
|
||||
esp_err_t err = ESP_OK;
|
||||
for (int y = 0; y < v_res && err == ESP_OK; y += draw_lines) {
|
||||
int chunk_lines = draw_lines;
|
||||
if (y + chunk_lines > v_res) {
|
||||
chunk_lines = v_res - y;
|
||||
}
|
||||
|
||||
fill_test_pattern_chunk(draw_buf, y, chunk_lines, h_res, v_res);
|
||||
err = esp_lcd_panel_draw_bitmap(s_display.panel_handle, 0, y, h_res, y + chunk_lines, draw_buf);
|
||||
if (err != ESP_OK) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Wait for queued color transfer to finish before reusing draw_buf.
|
||||
err = esp_lcd_panel_io_tx_param(s_display.io_handle, -1, NULL, 0);
|
||||
}
|
||||
|
||||
heap_caps_free(draw_buf);
|
||||
|
||||
if (err == ESP_OK) {
|
||||
ESP_LOGI(TAG, "draw test pattern done");
|
||||
}
|
||||
return err;
|
||||
#endif
|
||||
esp_err_t platform_display_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)
|
||||
{
|
||||
return lcd_module_show_raster_1bpp(raster, raster_len, width, height, timeout_ms, err, err_len);
|
||||
}
|
||||
|
||||
515
components/platform/src/display_st7789p3.c
Normal file
515
components/platform/src/display_st7789p3.c
Normal file
@@ -0,0 +1,515 @@
|
||||
#include "display_st7789p3.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#define ST7789P3_CMD_DELAY_FLAG 0x80
|
||||
#define ST7789P3_CMD_DATA_LEN_MASK 0x1F
|
||||
#define ST7789P3_TX_MAX_CHUNK_BYTES 1024
|
||||
#define ST7789P3_SWAP_BUF_PIXELS 256
|
||||
|
||||
typedef struct {
|
||||
uint8_t cmd;
|
||||
uint8_t data[16];
|
||||
uint8_t data_bytes;
|
||||
} st7789p3_lcd_init_cmd_t;
|
||||
|
||||
struct st7789p3_panel_t {
|
||||
st7789p3_config_t cfg;
|
||||
spi_device_handle_t spi;
|
||||
bool bus_owned;
|
||||
};
|
||||
|
||||
static const char *TAG = "st7789p3";
|
||||
|
||||
// Sequence verified on the LCD used by docs/lcd_verification.
|
||||
DRAM_ATTR static const st7789p3_lcd_init_cmd_t s_st7789p3_init_cmds[] = {
|
||||
{0x36, {0x00}, 1},
|
||||
{0x3A, {0x55}, 1},
|
||||
{0xB2, {0x0C, 0x0C, 0x00, 0x33, 0x33}, 5},
|
||||
{0xB7, {0x45}, 1},
|
||||
{0xBB, {0x2B}, 1},
|
||||
{0xC0, {0x2C}, 1},
|
||||
{0xC2, {0x01, 0xFF}, 2},
|
||||
{0xC3, {0x11}, 1},
|
||||
{0xC4, {0x20}, 1},
|
||||
{0xC6, {0x0F}, 1},
|
||||
{0xD0, {0xA4, 0xA1}, 2},
|
||||
{0xE0, {0xD0, 0x00, 0x05, 0x0E, 0x15, 0x0D, 0x37, 0x43, 0x47, 0x09, 0x15, 0x12, 0x16, 0x19}, 14},
|
||||
{0xE1, {0xD0, 0x00, 0x05, 0x0D, 0x0C, 0x06, 0x2D, 0x44, 0x40, 0x0E, 0x1C, 0x18, 0x16, 0x19}, 14},
|
||||
{0x11, {0}, ST7789P3_CMD_DELAY_FLAG},
|
||||
{0, {0}, 0xFF},
|
||||
};
|
||||
|
||||
static esp_err_t st7789p3_validate_config(const st7789p3_config_t *config)
|
||||
{
|
||||
if (config == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (config->pin_mosi < 0 || config->pin_sclk < 0 || config->pin_cs < 0 || config->pin_dc < 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (config->h_res == 0 || config->v_res == 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (config->spi_mode > 3) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t st7789p3_configure_gpio(st7789p3_handle_t panel)
|
||||
{
|
||||
uint64_t pin_mask = 0;
|
||||
|
||||
pin_mask |= (1ULL << panel->cfg.pin_dc);
|
||||
if (panel->cfg.pin_rst >= 0) {
|
||||
pin_mask |= (1ULL << panel->cfg.pin_rst);
|
||||
}
|
||||
if (panel->cfg.pin_bckl >= 0) {
|
||||
pin_mask |= (1ULL << panel->cfg.pin_bckl);
|
||||
}
|
||||
|
||||
gpio_config_t io_conf = {
|
||||
.pin_bit_mask = pin_mask,
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pull_up_en = GPIO_PULLUP_DISABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
};
|
||||
|
||||
return gpio_config(&io_conf);
|
||||
}
|
||||
|
||||
static esp_err_t st7789p3_spi_write(st7789p3_handle_t panel, int dc_level, const void *data, size_t data_size)
|
||||
{
|
||||
if (data_size == 0) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t ret = gpio_set_level(panel->cfg.pin_dc, dc_level);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
spi_transaction_t t = {0};
|
||||
t.length = data_size * 8;
|
||||
t.tx_buffer = data;
|
||||
|
||||
return spi_device_transmit(panel->spi, &t);
|
||||
}
|
||||
|
||||
static esp_err_t st7789p3_send_cmd(st7789p3_handle_t panel, uint8_t cmd)
|
||||
{
|
||||
return st7789p3_spi_write(panel, 0, &cmd, 1);
|
||||
}
|
||||
|
||||
static esp_err_t st7789p3_send_data(st7789p3_handle_t panel, const void *data, size_t data_size)
|
||||
{
|
||||
const uint8_t *data_ptr = (const uint8_t *)data;
|
||||
size_t remaining = data_size;
|
||||
size_t tx_chunk_size = ST7789P3_TX_MAX_CHUNK_BYTES;
|
||||
|
||||
if (panel->cfg.max_transfer_sz > 0 && panel->cfg.max_transfer_sz < tx_chunk_size) {
|
||||
tx_chunk_size = panel->cfg.max_transfer_sz;
|
||||
}
|
||||
|
||||
while (remaining > 0) {
|
||||
size_t chunk = (remaining > tx_chunk_size) ? tx_chunk_size : remaining;
|
||||
esp_err_t ret = st7789p3_spi_write(panel, 1, data_ptr, chunk);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
data_ptr += chunk;
|
||||
remaining -= chunk;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t st7789p3_send_color_data(st7789p3_handle_t panel, const uint16_t *color_data, size_t pixel_count)
|
||||
{
|
||||
if (color_data == NULL || pixel_count == 0) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
if (!panel->cfg.swap_color_bytes) {
|
||||
return st7789p3_send_data(panel, color_data, pixel_count * sizeof(uint16_t));
|
||||
}
|
||||
|
||||
uint8_t swap_buf[ST7789P3_SWAP_BUF_PIXELS * sizeof(uint16_t)];
|
||||
size_t offset = 0;
|
||||
while (offset < pixel_count) {
|
||||
size_t chunk = pixel_count - offset;
|
||||
if (chunk > ST7789P3_SWAP_BUF_PIXELS) {
|
||||
chunk = ST7789P3_SWAP_BUF_PIXELS;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < chunk; ++i) {
|
||||
uint16_t color = color_data[offset + i];
|
||||
swap_buf[i * 2] = (uint8_t)(color >> 8);
|
||||
swap_buf[i * 2 + 1] = (uint8_t)(color & 0xFF);
|
||||
}
|
||||
|
||||
esp_err_t ret = st7789p3_spi_write(panel, 1, swap_buf, chunk * sizeof(uint16_t));
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
offset += chunk;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t st7789p3_run_init_cmds(st7789p3_handle_t panel)
|
||||
{
|
||||
for (size_t i = 0; s_st7789p3_init_cmds[i].data_bytes != 0xFF; i++) {
|
||||
const st7789p3_lcd_init_cmd_t *init_cmd = &s_st7789p3_init_cmds[i];
|
||||
uint8_t data_bytes = init_cmd->data_bytes & ST7789P3_CMD_DATA_LEN_MASK;
|
||||
uint8_t data[16] = {0};
|
||||
|
||||
if (data_bytes > 0) {
|
||||
memcpy(data, init_cmd->data, data_bytes);
|
||||
}
|
||||
|
||||
if (init_cmd->cmd == 0x36 && data_bytes == 1) {
|
||||
data[0] = panel->cfg.madctl_val;
|
||||
}
|
||||
|
||||
esp_err_t ret = st7789p3_send_cmd(panel, init_cmd->cmd);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = st7789p3_send_data(panel, data, data_bytes);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (init_cmd->data_bytes & ST7789P3_CMD_DELAY_FLAG) {
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t st7789p3_new_panel(const st7789p3_config_t *config, st7789p3_handle_t *ret_panel)
|
||||
{
|
||||
if (ret_panel == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
*ret_panel = NULL;
|
||||
|
||||
esp_err_t ret = st7789p3_validate_config(config);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
st7789p3_handle_t panel = calloc(1, sizeof(*panel));
|
||||
if (panel == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
panel->cfg = *config;
|
||||
if (panel->cfg.pclk_hz == 0) {
|
||||
panel->cfg.pclk_hz = 10 * 1000 * 1000;
|
||||
}
|
||||
if (panel->cfg.queue_size <= 0) {
|
||||
panel->cfg.queue_size = 7;
|
||||
}
|
||||
if (panel->cfg.max_transfer_sz == 0) {
|
||||
panel->cfg.max_transfer_sz = panel->cfg.h_res * 20 * sizeof(uint16_t) + 8;
|
||||
}
|
||||
|
||||
ret = st7789p3_configure_gpio(panel);
|
||||
if (ret != ESP_OK) {
|
||||
free(panel);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (panel->cfg.init_spi_bus) {
|
||||
spi_bus_config_t buscfg = {
|
||||
.mosi_io_num = panel->cfg.pin_mosi,
|
||||
.miso_io_num = panel->cfg.pin_miso,
|
||||
.sclk_io_num = panel->cfg.pin_sclk,
|
||||
.quadwp_io_num = -1,
|
||||
.quadhd_io_num = -1,
|
||||
.max_transfer_sz = panel->cfg.max_transfer_sz,
|
||||
};
|
||||
|
||||
ret = spi_bus_initialize(panel->cfg.spi_host, &buscfg, SPI_DMA_CH_AUTO);
|
||||
if (ret == ESP_OK) {
|
||||
panel->bus_owned = true;
|
||||
} else if (ret == ESP_ERR_INVALID_STATE) {
|
||||
ESP_LOGW(TAG, "SPI host already initialized, skip bus init");
|
||||
} else {
|
||||
free(panel);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
spi_device_interface_config_t devcfg = {
|
||||
.clock_speed_hz = panel->cfg.pclk_hz,
|
||||
.mode = panel->cfg.spi_mode,
|
||||
.spics_io_num = panel->cfg.pin_cs,
|
||||
.queue_size = panel->cfg.queue_size,
|
||||
};
|
||||
|
||||
ret = spi_bus_add_device(panel->cfg.spi_host, &devcfg, &panel->spi);
|
||||
if (ret != ESP_OK) {
|
||||
if (panel->bus_owned) {
|
||||
spi_bus_free(panel->cfg.spi_host);
|
||||
}
|
||||
free(panel);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*ret_panel = panel;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t st7789p3_del(st7789p3_handle_t panel)
|
||||
{
|
||||
if (panel == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
esp_err_t local_ret = spi_bus_remove_device(panel->spi);
|
||||
if (local_ret != ESP_OK) {
|
||||
ret = local_ret;
|
||||
}
|
||||
|
||||
if (panel->bus_owned) {
|
||||
local_ret = spi_bus_free(panel->cfg.spi_host);
|
||||
if (ret == ESP_OK && local_ret != ESP_OK) {
|
||||
ret = local_ret;
|
||||
}
|
||||
}
|
||||
|
||||
free(panel);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t st7789p3_set_backlight(st7789p3_handle_t panel, bool on)
|
||||
{
|
||||
if (panel == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (panel->cfg.pin_bckl < 0) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
int on_level = panel->cfg.backlight_on_level ? 1 : 0;
|
||||
int off_level = on_level ? 0 : 1;
|
||||
return gpio_set_level(panel->cfg.pin_bckl, on ? on_level : off_level);
|
||||
}
|
||||
|
||||
esp_err_t st7789p3_set_madctl(st7789p3_handle_t panel, uint8_t madctl_val)
|
||||
{
|
||||
if (panel == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
panel->cfg.madctl_val = madctl_val;
|
||||
esp_err_t ret = st7789p3_send_cmd(panel, 0x36);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
return st7789p3_send_data(panel, &madctl_val, 1);
|
||||
}
|
||||
|
||||
esp_err_t st7789p3_set_invert(st7789p3_handle_t panel, bool invert_color)
|
||||
{
|
||||
if (panel == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
panel->cfg.invert_color = invert_color;
|
||||
return st7789p3_send_cmd(panel, invert_color ? 0x21 : 0x20);
|
||||
}
|
||||
|
||||
esp_err_t st7789p3_set_swap_color_bytes(st7789p3_handle_t panel, bool swap_color_bytes)
|
||||
{
|
||||
if (panel == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
panel->cfg.swap_color_bytes = swap_color_bytes;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t st7789p3_reset(st7789p3_handle_t panel)
|
||||
{
|
||||
if (panel == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (panel->cfg.pin_rst < 0) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t ret = gpio_set_level(panel->cfg.pin_rst, 0);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
ret = gpio_set_level(panel->cfg.pin_rst, 1);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t st7789p3_init(st7789p3_handle_t panel)
|
||||
{
|
||||
if (panel == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
esp_err_t ret = st7789p3_set_backlight(panel, false);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = st7789p3_reset(panel);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = st7789p3_run_init_cmds(panel);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Re-assert MADCTL after init sequence while backlight is still off,
|
||||
// to avoid a visible first-frame transition on some panels.
|
||||
ret = st7789p3_set_madctl(panel, panel->cfg.madctl_val);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = st7789p3_set_invert(panel, panel->cfg.invert_color);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = st7789p3_send_cmd(panel, 0x29);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
return st7789p3_set_backlight(panel, true);
|
||||
}
|
||||
|
||||
esp_err_t st7789p3_set_window(
|
||||
st7789p3_handle_t panel, uint16_t x_start, uint16_t y_start, uint16_t x_end, uint16_t y_end)
|
||||
{
|
||||
if (panel == NULL || x_start >= x_end || y_start >= y_end) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (x_end > panel->cfg.h_res || y_end > panel->cfg.v_res) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
uint8_t x_data[4] = {
|
||||
(uint8_t)(x_start >> 8),
|
||||
(uint8_t)(x_start & 0xFF),
|
||||
(uint8_t)((x_end - 1) >> 8),
|
||||
(uint8_t)((x_end - 1) & 0xFF),
|
||||
};
|
||||
uint8_t y_data[4] = {
|
||||
(uint8_t)(y_start >> 8),
|
||||
(uint8_t)(y_start & 0xFF),
|
||||
(uint8_t)((y_end - 1) >> 8),
|
||||
(uint8_t)((y_end - 1) & 0xFF),
|
||||
};
|
||||
|
||||
esp_err_t ret = st7789p3_send_cmd(panel, 0x2A);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
ret = st7789p3_send_data(panel, x_data, sizeof(x_data));
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = st7789p3_send_cmd(panel, 0x2B);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
ret = st7789p3_send_data(panel, y_data, sizeof(y_data));
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return st7789p3_send_cmd(panel, 0x2C);
|
||||
}
|
||||
|
||||
esp_err_t st7789p3_draw_bitmap(st7789p3_handle_t panel,
|
||||
uint16_t x_start,
|
||||
uint16_t y_start,
|
||||
uint16_t x_end,
|
||||
uint16_t y_end,
|
||||
const uint16_t *color_data)
|
||||
{
|
||||
if (panel == NULL || color_data == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
esp_err_t ret = st7789p3_set_window(panel, x_start, y_start, x_end, y_end);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t pixel_count = (size_t)(x_end - x_start) * (size_t)(y_end - y_start);
|
||||
return st7789p3_send_color_data(panel, color_data, pixel_count);
|
||||
}
|
||||
|
||||
esp_err_t st7789p3_fill_color(st7789p3_handle_t panel, uint16_t color)
|
||||
{
|
||||
if (panel == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
static const size_t fill_pixels = 256;
|
||||
uint16_t *fill_buffer = heap_caps_malloc(fill_pixels * sizeof(uint16_t), MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
|
||||
if (fill_buffer == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
uint16_t fill_color = color;
|
||||
if (panel->cfg.swap_color_bytes) {
|
||||
fill_color = (uint16_t)((color >> 8) | (color << 8));
|
||||
}
|
||||
for (size_t i = 0; i < fill_pixels; i++) {
|
||||
fill_buffer[i] = fill_color;
|
||||
}
|
||||
|
||||
esp_err_t ret = st7789p3_set_window(panel, 0, 0, panel->cfg.h_res, panel->cfg.v_res);
|
||||
if (ret != ESP_OK) {
|
||||
free(fill_buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t total_pixels = (size_t)panel->cfg.h_res * (size_t)panel->cfg.v_res;
|
||||
while (total_pixels > 0) {
|
||||
size_t pixels_to_send = (total_pixels > fill_pixels) ? fill_pixels : total_pixels;
|
||||
ret = st7789p3_send_data(panel, fill_buffer, pixels_to_send * sizeof(uint16_t));
|
||||
if (ret != ESP_OK) {
|
||||
free(fill_buffer);
|
||||
return ret;
|
||||
}
|
||||
total_pixels -= pixels_to_send;
|
||||
}
|
||||
|
||||
free(fill_buffer);
|
||||
return ESP_OK;
|
||||
}
|
||||
Reference in New Issue
Block a user