【立创·实战派ESP32-S3】文档教程
第 11 章 LVGL
LVGL(Light and Versatile Graphics Library)是一个开源的图形用户界面库,旨在为嵌入式系统提供轻量级、可移植、灵活且易于使用的图形用户界面解决方案。
本章例程,学习如何在 esp32 上添加 lvgl 接口。本例程的可以作为 lvgl 模板,在此基础上开发更多的 lvgl 界面程序。
11.1 使用例程
把开发板提供的【08-lcd_lvgl】例程复制到你的实验文件夹当中,并使用 VSCode 打开工程。
连接开发板到电脑,在 VSCode 上选择串口号,选择目标芯片为 esp32s3,串口下载方式,然后点击“一键三联”按钮,等待编译下载打开终端。
开发板开始运行程序后,会运行 lvgl 提供的 lv_demo_benchmark()程序,即基准测试程序。基准测试程序中,会依次显示所有 lvgl 提供的组件,并显示每秒帧数。
当运行完所有的组件后,会显示一个表格,总结刚才的运行效果,可以使用手指上下滑动查看结果。
打开 mian.c 文件,除了 lv_demo_benchmark()函数外,还有 4 个函数可以运行,这些都是 lvgl demos,选择一个运行,其它的都注释掉,然后再点击“一键三联”下载看结果。
void app_main(void)
{
bsp_i2c_init(); // I2C初始化
pca9557_init(); // IO扩展芯片初始化
bsp_lvgl_start(); // 初始化液晶屏lvgl接口
/* 下面5个demos 只打开1个运行 */
lv_demo_benchmark();
// lv_demo_keypad_encoder();
// lv_demo_music();
// lv_demo_stress();
// lv_demo_widgets();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
这其中的 music 例程显示效果不是很好,其它的都可以。因为这里的 music 例程推荐的最佳分辨率是 480x272,而我们的液晶屏分辨率是 320*240。
11.2 例程讲解
点击打开 main.c 文件,找到 app_main 函数。
void app_main(void)
{
bsp_i2c_init(); // I2C初始化
pca9557_init(); // IO扩展芯片初始化
bsp_lvgl_start(); // 初始化液晶屏lvgl接口
/* 下面5个demos 只打开1个运行 */
lv_demo_benchmark();
// lv_demo_keypad_encoder();
// lv_demo_music();
// lv_demo_stress();
// lv_demo_widgets();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
前两个函数在之前介绍过了,现在我们看看 bsp_lvgl_start()液晶屏初始化 LVGL 接口函数是怎么运行的。后面的 5 个 lvgl demos 函数的介绍,可以看 lvgl/demos 文件夹下对应 demo 的 readme.md 文件。
bsp_lvgl_start() 函数
// 开发板显示初始化
void bsp_lvgl_start(void)
{
/* 初始化LVGL */
lvgl_port_cfg_t lvgl_cfg = ESP_LVGL_PORT_INIT_CONFIG();
lvgl_port_init(&lvgl_cfg);
/* 初始化液晶屏 并添加LVGL接口 */
disp = bsp_display_lcd_init();
/* 初始化触摸屏 并添加LVGL接口 */
disp_indev = bsp_display_indev_init(disp);
/* 打开液晶屏背光 */
bsp_display_backlight_on();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
这是初始化液晶屏和 LVGL 接口的函数。
前两条语句初始化 lvgl,直接调用 esp_lvgl_port 组件中的语句实现的,不必深究。
第 3 条语句,初始化液晶屏,并添加到 LVGL 接口,这个函数就是在 esp32_s3_szp.c 文件中实现的,我们找到这个函数的定义。
// 液晶屏初始化+添加LVGL接口
static lv_disp_t *bsp_display_lcd_init(void)
{
/* 初始化液晶屏 */
bsp_display_new(); // 液晶屏驱动初始化
app_lcd_set_color(0xffff); // 设置整屏背景白色
esp_lcd_panel_disp_on_off(panel_handle, true); // 打开液晶屏显示
/* 液晶屏添加LVGL接口 */
ESP_LOGD(TAG, "Add LCD screen");
const lvgl_port_display_cfg_t disp_cfg = {
.io_handle = io_handle,
.panel_handle = panel_handle,
.buffer_size = BSP_LCD_H_RES * BSP_LCD_DRAW_BUF_HEIGHT, // LVGL缓存大小
.double_buffer = true, // 是否开启双缓存
.hres = BSP_LCD_H_RES, // 液晶屏的宽
.vres = BSP_LCD_V_RES, // 液晶屏的高
.monochrome = false, // 是否单色显示器
/* Rotation的值必须和液晶屏初始化里面设置的 翻转 和 镜像 一样 */
.rotation = {
.swap_xy = true, // 是否翻转
.mirror_x = true, // x方向是否镜像
.mirror_y = false, // y方向是否镜像
},
.flags = {
.buff_dma = false, // 是否使用DMA 注意:dma与spiram不能同时为true
.buff_spiram = true, // 是否使用PSRAM 注意:dma与spiram不能同时为true
}
};
return lvgl_port_add_disp(&disp_cfg);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
该函数中,前 3 条语句初始化液晶屏,之前章节已经介绍过,这里就不做详细介绍了。
后面添加 LVGL 接口的程序,使用 lvgl_port_add_disp()函数实现,lvgl_port_add_disp()函数的入口参数,在前面进行配置。
配置这里需要注意两处地方。
第 1 处:
ratation 里面的定义,需要和液晶屏初始化的相关参数保持一致,什么意思呢?我们看液晶屏初始化函数 bsp_display_new()里面的 esp_lcd_panel_swap_xy()函数和 esp_lcd_panel_mirror()函数。
esp_lcd_panel_swap_xy(panel_handle, true); // 显示翻转
esp_lcd_panel_mirror(panel_handle, true, false); // 镜像
2
esp_lcd_panel_swap_xy()函数中的第 2 个参数是 true,所以这里 ratation 里面的 sway_xy 也需要写成 true。
esp_lcd_panel_mirror()函数中的第 2 个参数是 true,所以这里的 mirror_x 也赋值为 true,第 3 个参数是 false,所以 ratation 里面的 mirror_y 也赋值为 false。
第 2 处:
flags 里面的定义,buff_dma 和 buff_spiram 只能选一个为 true,不能同时使用,要么使用 DMA 传输,要么使用 PSRAM。这个在 lvgl_port_add_disp()函数的定义里面可以看到,相关代码片段如下:
uint32_t buff_caps = MALLOC_CAP_DEFAULT;
if (disp_cfg->flags.buff_dma && disp_cfg->flags.buff_spiram) {
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "Alloc DMA capable buffer in SPIRAM is not supported!");
} else if (disp_cfg->flags.buff_dma) {
buff_caps = MALLOC_CAP_DMA;
} else if (disp_cfg->flags.buff_spiram) {
buff_caps = MALLOC_CAP_SPIRAM;
}
2
3
4
5
6
7
8
回到 bsp_lvgl_start()函数里面,液晶屏添加 LVGL 接口后,接下来就是触摸屏添加到 LVGL 接口。
bsp_display_indev_init()用于添加液晶屏接口到 LVGL,该函数也位于 esp32_s3_szp.c 文件中,如下所示:
// 触摸屏初始化+添加LVGL接口
static lv_indev_t *bsp_display_indev_init(lv_disp_t *disp)
{
/* 初始化触摸屏 */
if (tp == NULL) {
BSP_ERROR_CHECK_RETURN_NULL(bsp_touch_new(NULL, &tp));
}
assert(tp);
/* 添加LVGL接口 */
const lvgl_port_touch_cfg_t touch_cfg = {
.disp = disp,
.handle = tp,
};
return lvgl_port_add_touch(&touch_cfg);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
和 bsp_display_lcd_init()函数的结构一样,先是初始化触摸屏芯片,然后再添加到 LVGL 接口。
bsp_touch_new()用于初始化触摸屏,该函数也位于 esp32_s3_szp.c 文件中,如下所示:
// 触摸屏初始化
esp_err_t bsp_touch_new(esp_lcd_touch_handle_t *ret_touch)
{
/* Initialize touch */
esp_lcd_touch_config_t tp_cfg = {
.x_max = BSP_LCD_V_RES,
.y_max = BSP_LCD_H_RES,
.rst_gpio_num = GPIO_NUM_NC, // Shared with LCD reset
.int_gpio_num = GPIO_NUM_NC,
.levels = {
.reset = 0,
.interrupt = 0,
},
.flags = {
.swap_xy = 1,
.mirror_x = 1,
.mirror_y = 0,
},
};
esp_lcd_panel_io_handle_t tp_io_handle = NULL;
esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_FT5x06_CONFIG();
ESP_RETURN_ON_ERROR(esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)BSP_I2C_NUM, &tp_io_config, &tp_io_handle), TAG, "");
ESP_ERROR_CHECK(esp_lcd_touch_new_i2c_ft5x06(tp_io_handle, &tp_cfg, ret_touch));
return ESP_OK;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
开发板上使用的触摸屏型号是 FT6336,此函数就是用来初始化这个芯片。该驱动函数并不用自己写,使用乐鑫组件库中的 esp_lcd_touch_ft5x06 组件就可以。
该函数中的 esp_lcd_new_panel_io_i2c()函数和 esp_lcd_touch_new_i2c_ft5x06()函数,函数如何定义的不必深究,只需要知道,先调用 esp_lcd_new_panel_io_i2c()函数,再调用 esp_lcd_touch_new_i2c_ft5x06()函数就可以初始化完成,其中的入口参数配置好就可以。
入口参数配置这里,需要注意两处地方。
第 1 处:
.x_max
成员,赋值为高度,即 BSP_LCD_V_RES
。
.y_max
成员,赋值为宽度,即 BSP_LCD_H_RES
。
第 2 处:
.flags
成员中的值,与 bsp_display_lcd_init()函数中 .rotation
中的值保持一致。
再看 bsp_display_indev_init()函数中添加 lvgl 的语句,比较简单,调用 lvgl_port_add_touch()函数实现,该函数位于 esp_lvgo_port 组件中,不必深究。
以上就是 bsp_lvgl_start()函数中实现的主要功能,即初始化液晶屁和触摸屏后,都添加到 LVGL 接口。
11.3 例程制作过程
我们在上一章例程【07-lcd_camera】的基础上修改,把上一章的例程,复制粘贴到本文件夹,然后把名称修改为 08-lcd_lvgl,然后在硬盘上双击进入文件夹,把不需要的文件先删除。
如上图所示,把蓝底的文件都删除。最后剩下的文件如下图所示:
使用 VSCode 打开这个工程,先把一级目录下的 CMakeLists.txt 中的文件名称修改为 lcd_lvgl。
project(lcd_lvgl)
这次我们还是参考乐鑫的 esp-bsp 库(第 9 章已经下载了这个库),使用 VSCode 打开 esp-bsp 文件夹,依次点击打开 bsp/esp32_s3_eye/esp32_s3_eye.c 文件,在第 305 行,找到 bsp_display_lcd_init()函数,如下代码所示:
static lv_display_t *bsp_display_lcd_init(const bsp_display_cfg_t *cfg)
{
assert(cfg != NULL);
esp_lcd_panel_io_handle_t io_handle = NULL;
esp_lcd_panel_handle_t panel_handle = NULL;
const bsp_display_config_t bsp_disp_cfg = {
.max_transfer_sz = BSP_LCD_DRAW_BUFF_SIZE * sizeof(uint16_t),
};
BSP_ERROR_CHECK_RETURN_NULL(bsp_display_new(&bsp_disp_cfg, &panel_handle, &io_handle));
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
esp_lcd_panel_disp_off(panel_handle, false);
#else
esp_lcd_panel_disp_on_off(panel_handle, true);
#endif
/* Add LCD screen */
ESP_LOGD(TAG, "Add LCD screen");
const lvgl_port_display_cfg_t disp_cfg = {
.io_handle = io_handle,
.panel_handle = panel_handle,
.buffer_size = cfg->buffer_size,
.double_buffer = cfg->double_buffer,
.hres = BSP_LCD_H_RES,
.vres = BSP_LCD_V_RES,
.monochrome = false,
/* Rotation values must be same as used in esp_lcd for initial settings of the screen */
.rotation = {
.swap_xy = false,
.mirror_x = false,
.mirror_y = false,
},
.flags = {
.buff_dma = cfg->flags.buff_dma,
.buff_spiram = cfg->flags.buff_spiram,
#if LVGL_VERSION_MAJOR >= 9
.swap_bytes = (BSP_LCD_BIGENDIAN ? true : false),
#endif
}
};
return lvgl_port_add_disp(&disp_cfg);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
把它复制到我们的例程中 esp32_s3_szp.c 文件中,放到 bsp_lcd_init()函数后面就可以。
这个函数大致分为两部分,前半部分代码(3~15 行)调用 bsp_display_new()初始化液晶屏,后半部分代码(17~42 行)调用 lvgl_port_add_disp()函数向 lvgl 添加液晶屏句柄。
先修改前半部分初始化液晶屏的代码。
把 3~15 行初始化液晶屏部分全部删除,然后复制 bsp_lcd_init()函数中的前 3 个函数作为初始化代码,背光显示不用复制,把背景颜色修改为 0xffff,即白色,如果没有这个全屏显示颜色的函数,在上电时,液晶屏会先显示乱码。
再看后面添加 LVGL 接口的部分。
把 .buffer_size
赋值为 BSP_LCD_H_RES * BSP_LCD_DRAW_BUF_HEIGHT
,BSP_LCD_H_RES
是液晶屏的宽度,即 320,BSP_LCD_DRAW_BUF_HEIGHT
是一次要绘制的高度,此高度受芯片内存的限制,如果 lvgl 的缓存放到外部 PSRAM 中,这里可以定义为 240,即全高度,如果没有放在外部 PSRAM 中,而是使用内部内存和 DMA 传输,这里就不能写成 240,因为内存不够,我们把这个定义为 20 就可以,因为 lvgl 建议这个高度至少是整屏的 1/10 效果才好,写到 esp32_s3_szp.h 文件中。
把 .double_buffer
赋值为 true,表示开启双缓存,双缓存会增加刷新显示速度。
把 .rotation
里面的 .swap_xy
和 .mirror_x
修改为 true,.mirror_y
保持 false。和液晶屏初始化中相关函数的定义保持一致。本章的例程讲解小节对此做了详细解释。
把 .flags
里面的 .buff_dma
和 .buff_spiram
的其中一个改成 true,另外一个改成 false。表示 buff 使用 DMA 传输还是使用 SPIRAM 传输,用哪个都行,但是只能开启其中一个。本章的例程讲解小节对此做了详细解释。
把最后面的条件语句和内容删除,因为我们使用的 lvgl 版本是 8.3.11,不是 9。
把这个函数的入口参数删除,换成 void。
函数返回数据类型,把 lv_display_t 修改为 lv_disp_t。
最后修改好的函数如下所示:
static lv_disp_t *bsp_display_lcd_init(void)
{
/* 初始化液晶屏 */
bsp_display_new(); // 液晶屏驱动初始化
app_lcd_set_color(0xffff); // 设置整屏背景白色
esp_lcd_panel_disp_on_off(panel_handle, true); // 打开液晶屏显示
/* Add LCD screen */
ESP_LOGD(TAG, "Add LCD screen");
const lvgl_port_display_cfg_t disp_cfg = {
.io_handle = io_handle,
.panel_handle = panel_handle,
.buffer_size = BSP_LCD_H_RES * BSP_LCD_DRAW_BUF_HEIGHT,
.double_buffer = true,
.hres = BSP_LCD_H_RES,
.vres = BSP_LCD_V_RES,
.monochrome = false,
/* Rotation values must be same as used in esp_lcd for initial settings of the screen */
.rotation = {
.swap_xy = true,
.mirror_x = true,
.mirror_y = false,
},
.flags = {
.buff_dma = false,
.buff_spiram = true,
}
};
return lvgl_port_add_disp(&disp_cfg);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
到此,液晶屏显示就处理完成了,下面,添加触摸屏初始化函数,并把触摸屏句柄添加到 lvgl。
开发板触摸屏控制器型号为 FT6336,这个芯片的驱动程序不需要我们自己写,使用乐鑫组件 esp_lcd_touch_ft5x06 就可以。
触摸屏的初始化程序,我们参考 esp-bsp 中的 bsp/esp32_s3_korvo_2/esp32_s3_korvo_2.c 文件,找到第 398 行,bsp_touch_new()函数,如下所示:
esp_err_t bsp_touch_new(const bsp_touch_config_t *config, esp_lcd_touch_handle_t *ret_touch)
{
/* Initialize touch */
const esp_lcd_touch_config_t tp_cfg = {
.x_max = BSP_LCD_H_RES,
.y_max = BSP_LCD_V_RES,
.rst_gpio_num = GPIO_NUM_NC, // Shared with LCD reset
.int_gpio_num = BSP_LCD_TOUCH_INT,
.levels = {
.reset = 0,
.interrupt = 0,
},
.flags = {
.swap_xy = 0,
.mirror_x = 1,
.mirror_y = 0,
},
};
esp_lcd_panel_io_handle_t tp_io_handle = NULL;
const esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_TT21100_CONFIG();
BSP_ERROR_CHECK_RETURN_ERR(bsp_i2c_init());
ESP_RETURN_ON_ERROR(esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)BSP_I2C_NUM, &tp_io_config, &tp_io_handle), TAG, "");
return esp_lcd_touch_new_i2c_tt21100(tp_io_handle, &tp_cfg, ret_touch);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
把这个函数复制到我们的例程中 esp32_s3_szp.c 文件中,放到 bsp_display_lcd_init()函数后面就可以。
接下来开始修改。
把 .x_max
和 .y_max
后面的值对调,即 .x_max
赋值为 BSP_LCD_V_RES
,把 .y_max
赋值为 BSP_LCD_H_RES
。
把 .int_gpio_num
赋值修改为 GPIO_NUM_NC
,因为开发板硬件上没有连接触摸屏中断引脚。
把 .flags
中的 .swap_xy
和 .mirror_x
修改为 1,.mirror_y
修改为 0。
把 ESP_LCD_TOUCH_IO_I2C_TT21100_CONFIG
修改为 ESP_LCD_TOUCH_IO_I2C_FT5x06_CONFIG
。
删除 BSP_ERROR_CHECK_RETURN_ERR(bsp_i2c_init());
这条语句。因为 i2c 已经初始化过了。
把 esp_lcd_touch_new_i2c_tt21100
修改为 esp_lcd_touch_new_i2c_ft5x06
。
删除该函数的第 1 个入口参数。
最后修改好后的函数如下所示:
esp_err_t bsp_touch_new(esp_lcd_touch_handle_t *ret_touch)
{
/* Initialize touch */
const esp_lcd_touch_config_t tp_cfg = {
.x_max = BSP_LCD_V_RES,
.y_max = BSP_LCD_H_RES,
.rst_gpio_num = GPIO_NUM_NC, // Shared with LCD reset
.int_gpio_num = GPIO_NUM_NC,
.levels = {
.reset = 0,
.interrupt = 0,
},
.flags = {
.swap_xy = 0,
.mirror_x = 1,
.mirror_y = 0,
},
};
esp_lcd_panel_io_handle_t tp_io_handle = NULL;
const esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_FT5x06_CONFIG();
ESP_RETURN_ON_ERROR(esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)BSP_I2C_NUM, &tp_io_config, &tp_io_handle), TAG, "");
return esp_lcd_touch_new_i2c_tt21100(tp_io_handle, &tp_cfg, ret_touch);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
接下来,把 esp32_s3_korvo_2.c 文件中的 bsp_touch_new()函数后面的 bsp_display_indev_init()函数也复制到我们例程的 esp32_s3_szp.c 文件中,放到我们例程的 bsp_touch_new()函数后面就可以,代码如下所示:
static lv_indev_t *bsp_display_indev_init(lv_display_t *disp)
{
if (tp == NULL) {
BSP_ERROR_CHECK_RETURN_NULL(bsp_touch_new(NULL, &tp));
}
assert(tp);
/* Add touch input (for selected screen) */
const lvgl_port_touch_cfg_t touch_cfg = {
.disp = disp,
.handle = tp,
};
return lvgl_port_add_touch(&touch_cfg);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
把入口参数的数据类型由 lv_display_t 修改为 lv_disp_t。
把函数里面的 if(tp == NULL)条件去掉,直接写里面的函数,并且把 BSP_ERROR_CHECK_RETURN_NULL 修改为 ESP_ERROR_CHECK。bsp_touch_new()函数的第一个参数也去掉。
这里的的 tp,需要定义一个全局变量,放大 panel_handle 和 io_handle 定义的后面就可以。
static esp_lcd_touch_handle_t tp; // 触摸屏句柄
最后修改好的函数如下所示:
static lv_indev_t *bsp_display_indev_init(lv_disp_t *disp)
{
ESP_ERROR_CHECK(bsp_touch_new(&tp));
assert(tp);
/* Add touch input (for selected screen) */
const lvgl_port_touch_cfg_t touch_cfg = {
.disp = disp,
.handle = tp,
};
return lvgl_port_add_touch(&touch_cfg);
}
2
3
4
5
6
7
8
9
10
11
12
13
接着来自己写一个 bsp_lvgl_start()函数,如下所示:
// 开发板显示初始化
void bsp_lvgl_start(void)
{
/* 初始化LVGL */
lvgl_port_cfg_t lvgl_cfg = ESP_LVGL_PORT_INIT_CONFIG();
lvgl_port_init(&lvgl_cfg);
/* 初始化液晶屏 并添加LVGL接口 */
disp = bsp_display_lcd_init();
/* 初始化触摸屏 并添加LVGL接口 */
disp_indev = bsp_display_indev_init(disp);
/* 打开液晶屏背光 */
bsp_display_backlight_on();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
前两条语句,使用 lvgl_port_init()函数初始化 lvgl 的接口,它的参数配置为默认的 ESP_LVGL_PORT_INIT_CONFIG 就可以了。
然后调用刚才修改好的两个函数,分别是初始化液晶屏添加 LVGL 接口和初始化触摸屏添加 LVGL 的函数。
最后打开液晶屏背光。
这里面的 disp 和 disp_indev,定义成全局变量。放到定义 tp 后面就可以。
static lv_disp_t *disp; // 指向液晶屏
static lv_indev_t *disp_indev = NULL; // 指向触摸屏
2
在 esp32_s3_szp.h 文件中,声明 bsp_lvgl_start()函数。
void bsp_lvgl_start(void);
在 esp32_s3_szp.h 文件中,包含 lvgl 接口和触摸屏的头文件。
#include "esp_lcd_touch_ft5x06.h"
#include "esp_lvgl_port.h"
2
然后在主函数中,把原来初始化液晶屏和摄像头的函数都删除,再调用 bsp_lvgl_start()函数,如下所示:
void app_main(void)
{
bsp_i2c_init(); // I2C初始化
pca9557_init(); // IO扩展芯片初始化
bsp_lvgl_start(); // 初始化lvgl显示
}
2
3
4
5
6
7
8
这时候,就可以添加 lvgl 界面了。
我们直接调用 lvgl 的 demo 来测试就可以。
如下所示:
void app_main(void)
{
bsp_i2c_init(); // I2C初始化
pca9557_init(); // IO扩展芯片初始化
bsp_lvgl_start(); // 初始化lvgl显示
lv_demo_benchmark(); // 调用lvgl demo
}
2
3
4
5
6
7
8
调用 lvgo 的 demo,需要在 main.c 文件前面添加 demos/lv_demos.h 头文件。
#include "demos/lv_demos.h"
至此,程序就修改好了。
接下来添加组件。
在 idf_component.yml 文件中,注释掉或者删除掉 esp32-camera 组件,因为这个例程不使用摄像头。然后添加 esp_lvgl_port、lvgl、和 esp_lcd_touch_ft5x06 组件,如下所示:
## IDF Component Manager Manifest File
dependencies:
# espressif/esp32-camera: "^2.0.10" # 摄像头驱动
lvgl/lvgl: '~8.3.0'
espressif/esp_lvgl_port: '~1.4.0' # LVGL接口
espressif/esp_lcd_touch_ft5x06: '~1.0.6' # 触摸屏驱动
2
3
4
5
6
注意看,这里指定版本号用的符号是~,而不是^。先了解一下版本号,分为3个数字,用点隔开,第1个数字是主版本号,第2个数字是次版本号,第3个数字是补丁版本号。这两个符号的区别是,用~会下载补丁版本的最高版本,用^会下载次版本最高的版本。
比如:
1.4.0前面的符号是~,例程会从组件中心下载从1.4.0开始的1.4.*的最高版本,例如1.4.5。
1.4.0前面的符号是^,例程会从组件中心下载从1.4.0开始的1.*.*的最高版本,例如1.6.3。
如果对新的版本不放心,就想使用已经测试好的指定版本的组件,不用加符号,直接写1.4.0就可以。
如果没有添加lvgl组件,只添加esp_lvgl_port组件,esp_lvgl_port组件会自动添加lvgl组件,不过,它自动下载的lvgl版本号,就是由esp_lvgl_port组件来指定了。
上面去掉了esp_camera组件,esp32_s3_szp.c文件中调用这个组件的程序会报错,所以我们需要处理一下esp32_s3_szp.c文件中摄像头这里的程序。在esp32_s3_szp.h文件中,在摄像头相关的语句前后加上宏定义开关、头文件,以及条件编译,如下所示:
#define CAMERA_EN 0
#if CAMERA
#include "esp_camera.h"
// 这里是摄像头语句...
#endif
2
3
4
5
6
7
在esp32_s3_szp.c文件中,也加上条件编译语句,如下所示:
#if CAMERA_EN
// 这里是摄像头语句...
#endif
2
3
4
5
当需要使用摄像头的时候,把esp32_s3_szp.h文件中的CAMERA_EN定义成1就可以。
#define CAMERA_EN 1
现在就可以选择目标芯片和配置menuconfig了。
先选择目标芯片,再配置menuconfig,顺序不能反,而且,选择目标芯片后,要等右小角的进度条运行完毕,才可以点击进入menuconfig。
选择完目标芯片后,需要下载的组件,就都下载到managed_components文件夹下面了,如下图所示。其中,esp_lcd_touch组件是esp_lcd_touch_ft5x06的关联组件,只要添加esp_lcd_touch_ft5x06组件,esp_lcd_touch_ft5x06组件就会自动下载esp_lcd_touch组件。lvgl组件是esp32_lvgl_port组件的关联组件,只要下载esp32_lvgl_port组件,就会自动下载lvgl组件。
点击menuconfig图标打开配置界面,在选择目标芯片的时候,sdconfig.default文件里面的配置,已经配置到menuconfig了,现在继续修改需要的配置。
lvgl的颜色,需要设置一下反转,如下图所示:
我们在主函数中调用了lvgl的demo。lvgl 8.3.11源码中,有5个demo,位于lvgl组件中的demos文件夹下,主函数中调用的benchmark就是其中一个,5个demo如下图所示:
它们的使用方法是,在调用这些函数名称的文件中,先包含demos/lv_demos.h头文件,然后在menuconfig中勾选使能相应的demos,最后调用它们的运行函数就可以了。
比如,我们调用lv_demo_benchmark函数。
第一步:在main.c文件的最上方,包含了demos/lv_demos.h头文件。
第二步:点击打开benchmark文件夹,再点击打开lv_demo_benchmark.h文件,就可以看到可以调用的运行函数,选择第一个就可以。
第三步:在menuconfig中勾选Demos中带有benchmark关键字的选项。
完成以上三步,就可以调用了。
现在我们5个都想调用,所以把menuconfig中的5个demos都勾选,注意,不要勾选它的子选项,下图红色矩形框中的5条勾选就可以。
上图中的Demos位于menuconfig菜单的最下面,拉到底就可以看到。
上图中红色矩形前面的方框打勾,开启5个demo。它的子选项不要勾选,勾选后如下图所示:
demos中的的music,还需要使用lvgl的12像素和16像素大小字体,所以还需要把这两个字体勾选,14像素大小字体是默认勾选的,如下图所示:
点击“保存”后关闭。
在lvgl/demos中,依次找到各个例程的执行函数,写到主函数中,但是只能开一个,其它几个先注释掉,如下代码所示:
void app_main(void)
{
bsp_i2c_init(); // I2C初始化
pca9557_init(); // IO扩展芯片初始化
bsp_lvgl_start(); // 初始化液晶屏lvgl接口
/* 下面5个demos 只打开1个运行 */
lv_demo_benchmark();
// lv_demo_keypad_encoder();
// lv_demo_music();
// lv_demo_stress();
// lv_demo_widgets();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
然后选择串口号以及串口下载方式后,就可以点击“一键三联”按钮看结果了。
这5个demo中,music例程在320*240分辨率的屏幕上显示效果不是很好,其它几个例程OK。
如果没有问题的话,idf.py save-defconfig命令生成sdkconfig.defaults文件,此文件保存了你在menuconfig中做的所有改动配置,不包含默认的配置。