LVGL (Light and Versatile Graphics Library) 是一个很流行的免费开源嵌入式图形库,可以帮助很多 MCU、MPU 和显示类型创建好看的 UI。它由Gábor Kiss-Vámosi于2016年创建的一个开源项目,目前由来自世界各地的志愿者共同维护和开发。LVGL是用C语言编写的,遵循MIT协议,可以自由地使用和修改。LVGL支持多种操作系统,例如Linux、Windows、RTOS等,也可以在开发板上运行。LVGL还支持多种显示器驱动器和触摸屏驱动器,可以与不同大小和分辨率的显示器兼容。
LVGL还提供了多种语言的绑定,例如Python、Micropython、JavaScript等,以及多种开发工具,例如模拟器、视觉化设计器、字体转换器等。
1 硬件设计
ESP32参数如下:
参数 | 值 |
---|---|
型号 | esp32-s2 |
flash | 8M |
psram | 2M |
LCD屏幕参数如下:
参数 | 值 |
---|---|
尺寸 | 2.6寸 |
分辨率 | 240*240 |
接口 | spi |
驱动IC | ST7789V |
触摸 | 无 |
ESP32与LCD的接线如下:
esp32-s2 | lcd | 说明 |
---|---|---|
VCC | VCC | 电源正 |
GND | GND | 电源负 |
GPIO36SPI_CLK | CLK | SPI时钟线 |
GPIO35SPI_MOSI | SDI | SPI数据线,esp32输出,lcd输入 |
GPIO37SPI_MISO | SDO | SPI数据线,esp32输入,lcd输出,注:该引脚一般可以不用,除非你要读取LCD的信息 |
GPIO34 | CS | SPI片选 |
GPIO4 | WR(D/C) | 并口时作为写入信号/SPI时作为命令或参数的选择 |
GPIO1 | RST | LCD复位引脚,可以用普通IO控制,引脚不足的情况下也可以和esp32的复位引脚接到一起 |
GPIO2 | BL | 背光引脚 |
2 软件设计
注:本文是基于PlatformIO搭建的Arduino环境,所有软件代码也是基于这个环境编译。不知道怎么搭建的同学自行查阅资料,这个网上教程有很多的。
2.1 新建PlatformIO工程
输入项目名称,选择芯片及开发板,选择arduino架构。
配置项目参数(platformio.ini)。
注:具体的项目参数根据自己的实际情况配置。更多的参数详情及使用方法,可以在下面的链接查看。
https://docs.platformio.org/page/projectconf.html
https://docs.platformio.org/en/latest/platforms/espressif32.html
本文配置的参数如下:
[env:adafruit_metro_esp32s2]
platform = espressif32
board = adafruit_metro_esp32s2
framework = arduino
lib_deps =
bodmer/TFT_eSPI@^2.5.43
lvgl/lvgl@^8.3.11
monitor_speed = 115200
board_build.f_cpu = 240000000L
board_build.f_flash = 80000000L
board_build.flash_mode = qio
board_upload.flash_size = 8MB
board_upload.speed = 921600
board_upload.use_1200bps_touch = true
board_upload.wait_for_upload_port = true
build_flags =
-DBOARD_HAS_PSRAM
-mfix-esp32-psram-cache-issue
-DARDUINO_USB_CDC_ON_BOOT=1
-DARDUINO_USB_DFU_ON_BOOT=0
-DARDUINO_USB_MSC_ON_BOOT=0
-DCORE_DEBUG_LEVEL=0
lib_ldf_mode = deep+
2.2 添加开源库
在PIO Home界面选择Libraries,搜索需要的库并添加到自己的工程目录下。
注:每个PlatformIO工程的库都是独立的,所以你这里要把库添加到对应的工程,旧工程下载的库并不能直接用于新工程上。
本文需要使用的Arduino库如下:
Arduino库 | 版本 | 说明 |
---|---|---|
TFT_eSPI | 2.5.43 | 该库通过SPI方式驱动LCD,支持多种LCD常用驱动IC |
lvgl | 8.3.11 | lvgl库,方便做各种图形处理 |
配置流程大致如下:
添加成功后可以在工程目录下看到已添加的库,platformio.ini文件中也会自动生成依赖库代码。
2.3 配置LCD底层接口
2.3.1 LCD接口配置
TFT_eSPI
库集成了很多常用LCD驱动IC的接口,根据自己使用的LCD屏幕参数修改User_Setup.h
文件,即可调用底层的接口函数。
注:User_Setup.h定义的参数比较多,我这里就不一个个细说了。我主要讲几个重要的参数。
1)驱动IC
根据自己使用的LCD驱动IC打开对应的宏。注意这些驱动只能选择一个打开,不用的要注释掉。
2)RGB数据格式
RGB格式指的是像素点颜色数据的排列方式,一般就BGR和RGB两种,区别其实就是数据高位在前还是低位在前,这个主要是用于图片显示,要用哪种格式主要是看你要显示的内容是怎么排的,不确定的话可以先不改,调试的时候如果颜色不对的话再换过来就好了。
3)像素
根据自己屏幕的像素修改,也可以先不改,直接在后面应用的时候再改。
4)GPIO
根据自己的电路设置引脚,除了几个必要的引脚,有些引脚可以不配置。
如:RST可以通过硬件和MCU的RST接到一起,软件配置成-1即可。BL背光也可以硬件直接控制。
注意:相同的GPIO定义只能打开一个,默认有些打开了的要注释掉。
特别说明:建议使用2.4.0以上版本,因为之前的版本关于ESP32的引脚定义是分成HSPI和VSPI的,默认使用VSPI,如果要用HSPI要打开USE_HSPI_PORT定义,但是这套框架兼容性不强,不适用于ESP32-S2和ESP32-C2,而2.4.0以上版本取消了这个定义,直接都按引脚号来定义,这样一来就不用区分HSPI和VSPI了。
5)字库
TFT_eSPI自带的这些字库你可以直接用,如果你有自己的字库不用这里的话也可以注释掉。flash空间足够的情况下,这点代码加不加都没关系。
6)SPI通讯速率
SPI通讯速率一般默认即可,默认的这个速率一般是足够了的,如果需要更快的话可以自己修改。
7)ESP32的特殊定义
TFT_eSPI旧版本关于ESP32的SPI接口是分为HSPI和VSPI两种的,默认使用VSPI,如果要用HSPI要打开USE_HSPI_PORT定义,如果你只是用ESP32,那这个框架是没什么问题的。
但是我之前因为项目需要从ESP32改用ESP32-S2,结果发现ESP32-S2就没有HSPI和VSPI,所有接口都是FSPI,于是我就要把底层很多东西都改掉才能正常使用。
不过现在TFT_eSPI库2.4.0以上版本就已经把这个问题改掉了,兼容了ESP32-S2和ESP-C3,取消了USE_HSPI_PORT这个定义,SPI接口都以GPIO引脚号来定义。
所以,我建议都用新版本的库吧,兼容性更好,也不用去考虑用HSPI还是VSPI。
2.3.2 LCD驱动测试
TFT_eSPI库配置好了之后可以先烧录一个简单的程序,验证一下硬件接线和代码是否能正常运行。当然,到了后面把lvgl配置好再一步到位也行。
注意:如果此时已经把lvgl库加入到工程,又没有配置的话,编译会出错(找不到lv_conf.h文件),得把lvgl库删除或者配置好lvgl库再测试。
测试代码如下:
#include <Arduino.h>
#include <SPI.h>
#include <TFT_eSPI.h>
TFT_eSPI tft = TFT_eSPI();
void setup()
{
Serial.begin(115200);
tft.begin();
tft.setRotation(0);
tft.fillScreen(TFT_BLACK);
}
void loop()
{
tft.fillScreen(TFT_RED);
delay(1000);
tft.fillScreen(TFT_GREEN);
delay(1000);
tft.fillScreen(TFT_BLUE);
delay(1000);
}
测试结果:
如果能正常运行的话,屏幕每隔1s会依次点亮红绿蓝三种颜色。
2.4 配置lvgl
2.4.1 添加配置文件
复制lv_conf_template.h
到同一路径下,并改名为lvgl_conf.h
。否则编译会报错(找不到lv_conf.h文件)。
2.4.2 修改lvgl配置
lvgl里面的lvgl_conf.h
文件有很多配置参数,具体的我就不一一描述了。需要用到哪些配置,网上都能找到详细的教程。我这里讲几个必须要配置的东西。
1)把下面的宏打开。
2)启用自定义时钟
注意:没有一步的话会导致屏幕停留在第一帧的界面。
3)配置lvgl的显示接口
要把底层的LCD显示接口对接到lvgl这套框架里面,这样lvgl才能控制显示屏。
参考代码:
/* Display flushing */
void my_disp_flush(lv_disp_drv_t *disp_drv, 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_drv );
}
4)配置lvgl的触摸接口
注:因为我这里屏幕没有触摸接口,所以我这里把代码注释掉了,如果要用触摸接口的话,这里需要打开。
参考代码:
/*Read the touchpad*/
void my_touchpad_read( lv_indev_drv_t * indev_drv, 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 );
// }
}
2.4.3 lvgl代码测试
示例代码:
#include <Arduino.h>
#include <SPI.h>
#include <TFT_eSPI.h>
#include "lvgl.h"
TFT_eSPI tft = TFT_eSPI();
static lv_disp_draw_buf_t draw_buf;
/* Display flushing */
void my_disp_flush(lv_disp_drv_t *disp_drv, 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_drv );
}
/*Read the touchpad*/
void my_touchpad_read( lv_indev_drv_t * indev_drv, 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 );
// }
}
void lvgl_user_init(void)
{
lv_init();
/*Set the touchscreen calibration data,
the actual data for your display can be acquired using
the Generic -> Touch_calibrate example from the TFT_eSPI library*/
// uint16_t calData[5] = { 275, 3620, 264, 3532, 1 };
// tft.setTouch( calData );
lv_color_t* buf1 = (lv_color_t*) heap_caps_malloc(240 * 240, MALLOC_CAP_SPIRAM);
// lv_color_t* buf2 = (lv_color_t*) heap_caps_malloc(240 * 240, MALLOC_CAP_SPIRAM);
lv_disp_draw_buf_init( &draw_buf, buf1, NULL, 240 * 240);
/*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 = 240;
disp_drv.ver_res = 240;
disp_drv.flush_cb = my_disp_flush;
disp_drv.full_refresh = 1;
disp_drv.draw_buf = &draw_buf;
lv_disp_drv_register(&disp_drv);
/*Initialize the (dummy) input device driver*/
static 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);
/*Get lvgl version*/
String LVGL_Arduino = "Hello LVGL! ";
LVGL_Arduino += String('V') + lv_version_major() + "." + lv_version_minor() + "." + lv_version_patch(); // version
lv_obj_t *label = lv_label_create(lv_scr_act());
lv_label_set_text(label, LVGL_Arduino.c_str());
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0); //display
}
void setup()
{
Serial.begin(115200);
tft.begin();
tft.setRotation(0);
tft.fillScreen(TFT_BLACK);
lvgl_user_init();
}
void loop()
{
lv_timer_handler(); /* let the GUI do its work */
delay(5);
}
测试结果:
代码正常运行,调用lvgl的文本控件,打印lvgl的版本号。
结束语
我这里只是举了一个简单的例子讲解如何搭建lvgl的环境,实际上lvgl还有很多的控件和功能,也有一些demo可以运行测试,这些就留给你们继续探索了。如果还有什么问题,欢迎在评论区留言。
完整工程源码下载链接
想了解更多Arduino的内容,可以关注一下博主,后续我还会继续分享更多的经验给大家。
基于Arduino的开发教程汇总:
https://blog.csdn.net/ShenZhen_zixian/article/details/121659482
如果这篇文章能够帮到你,就…你懂得。