ESP32使用LVGL GUI库,在单片机设备上使用现代且华丽的GUI。

本文基于ESP32使用Arduino框架驱动树莓派3.5寸LCD屏幕,首先确保TFT_eSPI能够正常驱动3.5寸LCD屏幕工作。如果没有设置好驱动,请首先参考此处引用文章进行LCD屏幕的设置。

LVGL是一个开源图形库,提供创建嵌入式GUI所需的一切,具有易于使用的图形元素、优美的视觉效果和较低的内存占用。本文最终的效果如图。







1.在arduino ide中安装lvgl和lv_examples库

此处使用7.11.0版本,8.x版本目前正在开发阶段。

2.配置LVGL

在arduino ide中,项目 -> 显示项目文件夹,在文件管理器中打开当前项目文件夹,然后向上一级目录,找到libraries文件夹并进入,进入lvgl文件夹,复制lv_conf_template.h文件,向上一级目录,粘贴并重命名为lv_conf.h。此时lv_conf.hlibraries文件夹中。

修改如下内容:

第10行,将其设为1。

#if 1 /*Set it to "1" to enable content*/

第23行,输入自己显示器的尺寸,笔者的尺寸大小是480x320

#define LV_HOR_RES_MAX          (480)
#define LV_VER_RES_MAX          (320)

第32行,设置自己显示器的颜色深度,笔者对自己显示器的颜色深度也不是很了解,故使用的默认值16。

第303行,将LV_TICK_CUSTOM设为1

#define LV_TICK_CUSTOM     1

第384行,将列出的字体全部设为1,在lv_examples中,用到了很多不同的字体大小。

#define LV_FONT_MONTSERRAT_8     1
#define LV_FONT_MONTSERRAT_10    1
#define LV_FONT_MONTSERRAT_12    1
#define LV_FONT_MONTSERRAT_14    1
#define LV_FONT_MONTSERRAT_16    1
#define LV_FONT_MONTSERRAT_18    1
#define LV_FONT_MONTSERRAT_20    1
#define LV_FONT_MONTSERRAT_22    1
#define LV_FONT_MONTSERRAT_24    1
#define LV_FONT_MONTSERRAT_26    1
#define LV_FONT_MONTSERRAT_28    1
#define LV_FONT_MONTSERRAT_30    1
#define LV_FONT_MONTSERRAT_32    1
#define LV_FONT_MONTSERRAT_34    1
#define LV_FONT_MONTSERRAT_36    1
#define LV_FONT_MONTSERRAT_38    1
#define LV_FONT_MONTSERRAT_40    1
#define LV_FONT_MONTSERRAT_42    1
#define LV_FONT_MONTSERRAT_44    1
#define LV_FONT_MONTSERRAT_46    1
#define LV_FONT_MONTSERRAT_48    1

3.配置lv_examples

lv_examples提供了很多的lvgl例程。

进入步骤2中描述的libraries文件夹,进入lv_examples文件夹,复制lv_ex_conf_template.h,向上一级目录,粘贴并重命名为lv_ex_conf.h。此时lv_ex_conf.hlv_conf.h在同一文件夹中。

打开lv_ex_conf.h并修改:

第10行,将其设为1

#if 1 /*Set it to "1" to enable the content*/

下面的内容使用什么就将其设为1,在这里我们开启benchmark和music例子。benchmark用来测试单片机的性能,music是一个音乐播放器。

第40行,

#define LV_USE_DEMO_BENCHMARK   1

第47行,

#define LV_USE_DEMO_MUSIC      1

4.编写主文件

打开main.ino文件,文件内容如下:

#include <Arduino.h>
//#include "./includes/oled.h"
#include <lvgl.h>
#include <TFT_eSPI.h>
#include <lv_examples.h>

// extern Adafruit_SH1106G display;

TFT_eSPI tft = TFT_eSPI(); /* TFT instance */
static lv_disp_buf_t disp_buf;
static lv_color_t buf[LV_HOR_RES_MAX * 10];

#if USE_LV_LOG != 0
/* Serial debugging */
void my_print(lv_log_level_t level, const char *file, uint32_t line, const char *dsc)
{

  Serial.printf("%s@%d->%s\r\n", file, line, dsc);
  Serial.flush();
}
#endif

/* Display flushing */
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);
}

/*Read the touchpad*/
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*/
}

void setup()
{
  // put your setup code here, to run once:
  // Serial.begin(9600);
  // //testdrawcircle();
  // display.begin(i2c_Address,true);
  // testdrawbitmap(logo16_glcd_bmp, LOGO16_GLCD_HEIGHT, LOGO16_GLCD_WIDTH);
  Serial.begin(115200); /* prepare for possible serial debug */

  lv_init();

#if USE_LV_LOG != 0
  lv_log_register_print_cb(my_print); /* register print function for debugging */
#endif

  tft.begin();        /* TFT init */
  tft.setRotation(1); /* Landscape orientation */

  uint16_t calData[5] = {275, 3620, 264, 3532, 1};
  tft.setTouch(calData);

  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 = 480;
  disp_drv.ver_res = 320;
  disp_drv.flush_cb = my_disp_flush;
  disp_drv.buffer = &disp_buf;
  lv_disp_drv_register(&disp_drv);

  /*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);

  // lv_demo_benchmark();
  lv_demo_music();
}

void loop()
{
  // put your main code here, to run repeatedly:
  // testdrawcircle();
  // Serial.println("hello");
  // delay(1000);
  // Serial.println(getCpuFrequencyMhz());
  // delay(1000);
  lv_task_handler();
  delay(5);
}

其中,部分注释掉的内容是上一篇文章中用来测试LCD驱动的文件。

其中,第105、106行分别是benchmark和music的测试代码。

最终的显示效果如文章开头所示,在benchmark的测试结果中,笔者的esp32芯片的性能结果为

FPS:10

Slow but common cases.