Login
1 branch 0 tags
Ben (T14/NixOS) Improved esp32 config and font handling e1eb80a 1 month ago 52 Commits
moon / esp32 / main / lvgl_port_esp32.c
/**
 * @file lvgl_port_esp32.c
 * LVGL display driver for ESP32-S3 with ST7735
 * RGB565 double-buffered with async SPI DMA
 */

#include "lvgl_port.h"
#include "moon.h"
#include "st7735.h"

// Double draw buffers for RGB565 partial refresh (32 lines at 160px)
#define DRAW_BUF_LINES 32
static uint16_t draw_buf1[SCREEN_WIDTH * DRAW_BUF_LINES];
static uint16_t draw_buf2[SCREEN_WIDTH * DRAW_BUF_LINES];

// SPI transaction for async transfers
static spi_transaction_t spi_trans;

static lv_display_t *display;

// Flush callback - byte-swap RGB565 in-place and queue async SPI transfer
static void flush_cb(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map) {
    (void)disp;

    int x1 = area->x1;
    int y1 = area->y1;
    int x2 = area->x2;
    int y2 = area->y2;
    int pixel_count = (x2 - x1 + 1) * (y2 - y1 + 1);

    // Byte-swap RGB565 in-place (LVGL is little-endian, ST7735 is big-endian)
    uint16_t *pixels = (uint16_t *)px_map;
    for (int i = 0; i < pixel_count; i++) {
        pixels[i] = __builtin_bswap16(pixels[i]);
    }

    // Set window and queue async DMA transfer
    st7735_set_window(x1, y1, x2, y2);
    st7735_write_pixels_async(pixels, pixel_count, &spi_trans);

    // Do NOT call lv_display_flush_ready() here — flush_wait_cb handles it
}

// Wait callback - blocks until previous DMA completes, then signals LVGL
static void flush_wait_cb(lv_display_t *disp) {
    st7735_wait_async();
    lv_display_flush_ready(disp);
}

void lvgl_port_init(void) {
    lv_init();

    // Create display with RGB565 color format
    display = lv_display_create(SCREEN_WIDTH, SCREEN_HEIGHT);
    lv_display_set_color_format(display, LV_COLOR_FORMAT_RGB565);

    // Set double draw buffers
    lv_display_set_buffers(display, draw_buf1, draw_buf2,
                           sizeof(draw_buf1), LV_DISPLAY_RENDER_MODE_PARTIAL);

    // Set flush and flush-wait callbacks
    lv_display_set_flush_cb(display, flush_cb);
    lv_display_set_flush_wait_cb(display, flush_wait_cb);
}

lv_display_t *lvgl_port_get_display(void) {
    return display;
}