various bug fixes
This commit is contained in:
@@ -1,38 +1,34 @@
|
||||
/*
|
||||
* MTG RFID Companion - ST7789 display manager (Core 1)
|
||||
* MTG RFID Companion - ST7789 display manager with LVGL (ponytail: native library)
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "esp_lcd_panel_ops.h"
|
||||
#include "esp_lcd_panel_vendor.h"
|
||||
#include "esp_lcd_panel_st7789.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "tjpgd.h"
|
||||
#include "hw_pins.h"
|
||||
#include "spi_bus_manager.h"
|
||||
#include "display_manager.h"
|
||||
#include "ui_font.h"
|
||||
|
||||
static const char *TAG = "display";
|
||||
|
||||
#define LCD_HOST HW_SPI_HOST
|
||||
#define LCD_PIXEL_CLOCK_HZ (20 * 1000 * 1000)
|
||||
#define LCD_PIXEL_CLOCK_HZ (16 * 1000 * 1000)
|
||||
#define LCD_CMD_BITS 8
|
||||
#define LCD_PARAM_BITS 8
|
||||
#define LCD_TRANS_QUEUE 10
|
||||
|
||||
#define JPEG_POOL_SIZE (10 * 1024)
|
||||
#define JPEG_SBUF (512) /* must match CONFIG_JD_SZBUF */
|
||||
static esp_lcd_panel_handle_t s_panel = NULL;
|
||||
static lv_disp_t *s_disp = NULL;
|
||||
|
||||
static esp_lcd_panel_handle_t s_panel;
|
||||
static uint16_t *s_staging = NULL; /* 240 px line staging buffer for JPEG */
|
||||
|
||||
static esp_err_t panel_io_init(esp_lcd_panel_io_handle_t *io)
|
||||
esp_err_t display_manager_init(void)
|
||||
{
|
||||
if (s_disp != NULL) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* 1. Initialize ST7789 panel via esp_lcd */
|
||||
esp_lcd_panel_io_handle_t io = NULL;
|
||||
esp_lcd_panel_io_spi_config_t io_config = {
|
||||
.dc_gpio_num = HW_LCD_DC_GPIO,
|
||||
.cs_gpio_num = HW_LCD_CS_GPIO,
|
||||
@@ -40,19 +36,9 @@ static esp_err_t panel_io_init(esp_lcd_panel_io_handle_t *io)
|
||||
.lcd_cmd_bits = LCD_CMD_BITS,
|
||||
.lcd_param_bits = LCD_PARAM_BITS,
|
||||
.spi_mode = 0,
|
||||
.trans_queue_depth = LCD_TRANS_QUEUE,
|
||||
.trans_queue_depth = 10,
|
||||
};
|
||||
return esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_HOST, &io_config, io);
|
||||
}
|
||||
|
||||
esp_err_t display_manager_init(void)
|
||||
{
|
||||
if (s_panel != NULL) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_lcd_panel_io_handle_t io = NULL;
|
||||
esp_err_t ret = panel_io_init(&io);
|
||||
esp_err_t ret = esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_HOST, &io_config, &io);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "panel io init failed: %s", esp_err_to_name(ret));
|
||||
return ret;
|
||||
@@ -72,214 +58,48 @@ esp_err_t display_manager_init(void)
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_reset(s_panel));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_init(s_panel));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_invert_color(s_panel, true));
|
||||
/* No panel mirror: the font bit-order fix in display_put_text already
|
||||
* yields correct (non-mirrored) glyphs. Mirroring here would flip the
|
||||
* whole frame and re-break the text. */
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_mirror(s_panel, false, false));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(s_panel, true));
|
||||
|
||||
s_staging = malloc(DISP_W * sizeof(uint16_t));
|
||||
if (s_staging == NULL) {
|
||||
ESP_LOGE(TAG, "staging buffer alloc failed");
|
||||
return ESP_ERR_NO_MEM;
|
||||
/* 2. Initialize LVGL port on Core 1 */
|
||||
lvgl_port_cfg_t port_cfg = ESP_LVGL_PORT_INIT_CONFIG();
|
||||
port_cfg.task_affinity = 1;
|
||||
ret = lvgl_port_init(&port_cfg);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "lvgl port init failed: %s", esp_err_to_name(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "ST7789 %dx%d ready (CS=%d DC=%d RST=%d)",
|
||||
/* 20 lines buffer (240 * 20 * 2 = 9.6 KB) - lean SRAM footprint */
|
||||
lvgl_port_display_cfg_t disp_cfg = {
|
||||
.io_handle = io,
|
||||
.panel_handle = s_panel,
|
||||
.buffer_size = DISP_W * 20,
|
||||
.double_buffer = false,
|
||||
.hres = DISP_W,
|
||||
.vres = DISP_H,
|
||||
.monochrome = false,
|
||||
.rotation = {
|
||||
.swap_xy = false,
|
||||
.mirror_x = false,
|
||||
.mirror_y = false,
|
||||
},
|
||||
.flags = {
|
||||
.buff_dma = true,
|
||||
},
|
||||
};
|
||||
s_disp = lvgl_port_add_disp(&disp_cfg);
|
||||
if (s_disp == NULL) {
|
||||
ESP_LOGE(TAG, "lvgl_port_add_disp failed");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "ST7789 %dx%d with LVGL ready (CS=%d DC=%d RST=%d)",
|
||||
DISP_W, DISP_H, HW_LCD_CS_GPIO, HW_LCD_DC_GPIO, HW_LCD_RST_GPIO);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void display_draw_bitmap(int x0, int y0, int x1, int y1, const uint16_t *pixels)
|
||||
lv_disp_t *display_get_disp(void)
|
||||
{
|
||||
if (s_panel == NULL || pixels == NULL) {
|
||||
return;
|
||||
}
|
||||
esp_lcd_panel_draw_bitmap(s_panel, x0, y0, x1, y1, pixels);
|
||||
}
|
||||
|
||||
void display_fill_rect(int x0, int y0, int x1, int y1, uint16_t color)
|
||||
{
|
||||
if (x0 < 0) x0 = 0;
|
||||
if (y0 < 0) y0 = 0;
|
||||
if (x1 > DISP_W) x1 = DISP_W;
|
||||
if (y1 > DISP_H) y1 = DISP_H;
|
||||
if (x1 <= x0 || y1 <= y0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int width = x1 - x0;
|
||||
int height = y1 - y0;
|
||||
|
||||
static uint16_t line[DISP_W];
|
||||
for (int i = 0; i < width; i++) {
|
||||
line[i] = color;
|
||||
}
|
||||
for (int r = 0; r < height; r++) {
|
||||
display_draw_bitmap(x0, y0 + r, x1, y0 + r + 1, line);
|
||||
}
|
||||
}
|
||||
|
||||
void display_fill(uint16_t color)
|
||||
{
|
||||
display_fill_rect(0, 0, DISP_W, DISP_H, color);
|
||||
}
|
||||
|
||||
int display_put_text(int x, int y, const char *str, uint16_t fg, uint16_t bg)
|
||||
{
|
||||
int x_start = x;
|
||||
|
||||
while (str != NULL && *str != '\0') {
|
||||
char c = *str++;
|
||||
if (c >= FONT5X7_FIRST_CHAR && c <= FONT5X7_LAST_CHAR) {
|
||||
const uint8_t *glyph = font5x7[c - FONT5X7_FIRST_CHAR];
|
||||
/* Row-major [row][col]. The font table stores each row with the
|
||||
* MSB as the LEFT column, so read bit (W-1-gx) for column gx. */
|
||||
uint16_t cell[FONT5X7_H][FONT5X7_W];
|
||||
|
||||
for (int gy = 0; gy < FONT5X7_H; gy++) {
|
||||
for (int gx = 0; gx < FONT5X7_W; gx++) {
|
||||
cell[gy][gx] = (glyph[gy] >> (FONT5X7_W - 1 - gx)) & 1 ? fg : bg;
|
||||
}
|
||||
}
|
||||
if (x + FONT5X7_W <= DISP_W && y + FONT5X7_H <= DISP_H) {
|
||||
display_draw_bitmap(x, y, x + FONT5X7_W, y + FONT5X7_H, &cell[0][0]);
|
||||
}
|
||||
x += FONT5X7_W + 1;
|
||||
} else if (c == ' ') {
|
||||
x += FONT5X7_W + 1;
|
||||
}
|
||||
}
|
||||
return x - x_start;
|
||||
}
|
||||
|
||||
void display_put_text_center(int y, const char *str, uint16_t fg, uint16_t bg)
|
||||
{
|
||||
int len = (int)strlen(str);
|
||||
int width = len * (FONT5X7_W + 1) - 1;
|
||||
int x = (DISP_W - width) / 2;
|
||||
if (x < 0) x = 0;
|
||||
display_put_text(x, y, str, fg, bg);
|
||||
}
|
||||
|
||||
void display_draw_big_text(int x, int y, const char *str, int scale, uint16_t fg, uint16_t bg)
|
||||
{
|
||||
int cell_w = FONT5X7_W * scale;
|
||||
int cell_h = FONT5X7_H * scale;
|
||||
int num = (int)strlen(str);
|
||||
if (num <= 0) {
|
||||
return;
|
||||
}
|
||||
if (x + num * (cell_w + scale) - scale > DISP_W || y + cell_h > DISP_H) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Staging buffer for one glyph, scaled */
|
||||
uint16_t *glyph_buf = malloc((size_t)cell_w * cell_h * sizeof(uint16_t));
|
||||
if (glyph_buf == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < num; i++) {
|
||||
char c = str[i];
|
||||
if (c < FONT5X7_FIRST_CHAR || c > FONT5X7_LAST_CHAR) {
|
||||
x += cell_w + scale;
|
||||
continue;
|
||||
}
|
||||
const uint8_t *g = font5x7[c - FONT5X7_FIRST_CHAR];
|
||||
|
||||
for (int gy = 0; gy < FONT5X7_H; gy++) {
|
||||
for (int gx = 0; gx < FONT5X7_W; gx++) {
|
||||
uint16_t color = ((g[gy] >> gx) & 1) ? fg : bg;
|
||||
for (int sy = 0; sy < scale; sy++) {
|
||||
for (int sx = 0; sx < scale; sx++) {
|
||||
glyph_buf[(gy * scale + sy) * cell_w + (gx * scale + sx)] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
display_draw_bitmap(x, y, x + cell_w, y + cell_h, glyph_buf);
|
||||
x += cell_w + scale;
|
||||
}
|
||||
free(glyph_buf);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* tjpgd streaming decode */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
typedef struct {
|
||||
FILE *f;
|
||||
} jpeg_io_t;
|
||||
|
||||
static size_t jpeg_input_cb(JDEC *jd, uint8_t *buf, size_t ndata)
|
||||
{
|
||||
jpeg_io_t *io = (jpeg_io_t *)jd->device;
|
||||
if (io == NULL || io->f == NULL) {
|
||||
return 0;
|
||||
}
|
||||
if (buf == NULL) {
|
||||
/* tjpgd requests skipping bytes during header scan */
|
||||
if (fseek(io->f, (long)ndata, SEEK_CUR) == 0) {
|
||||
return ndata;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return fread(buf, 1, ndata, io->f);
|
||||
}
|
||||
|
||||
static int jpeg_output_cb(JDEC *jd, void *bitmap, JRECT *rect)
|
||||
{
|
||||
(void)jd;
|
||||
uint16_t *pix = (uint16_t *)bitmap;
|
||||
int w = rect->right - rect->left + 1;
|
||||
int h = rect->bottom - rect->top + 1;
|
||||
|
||||
for (int row = 0; row < h; row++) {
|
||||
/* Convert little-endian RGB565 words (tjpgd) to big-endian wire order */
|
||||
for (int col = 0; col < w; col++) {
|
||||
uint16_t v = pix[row * w + col];
|
||||
s_staging[col] = (uint16_t)((v << 8) | (v >> 8));
|
||||
}
|
||||
display_draw_bitmap(rect->left, rect->top + row, rect->left + w, rect->top + row + 1, s_staging);
|
||||
}
|
||||
return 1; /* continue decoding */
|
||||
}
|
||||
|
||||
esp_err_t display_show_jpeg_file(const char *path)
|
||||
{
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (f == NULL) {
|
||||
ESP_LOGE(TAG, "cannot open %s", path);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
jpeg_io_t io = { .f = f };
|
||||
JDEC jdec;
|
||||
void *pool = malloc(JPEG_POOL_SIZE);
|
||||
if (pool == NULL) {
|
||||
ESP_LOGE(TAG, "jpeg pool alloc failed");
|
||||
fclose(f);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
JRESULT jr = jd_prepare(&jdec, jpeg_input_cb, pool, JPEG_POOL_SIZE, &io);
|
||||
if (jr != JDR_OK) {
|
||||
ESP_LOGE(TAG, "jd_prepare failed (%d)", jr);
|
||||
goto done;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "decoding %s: %ux%u", path, (unsigned)jdec.width, (unsigned)jdec.height);
|
||||
jr = jd_decomp(&jdec, jpeg_output_cb, 0);
|
||||
if (jr != JDR_OK) {
|
||||
ESP_LOGE(TAG, "jd_decomp failed (%d)", jr);
|
||||
goto done;
|
||||
}
|
||||
ESP_LOGI(TAG, "JPEG displayed: %s", path);
|
||||
ret = ESP_OK;
|
||||
|
||||
done:
|
||||
free(pool);
|
||||
fclose(f);
|
||||
return ret;
|
||||
return s_disp;
|
||||
}
|
||||
Reference in New Issue
Block a user