一.lvgl介绍
LVGL(Light and Versatile Graphics Library)是一个免费的轻量级开源图形库,其主要特
征有:
1,丰富的部件:开关、按钮、图表、列表、滑块、图片,等等。
2,高级图形属性:具有动画、抗锯齿、不透明度、平滑滚动等高级图形属性。
3,支持多种输入设备:如触摸屏、鼠标、键盘、编码器等。
4,支持多语言:UTF-8 编码。
5,支持多显示器:它可以同时使用多个 TFT 或者单色显示器。
6,支持多种样式属性:它具有类 CSS 的样式,支持自定义图形元素。
7,独立于硬件之外:它可以与任何微控制器或显示器一起使用。
8,可扩展性:它能够以小内存运行(最低 64 kB 闪存,16 kB RAM 的 MCU)。
9,支持操作系统、外部存储器和 GPU(不是必需的)。
10,具有高级图形效果:可进行单帧缓冲区操作。
11, 纯 C 编写: LVGL 基于 C 语言编写,以获得最大的兼容性。
综上可知:LVGL 是一款具有丰富部件,具备高级图形特性,支持多种输入设备和多国语
言,独立于硬件之外的开源图形库。LVGL 官方地址为:https://lvgl.io/,该网页主要包含用户
文档、图片转换器和字体转换器,
二.lvgl原码介绍
我们在从官网解压出源码后,这是源码的目录

由上图可知,lvgl源码的目录下有很多文件和文件夹,但用户并不需要完全了解它们,我们只需要了解与移植相关的部分就号。
| 文件 | 说明 |
|---|---|
| demos | LVGL提供的综合演示源码,有音乐播放器等 |
| docs | LVGL文献,主要说明LVGL每个部件的使用方法 |
| env_support | 环境支持(MDK,ESP) |
| examples | LVGL例程源码和LVGL输入设备驱动,显示屏驱动文件 |
| scrpts | LVGL手稿(与python有关) |
| tests | 官方测试文件,该文件夹用户无需了解 |
| lvgl_conf_template.h | LVGL的裁剪文件,用户可通过配置这个文件对LVGL功能进行裁剪 |
| lvgl | LVGL包含的头文件 |
上述表中,与LVGL移植有关的有examples文件夹lvgl_conf_template.h和lvgl.h文件,其他部分均与移植无关,用户可以选择忽略,也就是我们只需要修改这几个文集里的内容,即可完成移植。
2.1 examples文件夹
该文件夹主要包含 LVGL 部件实例、动画实例、其他第三方库实例以及输入设备和显示器,驱动文件等内容

上表中,只有 porting 文件夹与移植相关,其他文件夹中存放的是各种实例。

我们可以看到,官方已经给我们提供好了移植文件,我们只需要修改这里面的内容,例如lv_por_dis_template.h里面就是驱动文件,我们只需要把自己的驱动文件给移植进去,就可以显示了。其他对应的分别是文件系统的移植,触摸功能的移植等。
2.1 src文件夹
该文件夹主要包含 LVGL 源文件(部件源码、多种解码库)

三.ESP32-idf移植lvgl
首先,我们先去乐鑫官网找到esp环境下lvgl 8.3版本的源码。

然后我们新建一个工程,将lvgl的源码放入到components组件中

注意,在这里我们需要先打开esp32的sdk配置,将Uncheck this to use custom lv_conf.h取消勾选,这个选项的意思是取消选中此选项以使用自定义的 lv_conf.h”。如果选中此选项,则会使用默认的lv_conf.h的配置,我们则可以通过idf.py menuconfig或者SDK编译器来对lv_conf.h这个进行修改。如果取消,则需要自己通过lv_conf.h文件进行配置

由于sdk编译器每次设置都需要全部重新编译速度太慢,所以这里我们选择手动提供一个lv_conf.h这个文件。
我们复制lvgl目录下的lv_conf_template.h这个文件,将这个文件改名lv_conf.h放入到src文件夹下,接下来我们就通过配置lv_conf.h文件来对lvgl的功能进行配置和裁剪。
具体配置我也是参考了https://blog.csdn.net/m0_63567117/article/details/145117221这篇文章。
我们以music_demo这个示例来进行解释。
1.首先,我们需要给lvgl配置时间戳,让lvgl知道此时已经运行了多长时间,我们可以通过一个定时器来提供,例如
const esp_timer_create_args_t lvgl_tick_timer_args = {
.callback = &increase_lvgl_tick,
.name = "lvgl_tick"
};
esp_timer_handle_t lvgl_tick_timer = NULL;
ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));
ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, 1 * 1000)); //创建定时器,更新LVGL的内部时钟基准
static void increase_lvgl_tick(void *arg)
{
lv_tick_inc(1);
}
在定时器里调用 lv_tick_inc(1);给lvgl提供当前的时间戳
我们也可也在lv_conf.h里配置,注释掉Arduino部分,,打开esp部分。

2.接下来就是要打开music_demo的开关

这样我们就完成了配置部分。
3.1移植显示驱动
我们要想使用lvgl,显示驱动是必不可少的,我们首先将example里的porting中显示驱动文件给复制出来,当然也可以在原有的基础上进行修改。

改名叫lv_port_disp.c/h

#if 1
/*********************
* INCLUDES
*********************/
#include "lv_port_disp.h"
#include <stdbool.h>
#include "spi.h"
#include "lcd.h"
/*********************
* DEFINES
*********************/
#ifndef MY_DISP_HOR_RES
#warning Please define or replace the macro MY_DISP_HOR_RES with the actual screen width, default value 320 is used for now.
#define MY_DISP_HOR_RES 320
#endif
#ifndef MY_DISP_VER_RES
#warning Please define or replace the macro MY_DISP_HOR_RES with the actual screen height, default value 240 is used for now.
#define MY_DISP_VER_RES 240
#endif
static void disp_init(void);
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p);
void lv_port_disp_init(void)
{
disp_init();
//缓冲区开辟
int DISP_BUF_SIZE =MY_DISP_HOR_RES * ((MY_DISP_VER_RES)/10);
static lv_disp_draw_buf_t draw_buf_dsc;
#if defined(CONFIG_GRAPHICS_USE_PSRAM)
lv_color_t *buf_1 = heap_caps_malloc(DISP_BUF_SIZE*2, MALLOC_CAP_SPIRAM);
lv_color_t *buf_2 = heap_caps_malloc(DISP_BUF_SIZE*2, MALLOC_CAP_SPIRAM);
#else
lv_color_t *buf_1 = heap_caps_malloc(DISP_BUF_SIZE*2, MALLOC_CAP_DMA);
lv_color_t *buf_2 = heap_caps_malloc(DISP_BUF_SIZE*2, MALLOC_CAP_DMA);
#endif
lv_disp_draw_buf_init(&draw_buf_dsc, buf_1, buf_2,DISP_BUF_SIZE);
//注册显示
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = MY_DISP_HOR_RES;
disp_drv.ver_res = MY_DISP_VER_RES;
disp_drv.flush_cb = disp_flush;
disp_drv.draw_buf = &draw_buf_dsc;
lv_disp_drv_register(&disp_drv);
}
static void disp_init(void)
{
spi2_init();
lcd_init();
}
volatile bool disp_flush_enabled = true;
void disp_enable_update(void)
{
disp_flush_enabled = true;
}
void disp_disable_update(void)
{
disp_flush_enabled = false;
}
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
lcd_set_window(area->x1, area->y1, area->x2, area->y2);
size_t size = (size_t)lv_area_get_width(area) * (size_t)lv_area_get_height(area);
lcd_write_data(color_p, size * 2);
//lcd_lvgl_flush(area->x1, area->y1, area->x2, area->y2,color_p, size);
lv_disp_flush_ready(disp_drv);
}
#else
#endif
接下来就按照官方推荐开始移植自己的lcd驱动,注意disp_flush就是绘图的基本函数,所有图标的绘制都建立在这个上面,其实就是颜色填充函数,在使用前记得把.c/h头文件里的if 0改成if1。
之后我们在主函数里调用lv_demo_music这个是示例
/*
* @Author: E-mail 1247221946@qq.com
* @Date: 2025-01-13 14:12:05
* @LastEditors: E-mail 1247221946@qq.com
* @LastEditTime: 2025-01-13 15:47:40
* @FilePath: \LVGL\main\main.c
* @Description:
*
* Copyright (c) 2025 by 1247221946@qq.com, All Rights Reserved.
*/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "lvgl.h"
#include "freertos/semphr.h"
#include "esp_system.h"
#include "lv_port_disp.h"
#include "nvs_flash.h"
#include "esp_system.h"
#include "esp_chip_info.h"
#include "esp_psram.h"
#include "esp_flash.h"
#include "esp_timer.h"
#include "demos/lv_demos.h"
#include "lv_port_indev.h"
i2c_obj_t i2c0_master;
static void lvgl_act(void *pvParameter);
void app_main(void)
{
xTaskCreatePinnedToCore(lvgl_act, "gui_app", 4096 * 2, NULL, 0, NULL, 1);
}
static void lvgl_act(void *pvParameter)
{
esp_err_t ret;
ret = nvs_flash_init(); /* 初始化NVS */
if (ret == ESP_ERR_NVS_NO_FREE_PAGES
|| ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
i2c0_master = iic_init(I2C_NUM_0); /* 初始化IIC0 */
xl9555_init(i2c0_master); /* IO扩展芯片初始化 */
lv_init();
lv_port_disp_init();
lv_port_indev_init();
// lv_demo_benchmark();
//lv_demo_widgets();
lv_demo_music();
// lv_obj_t*switch_obj = lv_switch_create(lv_scr_act());
// lv_obj_set_size(switch_obj,120,60);
// lv_obj_align(switch_obj,LV_ALIGN_CENTER,0,0);
// /*Change the active screen's background color*/
// lv_obj_set_style_bg_color(lv_scr_act(), lv_color_hex(0x003a57), LV_PART_MAIN);
// /*Create a white label, set its text and align it to the center*/
// lv_obj_t * label = lv_label_create(lv_scr_act());
// lv_label_set_text(label, "Hello world");
// lv_obj_set_style_text_color(label, lv_color_hex(0xffffff), LV_PART_MAIN);
// lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
while (1)
{
/* Delay 1 tick (assumes FreeRTOS tick is 10ms */
vTaskDelay(pdMS_TO_TICKS(10));
lv_task_handler();
}
}
在我们移植显示驱动时,要遵循以下几个步骤
(1)调用 lcd_init 函数初始化 lcd 屏
(2)根据芯片实际情况选择缓冲配置,通过 heap_caps_malloc 函数去申请内存
单缓冲方式:使用一个缓冲区(lv_disp_draw_buf_t draw_buf_dsc_1;)双缓冲区:使用两个缓冲区进行(static lv_disp_draw_buf_t draw_buf_dsc_2;)全屏双缓冲分区:每次刷新全部屏幕,并且使用两个缓冲分区(static lv_disp_draw_buf_t draw_buf_dsc_3;)
注意:缓冲区开辟的空间最好要大于屏幕的1/10的区域,这样才不会卡。例如320240,RGB565的屏幕,缓冲区最好大于 (320240*16/2)/10
(3)调用 lv_dis_draw_buf_init 函数初始化显示缓冲区
(4)调用 lv_disp_drv_init 函数初始化显示设备
(5)设置显示设备的分辨率(高度和宽度)
(6)注册显示驱动回调函数 lvgl_disp_flush_cb(回调函数的参数是固定的,内部主要调
用打点函数实现)
(7)设置显示驱动的缓冲区 disp_buf
(8)调用 lv_disp_drv_register 函数注册显示驱动到 LVGL 列表中
补充:
在ESP32里LVGL和WIFI同时使用会造成空间不足,所以我们可以将LVGL的存储区域放在外部的PSRAM里

偏移地址修改成0x3c200000




1万+

被折叠的 条评论
为什么被折叠?



