ESP-Techpedia Logo
  • ESP FAQ
  • ESP Friends
    • Get Started
    • Advanced Development
      • Component Management and Usage
      • Advanced Code Debugging
      • Performance Optimization
      • Network Protocol Related
      • System Related
      • Low Power Bluetooth Application Note
      • LCD Application Development Notes
        • Overview
        • SPI/QSPI Interface Screen Related Issues Summary
        • I80 Interface Screen Related Issues Summary
        • Summary of Issues Related to RGB Interface Screen
        • Summary of Issues Related to MIPI-DSI Interface Screen
        • GUI Optimization Summary
    • Solution Introduction
    • Tool Recommendation
ESP-Techpedia
  • »
  • ESP Friends »
  • Advanced Development »
  • LCD Application Development Notes »
  • SPI/QSPI Interface Screen Related Issues Summary
  • Edit on GitHub

SPI/QSPI Interface Screen Related Issues Summary

[中文]

This document mainly summarizes the common problems in the development process of SPI/QSPI interface screens and provides corresponding solutions.

Related Documents

Detailed Explanation of SPI/QSPI Interface Screen Development

Related Examples

SPI Interface Screen Driver Example

QSPI Interface Screen Driver Example (with on-screen RAM)

QSPI Interface Screen Driver Example (without on-screen RAM)

Pin Wiring

Pin connection suggestions:

  • TE (Tearing Effect): If the platform has PSRAM and needs to prevent tearing, it can be connected; otherwise, it can be omitted.

  • CS (Chip Select): Strongly recommended to keep, not recommended to omit.

  • Reset: If there are sufficient pin resources, it is recommended to connect to a controllable IO; otherwise, it can be pulled high and reset by software.

  • SPI interface screen occupies at least 4 IOs (SCLK, MOSI, MISO, and CS or DC/RS control signals).

SPI Dual Screen Solution

SPI dual screen different display (i.e., two screens display different content) and same display (i.e., two screens display the same content) can be achieved by controlling the CS line of the screen. Performance reference: ESP32-S3 can support dual screen resolution 360 × 360, different display frame rate is about 20 fps.

SRAM Saving

I. Preliminary Explanation

  • ESP-IDF’s SPI driver does not yet have the ability to directly send PSRAM data through GDMA

  • When using esp_lcd_panel_io_spi to refresh the screen and the color data is located in PSRAM, the driver will detect DMA unreachable in spi_master.c::setup_priv_desc():

    1. Reallocate temporary buffer in internal SRAM (with MALLOC_CAP_DMA);

    2. memcpy the color data to this buffer;

    3. Then DMA sends data from SRAM to LCD.

    This “moving → copying → DMA” process will occur repeatedly, consuming extra SRAM.

II. Problem Phenomenon

  • When the system runs high SRAM occupancy modules such as Wi-Fi / BLE at the same time, the temporary buffer competes for SRAM, which may cause:

    • Other components fail to apply for memory;

    • Report color trans fail during screen refresh;

    • LVGL task WDT (watchdog) reset.

III. Cause Analysis

  • The temporary copy from PSRAM to SRAM is the fallback mechanism of the spi_master driver, ensuring that it can still work when DMA cannot directly reach external RAM.

  • Internal SRAM not only stores task stacks, FreeRTOS objects, Wi-Fi caches, but also carries SPI screen refresh temporary buffers, which can easily lead to contention.

  • The size of the temporary buffer is approximately max_transfer_sz × queued transaction number, where the number of transactions is determined by trans_queue_depth.

IV. Avoidance / Optimization Scheme

  1. Reduce SRAM occupancy through configuration

    • Reduce spi_bus_config_t.max_transfer_sz: The smaller the number of bytes transferred by DMA at a time, the smaller the temporary buffer for a single time.

      It is recommended to set it to LCD_H_RES × number of rows × 2 in row refresh mode, and control the number of rows within 20~60 rows.

    • Lower esp_lcd_panel_io_spi_config_t.trans_queue_depth: The fewer queued transactions, the fewer existing buffer copies. In non-concurrent screen refresh scenarios, setting it to 2~4 is sufficient.

    Example configuration code:

    ESP_LOGI(TAG, "Initialize SPI bus");
    spi_bus_config_t buscfg = {
        .sclk_io_num = EXAMPLE_PIN_NUM_SCLK,
        .mosi_io_num = EXAMPLE_PIN_NUM_MOSI,
        .miso_io_num = EXAMPLE_PIN_NUM_MISO,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
        .max_transfer_sz = EXAMPLE_LCD_H_RES * 20 * sizeof(uint16_t),
    };
    ESP_ERROR_CHECK(spi_bus_initialize(LCD_HOST, &buscfg, SPI_DMA_CH_AUTO));
    
    ESP_LOGI(TAG, "Install panel IO");
    esp_lcd_panel_io_handle_t io_handle = NULL;
    esp_lcd_panel_io_spi_config_t io_config = {
        .dc_gpio_num = EXAMPLE_PIN_NUM_LCD_DC,
        .cs_gpio_num = EXAMPLE_PIN_NUM_LCD_CS,
        .pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,
        .lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS,
        .lcd_param_bits = EXAMPLE_LCD_PARAM_BITS,
        .spi_mode = 0,
        .trans_queue_depth = 2,
    };
    
  2. Reasonably plan Wi-Fi and graphic task stack / cache

  3. Monitor SRAM water level at runtime

  4. When using LVGL, turning off LV_ATTRIBUTE_FAST_MEM_USE_IRAM can save a lot of memory. (This configuration is not enabled by default)

ESP32-P4 SPI clock rate cannot be increased

  • Problem: When the clock rate is increased to above 20 MHz, the error invalid sclk speed is reported

  • Solution: Refer to patch

SPI bus lock spi_bus_lock has concurrency race in multi-core scenarios

Suggestion: It is recommended that the user control the SPI interrupt and LVGL task to run on the same CPU core.

#include "driver/spi_master.h"

#define SPI_HOST   SPI2_HOST          // Take SPI2 as an example
#define LCD_CORE   APP_CPU_NUM        // Target core (1: APP_CPU, 0: PRO_CPU)

static void lcd_bus_init(void)
{
    spi_bus_config_t buscfg = {
        .mosi_io_num = GPIO_NUM_7,
        .miso_io_num = GPIO_NUM_2,
        .sclk_io_num = GPIO_NUM_6,
        .max_transfer_sz = 4096,
        .isr_cpu_id = LCD_CORE,       // ★ Specify which core the SPI interrupt is registered to
    };

    ESP_ERROR_CHECK(spi_bus_initialize(SPI_HOST, &buscfg, SPI_DMA_CH_AUTO));
}
static void lvgl_task(void *arg)
{
    for (;;) {
        lv_timer_handler();
        vTaskDelay(pdMS_TO_TICKS(5));
    }
}

void app_main(void)
{
    lcd_bus_init();   // The bus must be initialized first

    xTaskCreatePinnedToCore(lvgl_task,
                            "lvgl",
                            8192,
                            NULL,
                            5,
                            NULL,
                            LCD_CORE);   // ★ Same as .isr_cpu_id
}
Previous Next

© Copyright 2016 - 2025, Espressif Systems (Shanghai) Co., Ltd.

  • Built with Sphinx using a theme based on Read the Docs Sphinx Theme.
  • Download PDF