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();
}
}