文章版本7.10,LVGL更新极快,不同版本配置方法可能存在差异
触摸搞了好几天,坑太多了,好在总算飞过来了。顺便把显示驱动也写了;提前说一下,我的硬件是2.4寸ILI9341驱动屏幕+XPT2046电阻触摸,触摸芯片和屏幕共用SPI数据和时钟引脚,只有CS引脚不同。
1.TFT_eSPI库不只是显示驱动,还带有触摸驱动,可以打开并尝试TFT_eSPI/examples/Generic/Touch_calibrate/Touch_calibrate.ino例程,看是否能成功实现触摸。如果可以实现则不需要第三方驱动,省去API适配的麻烦。
2.LVGL支持多平台,ino文件是专为Arduino平台编写的。官方LVGL_Arduino.ino文件已带有输入设备(indev)配置语句。如果你手头里有lv_port_indev_template.c和lv_port_indev_template.h文件的话,它们没有用,至少Arduino用不到。
3.官方的LVGL_Arduino.ino文件有错误,没有计时语句,会导致LVGL对触摸没有响应。另外缺少SPI库,导致驱动在底层无法与触摸芯片通信。
打开TFT_eSPI/User_Setup_Select.h,找到:
#include // Default setup is root library folder
并删掉它前面的注释符号,这是用来选择驱动配置文件的,TFT_eSPI有很多默认配置文件,这里我们选择User_Setup,自己来配置屏幕。接下来打开User_Setup.h。
在这个文件里你要选择屏幕驱动、定义引脚并配置SPI速度,部分显示驱动还会有自己的专有配置项,注意看注释,或者用编辑器的查找来搜索一下自己显示驱动的型号。
如果你用的是ESP32的话,到文件底部找到
// The ESP32 has 2 free SPI ports i.e. VSPI and HSPI, the VSPI is the default.
// If the VSPI port is in use and pins are not accessible (e.g. TTGO T-Beam)
// then uncomment the following line:
//#define USE_HSPI_PORT
并把#define USE_HSPI_PORT前的注释符号去掉,这句话用来解决硬件SPI的占用问题
踩坑指南01里说的启用CS引脚也是在这里完成的,举个栗子:
// The hardware SPI can be mapped to any pins
#define TFT_MISO 19
#define TFT_MOSI 23
#define TFT_SCLK 18
#define TFT_CS 15 // Chip select control pin
#define TFT_DC 2 // Data Command control pin
#define TFT_RST 4 // Reset pin (could connect to RST pin)
//#define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST
#define TOUCH_CS 12 // Chip select pin (T_CS) of touch screen
要有#define TOUCH_CS这一条,因为文件里有一块条件定义,如果CS引脚没有被定义,触摸相关模块不会被编译,导致ino文件touch部分报错。所以要么#define TOUCH_CS,要么把touch部分代码全删掉。
LVGL支持多种输入设备,包括鼠标、键盘、编码器等,这里介绍触摸屏(Touchpad)配置方法。
打开LVGL_Arduino.ino,在一群include里加一句:
#include
加完之后总共要有四句include,如下:
#include
#include
#include
#include
我们找到最下方只有两行的loop()函数;添加一行代码,完成后如下:
void loop()
{
lv_task_handler(); /* let the GUI do its work */
lv_tick_inc(5);
delay(5);
}
lv_tick_inc(5)是告诉LVGL,5毫秒过去了。没有这句话,LVGL会失去时钟,因此无法做出响应。
完成了,就两步,简单吧。
最后,我们来看一下官方给的ino文件。主要看一下LittleVGL和TFT_eSPI是如何对接的。首先文件顶部,为TFT_eSPI类创建名为tft的对象实例:
TFT_eSPI tft = TFT_eSPI(); /* TFT instance */
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
uint32_t w = (area->x2 - area->x1 + 1);
uint32_t h = (area->y2 - area->y1 + 1);
tft.startWrite();
tft.setAddrWindow(area->x1, area->y1, w, h);
tft.pushColors(&color_p->full, w * h, true);
tft.endWrite();
lv_disp_flush_ready(disp);
}
lv_disp_buf_init(&disp_buf, buf, NULL, LV_HOR_RES_MAX * 10);
/*Initialize the display*/
lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = 320;
disp_drv.ver_res = 240;
disp_drv.flush_cb = my_disp_flush;
disp_drv.buffer = &disp_buf;
lv_disp_drv_register(&disp_drv);
上半个是需要自己写的显示刷新函数,因为TFT_eSPI是官方钦点的驱动,所以这里官方写好了。下半个是LVGL的显示输出设备定义,LVGL其实不在乎屏幕是什么,定义好显示设备后它就会把每帧的像素信息交给设备指定处理函数,即my_disp_flush;my_disp_flush里面都是TFT_eSPI定义的方法,把像素信息显示到屏幕上。
输入和输出设备对LVGL都一样,都是Device,所以代码和显示部分差不多
bool my_touchpad_read(lv_indev_drv_t * indev_driver, lv_indev_data_t * data)
{
uint16_t touchX, touchY;
bool touched = tft.getTouch(&touchX, &touchY, 600);
if(!touched) {
data->state = LV_INDEV_STATE_REL;
} else {
data->state = LV_INDEV_STATE_PR;
/*Set the coordinates*/
data->point.x = touchX;
data->point.y = touchY;
Serial.print("Data x");
Serial.println(touchX);
Serial.print("Data y");
Serial.println(touchY);
}
return false; /*Return `false` because we are not buffering and no more data to read*/
}
/*SETUP函数里散落了一些触摸屏初始化和校准语句*/
tft.begin(); /* TFT init */
tft.setRotation(1); /* Landscape orientation */
uint16_t calData[5] = { 275, 3620, 264, 3532, 1 };
tft.setTouch(calData);
/*Initialize the (dummy) input device driver*/
lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = my_touchpad_read;
lv_indev_drv_register(&indev_drv);
底部是输入设备定义,告诉LVGL这里有一个Touchpad触摸设备,LVGL会定时去执行一下my_touchpad_read,定时的时间在lv_conf里设置,默认30ms。my_touchpad_read就是处理函数,告知触摸状态和最后坐标。