Fork me on GitHub
0%

LVGL简介和移植说明

简介

LVGL(Light and Versatile Graphics Library)是一个 轻量级、开源、高度可定制的图形界面库,专为嵌入式系统设计,适用于低资源的 MCU(如 STM32、ESP32、RP2040 等)和中高端平台(如 Linux framebuffer)。

核心特性

属性 内容
📦 名称 LVGL(Light and Versatile Graphics Library)
官网 https://lvgl.io
🔧 授权协议 MIT(可商用、闭源)
🧱 架构特点 跨平台、平台无关、完全移植依赖驱动层
📲 输入支持 触摸屏、按键、编码器等
🖥️ 显示支持 单缓存、双缓存、DMA、GPU 加速
🎯 最小资源需求 4 kB Flash,16 kB RAM起可运行
🌍 代码仓库 https://github.com/lvgl

代码结构

本节代码目录结构以lvgl-v8.3.11为例进行说明,一级目录全部进行展示,二级目录针对重点文件进行保留

1
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
44
45
46
47
48
49
50
51
.
├── CMakeLists.txt # 顶层规则定义文件
├── component.mk # LVGL 在 ESP-IDF(尤其是 v3.x ~ v4.x)中的旧版组件构建系统用的配置文件
├── demos # 官方高级演示程序
│   ├── benchmark # 测试不同控件在设备上的渲染速度和性能瓶颈
│   ├── keypad_encoder # 使用外部按键矩阵或旋转编码器进行 UI 控制
│   ├── music # 音乐播放器UI
│   ├── stress # 测试 LVGL 在高负载(大量对象创建/删除、事件触发)下的稳定性
│   └── widgets # 所有基本控件的集中演示(按钮、滑块、进度条、图表等)
├── docs # 文档(提供开发手册、用户指南和 API 说明)
├── env_support # 支持不同开发环境和操作系统平台的适配层
│   ├── cmake # 提供 CMake 支持的模板、宏(基于CMAKE工程)
│   ├── cmsis-pack # 兼容 ARM CMSIS-Pack 格式的元数据和描述文件(Keil、IAR、STM32CubeIDE)
│   ├── rt-thread # RT-Thread 实时操作系统提供组件封装和配置
│   └── zephyr # Zephyr RTOS 提供模块封装
├── examples # 功能示例集(动画、样式、控件等)
│   ├── anim # 动画系统示例(值动画、路径动画、对象移动动画)
│   ├── arduino # Arduino 平台上的 LVGL 示例
│   ├── assets # 资源(字体、图片)
│   ├── event # 事件系统示例(对象点击、按下、长按、输入事件等)
│   ├── get_started # 简单基础示例(创建按钮、标签等)
│   ├── layouts # 布局系统:Flex、Grid 布局的使用方法
│   ├── libs # 第三方库/扩展库示例(PNG 解码、文件系统接入等)
│   ├── others # 杂项示例,如动画路径自定义、屏幕切换等
│   ├── porting # 移植适配参考(显示、输入、tick)
│   ├── scroll # 滚动相关示例(页面滚动、滑动响应)
│   ├── styles # 样式系统使用(颜色、边框、阴影、透明度、状态样式等)
│   └── widgets # 每种 LVGL 控件的单独示例(lv_btn, lv_label, lv_slider等)
├── idf_component.yml # ESP-IDF 构建系统所使用的组件描述文件(适用于ESP-IDF v4.1+)
├── Kconfig # 提供 menuconfig 菜单配置
├── library.json # PlatformIO 的库描述文件
├── library.properties # Arduino 库描述文件
├── LICENCE.txt # LVGL 的开源许可证(MIT License)
├── lv_conf_template.h # LVGL 配置文件(复制重命名为 lv_conf.h使用)
├── lvgl.h # LVGL 主头文件,统一包含 src/lvgl.h,导出对外使用
├── lvgl.mk # 顶层 Makefile规则
├── lvgl.pc.in # 用于 pkg-config 系统的配置模板,支持桌面 Linux 下的自动依赖管理
├── README.md # 项目总说明文档(英文版本,描述 LVGL 功能、构建方式、平台支持等)
├── README_pt_BR.md # 葡萄牙语(巴西)版本
├── README_zh.md # 文档中文文档版本
├── SConscript # 用于 SCons 构建系统,适配如 RT-Thread 等平台
├── scripts # 工具和辅助脚本集合(用于自动化文档生成、代码格式化、版本管理、示例处理等工作)
├── src # LVGL核心库源码(最重要),包含 core、widgets、draw 等模块
│   ├── core # LVGL 的对象系统、事件、屏幕、焦点组等(lv_obj.c 等)
│   ├── draw # 图形渲染接口,软绘图或 GPU 加速都从这里调用
│   ├── extra # 扩展模块:如 keyboard、calendar、span 等功能控件
│   ├── font # 字体管理、字模缓存、符号字体支持等
│   ├── hal # 硬件抽象层接口,如 flush_cb, read_cb, indev_drv 注册
│   ├── misc # 工具代码:如内存管理、颜色转换、链表、区域计算等
│   └── widgets # 所有控件组件:如 label、btn、slider、chart 等
└── tests # 测试和验证框架

移植

本部分以单片机(MCU)类型系统的移植为例说明。本次移植方式不破坏源码目录,通过手动构建CMAKE的引用源码目录和自定义的移植文件而实现lvgl最小编译系统,具体说明如下:

  1. 建立基础框架
    新建CMakeLists.txt 和 main.c文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // CMakeLists.txt

    cmake_minimum_required(VERSION 3.12)
    project(lvgl C CXX ASM)
    add_compile_options(-Wall -O2)

    set(SOURCES main.c)

    add_executable(${PROJECT_NAME} ${SOURCES})
    1
    2
    3
    4
    5
    6
    7
    8
    // main.c

    #include <stdio.h>

    int main(int argc, char* argv[])
    {
    return 0;
    }
  2. 添加LVGL功能实现的源码

    以下是 CMakeLists.txt 需要新添加的内容,lvgl-v8.3是lvgl源码目录自行选择自己的目录即可,另外还需要添加其它依赖否则头文件将找不到

    1
    2
    3
    4
    5

    file(GLOB_RECURSE LVGL_SOURCES lvgl-v8.3/src/*.c)
    include_directories(lvgl-v8.3)
    add_compile_options(-DLV_LVGL_H_INCLUDE_SIMPLE=ON -DLV_CONF_INCLUDE_SIMPLE=ON)
    set(SOURCES main.c ${LVGL_SOURCES})
  3. 添加移植文件

    • 配置文件lv_conf.h

      将源码目录 lv_conf_template.h 文件拷贝出来重命名成 lv_conf.h,并将里面的条件判断语句置成1使能配置

    • 显示适配文件lv_port_disp.c & lv_port_disp.h

      将源码目录 examples/porting/lv_port_disp_template.c 和 examples/porting/lv_port_disp_template.h 文件拷贝出来并分别重命名成lv_port_disp.c和lv_port_disp.h

    • 输入(键盘、触摸、按键)适配文件lv_port_indev.c & lv_port_indev.h

      将源码目录 examples/porting/lv_port_indev_template.c 和 examples/porting/lv_port_indev_template.h 文件拷贝出来并分别重命名成lv_port_indev.c和lv_port_indev.h

  4. 编译适配文件

    1
    2
    // 将适配文件添加进编译选项
    set(SOURCES main.c ${LVGL_SOURCES} lv_port_disp.c lv_port_indev.c)
  5. 修改显示适配文件

    • 添加编译
      将源文件和头文件的if 0语句换成if 1进行编译,同时带有templete的头文件引用换成重命名后的

    • 定义屏幕宽高
      MY_DISP_HOR_RES 和 MY_DISP_VER_RES 分别配置成屏幕实际的宽高尺寸,并去掉条件编译选项取消警告

    • 屏幕初始化API

      disp_init()函数调用屏幕驱动初始化函数:包括初始化数组的写入和模式的配置等功能,其它地方初始化同样可以,保证一次即可

    • 屏幕刷新API
      disp_flush()函数调用屏幕驱动画点函数LCD_DrawPoint( x, y, color_p->full) ,xy是坐标值,color_p->full是16位的颜色值,后续具体是使用dma还是cpu模式就自行优化只是效率和效果问题

    • 配置缓存方式

      默认提供了三种缓存配置方式:单buffer大小10行;双buffer大小各10行;双buffer大小各一帧。这三种方式的配置取决于内存大小、想要的目标效果等等方面。目前单纯显示配制成第一种方式即可,注释掉2和3方式(line83 - line94)

  6. 修改触摸适配文件

    • 添加编译
      将源文件和头文件的if 0语句换成if 1进行编译,并把带有templete的头文件引用换成重命名后的,同时将关于lvgl的引用更换成 #include “lvgl.h”,否则编译出错

    • 触摸初始化API

      touchpad_init()函数调用触摸驱动函数:主要是i2c设备的注册同时可以读CHIPID验证是否注册成功

    • 是否有触摸事件发生API

      touchpad_is_pressed()函数调用驱动的触摸状态检测函数,按下返回1,未按下返回0

    • 触摸坐标API

      touchpad_get_xy()函数调用驱动的坐标获取函数;

    • 其它

      对于其它没有用到的外部事件可以删除也可以保留不影响功能

  7. 提供时间基准(心跳)

    为LVGL提供准确的心跳。必须间隔精准地,调用时基函数:lv_tick_inc(),俗称心跳,让LVGL精准地知道时间的流逝;

    对于不同的芯片和SDK有TIMER的不同实现方式,通用方法就是这是1ms中断,中断handler调用 lv_tick_inc 即可

  8. 任务刷新调度

    间隔5ms左右,调用周期性任务函数: lv_timer_handler() ,它的作用是检查所有注册任务的时间戳,执行那些已经到期的任务,如:屏显更新、动画更新、触控、定时器事件等

    同样不同的环境实现不同,建议是在主函数的while中进行lv_timer_handler的循环调用,并进行相应间隔的延时,但一定不要在中断中调用,避免由于执行时间较长影响系统

  9. 添加主函数(测试)

    这里可以直接编译LVGL提供的默认例程进行测试,比如将demo/下的源文件添加到cmake编译,这里以widgets为例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    bool repeating_timer_callback(__unused struct repeating_timer *t) {
    lv_tick_inc(1);
    return true;
    }

    int main()
    {
    stdio_init_all();
    printf("(%s %s) Welcome PICO \n", __DATE__, __TIME__);

    struct repeating_timer timer;

    lv_init(); // LVGL初始化
    lv_port_disp_init(); // 注册LVGL显示任务
    lv_port_indev_init(); // 注册LVGL触屏任务
    add_repeating_timer_ms(1, repeating_timer_callback, NULL, &timer);
    lv_demo_widgets(); // 测试demo

    while(1) {
    ┆ lv_timer_handler();
    ┆ sleep_ms(5);
    }
    return 0;
    }

参考资料