前言:最近闲着无聊,看到手头正好有一块tft彩屏,想着拿来玩玩。既然用到了显示屏,自然是离不开ui设计,lvgl是嵌入式一个开源图形库,具备“Light”(轻量)和"Versatile"(可用性强)等特点。对于我而言,最难的莫过于最初的环境搭建了,许多学习的热情在此刻被逐渐浇灭。但功夫不负有心人,花了一天终于是学会了个大概。
跟随我的步伐,教你从0到1搭建LVGL开发环境!
硬件:
1. ESP32开发版
2. 1.44TFT彩屏 ST7735S驱动【淘宝】(学生党,买不起贵的彩屏,其他彩屏类似,官方内置了很多驱动库,认准自己屏幕的驱动,稍作修改即可)
知识储备:
1. LVGL官网: https://lvgl.io/
2. LVGL文档:【官网文档】 (可能需要科学上网) 【github】
3. 其他推荐文档
库文件下载
一个完善的LVGL工程文件,其目录下至少包含以下库文件
库文件 | 说明 | 链接 |
---|---|---|
lvgl | lvgl主库 | https://github.com/lvgl/lvgl |
lv_demos | lvgl官方示例库 | https://github.com/lvgl/lv_demos |
TFT_eSPI | tft彩屏驱动库 | https://github.com/Bodmer/TFT_eSPI |
TFT_Touch | tft彩屏触摸屏驱动库 | https://github.com/Bodmer/TFT_Touch |
将这些库都下载到本地以做备用!
我这里使用的是VScode+PlatformIO环境(搭建教程见入口),用arduino IDE开发类似。
在platform.ini配置文件中加入monitor_speed = 115200,其作用就是修改串口频率,lvgl官方例程使用的都是115200的频率
说明:lvgl,TFT_eSPI,TFT_Touch都是能在platform平台上直接下载到的,除lv_demos除外,需要手动添加,因此,为了统一,我这里全部统一为手动添加。
右击 .pio\libdeps\esp32doit-devkit-v1
从资源管理器中打开,在这个目录下添加所需的库文件
打开.vscode/c_cpp_properties.json
,添加新添加库文件的路径
下面的也复制一份
注:没标错就是没错,标错的查看路径是否正确,这里没配置成功的话,到后面编译时报错xx.h文件找不到,99%的原因都是在这里,由于是手动添加,因此platform.ini配置文件不用添加导入的库文件,但要添加lib_deps字段(后边留空),否则编译的时候会自动修改上述的json文件的
打开.pio\build\esp32doit-devkit-v1\TFT_eSPI\User_Setup_Select.h配置文价进行修改
配置文件只能取消注释一个,取消注释多个会报错,官方内置了60多个常见的屏幕驱动的配置文件,当然,也可以用户自己设定一个配置文件,就是./User_Setup.h配置文件,取消注释并到该文件下进行相关选项的配置,
由于这里已经有ST7735的配置文件了,我就直接用官方的了。
进入.pio\build\esp32doit-devkit-v1\TFT_eSPI\User_Setups\Setup7_ST7735_128x128.h文件中,进行略微的修改
注:所有的配置文件,一定要抱着非必要的,不知道其作用的就不改的原则进行修改,否则将会吃很多亏的(亲身经历)
这里附一张ESP32引脚图
按上述修改后就可以测试彩屏是否正常配置了,按顺序连接好线后,打开
.\src\main.cpp
文件,加入官方提供的颜色测试代码,在.pio\build\esp32doit-devkit-v1\TFT_eSPI\examples\Test and diagnostics\Colour_Test\Colour_Test.ino
,如下
#include
#include // Hardware-specific library
TFT_eSPI tft = TFT_eSPI(); // Invoke custom library
void setup(void) {
tft.init();
tft.fillScreen(TFT_BLACK);
// Set "cursor" at top left corner of display (0,0) and select font 4
tft.setCursor(0, 0, 4);
// Set the font colour to be white with a black background
tft.setTextColor(TFT_WHITE, TFT_BLACK);
// We can now plot text on screen using the "print" class
tft.println("Intialised default\n");
tft.println("White text");
tft.setTextColor(TFT_RED, TFT_BLACK);
tft.println("Red text");
tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.println("Green text");
tft.setTextColor(TFT_BLUE, TFT_BLACK);
tft.println("Blue text");
delay(5000);
}
void loop() {
// 设置文字颜色并打印
tft.invertDisplay( false ); // Where i is true or false
tft.fillScreen(TFT_BLACK);
tft.setCursor(0, 0, 4);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.println("Invert OFF\n");
tft.println("White text");
tft.setTextColor(TFT_RED, TFT_BLACK);
tft.println("Red text");
tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.println("Green text");
tft.setTextColor(TFT_BLUE, TFT_BLACK);
tft.println("Blue text");
delay(5000);
// Binary inversion of colours
// 反转屏幕颜色
tft.invertDisplay( true ); // Where i is true or false
tft.fillScreen(TFT_BLACK);
tft.setCursor(0, 0, 4);
// 设置文字颜色并打印
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.println("Invert ON\n");
tft.println("White text");
tft.setTextColor(TFT_RED, TFT_BLACK);
tft.println("Red text");
tft.setTextColor(TFT_GREEN, TFT_BLACK);
tft.println("Green text");
tft.setTextColor(TFT_BLUE, TFT_BLACK);
tft.println("Blue text");
delay(5000);
}
代码各个函数的用途很好理解,上传到ESP32开发版上后查看文字的颜色是否跟文字描述的颜色相符,不符则看前2张图上的说明,彩屏若配置不成功,那后面的lvgl自然也无法显示了
同TFT_eSPI库,lvgl也有配置文件进行修改
打开.pio\libdeps\esp32doit-devkit-v1\lvgl\lv_conf_template.h
,复制一份文件到同目录下,并重命名为lv_conf.h
打开.pio\libdeps\esp32doit-devkit-v1\lvgl\examples\arduino\LVGL_Arduino\LVGL_Arduino.ino
文件,复制代码到/src/main.cpp
中,官方给的案例包含触摸功能测试,但由于我的彩屏没有触摸功能,因此需要对代码进行修改,如下
#include
#include
/*If you want to use the LVGL examples,
make sure to install the lv_examples Arduino library
and uncomment the following line.
#include
*/
#include
TFT_eSPI tft = TFT_eSPI(); /* TFT instance */
/*屏幕的宽高在这里修改*/
static const uint32_t screenWidth = 128;
static const uint32_t screenHeight = 128;
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf[ screenWidth * 10 ];
//开启日志后调用的函数,启用该功能需要修改lvgl_conf.h的对应功能
#if LV_USE_LOG != 0
/* Serial debugging */
void my_print( lv_log_level_t level, const char * file, uint32_t line, const char * fn_name, const char * dsc )
{
Serial.printf( "%s(%s)@%d->%s\r\n", file, fn_name, line, dsc );
Serial.flush();
}
#endif
/* 刷新屏幕 */
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 );
}
void setup()
{
Serial.begin( 115200 ); /* prepare for possible serial debug */
Serial.println( "Hello Arduino! (V8.0.X)" );
Serial.println( "I am LVGL_Arduino" );
lv_init();
#if LV_USE_LOG != 0
lv_log_register_print_cb( my_print ); /* register print function for debugging */
#endif
tft.begin(); /* TFT init */
tft.setRotation( 3 ); /* 旋转屏幕,n * 90度 ,3表示270度*/
// 建立一个屏幕宽*10大小的缓冲区
lv_disp_draw_buf_init( &draw_buf, buf, NULL, screenWidth * 10 );
/*初识化屏幕*/
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 );
/*初识化输入设备*/
static lv_indev_drv_t indev_drv;
lv_indev_drv_init( &indev_drv );
indev_drv.type = LV_INDEV_TYPE_POINTER;
lv_indev_drv_register( &indev_drv );
#if 0 //取消注释会在屏幕中显示文字
/* Create simple label */
lv_obj_t *label = lv_label_create( lv_scr_act() );
lv_label_set_text( label, "Hello Arduino! (V8.0.X)" );
lv_obj_align( label, LV_ALIGN_CENTER, 0, 0 );
#else
// 取消注释会启用对应的案例
lv_demo_widgets(); // OK
// lv_demo_benchmark(); // OK
// lv_demo_keypad_encoder(); // works, but I haven't an encoder
// lv_demo_music(); // NOK
// lv_demo_printer();
// lv_demo_stress(); // seems to be OK
#endif
Serial.println( "Setup done" );
}
void loop()
{
lv_timer_handler(); /* 在循环中让lvgl处理一些相应的事件 */
delay( 5 );
}
既然是ui设计,如何设计一个属于ui呢?这就需要学习LVGL官方的组件了,如文字,滑动条,输入框等等,这种开发方式可能就跟我之前学的pyqt5类似吧,从不同组件中挑选符合自己的积木,最终搭成堡垒,当然,你也可以从别人的lvgl的UI中移植过来,用C语言开发的lvgl的UI部分都是通用的,下面给出一个简单UI示例
//创建一个标签,如同画板(lv_src_act()用于获取当前活跃的屏幕)
lv_obj_t *label = lv_label_create( lv_scr_act() );
// 在画板上写上文字
lv_label_set_text( label, "Hello World!I'm fine!" );
// 设置画板上的对齐方式,也就是布局
lv_obj_align( label, LV_ALIGN_CENTER, 0, 0 );
那UI部分有了,如何使用呢?
其实上上案例中的代码是对于所有的arduino开发板都通用的,基本就是套模板使用,当然,更深层次的使用就需要自己慢慢探索了
每设计一个UI就要刷固件岂不是很麻烦?因此,借助LVGL模拟器,可以直接在电脑上查看自己设计的UI,满足心中预想后就可移植到Arduino工程中。
由于文章篇幅过大,因此在下一篇博客中再介绍—>【传送门】