简介面向嵌入式开发者的LVGL图形库资源包围绕在VSCode中运行LVGL例程这一场景整理。LVGL是专为微控制器设计的开源图形界面库提供按钮、文本框、进度条等丰富组件及动画效果适合在资源有限的小屏幕上构建交互界面。压缩包共1803个文件总体积25.95MB主体为C/C源码文件.c、.h约700余个其次是编译中间文件.obj、.a、.dll等另有Python辅助脚本、CMake构建配置、Markdown与reStructuredText文档、示例图片和字体文件以及SDL2相关库便于在PC端模拟预览目录结构清晰便于按需检索。资源发布至今已有2090人学习热度可观。包内包含大量示例代码与构建脚本支持在不同微控制器平台上进行移植例程覆盖初始化、组件创建、事件响应等核心流程并附有详细文档能够帮助开发者理解LVGL的项目结构和移植方式快速上手自己的嵌入式GUI项目。1. LVGL 下载到底要抄哪份源码跑通 VSCode 例程前先解决选型问题做嵌入式 GUI 的开发者十有八九会遇到 LittlevGL 这套开源图形库也就是现在常说的 LVGL。想快速评估它能不能用在你的板子上最省事的办法不是在开发板上点灯而是先在 PC 上用 VSCode 把官方例程跑起来。很多人卡住的第一关不是代码而是「下载」到底下载哪个仓库、哪个分支、例程怎么从源码里露出来、VSCode 里要配哪些插件才能编译。这篇笔记把这条链路拆开讲源码怎么拿、工程怎么组织、SDL 模拟器怎么接、VSCode 里怎么编译运行、以及新手几乎都会遇到的几个翻车点。适合手里有开发板但想先在 PC 上验证 UI 效果的开发者也适合刚接触 LVGL、想用一个最小工程看懂它工作方式的同学。目标很直接从零开始在 VSCode 里把一个 LVGL 例程跑出窗口并且知道改哪些配置能让它贴近你的目标硬件。2. LVGL 源码获取与目录结构下载入口、版本取舍和最小工程三件事2.1 离线压缩包和 Git Clone 两条下载路径分别适合什么人获取 LVGL 源码最常见的有两条路径。一条是去官网或开源托管平台下载发布版压缩包适合只用某个固定版本、不打算跟进上游更新的项目另一条是直接用 Git 克隆仓库适合要长期维护、需要拉取修复和新增控件的场景。以 Release 页面为准不要下错分支。# 路径一克隆主仓库含核心源码和文档只拉最新 commit git clone https://github.com/lvgl/lvgl.git # 路径二克隆模拟器工程含 PC 仿真所需驱动适合 VSCode 跑例程 git clone https://github.com/lvgl/lv_port_pc_vscode.git如果只需要核心源码克隆 lvgl 仓库就够了。但要在 VSCode 里把例程跑出窗口光有核心源码还不够还需要显示驱动、输入驱动和模拟器工程。官方已经帮你把这些打包成了一个独立的模拟器工程克隆它可以直接得到一套完整的可运行项目适合不想从零拼 CMake 的人。发布版压缩包和 Git Clone 各有坑压缩包版本固定但后续要升级就得重新下载对比差异Git Clone 默认拉的是主干分支API 可能和网上教程对不上。我一般建议项目管理用发布版 Tag个人学习用最新稳定 Tag不要跟主干。2.2 版本取舍为什么选 8.x 还是 9.x 会影响你能不能抄作业LVGL 的版本迭代里8.x 和 9.x 是两个绕不开的选择。网上大量教程、博客和问答都基于 8.3资料最全碰到报错一搜就有答案9.x 重构了部分 API比如移除了旧的 lv_conf.h 里部分配置项字体和主题系统也做了调整老代码直接搬上去会报编译错误。如果你第一次上手建议选 8.3 的发布 Tag先把例程跑通再考虑升级。# 切到一个稳定的发布 Tag避免主干 API 变动影响排查 git checkout v8.3.11版本选型直接决定了你接下来能在网上搜到多少「同款报错」。用 8.3 遇到问题几乎都能搜到现成解答用 9.x 遇到问题可能要翻官方迁移文档。例程本身差别不大但底层 API 的名字和参数变了报错信息看起来完全不一样。先跑通再升级比一开始就追新版本省时间。我自己的习惯是评估阶段固定一个版本跑例程等到真正为硬件写界面时再决定要不要升级。LVGL 的 API 在 8.x 和 9.x 之间有兼容层思路但官方明确表示 9.x 是破坏性更新所以不要指望直接替换源码就完事。2.3 目录结构里哪些文件是例程直接依赖的别在源码里迷路把仓库拉下来之后你会看到一堆目录。真正和 VSCode 跑例程强相关的只有几个核心源码、驱动层、示例代码和配置文件模板。核心源码在 lvgl/src 下按控件、绘图、字体、事件等模块划分lv_drivers 目录存放显示和输入设备的驱动抽象示例代码在 lvgl/examples 或独立的 demos 目录里lv_conf.h 模板是全局配置入口。// lv_conf.h 是 LVGL 的全局配置开关模板在 lvgl 目录下 #define LV_COLOR_DEPTH 32 // 颜色深度PC 模拟器建议 32MCU 常用 16 #define LV_USE_DEMO_WIDGETS 1 // 启用控件演示跑例程必须打开 #define LV_FONT_MONTSERRAT_14 1 // 内置字体至少开一个小字号否则界面没文字这个配置文件决定了 LVGL 编译时包含哪些功能。例程里用到的控件集会检查这些宏比如 LV_USE_DEMO_WIDGETS 没打开demo 代码根本不会被编译进去。很多新手下载源码后直接编译报「找不到符号」八成是这里没开。除了 lv_conf.h还要注意 lvgl 根目录下的 lvgl.h 头文件它是所有上层代码的唯一入口。你写的应用代码只需要 include 这一个头文件不需要逐个去 include 各个模块。例程里能直接调 lv_obj_create、lv_label_set_text 这类 API都是因为它把整个库的组织关系收拢好了。3. 模拟器与 VSCode 环境搭建SDL 方案如何让例程在桌面上跑起来3.1 为什么例程能在 PC 上运行LVGL 的驱动抽象与 SDL 的对接逻辑LVGL 本身不直接操作硬件它定义了一套显示驱动和输入驱动接口。在嵌入式平台上这些接口由你的底层驱动实现在 PC 上则用 SDL 库去模拟一个「虚拟屏幕」和「虚拟触摸屏」。SDL 创建一个窗口把 LVGL 绘制的内容实时显示出来鼠标点击会被转换成 LVGL 的触摸事件。这套抽象使得同一套 UI 代码可以在 PC 和开发板之间无缝切换。// 显示驱动注册告诉 LVGL 用哪个函数把像素刷到屏幕上 static lv_disp_draw_buf_t draw_buf; static lv_color_t buf[SDL_HOR_RES * 10]; lv_disp_drv_t disp_drv; lv_disp_drv_init(disp_drv); disp_drv.flush_cb sdl_display_flush; // SDL 模拟器提供的回写函数 disp_drv.hor_res SDL_HOR_RES; disp_drv.ver_res SDL_VER_RES; lv_disp_drv_register(disp_drv);模拟器的核心就是 flush_cb 函数。LVGL 绘制完一帧后会调用这个回调把像素数据交给 SDLSDL 再把它贴到窗口上。输入驱动同理把鼠标事件转换成 lv_indev_data_t 结构体。理解这一层后后面换到真实硬件时你只需要替换驱动注册部分业务代码完全不用动。3.2 VSCode 里装什么C/C 扩展、CMake 工具链与 SDL 依赖在 VSCode 里跑 LVGL 例程本质上是把 VSCode 当做一个 IDE 外壳底层编译靠的是 CMake 和编译器。Windows 上推荐用 MinGW-w64 搭配 CMake避免 MSVC 在 SDL 头文件和运行库上的一些兼容问题。VSCode 扩展只需要装三个C/C 扩展提供语法解析和调试支持CMake 扩展提供工程配置界面CMake Tools 扩展负责调用 CMake 命令完成构建。# Windows 上通过包管理器安装 MinGW-w64 和 CMake注意 x86_64 版本 pacman -S mingw-w64-x86_64-toolchain mingw-w64-x86_64-cmake编译器配置容易踩坑VSCode 的 CMake Tools 默认会去找系统里的 CMake 和编译器如果装了多个版本的 MinGW 或者之前装过 Visual Studio它会优先选一个「看起来能用」的实际编译时可能因为版本不匹配报一堆奇怪错误。我一般会在 .vscode/settings.json 里强制指定 CMake 生成器和编译器路径。{ cmake.generator: MinGW Makefiles, cmake.configureArgs: [ -DCMAKE_C_COMPILERgcc, -DCMAKE_CXX_COMPILERg ] }SDL 依赖的处理是另一个关键点。模拟器工程需要 SDL2 的开发库不只是运行库。Windows 上常见做法是下载 SDL2-devel 包把 include 和 lib 目录拷贝到工程里然后在 CMakeLists.txt 里指定路径。如果缺的是运行库编译能过但运行时报「找不到 SDL2.dll」如果缺的是开发库编译阶段就会报找不到 SDL.h。3.3 用 CMake 组织工程顶层 CMakeLists 的关键片段与最小配置模拟器工程自带了 CMakeLists.txt但为了理解它的工作方式最好还是手动看一遍关键配置。顶层 CMakeLists 做的事情可以概括为找到 SDL2、添加 lvgl 子目录、链接 lvgl 与驱动、生成可执行文件。官方模拟器工程已经把这三步封装好了你只需要让路径和编译器设置对你当前的系统有效。# 顶层 CMakeLists.txt 关键片段 add_subdirectory(lvgl) # 编译 LVGL 核心库 add_subdirectory(lv_drivers) # 编译显示器/触摸驱动 # 指定 SDL2 的安装路径Windows 下手动指定比 find_package 更可靠 set(SDL2_INCLUDE_DIR third_party/SDL2/include) set(SDL2_LIBRARY third_party/SDL2/lib/x64/SDL2.lib) add_executable(main demo/main.c lv_drivers/sdl/sdl_gpu.c) target_link_libraries(main lvgl lv_drivers SDL2)这段配置的价值在于暴露了三个最常需要改的地方SDL2 路径、可执行文件名、源文件列表。如果工程里找不到 SDL2优先检查 SDL2_INCLUDE_DIR 指向的目录里有没有 SDL.h如果链接报错优先检查 SDL2_LIBRARY 的 .lib 文件和运行时的 .dll 是否版本一致。32 位和 64 位混用也会导致链接错误MinGW 默认编 64 位就用 64 位的 SDL2 包。有的版本在 Windows 下还需要额外指定 SDL2 的运行时 DLL 拷贝到输出目录。CMake 里可以用 add_custom_command 实现但更省事的做法是直接把 SDL2.dll 复制到编译输出的 exe 同目录至少保证它能跑起来。4. 在 VSCode 中编译运行 LVGL 例程从打开工程到看到界面4.1 用官方模拟器工程做起点为什么不用自己拼的工程第一步强烈建议直接用官方模拟器工程不要从零创建 CMake 工程。原因很简单官方工程里的 CMakeLists、驱动注册代码和 lv_conf.h 是配套调好的确保克隆下来就能编译。你自己拼工程的话要先搞清楚 lvgl、lv_drivers、SDL 三方怎么链接排查问题的成本会高很多。# 克隆官方模拟器工程并切换到稳定 Tag git clone https://github.com/lvgl/lv_port_pc_vscode.git cd lv_port_pc_vscode git checkout v8.3.11克隆完成后用 VSCode 打开这个目录。首次打开时 CMake Tools 会自动识别 CMakeLists.txt并提示你选择编译器。选 MinGW 对应的 gcc 即可。这一步选择错了后面 CMake 配置会失败所以注意观察 VSCode 底部状态栏上的「Kit」显示的是什么。在这个工程里,main.c 已经写好了完整的初始化流程lv_init 初始化核心库、sdl_init 创建窗口、注册显示驱动和输入驱动、加载并运行一个示例 UI。你不需要写任何代码只需要确保编译配置正确。4.2 编译与运行常用构建命令、输出路径和参数调整CMake Tools 装好后VSCode 底部状态栏会出现 Build 按钮点击即可编译。想用命令行构建也完全可以我更习惯用命令行看得见完整输出报错更直观。构建成功后可执行文件会生成在 build 目录下。# 配置阶段指定生成器生成 build 目录 cmake -B build -G MinGW Makefiles # 编译阶段看到 Linking C executable 且无 error 即成功 cmake --build build -j4 # 运行直接执行生成的 exe ./build/main.exe构建出错时先看是哪个编译单元报错。最常见的错误集中在三种找不到头文件、找不到符号、类型不匹配。找不到头文件通常是 include 路径没指到 lvgl 目录找不到符号通常是 c 文件没被 add_executable 包含类型不匹配则多半是 LV_COLOR_DEPTH 配置和代码中某个类型的硬编码不一致。编译器的报错信息里会明确给到文件名和行号顺着看就行。运行后如果弹出一个窗口里面有各种控件、进度条、滑块说明例程已经跑通了。此时可以用鼠标拖拽滑块、点击按钮体验 LVGL 的事件响应。PC 模拟器的鼠标默认映射为触摸输入按下和松开分别对应触摸事件中间没有悬停状态这一点和手机触摸屏一致。4.3 窗口参数调整分辨率、缩放和模拟器运行逻辑例程跑起来后窗口的默认分辨率在 lv_conf.h 或驱动配置中定义。PC 模拟器一般用 800x480 或 1024x600 这类常见屏的尺寸方便预览你的 UI 在不同屏幕上的效果。这些宏在模拟器工程里可以直接修改改完重新编译就能看到新尺寸的窗口。// lv_drv_conf.h 或 main.c 中的 SDL 显示参数 #define SDL_HOR_RES 800 #define SDL_VER_RES 480 #define SDL_ZOOM 1.0缩放系数 SDL_ZOOM 是用来解决高分屏显示的。如果窗口在高 DPI 屏幕上看起来太小把缩放调到 2.0 可以放大显示但注意这不会改变 LVGL 内部的分辨率逻辑——它只是做了 SDL 层面的缩放。想模拟真实设备的视觉效果一般直接把分辨率调成设备真实横竖像素更实际。运行逻辑上模拟器的主循环由 SDL 的事件驱动。每帧 LVGL 会调用 lv_timer_handler() 处理定时器和动画SDL 的 while 循环里同时处理窗口事件。理解这个循环对后面排查「界面卡住」的问题很有帮助如果某个回调函数里做了阻塞操作整个界面会停住不动。5. VSCode 跑 LVGL 例程的避坑清单编译报错、黑窗口和路径问题5.1 头文件找不到SDL.h 报错原因多半是 include 路径没生效现象编译时提示 fatal error: SDL.h: No such file or directory。原因通常有两个一是 SDL2-devel 包没下载只有运行时 DLL二是 CMakeLists 里指定的 SDL2_INCLUDE_DIR 路径不对编译器根本没把 include 目录加入搜索路径。解决方法是先确认 SDL2-devel 包存在再检查 CMakeLists 里的变量是否指向了包含 SDL.h 的目录。# 如果 SDL2 放在工程根目录下的 third_party 里Include 路径要指到含 SDL.h 的目录 set(SDL2_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/third_party/SDL2-2.28.0/include)排查这类问题最快的验证方式是在 VSCode 里按 F12 跳转到 SDL.h如果跳不过去就说明 include 路径还没配置到位。另外一个隐蔽的点是大小写Linux 下区分大小写Windows 下 MinGW 不区分但如果代码里 include 的路径和你放文件的目录名不一致跨平台时会翻车。建议统一下层目录名。5.2 编译通过但运行报「找不到 SDL2.dll」动态库没进输出目录现象构建成功双击 exe 或命令行运行时弹窗提示找不到 SDL2.dll。原因很直白编译链接时用的是 SDL2.lib 导入库运行时需要 SDL2.dll 在 exe 同目录或系统 PATH 里。CMake 不会自动把 DLL 拷贝过来需要手动处理。解决办法是把 SDL2.dll 复制到 build/main.exe 同目录或者把 DLL 所在目录加进系统 PATH。# 把 SDL2.dll 复制到 exe 同目录最简单可靠 cp third_party/SDL2/bin/SDL2.dll build/如果不希望每次构建后都手动拷贝可以在 CMakeLists.txt 里加一条自定义命令让构建完成后自动复制。这条命令要注意路径的生成器表达式MinGW Makefiles 和 Visual Studio 的路径结构不同直接写死路径会换生成器就失效。长期用同一套工具链的话手动复制或写死路径都行。5.3 窗口能弹出来但画面卡死刷新率、回调阻塞和事件循环问题现象窗口出现了但内容是花的、黑的或者控件没反应。原因可能有几种最常见的是 lv_timer_handler 没有被周期性调用。模拟器的 while 循环里如果漏掉了这个调用LVGL 内部的定时器、动画、刷新机制全部失效界面就卡在当前帧。另一个常见原因是 flush_cb 实现里缺少帧缓冲区切换屏幕上只显示半个缓冲的数据。// 模拟器主循环的核心结构先处理 SDL 事件再驱动 LVGL 刷新 while (1) { SDL_Event event; while (SDL_PollEvent(event)) { // 窗口关闭、鼠标事件都喂给 SDL 驱动的输入处理 } lv_timer_handler(); // 漏掉这行界面必然卡死 SDL_Delay(5); // 小延时避免 CPU 空转 }还有一个非常隐蔽的坑刷新回调里执行了耗时操作比如在 flush_cb 里渲染字体或做软件缩放导致 LVGL 认为刷新没完成后续绘制全部排队等待。PC 模拟器因为性能好这种现象不常出现但一旦出现优先检查 flush_cb 是否在绘制结束后正确调用了 lv_disp_flush_ready。MCU 平台上这类问题更常见养成先看回调是否及时返回的习惯。5.4 文字全变成方块字体没编译进去反直觉但常见现象窗口出来了控件也能交互但所有文本显示为一排排小方块。原因不是 LCD 坏了而是 lv_conf.h 里对应的字体宏没有打开。LVGL 默认只编译很少的内置字体代码里指定的 Montserrat 字体如果没有被启用文本会用后备字体渲染——后备字体也是空的就变成了方块。解决方式是打开需要的字体宏。// lv_conf.h 中启用常用的内置字体 #define LV_FONT_MONTSERRAT_14 1 #define LV_FONT_MONTSERRAT_18 1注意字体的字号是固定的启用 14 号字体后用 16 号字号去渲染LVGL 不会自动缩放而是用默认字体替代可能还是方块。MCU 上为了省 flash通常会按实际用到的字号逐个开启或者用 LV_FONT_MONTSERRAT_16 等接近目标字号的字体。字库文件的体积和覆盖字符范围直接相关全角字符和 emoji 只能靠额外字库。5.5 调试器连不上或断点打不上launch.json 配置与调试路径不一致现象F5 启动调试后程序跑起来了但断点处不停下来VSCode 显示「未绑定断点」。原因通常是启动配置里的 program 路径与实际生成的 exe 路径不一致或者调试器没有用当前编译器链。CMake Tools 自动生成的调试配置有时会指到旧的构建目录重新 configure 之后路径变了但 launch.json 没跟着变。{ name: LVGL Simulator, type: cppdbg, request: launch, program: ${workspaceFolder}/build/main.exe, miDebuggerPath: gdb, cwd: ${workspaceFolder}/build }调试配置里 miDebuggerPath 指定为 gdb要确保它在 PATH 里或者写 MinGW 安装目录的完整路径。cwd 也值得注意模拟器如果依赖相对路径加载资源比如字体文件、图片工作目录不对会导致资源加载失败界面看起来缺东西。断点打不上时先确认 exe 是 debug 构建release 构建默认不开调试符号。5.6 改动 lv_conf.h 后编译没生效配置缓存的坑现象改了 LV_COLOR_DEPTH 或打开一个之前没用的功能重新编译后行为完全没变。原因常常是 CMake 缓存里还记录着旧的配置增删宏定义不会触发依赖文件的重新编译因为 CMake 检测不到 lv_conf.h 的变化。解决方式是先删掉 build 目录重新配置构建。# 改了 lv_conf.h 后最保险的做法是清掉缓存重新配置 rm -rf build cmake -B build -G MinGW Makefiles cmake --build build -j4这个坑在 Windows 上尤其容易踩因为 CMake 有时不会对 include 文件做足够敏感的依赖探测。C 编译器其实能通过头文件时间戳感知变更但如果编译是增量的、且依赖扫描不完整就会跳过重编。养成改了配置文件就顺手重建的习惯能省很多排查时间。6. 从例程到自己的界面改配置、加控件和调试三板斧6.1 改分辨率和颜色深度模拟你的目标屏幕例程跑通后第一件值得做的事是把它改成和你目标设备一致的显示参数。分辨率直接决定 UI 空间布局颜色深度影响内存占用和显示效果。在 lv_conf.h 里把 LV_COLOR_DEPTH 改成 16然后重新编译你会看到窗口里的渐变效果出现色阶断层这就是 16 位色深在模拟器上的直接反馈。// 模拟常见 4.3 寸 800x480 RGB 屏的参数组合 #define SDL_HOR_RES 800 #define SDL_VER_RES 480 #define LV_COLOR_DEPTH 16改配置不只是为了看得爽它对内存估算很有帮助。LVGL 的帧缓冲区大小等于 分辨率 × 色深位数 ÷ 8在 PC 上无所谓在 MCU 上这是饭吃。用模拟器先算出需要多大缓冲再决定要不要开双缓冲、是否用部分刷新这个流程比直接上板试错快得多。6.2 用调试器看控件树界面出问题的定位手段VSCode 调试器里最实用的技巧不是打断点看变量而是在 lv_timer_handler 或事件回调处下断点然后查看控件对象的内容。LVGL 的对象结构体里保存了坐标、父节点指针、样式指针和事件回调通过调试器的监视窗口可以查看这些字段。界面位置不对、点击没反应这类问题都能在对象树里找到线索。// 例程中创建控件后可以在断点处检查控件的布局参数 lv_obj_t *btn lv_btn_create(lv_scr_act()); lv_obj_set_pos(btn, 100, 100); // 断点停在这行后面查看 btn-coords 的四个变量调试器里看 lv_obj 结构体重点看 coords 成员它保存的是对象在屏幕上的实际坐标还有一个 align 相关字段。如果你发现控件位置和你设的一样但显示不在预期位置那通常不是布局问题而是父容器的样式或 padding 干扰。在 PC 模拟器上打断点排查 UI 问题比在嵌入式平台上用打印日志高效很多。6.3 改动例程代码的小习惯先复制 demo 再动手改最后说一个习惯不要直接在官方 demo 的源码文件上改业务逻辑。demo 文件是演示用的文件路径和功能划分不适合真实项目。正确做法是把例程里的控件创建逻辑复制到自己新建的 c 文件里保留 lvgl 头文件包含这样后续向 MCU 工程移植时可以直接搬文件。// 自己的 app_ui.c从 demo 里复制的控件创建逻辑可以原样搬移 #include lvgl.h void app_ui_create(void) { lv_obj_t *scr lv_scr_act(); lv_obj_t *label lv_label_create(scr); lv_label_set_text(label, Hello LVGL); lv_obj_center(label); }移植时注意 demo 里用到的 lv_demo_widgets 这类入口要替换成自己的 app_ui_createmain 函数里的调用点也要同步改。模拟器跑得通不代表 MCU 跑得动资源受限时要先检查控件数量和动画开销但那是下一个阶段的优化话题了。先把习惯养好后面的路会顺很多。这套流程走下来从下载源码到 VSCode 里跑通例程再到现在你已经能自己改参数、加控件基本把 LVGL 在 PC 上做原型验证的路子摸清了。以后换硬件平台时你只需要替换驱动注册部分这些界面代码依然能复用。整个验证链路里我摔得最多的就是版本不匹配和配置缓存所以希望你从第一天就养成固定 Tag、改动配置就重建的习惯希望帮到你。本文还有配套的精品资源点击获取