简介:面向嵌入式开发者和UI工程师,这份LVGL 9.2.2模拟器完整工程包旨在解决在Windows环境下使用VS2022编译运行LVGL模拟器的配置难题。资源包含约2000个文件,以C/C++源码为主(1078个.c、762个.h、51个.cpp),同时附带Python辅助脚本、TXT/MD说明文档、JSON配置及字体图片资源,覆盖LVGL官方demo、控件示例、SimSun等中文字体以及音频封面图片数据,便于直接导入VS2022进行工程配置与编译调试。压缩包共153.63MB,目录结构清晰,包含模拟器所需的全部源文件和依赖引用;目前已有517人学习使用。通过这套资源,开发者可快速验证LVGL 9.2.2的界面效果,省去从零移植和搜集依赖的流程,更适合需要基于VS2022做嵌入式UI原型验证或二次开发的工程师与学习者。 做嵌入式界面开发的朋友,十有八九都听过LVGL。最近我接了个小项目,需要在正式画板之前把一套设置页面的交互流程定下来,LVGL正好发到了9.2.2,所以我决定在VS2022里把官方模拟器跑起来。整个过程没有想象中顺利,光是SDL2依赖和子模块就折腾了小半天,但跑通之后,界面迭代速度确实快了不止一倍。这篇文章是我的完整记录,给打算从8.x升级到9.x,或者正在纠结怎么在Windows下跑LVGL模拟器的朋友做个参考。
1. 开始前的清醒认知:模拟器不是移植,而是界面先行
1.1 为什么我选择在PC上跑LVGL模拟器
做嵌入式GUI开发的同事都懂,一套界面改来改去,如果每次都烧录到板子再看效果,光编译下载一次就得几十秒,调试个边距和颜色能磨掉半天耐心。PC模拟器能直接解决这个痛点,它把LVGL跑在Windows窗口里,鼠标模拟触摸,改完代码立刻就能看效果,布局和交互逻辑可以快速试错。
更关键的是,模拟器能让你在硬件还没确定的时候先启动UI开发。我这次的项目主控还没选好,但需求方已经把交互稿画出来了,那么用模拟器先搭一套可点击、可滑动的Demo,给需求方确认效果,比对着PDF架构图讨论高效得多。等UI方向定了,再把同样的代码搬到真实板子,只换底层驱动,中间的业务逻辑和控件代码几乎不需要改动。
1.2 LVGL 9.2.2带来的变化
LVGL 9.x和8.x的差距不小,不是简单升级一下版本号就能跑的。比如样式系统改了,8.x里常用的lv_theme相关API在9.x中有所调整,控件的默认外观表现也不一样。我最早在9.2.2里直接粘了一段8.3的demo代码,编译出来一堆lv_obj_set_pos还能用,但某些lv_theme_init就找不到符号了,因为9.x把主题和样式重构成了新体系。
所以如果网上搜到的教程是8.x的,按照那套写法搬到9.2.2,大概率会碰到接口失效。最好直接看官方9.x的文档和例程,或者在模拟器工程里看自带的demo代码。这篇记录我们用的就是LVGL 9.2.2对应的分支,不是改一改就能糊弄过去的版本。
2. 环境准备:VS2022必须要有的两个组件
2.1 安装"使用C++的桌面开发"工作负载
VS2022默认安装的时候不一定带C++编译器和CMake工具,我一开始只装了.NET开发部分,结果打开CMake工程时VS直接提示找不到编译器,非常尴尬。需要打开Visual Studio Installer,找到已安装的VS2022,点“修改”,在“工作负载”里勾选**“使用C++的桌面开发”**,这个选项会同时带上MSVC编译器、Windows SDK和CMake工具。
注意CMake工具虽然名字叫“适用于Windows的CMake工具”,但在VS里打开模拟器工程后,它会自动调用CMake生成构建目录。如果之前电脑上单独装过CMake,版本最好新一点,LVGL 9.2.2的CMakeLists对最低版本有要求,太老的CMake会直接报CMake 3.21 or higher is required。VS2022自带的CMake通常能满足要求。
2.2 SDL2依赖:模拟器的显示底层
LVGL本身只做绘制,不负责创建窗口和处理鼠标事件。官方PC模拟器使用的是SDL2库,它是一个跨平台的多媒体库,在Windows上创建一个OpenGL窗口,然后把LVGL的像素缓冲区渲染出来。没有SDL2,程序编译能过,但链接时就会报一堆SDL_Init相关的未解析外部符号。
SDL2的安装方式我试过两种:一种是去SDL官网下载SDL2-devel-2.x.x-VC.zip,解压后把SDL2.lib和头文件路径告诉CMake;另一种是用vcpkg安装。这里我推荐vcpkg,因为版本管理方便,还能自动把SDL2的路径传给CMake,省掉手动配置环境变量的麻烦。vcpkg安装SDL2的命令:
git clone https://github.com/microsoft/vcpkg.git cd vcpkg .\bootstrap-vcpkg.bat .\vcpkg install sdl2:x64-windows安装完成后,在VS2022的CMake设置里把vcpkg的toolchain文件指过去,或者直接用-DCMAKE_TOOLCHAIN_FILE=[vcpkg路径]/scripts/buildsystems/vcpkg.cmake重新配置。这一步搞定之后,SDL2的include和lib目录都会被自动传递给CMake,后面编译就顺畅了。
3. 下载LVGL 9.2.2模拟器代码:仓库选择与版本锁定
3.1 官方模拟器仓库有哪些
光下载LVGL核心库是跑不起来的,它缺窗口、缺输入设备驱动,甚至没有一个main函数。官方专门维护了一个PC模拟器工程叫lv_port_pc_vscode,听名字像是给VSCode用的,实际上它同时支持VSCode和VS2022,内部通过CMake组织构建,会以子模块方式把lvgl、lv_drivers等仓库一起拉下来。
很多教程还会提到lv_sim_vscode_sdl这个仓库,也是官方维护的,但更偏VSCode。如果你用的是VS2022,我建议直接用lv_port_pc_vscode,它的CMakeLists对MSVC编译器的配置更完整,省得自己加链接参数。
3.2 克隆命令与版本切换
在Windows的CMD或者PowerShell里执行:
git clone https://github.com/lvgl/lv_port_pc_vscode.git cd lv_port_pc_vscode git fetch --tags git tag先看有哪些tag,找到对应当前最新的9.2.2版本。有些仓库的tag命名可能是v9.2.2,也可能是9.2.2,直接git checkout v9.2.2会报错说不存在这个路径,所以先看一眼再切。切换之后,一定要拉子模块:
git checkout v9.2.2 git submodule update --init --recursive这一步非常关键。lvgl和lv_drivers都是以公共仓库形式挂在子模块里的,如果不执行上面的命令,3rd-party目录或者lvgl目录会是空的,后边编译肯定找不到头文件。子模块拉取可能需要几分钟,取决于网络状况,耐心等。
拉完后进入lvgl目录,打开lv_version.h,能看到LVGL_VERSION_MAJOR是9,LVGL_VERSION_MINOR是2,LVGL_VERSION_PATCH是2,这就说明版本对齐了。版本对齐很重要,因为模拟器工程的CMakeLists有时会检查LVGL版本,如果核心库是8.x,编译时各种API都对不上。
4. 在VS2022里跑通Demo:配置、编译、启动一次到位
4.1 用CMake工程打开项目
不需要新建项目,VS2022天然支持直接打开CMake工程。在VS里点“文件” -> “打开” -> “CMake...”,选择刚刚克隆目录里的CMakeLists.txt。VS会开始解析CMake,首次配置会花几十秒,底部输出窗口会滚动一堆日志。
配置完成后,“解决方案资源管理器”的旁边会多出一个“CMake菜单位”,能看到生成缓存的结果。如果之前装了vcpkg,这里需要确认CMake确实找到SDL2。可以在顶部菜单栏选择“项目” -> “CMake设置(适用于...)”,在“CMake命令参数”里加上:
-DCMAKE_TOOLCHAIN_FILE=C:/path/to/vcpkg/scripts/buildsystems/vcpkg.cmake填完点“保存”,VS会重新生成CMake缓存。看到输出里出现Found SDL2这类日志,说明环境已经通了。
4.2 修改lv_conf.h打开Demo
模拟器工程默认可能没有打开任何demo界面,启动后要么黑屏,要么只显示一个空背景。需要先找到lv_conf.h,这个文件一般在工程根目录或者lvgl目录旁边,CMake会覆盖掉默认配置。我用的是工程自带的lv_conf.h,打开后找到LV_USE_DEMO相关的宏:
#define LV_USE_DEMO_WIDGETS 1 #define LV_DEMO_WIDGETS_SLIDESHOW 1同时确认几个基础配置:
#define LV_COLOR_DEPTH 16 #define LV_USE_LOG 1LV_COLOR_DEPTH设成16比较省内存,模拟器上显示也没问题;LV_USE_LOG打开后,运行窗口能看到LVGL的日志输出,比如控件创建和错误提示,排故障很有用。因为模拟器跑在PC上,RAM不是瓶颈,也可以直接把颜色深度设成32,显示效果会更好一点。
注意:如果LVGL 9.2.2的模拟器工程里把lv_conf.h放在了src目录外,你需要确认自己改的是真正参与编译的那个文件,别改了个副本。最笨也最有效的验证方式:在lv_conf.h最顶部加一行#error "conf loaded",编译时如果报这个错,说明文件被用上了,再把这行删掉。
4.3 编译与运行
CMake配置成功后,在VS的启动项下拉框里选择一个可执行目标,在这个工程里通常是main或demo。选好后直接按F5编译并调试,第一次编译耗时较长,因为SDL2和LVGL需要全部编译一遍。编译完成后会弹出一个SDL窗口,出现LVGL的widgets demo,里面包含按钮、滑块、仪表盘、文本框等控件,鼠标点击和拖拽就能模拟触摸操作。
如果窗口没有弹出来,先看输出窗口有没有报错,再检查SDL2.dll是否在生成目录里。使用静态SDL2库的话不需要dll,但如果是动态链接,跑起来会提示找不到SDL2.dll。最简单的方法是把SDL2的lib\x64下的SDL2.dll复制到生成的exe同目录下,重新运行即可。
5. 踩坑实录:LVGL 9与VS2022的磨合问题
5.1 找不到SDL.h / 链接库不匹配
第一次打开CMake工程,VS报了一堆C1083 cannot open include file: 'SDL.h': No such file or directory,这说明SDL2的头文件路径没有进入编译器搜索目录。我检查CMakeLists,发现它要求通过SDL2_DIR或vcpkg传入路径。如果死活找不到,可以在CMake设置里手动加两个变量:
-DSDL2_INCLUDE_DIR=C:/path/to/SDL2/include -DSDL2_LIBRARY=C:/path/to/SDL2/lib/x64/SDL2.lib配置后重新生成。提示:x86和x64的库不要混用,VS2022默认生成x64工程,如果下载的是SDL2的Win32库,链接时会出现一堆奇怪的LNK2019,实际上是位数不匹配。
5.2 submodule没有拉取导致lvgl目录为空
这是最容易踩的坑。克隆项目后如果直接打开CMake,配置会报错说找不到lvgl.h,或者找不到LVGL的模块。到目录下一看,lvgl文件夹几乎是空的,只有几个文件,真正的源码都不在。原因就是没执行git submodule update --init --recursive。
我的经验是克隆后先别急着开VS,先把子模块拉全。如果子模块也拉了,但还是找不到源码,检查一下lvgl文件夹里是否有.git文件,如果有,说明子模块已经正确关联;如果连这个都没有,建议重新执行一次:
git submodule sync --recursive git submodule update --init --recursive5.3 编译通过但运行瞬间退出的坑
这种情况最磨人:编译完全正常,没有任何错误,运行后窗口闪一下就没了。我遇到的原因是SDL窗口创建失败,LVGL初始化时指定的分辨率超过屏幕可用区域。在lv_conf.h里有个参数控制虚拟显示器的分辨率,默认可能是800x480,我在笔记本上没问题,但接到一台4K屏幕时窗口一闪而过,看了日志才发现SDL2创建窗口失败。
解决办法是把分辨率调低一点,或者把窗口初始大小改成和屏幕匹配:
#define LV_HOR_RES 800 #define LV_VER_RES 480如果确认分辨率没问题,再看lv_drivers里的monitor.c,它的SDL_Window创建参数可能写了固定宽高,要跟lv_conf.h保持一致。
5.4 VS2022的CMake缓存问题
改完lv_conf.h或者CMakeLists,重新编译时发现改动没生效,这是CMake缓存在作怪。VS2022默认把缓存生成在out/build目录下,有时候它检测不到外部文件的变化。我的做法是:把out目录整个删掉,重新打开CMake工程。虽然要再等一次完整配置,但能避免很多稀奇古怪的“不知道为什么就是没变”的问题。
删缓存前记得备份自己的修改,尤其是lv_conf.h,如果工程在lv_conf.h模板的基础上改了配置,删缓存不会影响它,只要不误删源码就行。
6. 模拟器跑通后,我用它做了什么:小项目复盘
6.1 用容器和布局快速搭一个设置页
模拟器能跑之后,我并没有停在demo界面,而是立刻开始搭自己项目的设置页。LVGL 9里的容器和flex布局很好用,以前8.x要做左对齐、右对齐还得手算坐标,9.x里一个lv_obj_set_layout就能自动排布。我搭了一个简单的页面,左侧是导航列表,右侧是控制面板:
lv_obj_t* cont = lv_obj_create(lv_scr_act()); lv_obj_set_size(cont, 480, 300); lv_obj_set_layout(cont, LV_LAYOUT_FLEX); lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_ROW);然后往容器里塞了几个按钮和滑块,跑在模拟器里,鼠标拖动滑块很跟手,这种“所见即所得”的感受是纯码代码想象不出来的。
6.2 添加当前时间控件和启动流程
模拟器跑UI还有个好处,就是可以完整验证定时器驱动的控件。我在页面顶部加了一个当前时间标签,用lv_timer_create每秒刷新一次:
lv_timer_t* timer = lv_timer_create(update_time_cb, 1000, NULL); static void update_time_cb(lv_timer_t* timer) { lv_label_set_text_fmt(time_label, "%02d:%02d:%02d", h, m, s); }在模拟器里能看到时间每秒跳动,切换页面再回来后,定时器依然正常,不会卡死。那段时间我一边改代码一边盯着时间控件走字,突然觉得以前在真机上频繁烧录纯属浪费时间,很多定时器和动画逻辑完全可以在PC上先跑稳。
6.3 模拟器验证后移植到真机的经验
最后说一点后续使用的体会。模拟器里的显示驱动、触控驱动和真机完全不一样,LVGL本身的控件代码可以复用,但底层的flush_cb和touchpad_read_cb必须替换。我在模拟器里把页面流程、动画曲线、控件间距都调好了,移植到STM32时,只写了LCD和触摸驱动,再把lv_conf.h里的颜色深度DMA设置匹配一下,剩下几乎没动UI逻辑。
这个“模拟器先行、真机收尾”的工作方式,后来成了我做小型嵌入式项目的基本流程。唯一要注意的是,模拟器和真机在字体渲染清晰度上会有一点差异,PC上看着舒服的中文小字号,在低分辨率屏上可能需要重新调整字号和字距。但整体上,先把模拟器跑起来,永远是性价比最高的起步动作。
本文还有配套的精品资源,点击获取