ESP32-S3 ST7789 LVGL Platformio esp_idf SPI

Hi Forum.

I have been fumbling around, trying to get a simple hello world working with ESP32-S3. I am new to LVGL and have only tried to use simple monochrome displays.

I have cloned LVGL9.1 into Platformio components, and I have included
#include “drivers/display/st7789/lv_st7789.h”
#include “lvgl/lvgl.h”

Using simple LVGL functions, everything compiles nicely, but is not of much use, without implementing a display driver, and here all the questions begin.

  • Is is correct, that if I use menuconfig to set up, I do not need any lv_conf.h file, since I do all the settings in menuconfig? I have enablet ST7789 under devices, and no costum lv_conf.

  • Using this driver, I do not need to implement any flush function, since its handled in the driver?

  • I am struggling to understand, if I must use lv_disp_drv_init or lv_st7789_create. Seems like lv_disp_drv_init is simpler, but I do not quite understand all the parameters, and how do I set what SPI to use? This is not set (as far as I see) in menuconfig, but surely the driver needs to know… ?

If anyone can point me in the right direction, it would be highly apreciated.

Br. Frank.

PS, here is my complete code so far, am I even on the right track?

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "lvgl/lvgl.h"
#include "driver/gpio.h"
#include "driver/spi_master.h"
#include "drivers/display/st7789/lv_st7789.h"
#include "esp_timer.h"
#include <string.h>

#define TAG "Main"

#define ST7789_SPI_HOST SPI3_HOST
#define ST7789_DMA_CHAN 1
#define ST7789_PIN_NUM_CS GPIO_NUM_13
#define ST7789_PIN_NUM_DC GPIO_NUM_14

#define ST7789_PIN_NUM_MOSI GPIO_NUM_11
#define ST7789_PIN_NUM_SCLK GPIO_NUM_12

#define ST7789_PIN_NUM_RST GPIO_NUM_10
#define ST7789_PIN_NUM_BCKL GPIO_NUM_15 // bruges vi ikke lige nu

// SPI device handle
spi_device_handle_t spi;

static void lv_tick_task(void *arg) {
    lv_tick_inc(portTICK_PERIOD_MS);
}

void display_flush(lv_display_t *disp, const lv_area_t *area, lv_color_t *color_p) {

    uint32_t size = (area->x2 - area->x1 + 1) * (area->y2 - area->y1 + 1);

    // Set the address window manually (adjust based on your ST7789 controller's requirements)
    // Example: sending a command to set column address range
    uint8_t cmd_column[] = {0x2A, 0x00, area->x1, 0x00, area->x2};
    spi_transaction_t t;
    memset(&t, 0, sizeof(t));
    t.length = 8 * sizeof(cmd_column); // 8 bits per byte
    t.tx_buffer = cmd_column;
    t.user = (void *)0; // D/C line low for command
    ESP_ERROR_CHECK(spi_device_polling_transmit(spi, &t));

    // Example: sending a command to set row address range
    uint8_t cmd_row[] = {0x2B, 0x00, area->y1, 0x00, area->y2};
    memset(&t, 0, sizeof(t));
    t.length = 8 * sizeof(cmd_row); // 8 bits per byte
    t.tx_buffer = cmd_row;
    t.user = (void *)0; // D/C line low for command
    ESP_ERROR_CHECK(spi_device_polling_transmit(spi, &t));

    // Example: sending a command to write RAM
    uint8_t cmd_write[] = {0x2C};
    memset(&t, 0, sizeof(t));
    t.length = 8 * sizeof(cmd_write); // 8 bits per byte
    t.tx_buffer = cmd_write;
    t.user = (void *)0; // D/C line low for command
    ESP_ERROR_CHECK(spi_device_polling_transmit(spi, &t));

    // Transmit data via SPI
    t.length = size * 8; // 8 bits per pixel (16-bit color depth)
    t.tx_buffer = (uint8_t *)color_p;
    t.user = (void *)1; // D/C line high for data
    ESP_ERROR_CHECK(spi_device_polling_transmit(spi, &t));

    // Tell LVGL that flushing is done
    lv_disp_flush_ready(disp);
}

void lvgl_driver_init(void)
{
// SPI bus configuration
    spi_bus_config_t buscfg = {
        .mosi_io_num = ST7789_PIN_NUM_MOSI,
        .sclk_io_num = ST7789_PIN_NUM_SCLK,
        .miso_io_num = -1,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
        .max_transfer_sz = 240 * 240 * 2 + 8 // Adjust based on your display resolution
    };
    ESP_ERROR_CHECK(spi_bus_initialize(ST7789_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO));

    // SPI device configuration
    spi_device_interface_config_t devcfg = {
        .clock_speed_hz = 10 * 1000 * 1000, // Clock out at 10 MHz
        .mode = 0,                          // SPI mode 0
        .spics_io_num = ST7789_PIN_NUM_CS,  // CS pin
        .queue_size = 7,
        .flags = SPI_DEVICE_HALFDUPLEX
    };
    ESP_ERROR_CHECK(spi_bus_add_device(ST7789_SPI_HOST, &devcfg, &spi));
}

void app_main(void)
{
    // Initialize LVGL
    lv_init();

    // Initialize SPI and I2C bus used by the drivers
    lvgl_driver_init();

    // Display resolution
    uint32_t hor_res = 240;
    uint32_t ver_res = 280;

    // Initialize display buffer
    static lv_color_t buf1[240 * 10]; // LV_HOR_RES_MAX
    static lv_color_t buf2[240 * 10]; // LV_HOR_RES_MAX

    // Initialize display driver
    lv_disp_t *disp = lv_display_create(hor_res, ver_res);
    lv_display_set_flush_cb(disp, &display_flush);
    lv_display_set_buffers(disp, &buf1, &buf2, sizeof(buf1), LV_DISP_RENDER_MODE_PARTIAL);

    // Set up tick task
    const esp_timer_create_args_t periodic_timer_args = {
        .callback = &lv_tick_task,
        .name = "periodic_gui"
    };
    esp_timer_handle_t periodic_timer;
    ESP_ERROR_CHECK(esp_timer_create(&periodic_timer_args, &periodic_timer));
    ESP_ERROR_CHECK(esp_timer_start_periodic(periodic_timer, 1 * 1000));

    // Example: Create a simple label
    lv_obj_t *label = lv_label_create(lv_scr_act());
    lv_label_set_text(label, "Hello, LVGL!");
    lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);

    // Handle LVGL tasks
    while (1) {
        vTaskDelay(pdMS_TO_TICKS(10));
        lv_task_handler();
    }
}

Hi,
Sorry i dont have an answer but do you have the fix for this?

Here is a good example implementing LVGL with both a display and touch, and with code that is running :slight_smile: