
最近“模拟器”这个词的热度是真不低我刷热搜时一半是游戏模拟器、手机模拟器一半是什么银行模拟器、支付宝模拟器。不过嵌入式圈子的“模拟器”完全是另一码事。做LVGL界面开发的朋友应该都有同感写控件、调布局、跑动画真正烦人的不是LVGL本身而是每次想看一眼效果都要编译、烧录、上板、看屏改一个像素都要循环一遍。尤其在项目前期界面方案还没定今天改圆角明天改配色反复刷板子能把人逼疯。这篇就讲我怎么用VS Code LVGL官方PC模拟器在5分钟内把界面跑起来。重点是这个方案全程不碰Windows环境变量不用手动设置PATH不用装乱七八糟的全局依赖。所有工具链和依赖库全部本地化换台电脑、换个同事拷贝整个工程文件夹就能直接编译运行。实测下来这应该是目前新手入门LVGL最快、最稳的一条路。如果你正准备学LVGL或者在STM32、ESP32上做UI但受够了“编译-烧录-看屏”的循环这篇文章就是给你写的。建议跟着做一遍你会在第3章结束时看到LVGL自带的Demo窗口出现在屏幕上。1. 为什么我坚持在PC上先跑LVGL模拟器1.1 上板调试的痛点刷一次固件好几分钟先说个很现实的场景。我早期在STM32上做LVGL界面当时用的还是F103系列编译一次固件加上烧录、复位、屏幕初始化前后差不多要一分钟。如果只是改了个按钮的坐标这一分钟完全白白浪费。遇到动画调试更痛苦动画参数没法直接看只能一次次烧录然后靠肉眼盯屏幕判断顺不顺滑。后来把UI代码搬到另一颗带屏幕的板子上情况好一点但刷机时间依然是最大的时间黑洞。有一次要调一个列表滚动的回弹效果我估了一下光烧录就花了将近两个小时而实际代码没改几行。上板子调试还有一个隐藏成本现场设备不一定随时在手边。办公桌上可能没有接好屏幕的开发板但一定有一台电脑。如果能在PC上直接看到一个跟真机效果几乎一致的窗口很多问题在写代码的阶段就能暴露。所以我现在的习惯是所有界面逻辑先在模拟器里跑通确认布局、交互、动画都没有问题再考虑往MCU上搬。模拟器就是我们界面开发的“预览窗”板子只管最终验证。1.2 模拟器方案横向对比为什么是官方PC模拟器 VS CodeLVGL官方仓库里其实提供了好几个模拟器方案。常用的有这么几类官方的PC模拟器lv_port_pc_eclipse基于SDL2库能在Windows/Linux上编译成桌面程序显示效果最接近真机。网页版模拟器比如lv_sim_web_designer、SimGui这些打开浏览器就能画界面方便是方便但性能有限复杂动画会卡而且很多人不习惯在浏览器里写C代码。真机方案也就是STM32/ESP32开发板屏幕效果最真实但调试成本最高。还有人在Linux桌面环境用Qt跑LVGL界面这种方案适合本身就熟悉Qt生态的开发者但对嵌入式场景来说Qt桌面环境和LVGL目标设备差距比较大参考意义有限。我最终选的是官方PC模拟器 VS Code组合。原因很直接官方模拟器代码和LVGL源码同源不会出现奇怪的不兼容问题VS Code轻量、插件生态好写代码、编译、调试在一个窗口内完成。更重要的是VS Code的CMake Tools插件能把“配置-编译-运行”串成一条流水线这正好是我下面要做的极简配置的基础。1.3 “免环境变量版”的核心逻辑依赖全部本地化传统配置LVGL模拟器的教程通常让你干这些事安装MinGW或MSYS2把bin目录加进系统PATH下载SDL2开发库也要配置环境变量或者手动改CMake缓存再设置什么CMAKE_PREFIX_PATH之类的高级选项。整套流程下来少说半小时而且每一步都可能出错。最怕的是环境变量配置失败明明按教程写了新开一个终端就是不生效——这种问题排查起来非常狼狈。我这套“免环境变量版”的思路很简单就是四个字依赖本地化。编译器用w64devkit这是一个免安装的GCC工具链压缩包解压出来就有gcc、g、gdb、mingw32-make不需要安装不需要配置PATH。SDL2库下载SDL2的MinGW开发包同样解压到工程目录下的deps文件夹里编译时通过CMake参数直接指定库的绝对路径。VS Code工程配置在.vscode/settings.json里写好编译器路径和SDL2路径CMake Tools插件会自动用这些配置去执行CMake不需要依赖系统环境变量。这样带来的最大好处是什么呢第一不污染系统环境不会出现“配了JDK环境变量影响Python配了Python影响编译链”这种连锁反应第二整个项目文件夹打包发给别人只要路径设置不变对方解压就能跑不需要重复配置第三出了问题排查起来非常简单因为所有路径都在settings.json里一眼看得到。2. 开工前的物料准备三个压缩包搞定一切2.1 物料清单与下载地址说实话这套方案要准备的材料少得可怜基本就四个东西物料作用来源VS Code代码编辑器和编译入口官网直接下载w64devkit免安装GCC/G工具链GitHub仓库Release页面SDL2-devel-mingw开发包LVGL模拟器的显示和鼠标输入依赖SDL官网下载lv_port_pc_eclipse仓库LVGL官方PC模拟器工程源码GitHub官方仓库这里有两个细节要特别提醒。第一SDL2开发包一定别下错版本。SDL官网的下载页面里Windows平台会同时给出Visual C版和MinGW版我们要选的是带mingw标识的。如果下载了VC版的库来配合GCC编译链接时会报各种奇怪的错误千万别踩这个坑。第二LVGL官方模拟器仓库在GitHub上叫lv_port_pc_eclipse名字里带Eclipse是因为官方最初为Eclipse CDT准备但工程本身带CMakeLists.txtVS Code完全可以直接使用。下载时优先用git的递归克隆命令把子模块一起拉下来git clone --recursive -b release/v8.3 https://github.com/lvgl/lv_port_pc_eclipse.git这里切到release/v8.3分支是为了稳定性。LVGL从v9开始结构调整比较大而目前教程、案例、还有各种移植参考绝大多数都基于v8.x。新手入门建议先用v8.3把流程走通后再去折腾新版不迟。2.2 目录结构设计建议按这个布局放文件为了真正做到“免环境变量、即拷即用”我建议把所有东西放在同一个工作区目录下结构大概是这样D:/lvgl_ws/ ├── w64devkit/ │ └── bin/ │ ├── gcc.exe │ ├── g.exe │ └── mingw32-make.exe ├── SDL2-2.30.1/ │ ├── include/ │ ├── lib/ │ └── bin/ │ └── SDL2.dll └── lv_port_pc_eclipse/ ├── CMakeLists.txt ├── lvgl/ ├── main.c ├── lv_conf.h └── ...w64devkit和SDL2都放在lv_port_pc_eclipse的外面和工程平级。这样工程目录保持干净依赖库也不会被误改。以后如果你同时做好几个LVGL项目可以共用同一个w64devkit和SDL2目录不用每个项目各存一份几百兆的依赖。2.3 下载与子模块处理的细节如果你用git命令克隆仓库记得确认lvgl目录下有内容而不是空文件夹。很多情况下网络不好子模块拉取半路失败结果CMake配置时找不到lvgl源码头文件报错无从下手。检查方法很简单看lv_port_pc_eclipse/lvgl/src目录里有没有lvgl.h和一堆源码文件。如果子模块确实没拉下来有两个补救办法在lv_port_pc_eclipse目录下重新执行 git submodule update --init --recursive手动到LVGL官方仓库下载对应版本的源码解压后把内容放进lvgl目录手动放源码的方式要注意版本一致性v8.3分支对应的LVGL源码就是v8.3.x版本别混用。LVGL版本号对不上编译往往报一堆API不存在的错误这种问题排查起来很费时间。3. 5分钟实操从零到看到LVGL Demo窗口3.1 0–1分钟解压工具链和依赖库w64devkit下载下来是一个压缩包直接解压到你规划好的位置。我习惯放在D:/lvgl_ws/w64devkit当然你也可以放在C盘或其他盘只要后续配置路径时保持一致就行。SDL2的MinGW开发包同样解压。这里注意一点某些版本的SDL2压缩包解压后可能带一层x86_64-w64-mingw32子目录也就是说实际的include和lib目录在里面。不管它长什么样你只要找到包含include、lib这两个子目录的那一层就行配置时路径指到这一层。解压这一步没有技术含量但路径千万不要带中文和特殊字符。LVGL的CMake脚本本身不存在中文路径支持问题但MinGW的make工具在中文路径下容易出幺蛾子。我用D:/lvgl_ws这个纯英文路径就是图个稳。3.2 1–2分钟VS Code打开工程并安装插件先装好VS Code这个不用多说。打开后到扩展市场搜索安装两个必装插件C/C微软官方出品提供代码补全、语法高亮、调试支持。CMake Tools也是微软官方插件集成了CMake配置、编译、运行整个流程。如果想用中文界面可以再装一个官方中文语言包这个不影响编译纯属个人偏好。安装完插件后用VS Code的“打开文件夹”功能选中lv_port_pc_eclipse目录。第一次打开时CMake Tools插件会在底部状态栏显示提示大意是“是否配置此工程”先不用急着点我们还要把编译器路径写明白再让它配置也不迟。3.3 2–3分钟配置CMake绕过环境变量这一步是整个方案的核心。我们做两件事把编译器绝对路径写进配置把SDL2的查找路径写进配置。这样CMake在配置工程时不需要在系统PATH里找任何东西直接按我们给的路径走。在lv_port_pc_eclipse目录下新建一个.vscode文件夹在里面创建settings.json文件写入以下内容{ cmake.generator: MinGW Makefiles, cmake.configureArgs: [ -DCMAKE_C_COMPILERD:/lvgl_ws/w64devkit/bin/gcc.exe, -DCMAKE_CXX_COMPILERD:/lvgl_ws/w64devkit/bin/g.exe, -DCMAKE_MAKE_PROGRAMD:/lvgl_ws/w64devkit/bin/mingw32-make.exe, -DSDL2_DIRD:/lvgl_ws/SDL2-2.30.1/lib/cmake/SDL2 ] }这里解释一下每个参数的意义。cmake.generator指定生成器为MinGW Makefiles对应我们用的GCC工具链。如果你机器上装了Visual StudioCMake默认可能会选Visual Studio生成器那样编译器就和SDL2不匹配了所以显式指定很有必要。CMAKE_C_COMPILER和CMAKE_CXX_COMPILER就是告诉CMake编译器pro在这里不要去PATH里找了。这是“免环境变量”最关键的一行。SDL2_DIR这个变量指向SDL2Config.cmake文件所在的目录。这是CMake官方推荐的SDL2查找方式。设置了这个工程里的find_package(SDL2 REQUIRED)就能直接命中不再依赖系统全局搜索。写完之后在VS Code里按CtrlShiftP打开命令面板输入“CMake: Delete Cache and Reconfigure”并执行。这一步会重新读取settings.json按我们的配置生成CMake缓存。第一次配置会比较慢需要扫描编译器和检查依赖请耐心等十几秒。3.4 3–5分钟编译、复制DLL、运行配置成功后VS Code底部状态栏会多出一排CMake相关的按钮找到Build按钮点下去开始编译。第一次编译LVGL源码加Demo机器不太行的话可能要一两分钟后续增量编译通常都是几秒钟的事情。编译结束后在build目录下会生成一个可执行文件名字在官方工程里一般是main.exe。我们还需要把SDL2的动态库复制到它旁边cp D:/lvgl_ws/SDL2-2.30.1/bin/SDL2.dll D:/lvgl_ws/lv_port_pc_eclipse/build/这一步必须做。Windows加载动态库时优先找程序exe同目录其次才找PATH。我们既然不配PATH就把DLL放在exe旁边简单直接。然后打开build目录双击main.exe或者用VS Code的CMake Tools面板点击运行按钮。几秒钟后屏幕上会弹出LVGL的模拟器窗口里面有官方自带的widgets demo按钮、滑块、进度条、图表一应俱全。用鼠标点一点、拖一拖你会发现触摸交互也生效了——鼠标就是触摸屏。到这里你的LVGL模拟器已经跑起来了。从下载依赖到看到窗口熟练以后确实能控制在5分钟以内。4. 把自己的界面塞进模拟器壳子会跑了接下来做什么4.1 建一个属于自己的UI文件看到Demo只是第一步真正干活的时候你肯定要写自己的界面。在工程目录下新建一个my_ui.c文件先写一个最简单的界面验证整个流程是通的#include lvgl.h void my_ui_init(void) { lv_obj_t *btn lv_btn_create(lv_scr_act()); lv_obj_set_pos(btn, 20, 20); lv_obj_set_size(btn, 100, 40); lv_obj_t *label lv_label_create(btn); lv_label_set_text(label, Hello LVGL); lv_obj_center(label); }这段代码创建了一个按钮和一个居中显示的文本标签在LVGL里是最基础不过的用法。文件建好后要在CMakeLists.txt里把它加进编译源文件列表。官方模拟器工程在CMakeLists的某个位置会有一组源文件声明有的版本是直接把main.c写死有的是用file(GLOB)自动扫描找到相应位置加上my_ui.c即可。如果你不想改CMake配置还有个更取巧的办法直接把my_ui.c的代码写进main.c文件里在main函数调用demo的位置改成调用你自己的init函数。省事但代码长了以后会很乱只适合临时验证用。4.2 改main.c切换入口打开main.c找到调用demo的那一行。官方工程默认会调用lv_demo_widgets()或者用条件编译控制几个demo入口。我们只需要把这行注释掉换成my_ui_init()// lv_demo_widgets(); my_ui_init();然后在main.c顶部加一句extern void my_ui_init(void);重新编译运行窗口里显示的就是你自己的按钮界面。以后在这个UI文件里加控件、加动画、加交互逻辑全部在模拟器里验证效率比刷板子高一个量级。4.3 高效迭代的小技巧模拟器调试界面的时候我一般不开调试器纯粹“改代码-编译-运行”循环就够了。CMake Tools插件的Build按钮和运行按钮都在底部状态栏鼠标点两下就行。想更快的话可以在命令面板绑定快捷键。另外有个很实用的技巧修改lv_conf.h里的LV_COLOR_DEPTH和显示分辨率时观察模拟器窗口的变化来判断真机效果。比如你最终目标屏幕是320x240的TFT就把模拟器窗口大小设成320x240这样在PC上看到的布局和真机基本一致不会出现“PC上挺好看上板就挤爆了”的情况。4.4 模拟器里的中文显示很多新手卡在中文乱码上。LVGL默认字体只包含ASCII字符直接lv_label_set_text设置中文屏幕上要么是空白要么是乱码。解决办法是启用CJK字体。打开lv_conf.h找到字体相关的配置打开一个支持中文的字体宏比如LV_FONT_SIMSUN_16_CJK。启用后再设置标签文本中文就可以正常显示了。实际项目开发时中文界面常用做法是用LVGL官方字体工具“LVGL Font Converter”把中文字体裁剪成需要的字模再作为自定义字体文件引入工程。这个在模拟器里同样适用字体文件放进去就能用不用区分平台。5. 实战中一定会遇到的5个坑5.1 找不到SDL2cmake error: Could NOT find SDL2这是初学者最容易遇到的第一道坎。现象是运行CMake配置时终端输出红色错误Could NOT find SDL2。检查顺序跟着走确认SDL2的lib/cmake/SDL2目录下确实存在SDL2Config.cmake文件。确认settings.json里SDL2_DIR拼写正确、目录分隔符用的是正斜杠/。确认修改settings.json后重新执行了“CMake: Delete Cache and Reconfigure”旧缓存不会自动失效。万一SDL2_DIR方式实在找不到也可以退一步在CMakeLists.txt里手动写include_directories和link_directories直接指定路径。但这个方法比较粗暴能跑通但不优雅。5.2 编译通过运行却提示缺SDL2.dllbuild目录下的main.exe单独运行时Windows会弹窗说找不到SDL2.dll。解决办法就是前面说的把SDL2.dll复制到exe旁边。如果你通过VS Code运行按钮启动同样也要保证DLL在exe同目录。有些教程会让你把SDL2的bin目录加进PATH这样系统全局都能找到DLL。但我们这期的主题是免环境变量所以坚持“DLL随exe走”的原则。你把整个build目录拷给别人对方也能直接运行。5.3 窗口一闪而过双击main.exe什么都没看到就退出了多半是程序启动时就报错崩溃或者错误对话框一闪而过。这时候用命令行方式运行可以看到具体日志cd build ./main.exe如果输出里有什么Segmentation fault、Access violation之类的提示多半是LVGL配置问题。常见的比如LV_MEM_SIZE设置太小模拟器加载大量demo控件导致内存不够。打开lv_conf.h把LV_MEM_SIZE从默认值调大一些比如16KB改成64KB重新编译即可。另外如果你在VS Code里配置了tasks.json建议在运行任务时选择integratedTerminal作为终端类型这样崩溃信息能在终端里多停留几秒不至于一闪而过根本看不清。5.4 鼠标点击无效或位置偏移模拟器鼠标充当触摸屏正常情况下点按钮、拖滑块都会生效。如果你发现鼠标位置和点击区域有明显偏移先检查lv_conf.h里的显示分辨率设置是否和模拟器窗口大小一致。官方模拟器默认是800x480或类似值你在系统里手动调整了窗口大小但代码里的分辨率没跟着改就会出现错位。鼠标点击完全无效的话检查lv_port_pc_eclipse工程里和输入设备相关的初始化代码看是不是lv_indev_drv的read_cb没有正确挂载到鼠标设备。官方demo功能完整一般不大会出现这个问题。5.5 中文Windows下编译输出乱码这个属于纯Windows环境问题。MinGW编译器在GBK编码的终端环境下输出UTF-8编码的中文日志显示就变成乱码。解决方法有两个方向一是把所有源文件统一保存为UTF-8编码并确保VS Code终端也设置为UTF-8二是如果工程里已经有中文硬编码的代码可以在CMakeLists.txt里加入编译选项add_compile_options(-fexec-charsetGBK)这样编译器会把字符串里的窄字符按GBK处理在中文Windows上显示正常。这个方法只影响最终运行时的字符编码不影响编译逻辑。如果你的目标设备最终用LVGL显示中文更推荐走字库方案用LVGL字体工具转换字模而不是依赖系统编码。5.6 常见问题速查表问题现象主要原因解决思路CMake找不到SDL2SDL2_DIR路径不对检查SDL2Config.cmake位置重新配置缓存运行提示缺DLLDLL未复制到exe目录把SDL2.dll放至build目录窗口一闪而过LV_MEM_SIZE过小或代码崩溃命令行运行看日志调整LV_MEM_SIZE鼠标点击偏移分辨率设置不一致对齐lv_conf.h分辨率和窗口尺寸编译后中文乱码源文件编码和终端编码不一致统一UTF-8或加-fexec-charsetGBK子模块源码为空git clone未拉全重新拉子模块或手动放LVGL源码6. 模拟器验证完怎么顺利搬到STM32/ESP32真机6.1 哪些代码不用动模拟器里跑通的UI代码包括你写的所有界面文件、lv_conf.h里的配置、动画和样式的逻辑这部分在真机上几乎原封不动就能使用。这正是LVGL跨平台特性的价值所在——界面层完全独立于底层硬件。lv_conf.h里的大部分配置也能直接搬过去但有几个参数要根据真机情况调整比如颜色深度LV_COLOR_DEPTH要和屏幕驱动匹配LV_MEM_SIZE要根据MCU的RAM重新设定不能照搬模拟器的值。6.2 哪些代码必须替换模拟器有专门的一套平台适配层真机上要换掉的主要是这两块显示驱动模拟器用SDL2将像素推送到窗口真机上要换成你的LCD驱动初始化代码和flush_cb回调函数把像素缓冲刷到SPI或并口屏幕。输入驱动模拟器用鼠标做触摸真机上换成触摸芯片的I2C或SPI读取代码更新lv_indev_drv的read_cb。这块其实就是LVGL官方文档里说的lv_port_disp和lv_port_indev移植。如果你用的是STM32网上大量例程都有现成模板ESP32上用ESP-IDF或者Arduino环境也有对应的LVGL移植包。移植的关键点是显示驱动flush_cb的调用时机。LVGL绘制完成后会调用flush_cb把显存内容发送给屏幕这个函数要和你的屏幕驱动完全配对否则会出现花屏、撕裂、显示错位等问题。模拟器里永远不需要关注这些而真机上一个flush函数没写对屏幕可能一片白或有色块。6.3 一个提醒模拟器调试通过不代表内存一定够这算是我给所有准备从模拟器转真机的朋友的真心提醒。模拟器跑在PC上内存以GB计你写再多控件都不会觉得卡。但STM32的内存是KB级LVGL默认的帧缓冲机制需要至少一块显存缓冲区颜色深度16位的情况下320x240分辨率的buffer大概就要150KB。如果LV_MEM_SIZE再开大点RAM分分钟见底。所以在上真机之前建议先在模拟器里养成查看LVGL内存使用量的习惯。LVGL提供了lv_mem_monitor函数可以在运行状态打印当前内存池的剩余量。模拟器里算出的消耗量对真机选型非常有参考价值至少能让你心里有底这颗MCU够不够跑我的界面。我个人在实际项目里整个UI方案的确定几乎全在模拟器里完成等上板的时候界面代码基本很少改动改的反而是屏参、触摸校准、背光控制这些硬件相关的东西。这套免环境变量流程帮我节省的时间保守估计也有几十个小时了。最后再分享一个小技巧整个D:/lvgl_ws目录是可以直接压缩发出去的。新同事拿到压缩包解压后只要保证目录结构不变打开VS Code装好CMake Tools和C/C插件直接点Build就能跑。彻底告别“帮我看看为什么我的环境跑不起来”这种问题把精力留给真正有意义的界面设计。