实现触摸屏Arduino ESP32:使用LVGL实现触摸屏界面美化
概述
上一篇已经完成了对显示触摸屏的驱动,能够正常的显示和触摸了。这篇引入一个嵌入式GUI:LVGL。这也正是我的最终目的。
LVGL
官网的介绍:
LVGL is an open-source graphics library providing everything you need to create embedded GUI with easy-to-use graphical elements, beautiful visual effects and low memory footprint.
LVGL是一个开源图形库,提供创建嵌入式GUI所需的一切,具有易于使用的图形元素、优美的视觉效果和较低的内存占用。
官方demo动图:
没啥好介绍的,可以去官方文档里看LVGL文档。还提供PDF版本。
引入LVGL
LVGL库可以直接兼容Arduino。
- 在github上直接把LVGL库下载下来。
- 将解压出来的LVGL库复制进上一篇中说的 sketchbook 目录下的 libraries 文件夹下。
- 将LVGL目录下的 lv_conf_template.h 文件改名为 lv_conf.h,放在和LVGL库文件夹同层的地方。
- 打开 lv_conf.h ,修改至少这三个地方。第一个,将图中的 0 修改为 1 。
第二个,根据屏幕的分辨率设置色深。
第三个,将外部定义时钟的宏打开,以使用Arduino的时钟函数接口。
至此底层的移植就已经完成了。还是非常简单友好的。
示例代码
官方提供了一个基于Arduino平台和TFT-eSPI库的示例代码。上面的步骤完成之后,重启Arduino IDE就可以在示例里找到了。
打开之后,需要修改的地方只有一个。如下图。官方使用的就是TFT-eSPI库来驱动显示屏,所以这里需要使用TFT-eSPI库提供的接口做出一些操作。
上一篇中,最后烧录进去的是TFT-eSPI库提供的 Touch_calibrate 例程,上图中圈起来要修改的地方也正是基于这个。
打开这个例程。
烧录进去之后,会出现下面这个画面,这个时候需要触摸箭头指向的四个角。
在四个角都触摸完毕之后,串口会输出一段数据,如下图。这就是需要在LVGL例程中需要修改的五个数据。也就是上图中框起来的五个数据。
将原例程中的五个数据修改为串口输入的五个数据。
示例测试
官方提供了示例 lv_demo_weights(),但是需要额外的操作,这里先简单测试一下是否可用。将下图中的#if 0
修改为#if 1
,即在屏幕中央绘制一个 label 组件,内容是LVGL版本信息。
在头文件处,将 lv_demo.h 注释掉。
然后点击上传即可。上传完成后就可以在屏幕上看到通过LVGL绘制出来的 label 组件了。
添加测试代码
将下图中函数代码复制进工程中,放在 setup() 函数上面就行,随便哪。
void lv_example_btn(void)
{
/*Properties to transition*/
static lv_style_prop_t props[] = {
LV_STYLE_TRANSFORM_WIDTH, LV_STYLE_TRANSFORM_HEIGHT, LV_STYLE_TEXT_LETTER_SPACE};
/*Transition descriptor when going back to the default state.
*Add some delay to be sure the press transition is visible even if the press was very short*/
static lv_style_transition_dsc_t transition_dsc_def;
lv_style_transition_dsc_init(&transition_dsc_def, props, lv_anim_path_overshoot, 250, 100, NULL);
/*Transition descriptor when going to pressed state.
*No delay, go to presses state immediately*/
static lv_style_transition_dsc_t transition_dsc_pr;
lv_style_transition_dsc_init(&transition_dsc_pr, props, lv_anim_path_ease_in_out, 250, 0, NULL);
/*Add only the new transition to he default state*/
static lv_style_t style_def;
lv_style_init(&style_def);
lv_style_set_transition(&style_def, &transition_dsc_def);
/*Add the transition and some transformation to the presses state.*/
static lv_style_t style_pr;
lv_style_init(&style_pr);
lv_style_set_transform_width(&style_pr, 10);
lv_style_set_transform_height(&style_pr, -10);
lv_style_set_text_letter_space(&style_pr, 10);
lv_style_set_transition(&style_pr, &transition_dsc_pr);
lv_obj_t * btn1 = lv_btn_create(lv_scr_act());
lv_obj_align(btn1, LV_ALIGN_CENTER, 0, -80);
lv_obj_add_style(btn1, &style_pr, LV_STATE_PRESSED);
lv_obj_add_style(btn1, &style_def, 0);
lv_obj_t * label = lv_label_create(btn1);
lv_label_set_text(label, "btn1");
/*Init the style for the default state*/
static lv_style_t style;
lv_style_init(&style);
lv_style_set_radius(&style, 3);
lv_style_set_bg_opa(&style, LV_OPA_100);
lv_style_set_bg_color(&style, lv_palette_main(LV_PALETTE_BLUE));
lv_style_set_bg_grad_color(&style, lv_palette_darken(LV_PALETTE_BLUE, 2));
lv_style_set_bg_grad_dir(&style, LV_GRAD_DIR_VER);
lv_style_set_border_opa(&style, LV_OPA_40);
lv_style_set_border_width(&style, 2);
lv_style_set_border_color(&style, lv_palette_main(LV_PALETTE_GREY));
lv_style_set_shadow_width(&style, 8);
lv_style_set_shadow_color(&style, lv_palette_main(LV_PALETTE_GREY));
lv_style_set_shadow_ofs_y(&style, 8);
lv_style_set_outline_opa(&style, LV_OPA_COVER);
lv_style_set_outline_color(&style, lv_palette_main(LV_PALETTE_BLUE));
lv_style_set_text_color(&style, lv_color_white());
lv_style_set_pad_all(&style, 10);
/*Init the pressed style*/
static lv_style_t style_pr_2;
lv_style_init(&style_pr_2);
/*Ad a large outline when pressed*/
lv_style_set_outline_width(&style_pr_2, 30);
lv_style_set_outline_opa(&style_pr_2, LV_OPA_TRANSP);
lv_style_set_translate_y(&style_pr_2, 5);
lv_style_set_shadow_ofs_y(&style_pr_2, 3);
lv_style_set_bg_color(&style_pr_2, lv_palette_darken(LV_PALETTE_BLUE, 2));
lv_style_set_bg_grad_color(&style_pr_2, lv_palette_darken(LV_PALETTE_BLUE, 4));
/*Add a transition to the the outline*/
static lv_style_transition_dsc_t trans;
static lv_style_prop_t props2[] = {LV_STYLE_OUTLINE_WIDTH, LV_STYLE_OUTLINE_OPA};
lv_style_transition_dsc_init(&trans, props2, lv_anim_path_linear, 300, 0, NULL);
lv_style_set_transition(&style_pr_2, &trans);
lv_obj_t * btn2 = lv_btn_create(lv_scr_act());
lv_obj_remove_style_all(btn2); /*Remove the style coming from the theme*/
lv_obj_add_style(btn2, &style, 0);
lv_obj_add_style(btn2, &style_pr_2, LV_STATE_PRESSED);
lv_obj_set_size(btn2, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_center(btn2);
lv_obj_t * label2 = lv_label_create(btn2);
lv_label_set_text(label2, "Button");
lv_obj_center(label2);
}
然后在之前测试的地方把原来的 label 代码注释,调用上方的函数。最后烧录。
效果如下图:
小结
至此LVGL就已经成功引入到ESP32上了。之后就可以直接通过LVGL的组件接口来进行界面的绘制了。
不过Arduino虽然引入很简单很方便,但是编辑器的功能上跟记事本差不多,没有联想之类的功能。这对LVGL这个API众多的库的编写很不友好,所以我们可以将Arduino作为编译器和烧录工具,正常的代码编写最好还是使用外部的编辑器,或者使用VS、CodeBlock等工具来编写、预览,然后再黏贴到Arduino中。下篇用VS进行模拟。