1. LVGL基础显示与触摸初始化
任务目标
在ESP32开发板上完成LVGL图形库的基础初始化,配置显示驱动(ILI9341 SPI屏)和触摸驱动(XPT2046电阻触摸),最终实现屏幕点亮并显示一个可点击的按钮,触摸按钮时LED状态翻转,以此验证显示与触摸链路均正常工作。
环境与依赖
- 硬件:ESP32 DevKitC、ILI9341 2.4寸SPI屏(含XPT2046触摸)、LED(GPIO2)
- 软件:ESP-IDF v5.x,LVGL v8.3.x(作为ESP-IDF组件)
- 接线:ILI9341_SCLK=GPIO18, MOSI=GPIO23, CS=GPIO5, DC=GPIO4, RST=GPIO16, BL=GPIO17;XPT2046_SCLK=GPIO18, MOSI=GPIO23, MISO=GPIO19, CS=GPIO21, IRQ=GPIO22
关键完整代码
// main.c — LVGL显示与触摸初始化最小实现
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "lvgl.h"
#include "ili9341.h" // 需在components中提供
#include "xpt2046.h" // 需在components中提供
#define PIN_LED 2
#define LVGL_TICK_MS 5
static const char *TAG = "lvgl_demo";
static spi_device_handle_t spi_dev;
// 显示缓冲(LVGL推荐双缓冲)
static lv_disp_draw_buf_t disp_buf;
static lv_color_t buf1[240*10];
static lv_color_t buf2[240*10];
// 触摸状态
static bool touch_pressed = false;
static lv_coord_t touch_x = 0, touch_y = 0;
// ---- SPI初始化 ----
static void spi_init(void) {
spi_bus_config_t buscfg = {
.mosi_io_num = 23,
.miso_io_num = 19,
.sclk_io_num = 18,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = 240*10*2
};
spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO);
spi_device_interface_config_t devcfg = {
.clock_speed_hz = 40*1000*1000,
.mode = 0,
.spics_io_num = 5,
.queue_size = 7,
};
spi_bus_add_device(SPI2_HOST, &devcfg, &spi_dev);
}
// ---- 显示驱动回调(LVGL调用)----
static void disp_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_p) {
ili9341_draw_bitmap(area->x1, area->y1, area->x2, area->y2, (uint16_t*)color_p);
lv_disp_flush_ready(drv);
}
// ---- 触摸驱动回调(LVGL调用)----
static void touch_read(lv_indev_drv_t *drv, lv_indev_data_t *data) {
data->state = touch_pressed ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL;
data->point.x = touch_x;
data->point.y = touch_y;
}
// ---- 触摸中断/轮询更新(简化:轮询读取)----
static void touch_update_task(void *arg) {
while (1) {
uint16_t x, y;
if (xpt2046_read(&x, &y)) { // 返回true表示触摸按下
touch_pressed = true;
touch_x = x;
touch_y = y;
} else {
touch_pressed = false;
}
vTaskDelay(pdMS_TO_TICKS(10));
}
}
// ---- 按钮事件回调 ----
static void btn_event_cb(lv_event_t *e) {
static bool led_on = false;
led_on = !led_on;
gpio_set_level(PIN_LED, led_on);
}
// ---- LVGL初始化 ----
static void lvgl_init(void) {
lv_init();
// 显示缓冲
lv_disp_draw_buf_init(&disp_buf, buf1, buf2, 240*10);
// 显示驱动
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = 240;
disp_drv.ver_res = 320;
disp_drv.flush_cb = disp_flush;
disp_drv.draw_buf = &disp_buf;
lv_disp_drv_register(&disp_drv);
// 触摸驱动
static lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = touch_read;
lv_indev_drv_register(&indev_drv);
}
// ---- LVGL心跳与任务处理 ----
static void lvgl_task(void *arg) {
while (1) {
lv_tick_inc(LVGL_TICK_MS);
lv_timer_handler();
vTaskDelay(pdMS_TO_TICKS(LVGL_TICK_MS));
}
}
void app_main(void) {
// GPIO
gpio_set_direction(PIN_LED, GPIO_MODE_OUTPUT);
gpio_set_level(PIN_LED, 0);
// 硬件初始化
spi_init();
ili9341_init(spi_dev, 16, 17); // DC=GPIO4, RST=GPIO16, BL=GPIO17
xpt2046_init(spi_dev, 21, 22); // CS=GPIO21, IRQ=GPIO22
// LVGL
lvgl_init();
// 创建UI:一个按钮
lv_obj_t *btn = lv_btn_create(lv_scr_act());
lv_obj_set_size(btn, 120, 50);
lv_obj_center(btn);
lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_CLICKED, NULL);
lv_obj_t *label = lv_label_create(btn);
lv_label_set_text(label, "Toggle LED");
lv_obj_center(label);
// 启动任务
xTaskCreate(touch_update_task, "touch", 4096, NULL, 5, NULL);
xTaskCreate(lvgl_task, "lvgl", 8192, NULL, 5, NULL);
ESP_LOGI(TAG, "LVGL initialized. Touch button to toggle LED.");
}
运行与验证
- 编译烧录:在ESP-IDF终端执行
idf.py set-target esp32→idf.py build flash monitor。 - 屏幕显示:上电后屏幕应点亮,中央显示一个带“Toggle LED”文字的按钮。
- 触摸验证:用手指或触控笔点击按钮,观察GPIO2外接LED状态翻转(亮→灭→亮)。同时串口监视器应输出日志
LVGL initialized...。 - 通过标准:屏幕无花屏/闪烁,按钮点击响应灵敏(每次点击LED翻转一次),触摸位置与视觉按钮位置基本吻合。
常见问题
- 屏幕白屏:检查SPI接线(SCLK/MOSI/CS/DC)及背光引脚是否使能(BL接GPIO17并拉高)。
- 触摸无响应:确认XPT2046的IRQ引脚是否接对(GPIO22),并检查触摸驱动初始化时CS引脚号是否正确。
- 编译报错:确保LVGL组件已通过
idf.py add-dependency "lvgl/lvgl^8.3.0"添加,且ili9341.h和xpt2046.h头文件路径已包含。