SPI/QSPI 接口屏幕相关问题总结
本文档主要总结了 SPI/QSPI 接口屏幕开发过程中常见的问题,并给出了相应的解决方法。
相关文档
相关示例
引脚接线
引脚连接建议:
TE(Tearing Effect):若平台有 PSRAM 且需要防撕裂,可选择接入;否则可省略。
CS(片选):强烈建议保留,不推荐省略。
Reset:若引脚资源充足,建议连接至可控 IO;否则可拉高并通过软件复位。
SPI 接口屏幕至少占用 4 个 IO(SCLK、MOSI、MISO 以及 CS 或 DC/RS 等控制信号)。
SPI 双屏方案
SPI 双屏异显(即双屏显示不同内容)与同显(即双屏显示相同内容)可通过控制屏幕 CS 线来实现。性能参考:ESP32-S3 可支持双屏分辨率 360 × 360,异显帧率约 20 fps。
SRAM 节省
一、前提说明
ESP-IDF 的 SPI 驱动暂不具备通过
GDMA
直接发送PSRAM
数据的能力当使用
esp_lcd_panel_io_spi
刷屏且颜色数据位于 PSRAM,驱动会在spi_master.c::setup_priv_desc()
检测 DMA 不可达后:在内部
SRAM
(带MALLOC_CAP_DMA
)重新分配临时缓冲区;将颜色数据
memcpy
到该缓冲区;再由 DMA 从 SRAM 发送数据至 LCD。
该「搬运 → 复制 → DMA」过程会反复发生,额外消耗 SRAM。
二、问题现象
系统同时运行 Wi-Fi / BLE 等高 SRAM 占用模块时,临时缓冲区抢占 SRAM,可能导致:
其他组件申请内存失败;
刷屏过程报 color trans fail;
LVGL 任务 WDT(看门狗)复位。
三、原因分析
PSRAM → SRAM 的临时拷贝是
spi_master
驱动的回退机制
,确保 DMA 无法直达外部 RAM 时仍能工作。内部 SRAM 既存放任务栈、FreeRTOS 对象、Wi-Fi 缓存,又承载 SPI 刷屏临时缓冲区,容易形成争用。
临时缓冲区大小约为
max_transfer_sz
× 已排队事务数,其中事务数由trans_queue_depth
决定。
四、规避 / 优化方案
通过配置减少 SRAM 占用
- 减小
spi_bus_config_t.max_transfer_sz
:一次 DMA 搬运字节数越小,单次临时缓冲区越小。 建议行刷模式下设置为
LCD_H_RES × 行数 × 2
,行数控制在20~60 行
。
- 减小
降低
esp_lcd_panel_io_spi_config_t.trans_queue_depth
:排队事务越少,同时存在的缓冲副本越少。非并发刷屏场景下,设为2~4
足矣。
示例配置代码:
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, };
合理规划 Wi-Fi 与图形任务栈 / 缓存
运行时监控 SRAM 水位
在使用 LVGL 时,关闭 LV_ATTRIBUTE_FAST_MEM_USE_IRAM 可以节省很多内存。(该配置默认情况下未开启)
ESP32-P4 SPI 时钟速率无法拉高
问题现象:当时钟速率拉高到 20 MHz 以上时,报错
invalid sclk speed
解决方案:参考 patch
SPI 总线锁 spi_bus_lock 在多核场景下存在并发竞态
建议: 用户控制 SPI 中断和 LVGL 任务固定运行在同一颗 CPU 核心上。
#include "driver/spi_master.h"
#define SPI_HOST SPI2_HOST // 以 SPI2 为例
#define LCD_CORE APP_CPU_NUM // 目标核(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, // ★ 指定 SPI 中断注册到哪颗核
};
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(); // 必须先初始化总线
xTaskCreatePinnedToCore(lvgl_task,
"lvgl",
8192,
NULL,
5,
NULL,
LCD_CORE); // ★ 与 .isr_cpu_id 相同
}