环境
单片机:ESP32
ESP32 框架:Arduino
LCD : ST7796 3.5寸 480*320 屏
电容触摸 IC: GT911
IDE: Visual Studio Code with Platoform IO extension
LVGL 版本:8.3.6
本文的移植方法同样适应于 STM32 等其他单片机或者 IDF 等其他 ESP32 框架。
移植前准备
移植前需要先搞好了液晶屏和触摸屏驱动。液晶屏驱动仅需实现一个描点函数就可以了。
移植前我已经使用 eSPI 驱动起了 ST7796 屏,别且实现了触摸驱动。
LVGL移植步骤
下载 LVGL 代码
下载 LVGL 文件,放置到 工程的 .lib 库中:

编辑配置文件
把 LVGL 源文件根目录下的 lv_conf_template.h 重命名为 lv_conf.h
首先把第10行左右的宏定义打开:
#if 1 /*Set it to "1" to enable content*/
更改 main.cpp 文件
主文件中导入 LVGL 库函数:
#include "../lib/lvgl/lvgl.h" // LVGL 库
添加一些全局变量,定义 UI 元素等:
lv_obj_t *screenMain;
lv_obj_t *btn1;
lv_obj_t *btn2;
lv_obj_t *label;
/*Change to your screen resolution*/
static const uint16_t screenWidth = 320;
static const uint16_t screenHeight = 480;
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf[ screenWidth * 10 ];
如果使用 Arduino 框架函数编程,需要把下面的86行左右的宏也打开:
/*Use a custom tick source that tells the elapsed time in milliseconds.
*It removes the need to manually update the tick with `lv_tick_inc()`)*/
#define LV_TICK_CUSTOM 1
两个函数
需要定义两个函数给 LVGL 使用,一个是 TFT 屏的描点函数,一个是触摸屏的获取触摸位置的函数。仅需提描点函数给 LVGL, 它就可以绘制出各种 UI 元素了。这个描点函数就是 TFT 驱动和 LVGL 之间的桥梁。
描点函数如下:
// 描点函数,给 LVGL 使用
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( ( uint16_t * )&color_p->full, w * h, true );
tft.endWrite();
lv_disp_flush_ready( disp );
}
提供给 LVGL 的获取触摸位置的函数如下:
/ 触摸函数,给 LVGL 使用
void my_input_read(_lv_indev_drv_t * drv, lv_indev_data_t*data)
{
uint8_t touches = ts.touched(GT911_MODE_POLLING);
if(touches == 0) return;
GTPoint p = ts.getPoint(0);
//Serial.printf("#%d %d,%d s:%d\n", p.trackId, p.x, p.y, p.area);
data->state = LV_INDEV_STATE_PR;
data->point.x = p.x;
data->point.y = p.y;
return; /*No buffering now so no more data read*/
}
添加 UI 事件处理函数
有了上述两个函数,LVGL 界面就可以跑起来了,但如果想要在点击按钮时有所反应,需要定义 UI 事件处理函数:
// UI 元素事件处理函数
static void event_handler_btn(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
if(code == LV_EVENT_CLICKED) {
LV_LOG_USER("Clicked");
if (e->target == btn1)
lv_label_set_text(label, "Start");
else if (e->target == btn2)
lv_label_set_text(label, "Stop");
}
else if(code == LV_EVENT_VALUE_CHANGED) {
LV_LOG_USER("Toggled");
}
}
完整 main.cpp 代码
完整的 main.cpp 代码如下:
#include <Arduino.h>
#include "../lib/TFT_eSPI/TFT_eSPI.h" // eSPI 库,实现 TFT 液晶屏驱动
#include "Wire.h" // I2C 库, GT911 电容触摸芯片接口
#include "../lib/GT911/GT911.h" // GT911 电容触摸屏驱动
#include "../lib/lvgl/lvgl.h" // LVGL 库
#define TOUCH_INT_PIN 35
TFT_eSPI tft = TFT_eSPI(); // Invoke custom library
GT911 ts = GT911();
lv_obj_t *screenMain;
lv_obj_t *btn1;
lv_obj_t *btn2;
lv_obj_t *label;
/*Change to your screen resolution*/
static const uint16_t screenWidth = 320;
static const uint16_t screenHeight = 480;
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf[ screenWidth * 10 ];
// 描点函数,给 LVGL 使用
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( ( uint16_t * )&color_p->full, w * h, true );
tft.endWrite();
lv_disp_flush_ready( disp );
}
// 触摸函数,给 LVGL 使用
void my_input_read(_lv_indev_drv_t * drv, lv_indev_data_t*data)
{
uint8_t touches = ts.touched(GT911_MODE_POLLING);
if(touches == 0) return;
GTPoint p = ts.getPoint(0);
//Serial.printf("#%d %d,%d s:%d\n", p.trackId, p.x, p.y, p.area);
data->state = LV_INDEV_STATE_PR;
data->point.x = p.x;
data->point.y = p.y;
return; /*No buffering now so no more data read*/
}
// UI 元素事件处理函数
static void event_handler_btn(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
if(code == LV_EVENT_CLICKED) {
LV_LOG_USER("Clicked");
if (e->target == btn1)
lv_label_set_text(label, "Start");
else if (e->target == btn2)
lv_label_set_text(label, "Stop");
}
else if(code == LV_EVENT_VALUE_CHANGED) {
LV_LOG_USER("Toggled");
}
}
void setup()
{
Serial.begin(115200);
String LVGL_Arduino = "Hello Arduino! ";
LVGL_Arduino += String('V') + lv_version_major() + "." + lv_version_minor() + "." + lv_version_patch();
Serial.println( LVGL_Arduino );
Serial.println( "I am LVGL_Arduino" );
tft.init();
tft.setRotation( 0 ); /* Landscape orientation, flipped */
Wire.begin(15, 14);
pinMode(TOUCH_INT_PIN, INPUT);
ts.begin();
lv_init();
lv_disp_draw_buf_init( &draw_buf, buf, NULL, screenWidth * 10 );
/*Initialize the display*/
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
/*Change the following line to your display resolution*/
disp_drv.hor_res = screenWidth;
disp_drv.ver_res = screenHeight;
disp_drv.flush_cb = my_disp_flush;
disp_drv.draw_buf = &draw_buf;
lv_disp_drv_register(&disp_drv);
/*Initialize the input device driver*/
static lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv); /*Descriptor of a input device driver*/
indev_drv.type = LV_INDEV_TYPE_POINTER; /*Touch pad is a pointer-like device*/
indev_drv.read_cb = my_input_read; /*Set your driver function*/
lv_indev_drv_register(&indev_drv); /*Finally register the driver*/
/*Create screen objects*/
screenMain = lv_scr_act();
label = lv_label_create(screenMain);
lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP);
lv_label_set_text(label, "Press a button");
lv_obj_align(label, LV_ALIGN_TOP_MID, 0, 30);
btn1 = lv_btn_create(screenMain);
lv_obj_add_event_cb(btn1, event_handler_btn, LV_EVENT_ALL, NULL);
lv_obj_align(btn1, LV_ALIGN_CENTER, -60, 0);
lv_obj_t * label1 = lv_label_create(btn1);
lv_label_set_text(label1, "Start");
btn2 = lv_btn_create(screenMain);
lv_obj_add_event_cb(btn2, event_handler_btn, LV_EVENT_ALL, NULL);
lv_obj_align(btn2, LV_ALIGN_CENTER, 60, 0);
lv_obj_t * label2 = lv_label_create(btn2);
lv_label_set_text(label2, "Stop");
lv_scr_load(screenMain);
}
void loop()
{
lv_timer_handler(); /* let the GUI do its work */
delay( 5 );
}

加作者微信:jiyuyun18(积雨云), 交流电子技术
留言:CSDN入群,加入电子技术讨论群
留言:ESP32, 加入 ESP32 技术讨论群
留言:CSDN资料,领取免费电子技术学习资料

