资讯动态

VSCode下配置LVGL 9.5模拟器:从环境搭建到调试实战

发布时间:2026/10/21 16:35:17 来源:尧图企业网站定制
LVGL 9.5 的模拟器在 VSCode 里跑起来是我这几年做小屏 UI 最顺手的一套工作流。写这个小项目纯粹解决一个问题不想为了看一个界面效果反复烧固件、插硬件也不想硬啃那些老旧的 8.x 示例。把 LVGL 9.5 的模拟器装进 VSCode配合调试打断点、看变量、改代码即时刷新整个体验会非常接近开发桌面程序。这篇就从头到尾记录我折腾的过程适合刚接触 LVGL 的初学者也适合从 8.x 往 9.x 迁移的老开发者直接照着抄作业。1. 为什么需要在 VSCode 里跑 LVGL 9.5 模拟器1.1 模拟器解决的真实痛点做嵌入式图形界面开发很多时间其实浪费在“等待”上改几个像素要重新编译固件编译没问题还要烧录烧录后还要接串口看日志。LVGL 的模拟器把这些问题一次性解决了它本质上就是在 PC 上用一个窗口模拟屏幕把所有 LVGL 的绘制逻辑、事件处理、动画都在本地跑起来。这意味着你可以在没有开发板的情况下先把整个界面框架、交互逻辑、视觉效果全部验证完再迁移到真实硬件上。LVGL 9.5 和之前的版本相比最大的变化是内部架构做了很大调整。比如lv_disp_t变成了lv_display_t绘制缓冲区的配置方式、输入设备的注册方式也都改了。网上很多旧教程拿 8.x 的代码直接塞给 9.x编译会出现大量“找不到符号”的错误。所以需要一套专门针对 9.5 的模拟器环境而不是简单地把旧模板拉下来改版本号。1.2 这套方案适合什么人我推荐下面这些读者重点参考第一次用 LVGL 9.x、想快速看到渲染效果的初学者从 8.x 迁移到 9.x、被新 API 折腾到崩溃的老用户已经在用模拟器、但想从 Eclipse 切到 VSCode 的人还有需要在 Windows 和 Linux 之间来回切换希望两边都能编译运行同一个 UI 工程的人。VSCode 在这里的角色是编辑器加调试入口不做花哨的集成。真正编译靠的是 CMake 加 Ninja图形底层用 SDL2调试器用 GDB。这个组合在 Windows 和 Linux 上都能跑只要你把对应的工具链装对后面几乎可以无缝切换。2. 环境准备一场工具链的前置工作模拟器环境有三个大件必须装好编译器、构建工具、SDL2 开发库。很多人卡住不是因为 LVGL 配置不对而是这三个大件没对齐后面我会给出每个平台的完整安装命令和自检方法。2.1 编译器与构建工具的选择在 Windows 上我强烈建议直接使用 MSYS2 环境下的 MinGW-w64不要用老旧的 Cygwin也不要用 Visual Studio 自带的那套命令行工具链。原因很简单SDL2 在 MSYS2 的 pacman 源里提供了现成的开发包下载快路径也清晰。你不需要自己拿源码去编译 SDL2省掉一整条踩坑路线。安装步骤如下去 MSYS2 官网下载安装包安装完成后会得到一个终端入口。在 MSYS2 终端里执行下面这一条命令把编译器、CMake、Ninja、GDB 和 SDL2 全部装齐pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-gdb mingw-w64-x86_64-SDL2 mingw-w64-x86_64-toolchain在 Linux 环境下更直接Ubuntu 或 Debian 系发行版执行下面命令sudo apt update sudo apt install build-essential cmake ninja-build libsdl2-dev gdb装完之后要验证一下工具是否就位。在 Windows 的 MSYS2 终端里执行gcc --version、cmake --version、ninja --version三条命令都能输出版本号才算过关。特别提醒VSCode 自带的集成终端还需要选对 shell否则 Windows 下可能默认调用 PowerShell环境变量找不到 gcc对后面调试有影响。我会在 VSCode 配置小节讲清楚怎么选。2.2 工作目录与 VSCode 扩展安装打开 VSCode在扩展市场搜索并安装三件套C/C、CMake Tools、CMake。C/C 扩展提供语法高亮、跳转和调试支持CMake Tools 提供从 VSCode 内部触发 cmake configure 和 build 的能力它还能自动识别 toolchain 路径省去很多手工配置。安装完成后按 CtrlShiftP 打开命令面板输入CMake: Select a Kit。在 Windows 上你应该能看到类似 GCC 8.1.0 x86_64-w64-mingw32 这样的选项选它就好了。如果列表里是空的说明 CMake Tools 没有检测到 MSYS2 的编译器你需要检查 MSYS2 的 mingw64 目录是否被加入了系统 PATH或者直接在 VSCode 的 settings.json 里手动指定编译器路径{ cmake.cmakePath: C:/msys64/mingw64/bin/cmake.exe, cmake.generator: Ninja, cmake.buildDirectory: ${workspaceFolder}/build }Linux 上通常 CMake Tools 会自动找到/usr/bin/gcc不需要额外配置。3. 组织 LVGL 9.5 模拟器工程目录与核心文件3.1 工程目录设计LVGL 官方维护了一个跨平台 PC 模拟器模板项目代号经常被简称为 lv_port_pc_eclipse因为最初是给 Eclipse 用的。实际在 VSCode 里我并不建议原样照搬那个模板因为它的 CMake 配置主要照顾 Eclipse 的导入流程里面有大量选项在 VSCode 里用不到还会引入不必要的混乱。我推荐手动搭建一个精简工程结构如下lvgl_9_simulator/ ├── CMakeLists.txt ├── lv_conf.h ├── main.c ├── .vscode/ │ ├── tasks.json │ ├── launch.json │ └── settings.json └── lvgl/ ├── CMakeLists.txt ├── lvgl.h └── src/关键点在于lvgl 目录可以直接从 LVGL 的 GitHub 仓库里把 release/v9.5 分支拷贝过来。如果网络不方便也可以直接下载压缩包解压到这个位置。lvgl 目录内部自带 CMakeLists.txt所以我们不需要自己写 LVGL 的编译规则只需要在顶层 CMakeLists.txt 里通过 add_subdirectory 把它引进来。3.2 lv_conf.h 配置头的关键点需要提前了解一个背景LVGL 9.x 引入了lv_conf_internal.h机制。如果你没有提供lv_conf.h编译系统会使用 LVGL 源码目录下的lv_conf_internal.h里面的默认参数阉割了很多功能而且字体、控件、扩展库的开关都是默认关闭状态。这意味着你编出来的 LVGL 是一个功能很弱的版本动画、中文、图表可能都没有。正确的做法是在工程根目录放一个lv_conf.h并且让 CMake 把它传给 LVGL。官方模板里一般会提供一个lv_conf.h.in或lv_conf.h.default复制一份改名为lv_conf.h即可。9.5 的lv_conf.h文件结构相比 8.x 也变了如果你手头只有旧版本的配置文件不建议直接替代容易漏掉新配置项。你可以在lv_conf.h里重点关注这几个开关#define LV_MEM_SIZE (64 * 1024 * 1024) // 内存池大小 #define LV_USE_FLEX 1 // 启用 Flex 布局 #define LV_USE_GRID 1 // 启用 Grid 布局 #define LV_FONT_MONTSERRAT_14 1 // 内置字体 #define LV_USE_LOG 1 // 日志输出 #define LV_LOG_LEVEL LV_LOG_LEVEL_WARN // 日志等级3.3 main.c 的职责划分模拟器的 main.c 只做三件事调用初始化接口、创建基本控件、进入主循环。9.5 的模拟器里显示驱动和输入设备驱动通常已经由 SDL2 后端的文件接好了你只需要在 main.c 里把指针初始化函数调一遍。核心部分可以参考下面的示例这段代码反映的是 9.x 的 API 风格别拿 8.x 的lv_disp_drv_register来套#include lvgl/lvgl.h int main(void) { lv_init(); // 模拟器的显示和输入设备初始化由 SDL2 模拟文件实现 lv_port_disp_init(); lv_port_indev_init(); // 创建一个示例界面 lv_obj_t * scr lv_screen_active(); lv_obj_set_style_bg_color(scr, lv_color_hex(0x1a1a2e), 0); lv_obj_t * label lv_label_create(scr); lv_label_set_text(label, LVGL 9.5 Simulator); static lv_style_t style_title; lv_style_init(style_title); lv_style_set_text_color(style_title, lv_color_hex(0xe8e8e8)); lv_style_set_text_font(style_title, lv_font_montserrat_20); lv_obj_add_style(label, style_title, 0); lv_obj_center(label); while(1) { lv_timer_handler(); lv_tick_inc(1); SDL_Delay(1); } return 0; }注意这里的lv_port_disp_init和lv_port_indev_init是由模拟器文件提供的不是 LVGL 核心的 API。在 9.5 的模拟器工程中相关的驱动代码会放在独立的lv_drivers或者sdl子目录里最终通过链接命令并入可执行文件。4. 编写 CMakeLists.txt把 LVGL 和 SDL2 组装起来4.1 顶层 CMake 配置详解CMakeLists.txt 是整个工程的枢纽。我最初偷懒直接抄官方模板结果 Windows 上一堆变量找不到后来自己写了一个精简版反而稳定得多。这个文件核心干两件事引入 LVGL 子目录、找到 SDL2 并链接。示例配置如下cmake_minimum_required(VERSION 3.20) project(lvgl9_simulator C) set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD_REQUIRED ON) set(LV_CONF_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lv_conf.h CACHE PATH LVGL config file path) set(LV_LVGL_BUILD_EXAMPLES OFF CACHE BOOL ) add_subdirectory(lvgl) find_package(SDL2 REQUIRED) add_executable(${PROJECT_NAME} main.c ) target_link_libraries(${PROJECT_NAME} PRIVATE lvgl SDL2::SDL2 ) if(WIN32) target_compile_definitions(${PROJECT_NAME} PRIVATE _CRT_SECURE_NO_WARNINGS) endif()有几处细节值得解释。第一LV_CONF_PATH必须在添加lvgl子目录之前设置因为 LVGL 的 CMake 逻辑会用这个变量去定位lv_conf.h位置不对就会回落到内部默认配置。第二SDL2 的 CMake 包在 MSYS2 和 Linux 下都能被find_package找到但前提是SDL2_DIR指定正确。Windows 上如果 find_package 失败可以在 CMake 配置时手动传参cmake -S . -B build -G Ninja -DCMAKE_PREFIX_PATHC:/msys64/mingw64这里把 MSYS2 的 mingw64 目录加到前缀搜索路径CMake 就能自动找到 SDL2 的 cmake 配置文件和头文件了。4.2 编译参数与优化选项LVGL 9.5 默认开启了LV_USE_ASSERT在模拟器调试阶段我建议保留这个选项可以帮你快速发现非法参数调用。等界面功能稳定后再把断言关掉提升运行速度。对于编译优化调试阶段用-O0 -gVSCode 的调试器才能准确显示变量。如果要测试性能改-O2重新构建。一个额外技巧在 CMake 里给 LVGL 添加LV_USE_PERF_MONITOR 1。这个宏一旦启用模拟器窗口的角落会显示当前刷新帧率和 CPU 占用实测下来对排查“动画掉帧”问题非常管用。但这个开关通常要在lv_conf.h里定义CMake 里不用动。4.3 手动编译还是用 CMake Tools你可以完全在 VSCode 的终端里执行以下命令完成整个构建cmake -S . -B build -G Ninja cmake --build build这三行命令是所有编译动作的根。CMake Tools 插件的作用只是帮你封装这些动作并且能抓取编译器错误输出定位到具体文件。如果你喜欢纯终端流完全可以不装 CMake Tools直接在终端跑命令。我个人习惯是在.vscode/settings.json里锁定 CMake Tools 的工作目录和构建参数避免插件自动生成的配置和我预期的不一致{ cmake.sourceDirectory: ${workspaceFolder}, cmake.buildDirectory: ${workspaceFolder}/build, cmake.generator: Ninja, cmake.configureArgs: [ -DCMAKE_PREFIX_PATHC:/msys64/mingw64 ] }这样在 VSCode 里按 CtrlShiftP 执行CMake: Build时行为和命令行完全一致。5. 配置 VSCode 调试环境从启动到断点5.1 生成并确认可执行文件编译完成后build目录下会生成一个可执行文件Windows 上是lvgl9_simulator.exeLinux 上是一个无后缀的二进制文件。在配置调试器之前先直接在终端里运行一次能弹出模拟器窗口就说明工程本身没大问题。运行窗口会显示一片背景色鼠标点击和键盘按键会被 SDL2 捕获然后交给 LVGL 的输入设备驱动。如果你造了一个按钮控件模拟器里的鼠标就是这个按钮的点击源。5.2 launch.json 调试配置VSCode 的调试核心是.vscode/launch.json下面这个配置适用于 Windows MSYS2 环境{ version: 0.2.0, configurations: [ { name: LVGL Simulator Debug, type: cppdbg, request: launch, program: ${workspaceFolder}/build/lvgl9_simulator.exe, args: [], stopAtEntry: false, cwd: ${workspaceFolder}, environment: [], externalConsole: false, MIMode: gdb, miDebuggerPath: C:/msys64/mingw64/bin/gdb.exe, setupCommands: [ { description: Enable pretty printing for gdb, text: -enable-pretty-printing, ignoreFailures: true } ], preLaunchTask: build-debug } ] }miDebuggerPath需要指向 MSYS2 里的 gdb千万不要用系统自带的旧版 gdb否则调试时会出现“无法启动程序”的莫名其妙问题。preLaunchTask指向一个编译任务这样按 F5 时自动先编译再运行。5.3 tasks.json 编译任务在.vscode/tasks.json里定义一个任务把编译动作封装起来{ version: 2.0.0, tasks: [ { label: build-debug, type: shell, command: cmake --build build, group: { kind: build, isDefault: true }, problemMatcher: [$gcc] } ] }配好后按 F5 就是完整的“编译 启动调试”。在断点命中的时候可以非常直观地看到lv_obj_t *结构体内部的属性值这比在上位机界面里逐步推测要高效很多。5.4 设置集成终端为 MSYS2这一步很多人会忽略。VSCode 默认的终端是 PowerShell但在 MSYS2 环境下PowerShell 无法继承 mingw64 的环境变量。解决办法是在.vscode/settings.json里设置终端为 MSYS2 的 bash{ terminal.integrated.profiles.windows: { MSYS2 Bash: { path: C:/msys64/usr/bin/bash.exe, args: [--login, -i] } }, terminal.integrated.defaultProfile.windows: MSYS2 Bash }设置好之后重新打开集成终端echo $PATH里能看到/mingw64/bin就代表环境正常。6. 跑通第一个可交互界面6.1 布局控件并监听事件模拟器编译通过后最爽的时刻就是页面渲染出来了。所以第一步我建议做一个小测试页一个按钮一个进度条一个标签点击按钮时进度条增加。这个闭环能验证显示、输入事件、样式更新三件事是否都正常。下面是一个 9.5 风格的示例注意事件回调方式跟 8.x 已经不同static lv_obj_t * bar; static void btn_event_cb(lv_event_t * e) { lv_obj_t * btn lv_event_get_target(e); LV_UNUSED(btn); int32_t value lv_bar_get_value(bar); value 10; if (value 100) value 0; lv_bar_set_value(bar, value, LV_ANIM_ON); } void create_demo_ui(void) { lv_obj_t * scr lv_screen_active(); lv_obj_set_style_pad_all(scr, 20, 0); bar lv_bar_create(scr); lv_obj_set_size(bar, 280, 20); lv_bar_set_value(bar, 0, LV_ANIM_OFF); lv_obj_align(bar, LV_ALIGN_CENTER, 0, -30); lv_obj_t * btn lv_button_create(scr); lv_obj_set_size(btn, 120, 40); lv_obj_align(btn, LV_ALIGN_CENTER, 0, 30); lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_CLICKED, NULL); lv_obj_t * label lv_label_create(btn); lv_label_set_text(label, Add 10); lv_obj_center(label); }这个例子里lv_screen_active()在 9.x 中是获取当前屏幕的方式以前叫lv_scr_act()。写代码时注意这类 API 差异。6.2 模拟器里的交互操作在模拟器窗口中直接用鼠标点击即可生成 LVGL 的点击事件对应真实设备上的触摸操作。SDL2 后端还把鼠标滚轮映射成了编码器事件、键盘部分按键映射成了按键事件。我在实测中确认9.5 模拟器还支持通过键盘改变焦点这对调试焦点管理逻辑很有帮助。需要特别留意的是模拟器只模拟了 LVGL 层级的交互不考虑真实硬件的杂讯。真实触摸屏会有抖动、延迟、坐标偏移这些模拟器里很难还原。所以模拟器适合验证“逻辑正确”不合适验证“体验细腻”。真机上该做的消抖、滤波、坐标校准不能因为模拟器流畅就漏掉。6.3 处理 SDL2 窗口初始化的细节如果你运行模拟器时发现窗口一闪而过或者没有窗口弹出问题十有八九出在 SDL2 初始化。模拟器代码里SDL_Init 必须在 lv_init 之后、创建窗口之前完成。而且在 Windows 上如果 SDL2 的动态库SDL2.dll不在可执行文件同目录程序启动时直接报错“找不到 SDL2.dll”。解决办法是把C:/msys64/mingw64/bin/SDL2.dll复制到 build 目录或者在 CMake 里加一条自定义命令让每次构建后自动拷贝add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ENV{MSYSTEM_PREFIX}/bin/SDL2.dll $TARGET_FILE_DIR:${PROJECT_NAME} )这个细节已经帮我避开了好多次本地环境配置麻烦。7. 高频问题与排查实录7.1 问题速查表下面是这段时间踩过且反复出现的坑整理成表格方便读者快速对照。现象原因解决办法找不到 SDL2/SDL.hSDL2 开发库未安装或 CMake 前缀路径不对安装 SDL2或在 CMake 时指定CMAKE_PREFIX_PATH大量 lv_ 符号未定义链接时没有把 LVGL 库放进去或链接顺序错误确保target_link_libraries包含lvgl且位于可执行文件之后编译时报Unknown type name SintMSYS2 环境不完整SDL 头文件依赖缺失重新完整安装mingw-w64-x86_64-toolchain模拟器窗口黑屏绘制缓冲初始化失败或显卡驱动不支持在 SDL 初始化后检查返回值必要时设置SDL_RENDERER_SOFTWARE标志界面显示但点击无反应输入设备驱动未初始化或事件处理器未运行确认调用了lv_port_indev_init()并检查主循环里lv_timer_handler()中文显示为方块没有启用含 CJK 字符集的字体在lv_conf.h启用带中文的字体或导入外部字体资源VSCode 调试器连不上 gdbmiDebuggerPath 指向了错误版本使用 MSYS2 下的gdb.exe完整路径7.2 链接顺序的经典教训有一个链接错误我想单独拎出来说因为很多人都被折磨过。GCC 在链接时库的顺序非常关键。如果在 CMakeLists 里把lvgl写在主程序前面链接时遇到未定义的符号链接器不会回头去前面的库文件里找于是爆出大量undefined reference to lv_*。正确写法是把所有库写在可执行文件源文件之后target_link_libraries(${PROJECT_NAME} PRIVATE lvgl SDL2::SDL2 )这个规则在普通 CMakeLists 里靠target_link_libraries的语义就能保证所以尽量别手动去拼gcc main.c -lvgl这种老式命令除非你很清楚库的排列顺序。7.3 从 8.x 迁移到 9.5 的 API 差异如果你有 8.x 的代码迁移时最容易踩到的坑有这么几个lv_disp_t要全部替换成lv_display_tlv_disp_drv_t换成lv_display_t的创建和相关 setter主题初始化方式变了旧的lv_theme_init相关接口已经重写样式属性设置从lv_style_set_bg_color变成lv_obj_set_style_bg_color需要额外传入选择器参数。这些不是普通的“改名”而是整个风格体系的调整。遇到编译错误时建议把 LVGL 9.5 仓库里的 example 代码作为权威参考比在网上搜 8.x 的答案要靠谱得多。我个人在实际操作中的体会是模拟器装上只算完成了第一步真正提升效率的是把它融入日常开发的每个环节。之前我设计一个复杂仪表盘每次都要烧板子一个下午只能改十几次用模拟器之后界面布局、状态切换、动画曲线都在 PC 上直接调半小时能迭代十几个版本最后再下板子做真机验证。最后再分享一个小技巧把lv_conf.h里的LV_USE_PERF_MONITOR打开模拟器里能实时看到 FPS。如果你发现某个界面的帧率掉到 30 以下别急着在真机上优化算法先在模拟器里定位是图片解码、阴影绘制还是碎片重绘导致的瓶颈改完再对比帧率变化这个循环足以解决大部分嵌入式 GUI 性能问题。模拟器不只是看效果的工具还是一座微型的性能实验室。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑