10 KiB
10 KiB
项目对比分析报告
1. 项目概述
本次分析对比了两个基于 ESP32 平台的 LCD 驱动项目,重点分析 d zbj 项目在适配 LVGL 时遇到的问题:
| 项目名称 | 路径 | 主要功能 |
|---|---|---|
| d zbj 项目 | /Users/rdzleo/Desktop/dzbj |
完整的嵌入式应用,包含 LCD 显示、触摸等功能 |
| QSPI LCD 驱动版本 | /Users/rdzleo/Desktop/qspi_lcd_驱动版本 |
专注于 LCD 驱动和触摸功能的实现,LVGL 适配成功 |
2. 项目结构对比
2.1 d zbj 项目结构
dzbj/
├── main/
│ ├── axis/ # 轴控制相关代码
│ ├── ble/ # BLE 蓝牙功能
│ ├── fatfs/ # 文件系统功能
│ ├── gpio/ # GPIO 控制
│ ├── lcd/ # LCD 驱动实现
│ ├── pages/ # 页面管理
│ ├── temp/ # 温度检测
│ ├── ui/ # UI 界面(SquareLine Studio 生成)
│ ├── wifi/ # WiFi 功能
│ └── main.c # 主函数
├── build/ # 构建输出
└── CMakeLists.txt # 构建配置
2.2 QSPI LCD 驱动版本结构
qspi_lcd_驱动版本/
├── main/
│ ├── display_touch_manager.h # 显示和触摸管理接口
│ ├── lcd_touch_driver.h # LCD 触摸驱动接口
│ └── main.c # 主函数
├── components/
│ └── lvgl/ # LVGL 组件
└── CMakeLists.txt # 构建配置
3. LCD 驱动实现对比
3.1 d zbj 项目 LCD 实现
核心文件:/Users/rdzleo/Desktop/dzbj/main/lcd/lcd.c
实现特点:
- 使用 QSPI 接口驱动 ST77916 LCD 控制器
- 直接调用 ESP-IDF 提供的 LCD 相关 API
- 分步骤初始化:SPI 总线 → LCD IO → LCD 面板 → 重置和初始化面板
- 支持触摸功能(CST816S 触摸控制器)
- 配置了双缓冲和 DMA 传输
关键代码:
void lcd_init(){
const spi_bus_config_t buscfg = ST77916_PANEL_BUS_QSPI_CONFIG(...);
spi_bus_initialize(SPI_LCD_HOST, &buscfg, SPI_DMA_CH_AUTO);
const esp_lcd_panel_io_spi_config_t io_config = ST77916_PANEL_IO_QSPI_CONFIG(...);
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi(...));
const st77916_vendor_config_t vendor_config = {
.flags = {
.use_qspi_interface = 1,
},
};
const esp_lcd_panel_dev_config_t panel_config = {...};
ESP_ERROR_CHECK(esp_lcd_new_panel_st77916(...));
esp_lcd_panel_reset(panel_handle);
esp_lcd_panel_init(panel_handle);
esp_lcd_panel_disp_on_off(panel_handle, true);
}
3.2 QSPI LCD 驱动版本实现
核心文件:通过 display_manager_init() 函数实现
实现特点:
- 使用显示管理器和触摸管理器的抽象层
- 封装了 LCD 初始化的细节
- 提供统一的接口函数
- 结构更清晰,职责更明确
关键代码:
void app_main(void) {
// 初始化显示管理器
esp_err_t ret = display_manager_init();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "显示管理器初始化失败: %s", esp_err_to_name(ret));
return;
}
// 获取显示驱动
lv_disp_t *display = display_manager_get_display();
if (display == NULL) {
ESP_LOGE(TAG, "获取显示驱动失败");
return;
}
// 初始化触摸管理器
ret = touch_manager_init(display);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "触摸管理器初始化失败: %s", esp_err_to_name(ret));
return;
}
}
3.3 驱动实现对比分析
| 对比项 | d zbj 项目 | QSPI LCD 驱动版本 |
|---|---|---|
| 接口方式 | 直接调用 ESP-IDF API | 使用抽象层管理器 |
| 代码复杂度 | 相对复杂,包含更多细节 | 更简洁,封装了细节 |
| 可维护性 | 模块化但分散 | 集中管理,更易维护 |
| 灵活性 | 高度灵活,可自定义各环节 | 标准化流程,灵活性稍低 |
| 初始化流程 | 线性初始化各组件 | 分层初始化,结构更清晰 |
| 错误处理 | 基本错误检查 | 详细的错误处理和日志 |
4. 初始化流程对比
4.1 d zbj 项目初始化流程
核心文件:/Users/rdzleo/Desktop/dzbj/main/main.c
初始化顺序:
i2c_init()- 初始化 I2C 总线nvs_init()- 初始化 NVS 存储lcd_init()- 初始化 LCD 面板touch_init()- 初始化触摸控制器lvgl_lcd_init()- 初始化 LVGL 显示fatfs_init()- 初始化文件系统pwm_init()- 初始化 PWM 背光app_test_display()- 显示测试屏幕
4.2 QSPI LCD 驱动版本初始化流程
核心文件:/Users/rdzleo/Desktop/qspi_lcd_驱动版本/main/main.c
初始化顺序:
display_manager_init()- 初始化显示管理器display_manager_get_display()- 获取显示驱动- 创建 LVGL 任务
- 等待 LVGL 任务启动
ui_init()- 初始化 SquareLine Studio UI- 等待 LVGL 完成初始渲染
touch_manager_init(display)- 初始化触摸管理器- 控制 LCD 开关状态
5. 技术选型对比
5.1 硬件平台
- d zbj 项目:ESP32-S3 系列
- QSPI LCD 驱动版本:ESP32-S3 系列
5.2 LCD 控制器
- d zbj 项目:ST77916S
- QSPI LCD 驱动版本:ST77916S
7.3 通信接口
- d zbj 项目:QSPI(LCD)、I2C(触摸)
- QSPI LCD 驱动版本:QSPI(LCD)、I2C(触摸)
7.4 软件框架
- d zbj 项目:ESP-IDF + LVGL + SquareLine Studio
- QSPI LCD 驱动版本:ESP-IDF + LVGL + SquareLine Studio
7.5 SDK 配置对比
| 配置项 | d zbj 项目 | QSPI LCD 驱动版本 |
|---|---|---|
| ESP-IDF 版本 | 5.4.2 | 最新稳定版 |
| 编译器 | xtensa-esp32s3-elf-gcc | xtensa-esp32s3-elf-gcc |
| 构建系统 | CMake | CMake |
| 目标芯片 | esp32s3 | esp32s3 |
7.6 LVGL 配置对比
| 配置项 | d zbj 项目 | QSPI LCD 驱动版本 |
|---|---|---|
| LVGL 版本 | 8.3.11 | 8.3.11 |
| 颜色深度 | 16位 (CONFIG_LV_COLOR_DEPTH_16=y) | 16位 (CONFIG_LV_COLOR_DEPTH_16=y) |
| 字节交换 | 启用 (CONFIG_LV_COLOR_16_SWAP=y) | 启用 (CONFIG_LV_COLOR_16_SWAP=y) |
| 内存大小 | 64KB (CONFIG_LV_MEM_SIZE_KILOBYTES=64) | 32KB (CONFIG_LV_MEM_SIZE_KILOBYTES=32) |
| 显示缓冲 | 双缓冲,支持 DMA | 双缓冲,支持 DMA |
| 任务优先级 | 4 | 4 |
| 任务堆栈大小 | 8192 | 8192 |
7.7 显示缓冲区配置对比
| 配置项 | d zbj 项目 | QSPI LCD 驱动版本 |
|---|---|---|
| 缓冲区大小计算 | LCD_WID*LCD_HIGH/5 |
EXAMPLE_LCD_H_RES * EXAMPLE_LVGL_DRAW_BUF_LINES (360*30) |
| 内存分配方式 | ESP-IDF 标准内存分配 | spi_bus_dma_memory_alloc 专门为 SPI DMA 分配 |
| 双缓冲 | 启用 | 启用 |
| DMA 支持 | 启用 | 启用 |
| 刷新机制 | LVGL 标准刷新 | 自定义回调 example_notify_lvgl_flush_ready |
7.8 UI 文件对比
| 对比项 | d zbj 项目 | QSPI LCD 驱动版本 |
|---|---|---|
| SquareLine Studio 版本 | 相同版本 | 相同版本 |
| UI 导出格式 | 压缩的 PNG 格式 | 压缩的 PNG 格式 |
| 屏幕数量 | 包含多个屏幕(Home、Set、Img) | 包含多个屏幕(Home、Set、Img) |
| 图片资源 | 包含相同的图片资源 | 包含相同的图片资源 |
| 组件结构 | 完全相同的组件结构 | 完全相同的组件结构 |
| 事件处理 | 相同的事件处理机制 | 相同的事件处理机制 |
7.9 关键差异分析
-
内存分配方式:
- qspi_lcd_驱动版本使用
spi_bus_dma_memory_alloc专门为 SPI DMA 传输分配内存 - d zbj 项目使用标准内存分配,可能不是最优的 DMA 内存
- qspi_lcd_驱动版本使用
-
显示缓冲区配置:
- qspi_lcd_驱动版本使用固定的 30 行作为缓冲区大小
- d zbj 项目使用
LCD_WID*LCD_HIGH/5计算缓冲区大小
-
初始化流程:
- qspi_lcd_驱动版本使用管理器模式,分层初始化,包含详细的错误处理
- d zbj 项目使用线性初始化流程,错误处理相对简单
-
刷新回调:
- qspi_lcd_驱动版本实现了自定义的
example_notify_lvgl_flush_ready回调 - d zbj 项目使用 LVGL 标准刷新机制
- qspi_lcd_驱动版本实现了自定义的
7.10 花屏问题分析
基于以上对比,导致 d zbj 项目花屏的可能原因包括:
-
内存分配问题:
- 未使用
spi_bus_dma_memory_alloc分配适合 DMA 传输的内存 - 可能导致 DMA 传输数据错误,引起花屏
- 未使用
-
显示缓冲区配置:
LCD_WID*LCD_HIGH/5计算出的缓冲区大小可能不合适- 缓冲区过小可能导致显示不完整,过大可能导致内存不足
-
初始化流程问题:
- 初始化顺序可能影响硬件状态
- 缺少必要的初始化步骤或延迟
-
刷新机制问题:
- 缺少自定义的刷新就绪回调
- 可能导致 LVGL 与硬件刷新不同步
7. 解决方案
7.1 具体措施
-
优化内存分配:
- 使用
spi_bus_dma_memory_alloc专门为 SPI DMA 传输分配内存 - 确保内存对齐和连续性,提高 DMA 传输效率
- 使用
-
调整缓冲区配置:
- 参考 QSPI LCD 驱动版本,使用固定行数的缓冲区大小(如 30 行)
- 确保缓冲区大小合理,避免内存不足或浪费
-
改进刷新机制:
- 实现自定义的
notify_lvgl_flush_ready回调 - 确保 LVGL 与硬件刷新同步,减少显示异常
- 实现自定义的
-
优化初始化流程:
- 参考 QSPI LCD 驱动版本的管理器模式
- 确保初始化顺序正确,添加必要的延迟
- 增加详细的错误处理和日志输出
8. 结论
通过对两个项目的详细对比分析,我们发现导致 d zbj 项目花屏的主要原因是:
- 内存分配方式不当:未使用专门的
spi_bus_dma_memory_alloc函数分配适合 DMA 传输的内存 - 显示缓冲区配置不合理:缓冲区大小计算方式可能导致内存使用不当
- 刷新机制不同步:缺少自定义的刷新就绪回调,可能导致 LVGL 与硬件刷新不同步
- 初始化流程差异:初始化顺序和错误处理的差异可能影响硬件状态
采用 QSPI LCD 驱动版本的实现方式,特别是内存分配、缓冲区配置和刷新机制的优化,可以有效解决 d zbj 项目的花屏问题,提高 LVGL 适配的稳定性和可靠性。