ESP32s3 black screen SPI issue

Hi! I’m having an issues with setting up my display.

I’m using LVGL v9 on ESP32s3 with st7789 attached.

My code was working fine, but after several days I can not run it anymore. I’m newbie so may be doing it wrong. So I have ESP32s3, attached to PC through USB and ESP-IDF plugin installed to vscode on linux. So Here is my code from main:

/*

  • SPDX-FileCopyrightText: 2010-2022 Espressif Systems (Shanghai) CO LTD
  • SPDX-License-Identifier: CC0-1.0
    */

#include <stdio.h>
#include <inttypes.h>
#include “sdkconfig.h”
#include “freertos/FreeRTOS.h”
#include “freertos/task.h”
#include “esp_chip_info.h”
#include “esp_flash.h”
#include “esp_system.h”
#include “…/components/lvgl_esp32_drivers/lvgl_tft/driver/esp_timer.h”

#include <stdbool.h>
#include <stdlib.h>
#include <string.h>

#include “esp_freertos_hooks.h”
#include “freertos/semphr.h”
#include “…/components/lvgl_esp32_drivers/lvgl_tft/driver/gpio.h”

#include “…/components/lvgl/lvgl.h”
#include “…/components/lvgl_esp32_drivers/lvgl_helpers.h”

#define LV_TICK_PERIOD_MS 1

SemaphoreHandle_t xGuiSemaphore;

static void lv_tick_task(void *arg);
static void guiTask(void *pvParameter);
static void create_demo_application(void);

static void lv_tick_task(void *arg) {
(void) arg;

lv_tick_inc(LV_TICK_PERIOD_MS);

}

static void create_demo_application(void)
{

}

static void guiTask(void *pvParameter) {

(void) pvParameter;
xGuiSemaphore = xSemaphoreCreateMutex();

lv_init();

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

lv_color_t* buf1 = heap_caps_malloc(DISP_BUF_SIZE * sizeof(lv_color_t), MALLOC_CAP_DMA);
assert(buf1 != NULL);

lv_color_t* buf2 = heap_caps_malloc(DISP_BUF_SIZE * sizeof(lv_color_t), MALLOC_CAP_DMA);
assert(buf2 != NULL);

static lv_disp_buf_t disp_buf;

uint32_t size_in_px = DISP_BUF_SIZE;

/* Initialize the working buffer depending on the selected display.*/
lv_disp_buf_init(&disp_buf, buf1, buf2, size_in_px);

lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.flush_cb = disp_driver_flush;

#if defined CONFIG_DISPLAY_ORIENTATION_PORTRAIT || defined CONFIG_DISPLAY_ORIENTATION_PORTRAIT_INVERTED
disp_drv.rotated = 1;
#endif

disp_drv.buffer = &disp_buf;
lv_disp_drv_register(&disp_drv);

/* Register an input device when enabled on the menuconfig */

#if CONFIG_LV_TOUCH_CONTROLLER != TOUCH_CONTROLLER_NONE
lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.read_cb = touch_driver_read;
indev_drv.type = LV_INDEV_TYPE_POINTER;
lv_indev_drv_register(&indev_drv);
#endif

/* Create and start a periodic timer interrupt to call lv_tick_inc */
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, LV_TICK_PERIOD_MS * 1000));

/* Create the demo application */
create_demo_application();

while (1) {
    /* Delay 1 tick (assumes FreeRTOS tick is 10ms */
    vTaskDelay(pdMS_TO_TICKS(10));

    /* Try to take the semaphore, call lvgl related function on success */
    if (pdTRUE == xSemaphoreTake(xGuiSemaphore, portMAX_DELAY)) {
        lv_task_handler();
        xSemaphoreGive(xGuiSemaphore);
   }
}

/* A task should NEVER return */
free(buf1);
free(buf2);

vTaskDelete(NULL);

}

void app_main(void)
{
xTaskCreatePinnedToCore(guiTask, “gui”, 4096*2, NULL, 0, NULL, 1);

printf("Hello world!\n");

/* Print chip information */
esp_chip_info_t chip_info;
uint32_t flash_size;
esp_chip_info(&chip_info);
printf("This is %s chip with %d CPU core(s), %s%s%s%s, ",
       CONFIG_IDF_TARGET,
       chip_info.cores,
       (chip_info.features & CHIP_FEATURE_WIFI_BGN) ? "WiFi/" : "",
       (chip_info.features & CHIP_FEATURE_BT) ? "BT" : "",
       (chip_info.features & CHIP_FEATURE_BLE) ? "BLE" : "",
       (chip_info.features & CHIP_FEATURE_IEEE802154) ? ", 802.15.4 (Zigbee/Thread)" : "");

unsigned major_rev = chip_info.revision / 100;
unsigned minor_rev = chip_info.revision % 100;
printf("silicon revision v%d.%d, ", major_rev, minor_rev);
if(esp_flash_get_size(NULL, &flash_size) != ESP_OK) {
    printf("Get flash size failed");
    return;
}

printf("%" PRIu32 "MB %s flash\n", flash_size / (uint32_t)(1024 * 1024),
       (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external");

printf("Minimum free heap size: %" PRIu32 " bytes\n", esp_get_minimum_free_heap_size());

for (int i = 10; i >= 0; i--) {
    printf("Restarting in %d seconds...\n", i);
    vTaskDelay(1000 / portTICK_PERIOD_MS);
}
printf("Restarting now.\n");
fflush(stdout);
esp_restart();

}

I can compile it without any errors and I can even flash it to the board. Through idf.py menuconfig I can set the ports for SPI (HSPI/SPI3_HOST) and backlight. I can turn the backlight on and off, but I have a blackscreen. Since the last build I had an errors on DMA channel (an error, telling that my board supports auto dma only), so I’ve changed the DMA channel to auto:

bool lvgl_spi_driver_init(int host,
int miso_pin, int mosi_pin, int sclk_pin,
int max_transfer_sz,
int dma_channel,
int quadwp_pin, int quadhd_pin)
{
assert((0 <= host) && (SPI_HOST_MAX > host));
const char *spi_names = {
“SPI1_HOST”, “SPI2_HOST”, “SPI3_HOST”
};

ESP_LOGI(TAG, "Configuring SPI host %s", spi_names[host]);
ESP_LOGI(TAG, "MISO pin: %d, MOSI pin: %d, SCLK pin: %d, IO2/WP pin: %d, IO3/HD pin: %d",
    miso_pin, mosi_pin, sclk_pin, quadwp_pin, quadhd_pin);

ESP_LOGI(TAG, "Max transfer size: %d (bytes)", max_transfer_sz);

spi_bus_config_t buscfg = {
    .miso_io_num = miso_pin,
    .mosi_io_num = mosi_pin,
    .sclk_io_num = sclk_pin,
    .quadwp_io_num = quadwp_pin,
    .quadhd_io_num = quadhd_pin,
    .max_transfer_sz = max_transfer_sz
};

ESP_LOGI(TAG, "Initializing SPI bus...");
#if defined (CONFIG_IDF_TARGET_ESP32S3)
**dma_channel = SPI_DMA_CH_AUTO;**
#endif
esp_err_t ret = spi_bus_initialize(host, &buscfg, (spi_dma_chan_t)dma_channel);
assert(ret == ESP_OK);

return ESP_OK != ret;

}

But I’m still having a black screen.

Also I’ve set the custom frequency of the SPI for communicating with my st7789 (20MHz) and on the oscilloscope I can see something unclear:

I have no idea where to move to solve this problem. Appreciate any ideas!-)

Here is my idf.py monitor output:

ESP-ROM:esp32s3-20210327
Build:Mar 27 2021
rst:0xc (RTC_SW_CPU_RST),boot:0x8 (SPI_FAST_FLASH_BOOT)
Saved PC:0x40375b14
— 0x40375b14: esp_restart_noos at /home/maxim/esp/v5.4.2/esp-idf/components/esp_system/port/soc/esp32s3/system_internal.c:162

SPIWP:0xee
mode:DIO, clock div:1
load:0x3fce2810,len:0x1564
load:0x403c8700,len:0x4
load:0x403c8704,len:0xd24
load:0x403cb700,len:0x2ed4
entry 0x403c8928
I (31) boot: ESP-IDF v5.4.2-dirty 2nd stage bootloader
I (31) boot: compile time Jul 3 2025 14:34:28
I (31) boot: Multicore bootloader
I (32) boot: chip revision: v0.2
I (35) boot: efuse block revision: v1.3
I (38) boot.esp32s3: Boot SPI Speed : 80MHz
I (42) boot.esp32s3: SPI Mode : DIO
I (46) boot.esp32s3: SPI Flash Size : 2MB
I (50) boot: Enabling RNG early entropy source…
I (54) boot: Partition Table:
I (57) boot: ## Label Usage Type ST Offset Length
I (63) boot: 0 nvs WiFi data 01 02 00009000 00006000
I (70) boot: 1 phy_init RF data 01 01 0000f000 00001000
I (76) boot: 2 factory factory app 00 00 00010000 00100000
I (83) boot: End of partition table
I (86) esp_image: segment 0: paddr=00010020 vaddr=3c040020 size=10e24h ( 69156) map
I (106) esp_image: segment 1: paddr=00020e4c vaddr=3fc94c00 size=02b9ch ( 11164) load
I (108) esp_image: segment 2: paddr=000239f0 vaddr=40374000 size=0c628h ( 50728) load
I (120) esp_image: segment 3: paddr=00030020 vaddr=42000020 size=321ach (205228) map
I (157) esp_image: segment 4: paddr=000621d4 vaddr=40380628 size=044fch ( 17660) load
I (161) esp_image: segment 5: paddr=000666d8 vaddr=600fe000 size=0001ch ( 28) load
I (168) boot: Loaded app from partition at offset 0x10000
I (168) boot: Disabling RNG early entropy source…
I (182) cpu_start: Multicore app
I (192) cpu_start: Pro cpu start user code
I (192) cpu_start: cpu freq: 160000000 Hz
I (192) app_init: Application information:
I (192) app_init: Project name: st7789fromScratch
I (197) app_init: App version: 1
I (200) app_init: Compile time: Jul 3 2025 17:54:11
I (205) app_init: ELF file SHA256: 06bb4a009…
I (209) app_init: ESP-IDF: v5.4.2-dirty
I (214) efuse_init: Min chip rev: v0.0
I (218) efuse_init: Max chip rev: v0.99
I (222) efuse_init: Chip rev: v0.2
I (225) heap_init: Initializing. RAM available for dynamic allocation:
I (232) heap_init: At 3FCA05D0 len 00049140 (292 KiB): RAM
I (237) heap_init: At 3FCE9710 len 00005724 (21 KiB): RAM
I (242) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM
I (247) heap_init: At 600FE01C len 00001FCC (7 KiB): RTCRAM
I (254) spi_flash: detected chip: generic
I (256) spi_flash: flash io: dio
W (259) spi_flash: Detected size(16384k) larger than the size in the binary image header(2048k). Using the size in the binary image header.
W (271) i2c: This driver is an old driver, please migrate your application code to adapt driver/i2c_master.h
I (281) sleep_gpio: Configure to isolate all GPIO pins in sleep state
I (288) sleep_gpio: Enable automatic switching of GPIO sleep configuration
I (294) main_task: Started on CPU0
I (304) main_task: Calling app_main()
Hello world!
This is esp32s3 chip with 2 CPU core(s), WiFi/BLE, silicon revision v0.2, 2MB external flash
Minimum free heap size: 332276 bytes
Restarting in 10 seconds…
I (324) lvgl_helpers: Display hor size: 240, ver size: 240
I (324) lvgl_helpers: Display buffer size: 9600
I (334) lvgl_helpers: Initializing SPI master for display
I (334) lvgl_helpers: Configuring SPI host SPI3_HOST
I (334) lvgl_helpers: MISO pin: -1, MOSI pin: 13, SCLK pin: 14, IO2/WP pin: -1, IO3/HD pin: -1
I (344) lvgl_helpers: Max transfer size: 19200 (bytes)
I (354) lvgl_helpers: Initializing SPI bus…
I (354) disp_spi: Adding SPI device
I (354) disp_spi: Clock speed: 20000000Hz, mode: 2, CS pin: -1
ST7789 initialization.
Enabling backlight.
I (804) st7789: Display orientation: PORTRAIT
I (804) st7789: 0x36 command value: 0xC0
Restarting in 9 seconds…
Restarting in 8 seconds…
Restarting in 7 seconds…
Restarting in 6 seconds…
Restarting in 5 seconds…
Restarting in 4 seconds…
Restarting in 3 seconds…
Restarting in 2 seconds…
Restarting in 1 seconds…
Restarting in 0 seconds…
Restarting now.

Well ok. Maybe my oscilloscope is not fast enough. I’ve slowed the SPI down to 1MHz and now I can clearly see normal SDA and MOSI signals. So this is my soft - it’s now working fine-)