1. ESP32-S3 SPI LCD与触摸屏驱动移植实战
最近在做一个物联网终端设备项目,需要为ESP32-S3开发板适配一块2.8寸的TFT液晶屏(ILI9341驱动)和电容触摸屏(FT6336G驱动)。虽然乐鑫官方提供了SPI_LCD_TOUCH的参考示例,但在实际移植过程中还是遇到了不少坑。今天就把完整的移植过程、问题排查经验分享给大家,特别会重点讲解如何适配非官方默认支持的触摸芯片。
2. 硬件环境搭建与配置
2.1 显示模块硬件连接
我使用的是一块240x320分辨率的ILI9341驱动TFT屏,通过SPI接口与ESP32-S3通信。具体引脚连接如下:
code复制#define EXAMPLE_PIN_NUM_SCLK 12 // SPI时钟线
#define EXAMPLE_PIN_NUM_MOSI 11 // SPI数据输出
#define EXAMPLE_PIN_NUM_MISO 14 // SPI数据输入(实际未使用)
#define EXAMPLE_PIN_NUM_LCD_DC 10 // 数据/命令选择线
#define EXAMPLE_PIN_NUM_LCD_RST 9 // 复位信号
#define EXAMPLE_PIN_NUM_LCD_CS 46 // 片选信号
#define EXAMPLE_PIN_NUM_BK_LIGHT 13 // 背光控制
重要提示:ESP32-S3的SPI引脚分配需要注意,某些GPIO在启动时有特殊用途(如GPIO46常用于SPI CS)。建议参考官方手册确认引脚可用性。
2.2 触摸模块硬件连接
触摸屏采用FT6336G芯片,通过I2C接口通信。与常见的STMPE610(SPI接口)不同,这带来了额外的移植工作:
code复制#define EXAMPLE_PIN_NUM_TOUCH_SDA 17 // I2C数据线
#define EXAMPLE_PIN_NUM_TOUCH_SCL 18 // I2C时钟线
#define EXAMPLE_PIN_NUM_TOUCH_INT 16 // 中断信号(可选)
#define EXAMPLE_PIN_NUM_TOUCH_RST 15 // 复位信号
3. 显示驱动移植详解
3.1 利用官方示例基础
乐鑫的esp-idf中已经提供了SPI_LCD_TOUCH示例,支持ILI9341和GC9A01两种显示驱动。在menuconfig中可以直接选择:
bash复制idf.py menuconfig
导航到:
code复制Example Configuration -> LCD controller model -> ILI9341
3.2 关键参数配置
在spi_lcd_touch_main.c中需要修改以下关键参数:
c复制// SPI时钟频率,20MHz是ILI9341的典型值
#define EXAMPLE_LCD_PIXEL_CLOCK_HZ (20 * 1000 * 1000)
// 背光控制逻辑电平
#define EXAMPLE_LCD_BK_LIGHT_ON_LEVEL 1
#define EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL !EXAMPLE_LCD_BK_LIGHT_ON_LEVEL
实测发现:如果屏幕出现花屏现象,可以尝试降低SPI时钟频率到10MHz。某些廉价屏幕的驱动IC可能无法稳定工作在20MHz。
4. 触摸驱动移植实战
4.1 官方驱动的问题
官方示例默认使用STMPE610(SPI接口)触摸芯片,而我们需要适配FT6336G(I2C接口)。虽然esp-idf组件仓库中有esp_lcd_touch_ft6x36驱动,但直接使用出现了无法识别的问题。
4.2 替代方案实施
经过调研,发现FT6336G与FT5x06系列寄存器兼容,于是采用以下步骤:
-
添加组件依赖:
bash复制cd components git clone https://github.com/espressif/esp_lcd_touch_ft5x06.git -
修改Kconfig配置:
在main/Kconfig.projbuild中添加:kconfig复制config EXAMPLE_LCD_TOUCH_CONTROLLER_FT5X06 bool "FT5X06 (兼容FT6336)" help Select this if using FT5x06 or compatible FT6336 touch controller -
初始化代码调整:
c复制#ifdef CONFIG_EXAMPLE_LCD_TOUCH_CONTROLLER_FT5X06 esp_lcd_panel_io_handle_t touch_io_handle = NULL; esp_lcd_panel_io_i2c_config_t touch_io_config = { .dev_addr = 0x38, // FT6336默认I2C地址 .control_phase_bytes = 1, .dc_bit_offset = 0, .lcd_cmd_bits = 8, .lcd_param_bits = 8, }; ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c(handle_i2c, &touch_io_config, &touch_io_handle)); esp_lcd_touch_config_t touch_config = { .x_max = 240, .y_max = 320, .rst_gpio_num = EXAMPLE_PIN_NUM_TOUCH_RST, .int_gpio_num = EXAMPLE_PIN_NUM_TOUCH_INT, }; esp_lcd_touch_handle_t touch_handle = NULL; ESP_ERROR_CHECK(esp_lcd_touch_new_i2c_ft5x06(touch_io_handle, &touch_config, &touch_handle)); #endif
4.3 校准与测试
触摸屏需要校准才能准确定位。添加以下测试代码:
c复制// 在main函数初始化后添加
uint16_t x[5] = {0};
uint16_t y[5] = {0};
bool pressed = false;
esp_lcd_touch_get_data(touch_handle);
for (int i = 0; i < 5; i++) {
pressed = esp_lcd_touch_get_coordinates(touch_handle, x+i, y+i, NULL, 1);
if (pressed) {
printf("Touch at (%d, %d)\n", x[i], y[i]);
}
vTaskDelay(pdMS_TO_TICKS(100));
}
5. LVGL图形库集成
5.1 基本配置
-
添加LVGL组件:
bash复制cd components git clone --recursive https://github.com/lvgl/lvgl.git -
修改
lv_conf.h关键配置:c复制#define LV_COLOR_DEPTH 16 // 匹配ILI9341的RGB565格式 #define LV_HOR_RES_MAX 240 // 水平分辨率 #define LV_VER_RES_MAX 320 // 垂直分辨率 #define LV_TICK_CUSTOM 1 // 使用FreeRTOS的tick计数
5.2 显示与触摸绑定
c复制// 显示刷新回调
static void lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) {
esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)drv->user_data;
int offsetx1 = area->x1, offsetx2 = area->x2;
int offsety1 = area->y1, offsety2 = area->y2;
esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2+1, offsety2+1, color_map);
lv_disp_flush_ready(drv);
}
// 触摸输入回调
static void lvgl_touch_cb(lv_indev_drv_t *drv, lv_indev_data_t *data) {
esp_lcd_touch_handle_t touch_handle = (esp_lcd_touch_handle_t)drv->user_data;
uint16_t touch_x[1] = {0};
uint16_t touch_y[1] = {0};
uint8_t touch_cnt = 0;
bool pressed = false;
esp_lcd_touch_get_data(touch_handle);
pressed = esp_lcd_touch_get_coordinates(touch_handle, touch_x, touch_y, &touch_cnt, 1);
if (pressed && touch_cnt > 0) {
data->point.x = touch_x[0];
data->point.y = touch_y[0];
data->state = LV_INDEV_STATE_PR;
} else {
data->state = LV_INDEV_STATE_REL;
}
}
6. 常见问题与解决方案
6.1 触摸坐标不准
现象:触摸位置与实际显示位置偏移
解决方案:
- 检查LVGL的
lv_conf.h中分辨率设置是否与物理屏幕一致 - 添加触摸校准代码:
c复制// 在触摸初始化后添加 esp_lcd_touch_set_swap_xy(touch_handle, true); // 根据需要调整 esp_lcd_touch_set_mirror_x(touch_handle, false); esp_lcd_touch_set_mirror_y(touch_handle, true);
6.2 屏幕闪烁或花屏
可能原因:
- SPI时钟频率过高
- 电源不稳定
- 复位时序问题
排查步骤:
- 降低SPI时钟频率测试
- 检查电源电压是否稳定(建议3.3V±5%)
- 增加复位延时:
c复制gpio_set_level(EXAMPLE_PIN_NUM_LCD_RST, 0); vTaskDelay(pdMS_TO_TICKS(100)); gpio_set_level(EXAMPLE_PIN_NUM_LCD_RST, 1); vTaskDelay(pdMS_TO_TICKS(100));
6.3 触摸无响应
诊断方法:
- 用逻辑分析仪检查I2C信号
- 确认FT6336G的I2C地址(通常0x38或0x48)
- 检查中断引脚是否配置正确
典型修复:
c复制// 修改I2C初始化参数
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = EXAMPLE_PIN_NUM_TOUCH_SDA,
.scl_io_num = EXAMPLE_PIN_NUM_TOUCH_SCL,
.sda_pullup_en = GPIO_PULLUP_ENABLE, // 重要!
.scl_pullup_en = GPIO_PULLUP_ENABLE, // 重要!
.master.clk_speed = 400000, // 400kHz标准模式
};
7. 性能优化技巧
7.1 双缓冲配置
在lv_conf.h中启用双缓冲可显著提升流畅度:
c复制#define LV_DISP_DEF_REFR_PERIOD 30 // 刷新周期30ms
#define LV_DISP_DEF_DOUBLE_BUFFER 1 // 启用双缓冲
7.2 SPI DMA传输
修改SPI主机配置启用DMA:
c复制spi_bus_config_t buscfg = {
.miso_io_num = EXAMPLE_PIN_NUM_MISO,
.mosi_io_num = EXAMPLE_PIN_NUM_MOSI,
.sclk_io_num = EXAMPLE_PIN_NUM_SCLK,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = 320*240*2 + 8, // 一帧数据大小
.flags = SPICOMMON_BUSFLAG_MASTER | SPICOMMON_BUSFLAG_DUAL,
};
7.3 任务优先级调整
合理设置LVGL任务优先级:
c复制xTaskCreate(lvgl_port_task, "LVGL", 4096, NULL, 5, NULL); // 优先级5
经过以上步骤,我们成功实现了ESP32-S3对ILI9341 LCD和FT6336G触摸屏的完整驱动支持,并集成了LVGL图形库。实际项目中,还需要根据具体硬件调整参数,但核心移植思路是相通的。
