☰
LVGL模拟器搭建指南:VS2022+SDL2环境配置与踩坑实录
2026/9/28 14:13:23 网站建设 项目流程

上个月给一个 STM32 仪表的项目选 UI 方案,板子还在打样,想着先把界面原型跑起来。以前用 Keil 调 LVGL,每次改完布局都要重新编译、烧录、插线、看屏,十分钟起步,改三次我就受不了了。于是打开 Visual Studio 2022 想搭一个 LVGL 模拟器,原以为半小时的活,结果折腾了差不多两个晚上,中间踩到一个非常经典的坑:打开社区模拟器工程,VS2022 直接报"无法找到 Visual Studio 2010 的生成工具(平台工具集 ='v100')"。

这篇文章把我最后的顺利路径记下来,以 LVGL 9.x + SDL2 + VS2022 Community 为组合,从环境配置讲到模拟器真正跑起来。适合平时在 Keil 里被移植折腾得头疼的同学,也适合嵌入式 UI 想先在 PC 上调好再搬回单片机的人。顺手会把 v8 和 v9 的差异点标出来,毕竟现在网上一搜,一半教程还是 v8 的 API,直接抄到 v9 上编译过不去,这是大多数人卡住的原因。

1. 为什么要把 LVGL 跑在 PC 上:模拟器到底解决了什么问题

1.1 嵌入式 UI 调试的效率瓶颈

只要你在单片机上调过 LVGL,一定经历过这个循环:改一个坐标,改一个字号,改一个容器间距,然后编译、下载、上电、截图、发现不对、再改。如果用的是 STM32F103 这类主频不高的芯片,编译一次两分钟,每次迭代都像在等锅烧水。

模拟器把这个问题直接拍扁了。在 PC 上跑 LVGL,改完代码后按一下 F5,几秒钟就能看到界面效果。它实际起到的作用就是 UI 原型验证:你可以在硬件回来之前,把布局、配色、交互、动画、字体渲染全部调完,最后再把稳定的代码原样搬回单片机,只需要改显示驱动和输入驱动两个接口,UI 层代码一行都不用动。

另外,LVGL 自带几十个控件和 demo 工程,你可以在 PC 上直接看官方示例长什么样,比自己对着文档猜率高太多了。

1.2 模拟器到底模拟了什么

很多人第一次接触 LVGL 模拟器会有个误会,以为它是虚拟机或者 QEMU 这类东西。其实不是。LVGL 本身是纯 C 写的软件渲染库,不绑定硬件平台,所以它根本不关心你运行在什么设备上,只要有三个东西就能跑:

  • 一个能往屏幕上刷像素的显示接口
  • 一个能提供触摸、按键或鼠标输入的输入接口
  • 一个周期性触发它刷新画面的主循环

PC 模拟器用 SDL2 这个跨平台多媒体库来充当上面三个角色。SDL2 开一个窗口,创建一块纹理,LVGL 把画好的像素丢进这块纹理窗口显示;鼠标的坐标和按键状态,被包装成 LVGL 的指针输入设备。

所以本质上,你在 PC 上做的不是"模拟"嵌入式环境,而是把 LVGL 也当成一个普通 Windows 程序跑起来了。这也是它为什么移植起来特别快的原因——LVGL 官方所有 UI 代码都是跨平台可编译的。

1.3 这套方案适合谁

这个流程最适合两类人:

一类是平时用 Keil、IAR 等集成开发环境做单片机开发,代码本身很熟练,但不太熟悉 VS 工程配置的工程师。另一类是想快速验证界面设计、但又不想买开发板的 UI 爱好者。如果你已经在 STM32 上成功跑过 LVGL,只是想找个更快的方式调 UI,那这篇你直接照着做就行,20 分钟内能见到窗口。

2. 动手前的准备:组件、源码和 SDL2 怎么选才不踩雷

2.1 VS2022 安装时候最容易漏的一项

VS2022 社区版是免费的,官网直接下载。安装时如果你只想写 Python 或者 C#,默认勾选就够了,但要用 LVGL 模拟器,必须勾选"使用 C++ 的桌面开发"这个工作负载。

这个工作负载包含了 MSVC 编译器、Windows SDK、C++ CMake 工具等一整套东西。不勾选的话,之后创建 C++ 空项目都会失败,更别提编译了。

另外说一句,VS Installer 的"单个组件"页面里我有看到过 v100 这样的老工具集选项,但我强烈不建议你去装。这个后面讲 v100 报错的时候会细说。

2.2 LVGL 源码:选 9.x 还是 8.x

打开 LVGL 的 GitHub 官方仓库,默认分支已经是 9.x 了。我的建议是直接用 9.x 的最新 release,比如 v9.2 以上,因为 9.x 的 API 比 v8 清晰很多,而且官方 demo 更丰富。但有一点你必须清楚:网上大量教程、博客、入门视频都是基于 v8.x 写的,里面的 API 名字跟 v9 对不上,抄到 v9 编译必然报错。

如果你时间紧张,又想跟着某个 v8 的教程一步步走,那也可以切到release/v8.4分支拉代码。但如果你是一个新项目,我真心建议直接学 v9,不要再接触老 API 了。老 API 里的lv_disp_drv_register、lv_disp_draw_buf_init在 v9 里已经完全换成了新的对象式创建方式。

我后文的代码全部基于 v9,但我会在每个关键节点注明 v8 对应的写法,让你在任何一版源码上都能调通。

2.3 SDL2 下载:选 VC 版而不是 MinGW 版

SDL2 的下载地址是 libsdl.org,进 download 页面,找SDL2-devel-2.30.x-VC.zip这个压缩包,注意一定是 VC 后缀,不是 MinGW 后缀。

原因很简单:Visual Studio 的 C/C++ 编译器和 MinGW 的编译器不是一家人,生成的 .lib 导入库格式不兼容。用 VC 版的 SDL2 库,VS2022 才能直接链接;拿 MinGW 版硬链,最后会甩出一堆 LNK2019 无法解析的外部符号,浪费时间。

解压之后你会看到 include、lib、cmake、bin 四个目录,记住这个结构,后面配置的时候会用到。Win 实际使用中可以只保留 include、lib 和 bin,其余可以删掉。

2.4 不要直接下老模拟器工程,自建工程反而更快

GitHub 上搜lv_sim_visual_studio能找到不少社区维护的模拟器工程,社区版的好处是下载下来直接打开,省去配置过程。但问题在于:这些工程很多停留在 VS2015、VS2017 时代,甚至有 VS2010 时代的旧工程文件,里面写死了平台工具集 v100 或 v110。你用 VS2022 打开,第一眼看到的就是最开头那个报错。

所以我的建议是走一条更稳的路:自己在 VS2022 里新建一个空 C++ 项目,手动把 LVGL 源码加进来,手动配置 SDL2 路径。前期多花 10 分钟,但每一步你都知道为什么,出了问题也知道去哪里排查,比对着一个黑盒旧工程瞎试好多了。

3. 手搓一个最小 VS2022 工程:目录、配置和入口代码

3.1 把 LVGL 源码放进工程

在项目根目录下建一个deps文件夹,把下载好的 LVGL 仓库整个放进去。注意我是把整个仓库放进去,不是只复制 src。因为 demo 目录里也有 .c 文件,头文件路径也依赖固定的目录层级,弄错了后面找头文件很麻烦。

然后在解决方案资源管理器里,右键"源文件" → 添加 → 现有项,进入 LVGL 目录,把src目录下所有 .c 文件都选上。VS 允许一次多选,具体操作是:进入lvgl/src文件夹,Ctrl+A 全选,点击添加。接着把demos目录下的 .c 文件也加进来。如果你想自己写 main.c,那就再添加一个新文件作为入口。

如果你以后要用 git,可以把 deps 下的 LVGL 作为 submodule 管理,但这篇文章不展开。

3.2 lv_conf.h 模板处理

LVGL 的所有配置都集中在lv_conf.h这个文件里。V9 的源码包中不会直接提供一个可以编译用的lv_conf.h,而是给了一个lv_conf.h.in模板,或者在某些版本里叫lv_conf_template.h。

你需要做的是:把这个模板复制一份,重命名为lv_conf.h,放到一个你能在工程里引用的位置。我是直接放在项目根目录下的config文件夹里,和 deps 平级。

打开lv_conf.h之后,有几个开关你需要现在就看一眼:

  • LV_COLOR_DEPTH:建议先设 32,模拟器显示效果最接近真彩。真机如果是 16 位屏,后面再改回来。
  • LV_USE_DEMO_WIDGETS:如果你想用官方控件 demo 验证画面,把它改为 1。
  • LV_USE_LOG:调试阶段改为 1,会输出 LVGL 内部日志。
  • LV_FONT_MONTSERRAT_14等字体开关:默认开启的就够用,中文字库后面会说。

然后关键一步:在 VS 的预处理器定义里加LV_CONF_INCLUDE_SIMPLE=1,这样 LVGL 内部头文件做#include "lv_conf.h"时,编译器的附加包含目录能找到你的配置文件。如果你不想用简单模式,也可以通过LV_CONF_PATH直接指定lv_conf.h的绝对路径,但简单模式最方便。

3.3 包含目录、库目录和附加依赖项

这部分是 VS 配置里最核心的。右键项目 → 属性,确保左上角配置是"所有配置",平台是"x64"(32/64 位问题我会单独说)。

C/C++ → 常规 → 附加包含目录,添加:

deps/lvgl deps/SDL2-2.30.x/include config

注意第一项加的是deps/lvgl而不是deps/lvgl/src,因为很多源码里的头文件是#include "lvgl/lvgl.h"这种带一层目录的写法,所以必须把 lvgl 根目录加进去。

链接器 → 常规 → 附加库目录,添加:

deps/SDL2-2.30.x/lib/x64

链接器 → 输入 → 附加依赖项,添加:

SDL2.lib SDL2main.lib

这里SDL2main.lib很重要,它负责把你写的main函数包装成 Windows 程序真正的入口。没有它,链接阶段大概率报 LNK2019 无法解析的外部符号 main。

3.4 x64 还是 x86:一次到位,别混

VS2022 新建的项目默认可能是 x86 或者 Any CPU,而 SDL2 的库目录里分 x86 和 x64 两个文件夹,如果你下载的 SDL2 包是 VC 版,它的 lib 目录下面会有 x86 和 x64 两个子目录。

我在项目配置里选的是 x64。如果你用 x86,那链接器的附加库目录就要改成deps/SDL2/lib/x86,不能混。混了之后会报 LNK1112:模块计算机类型"x86"与目标计算机类型"x64"冲突。

还有一点:VS 顶部解决方案平台下拉框如果显示的是"Debug Win32",记得打开配置管理器,新建一个 x64 平台,然后把前面所有配置都切到 x64 再编译。

3.5 必须加的预处理器定义

在 C/C++ → 预处理器 → 预处理器定义里,除了默认的WIN32、_DEBUG这些,再加这几个:

_ENABLE_EXTENDED_ALIGNED_STORAGE LV_CONF_INCLUDE_SIMPLE=1

_ENABLE_EXTENDED_ALIGNED_STORAGE这个名字看起来莫名其妙,但你不用它,编译 LVGL 的某些 C++ 头文件时会报一串跟std::aligned_storage相关的错误。这是 VS 2019 之后对 C++17 标准调整导致的,老教程没提是因为当年编译器版本不一样。

另外建议加一个NOMINMAX,可以避免 Windows.h 里的min/max宏干扰 LVGL 的代码逻辑,这个坑比较隐蔽,但加上没坏处。

3.6 C++ 语言标准

在 C/C++ → 语言 → C++ 语言标准里,选ISO C++17 标准 (/std:c++17)。LVGL 本身是 C 代码,VS 会用 C 编译器编译 .c 文件,但这个选项会影响包含进来的某些 Windows 头文件和 SDL 头文件的解析方式,C++17 兼容性最好。

3.7 把 SDL2.dll 带到生成目录

如果编译通过了,运行时报"由于找不到 SDL2.dll,无法继续执行代码",那是因为 SDL2 的动态库不在 exe 所在目录。

最简单粗暴的办法是手动把deps/SDL2/bin/SDL2.dll复制到x64/Debug或x64/Release目录。但我建议用"后期生成事件"自动复制:

右键项目 → 属性 → 生成事件 → 后期生成事件命令行,填:

xcopy /y "$(ProjectDir)deps\SDL2\bin\SDL2.dll" "$(OutDir)"

这样每次编译完都会自动把 dll 拷到输出目录,省得忘记。

3.8 最简可用代码骨架

配置好之后,先写一个最简代码确认环境通不通。新建 main.cpp,注意 VS 会按扩展名决定编译方式,.cpp文件可以编译 C 代码,没问题。

#include <SDL.h> #include "lvgl/lvgl.h" #define HOR_RES 800 #define VER_RES 480 int main(int argc, char* argv[]) { SDL_Init(SDL_INIT_VIDEO); SDL_Window* win = SDL_CreateWindow( "LVGL Simulator", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, HOR_RES, VER_RES, SDL_WINDOW_SHOWN); SDL_Renderer* renderer = SDL_CreateRenderer(win, -1, 0); SDL_Texture* texture = SDL_CreateTexture( renderer, SDL_PIXELFORMAT_RGB565, SDL_TEXTUREACCESS_STREAMING, HOR_RES, VER_RES); lv_init(); lv_display_t* disp = lv_display_create(HOR_RES, VER_RES); lv_display_set_resolution(disp, HOR_RES, VER_RES); lv_obj_t* label = lv_label_create(lv_screen_active()); lv_label_set_text(label, "Hello LVGL + VS2022"); lv_obj_center(label); while (1) { SDL_Event e; while (SDL_PollEvent(&e)) { if (e.type == SDL_QUIT) return 0; } lv_timer_handler(); SDL_Delay(5); } }

这个代码还没有写 flush 回调,所以窗口会出现,但画面上不会正常显示 LVGL 内容。这一步只是验证编译、链接和 SDL 初始化都正常。等第五章把 flush 和输入补上,画面才会真正亮起来。

如果你走到这一步编译还有错误,先别急,第四章把最常见的报错一次列清楚。

4. 编译失败日志逐条看:v100 工具集、LNK2019 和其他运行期地雷

4.1 平台工具集 v100 报错的两个解决路径

开头已经提到了这个报错:用旧工程打开时,VS2022 显示"无法找到 Visual Studio 2010 的生成工具(平台工具集 ='v100')"。

原理是这样的:VS 的每个 C++ 项目文件.vcxproj里都记录了当年创建这个工程时用的编译工具版本,叫平台工具集。VS2010 对应 v100,VS2012 对应 v110,VS2022 对应的是 v143。VS 只会默认安装当前版本的工具集,不会自动带老版本,所以用 VS2022 打开写着 v100 的工程,它就找不到对应的编译器了。

解决办法有两个:

一是改工程属性:右键项目 → 属性 → 常规 → 平台工具集,从 v100 改成 Visual Studio 2022 (v143)。如果改了之后还提示 Windows SDK 版本问题,再把常规里的 Windows SDK 版本改成"10.0(最新已安装版本)"。

二是去 VS Installer 里勾选"MSVC v100 生成工具"组件。但我不推荐,理由很简单:v100 是 VS2010 时代的编译器,对 C99/C11 的支持远不如现代编译器,LVGL 源码里有些写法它在标准模式下未必支持。而且为跑一个 demo 去装十年前的工具集,没有任何收益。

如果你是自己新建的工程,正常不会遇到这个报错。所以我一直强调,宁可前期多花十分钟自己配工程,也不要贪快直接下老工程。

4.2 LNK2019:无法解析的外部符号 main / SDL_main

这个报错有两种常见形态:

一种是LNK2019 无法解析的外部符号 main。原因通常是:你没有链接SDL2main.lib,同时工程设置成了 Windows 子系统。Windows 子系统希望找到一个 WinMain 作为入口,但你写的是 main,对不上。解决方法是去附加依赖项里加上 SDL2main.lib,或者把链接器 → 系统 → 子系统改成"控制台"。

另一种形态是无法解析的外部符号 SDL_main。这通常是因为你链接了 SDL2main.lib,但自己的 main 函数写成了int main(),参数列表不完整。SDL2 的入口包装机制要求你必须写标准签名:

int main(int argc, char* argv[])

如果项目里已经有别人写好的main函数,并且你重新定义了main,也会出现奇怪冲突。所以自建工程时一定要保证项目里只有一个 main。

4.3 _ENABLE_EXTENDED_ALIGNED_STORAGE 缺失导致的报错

如果你直接下载官方 LVGL 的某个 demo 工程在 VS2022 上编译,有很大概率遇到类似:

error C2338: static_assert failed: 'std::aligned_storage<Len, Align>::value'

或者一堆指向type_traits的模板错误。这个问题的根源是 MSVC 的 STL 在 C++17 模式下对std::aligned_storage增加了新的对齐校验,而某些库代码(比如 LVGL 或者你引用的 C++ 封装)还在用旧式的对齐方式。

解决办法就是在预处理器定义里加_ENABLE_EXTENDED_ALIGNED_STORAGE=1。这个宏是微软官方提供的"兼容开关",看到它别慌,加了就完事。

4.4 LNK1112:32 位库链接到了 64 位目标

如果你在 x64 平台下,附加库目录却指向 SDL2 的 x86 版本,VS 链接时会报 LNK1112。反过来也一样。

检查路径是不是真的指向了SDL2/lib/x64,并且解决方案平台是 x64,这个错误就消失了。有一个小技巧:在链接器的"命令行"页面里加上/VERBOSE:LIB,可以看到当前实际链接了哪些 .lib 文件,能帮你核对路径。

4.5 运行报错:找不到 SDL2.dll

编译链接都过了,点运行弹窗说缺 dll。这个问题前面讲过,把 SDL2.dll 复制到输出目录,或者配置后期生成事件自动拷贝。

多说一句:如果你把 SDL2.dll 复制过去了还是报找不到,很可能你打开的是旧版本 SDL2 而项目链接的是新版本 SDL2.lib,或者 dll 放错到了 Release 目录而你在跑 Debug。建议看输出窗口里 exe 的完整路径在哪里,再对照 dll 是否在同一个目录。

4.6 画面花屏或颜色错乱:LV_COLOR_DEPTH 和 SDL 像素格式不一致

这个坑非常隐蔽,因为它不会导致编译报错,只有画面颜色诡异:文字变成紫色,背景变成绿色之类。

原因是 LVGL 输出的像素格式和 SDL 纹理期望的像素格式不匹配。LVGL 根据LV_COLOR_DEPTH决定每个像素是 16 位 RGB565 还是 32 位 ARGB8888,而 SDL 纹理创建时也需要指定SDL_PIXELFORMAT_RGB565或SDL_PIXELFORMAT_ARGB8888。

如果你的LV_COLOR_DEPTH是 32,但 SDL 纹理创建时用 RGB565,画面就会花。

经验做法是两个地方统一按条件编译:

#if LV_COLOR_DEPTH == 16 SDL_PixelFormatEnum fmt = SDL_PIXELFORMAT_RGB565; #else SDL_PixelFormatEnum fmt = SDL_PIXELFORMAT_ARGB8888; #endif

这样改了 LV_COLOR_DEPTH 之后,SDL 这边自动跟随,不会再花屏。

4.7 编译错误满天飞,但其实是头文件路径不对

还有一种情况是:代码看起来没问题,但 VS 报一堆找不到lvgl.h、找不到SDL.h的错误。这种基本都是附加包含目录没有写对。

VS 不会自动感知你项目里某个文件夹里有 lvgl,你必须在附加包含目录里把deps/lvgl、deps/SDL2/include、config三个路径都加进去。路径最好用相对路径,以$(ProjectDir)开头,方便换电脑后还能编译。

5. 让 UI 真正亮起来:显示缓冲、触摸输入与主循环的配合

5.1 初始化顺序为什么这么重要

只要你接触过图形库,就会发现初始化顺序永远是最容易出事的地方。LVGL 模拟器和真实 LCD 驱动的初始化顺序逻辑是一样的:先准备好"显示硬件",再初始化 UI 系统。

具体来说,SDL 窗口、渲染器、纹理必须在lv_init()之前创建好。为什么?因为 LVGL 一旦开始跑,第一帧就有可能调用你注册的 flush 回调,而 flush 回调里要用到 SDL 纹理。如果纹理还没创建,回调就是空指针解引用,程序直接崩溃。

推荐的初始化顺序:

  1. SDL_Init(SDL_INIT_VIDEO)
  2. 创建 SDL 窗口、渲染器、纹理
  3. lv_init()
  4. 创建 LVGL 显示设备,设置缓冲区和 flush 回调
  5. 创建 LVGL 输入设备,设置鼠标读取回调
  6. 加载界面代码(demo 或你自己的 UI)
  7. 进入主循环

5.2 显示缓冲区的设置逻辑

LVGL 是一个局部刷新图形库,它不需要把整个屏幕的像素都保存下来,它只维护一个较小的绘制缓冲区,每次只画画面变化的部分,然后通过 flush 回调把这块区域的像素推给显示驱动。

lv_display_set_buffers就是干这个的。它接收两个缓冲区指针,一个用于绘制,一个用于交替刷新,即双缓冲机制。缓冲区的大小决定了每帧最大绘制面积。PC 上内存充裕,开个800 * 100像素的缓冲区就够用了:

static lv_color_t buf1[HOR_RES * 100]; static lv_color_t buf2[HOR_RES * 100]; lv_display_set_buffers(disp, buf1, buf2, sizeof(buf1), LV_DISPLAY_RENDER_MODE_PARTIAL);

注意buf1和buf2必须是静态或全局的,不能是局部数组。栈空间默认只有 1MB,而800 * 100 * sizeof(lv_color_t)在 32 位颜色下大约是 320KB,两个就是 640KB,放栈上必然溢出。

如果你换成 16 位颜色,一个缓冲区就是 160KB,两个 320KB,栈还是有可能爆,所以一律放静态区最稳。

LV_DISPLAY_RENDER_MODE_PARTIAL表示渲染部分区域,缓冲区只需要覆盖屏幕的一部分即可。如果缓冲区尺寸等于全屏,也可以设为LV_DISPLAY_RENDER_MODE_FULL,但在模拟器里没必要。

5.3 flush 回调到底做了什么

flush 回调是 LVGL 与显示驱动之间的唯一桥梁。每次 LVGL 完成一块区域的绘制,就把这块区域的像素数据和坐标范围通过回调传给你,你需要把像素数据送出去显示。

在模拟器里,这个动作分为三步:

static void lvgl_flush_cb(lv_display_t* display, const lv_area_t* area, uint8_t* px_map) { int w = lv_display_get_horizontal_resolution(display); int h = lv_display_get_vertical_resolution(display); SDL_UpdateTexture(sdl_texture, NULL, px_map, w * sizeof(lv_color_t)); SDL_RenderClear(sdl_renderer); SDL_RenderCopy(sdl_renderer, sdl_texture, NULL, NULL); SDL_RenderPresent(sdl_renderer); lv_display_flush_ready(display); }

第一步SDL_UpdateTexture把 LVGL 像素数据拷贝到 SDL 纹理里。第二步SDL_RenderClear清空渲染器,SDL_RenderCopy把纹理绘制到渲染器上,SDL_RenderPresent把画面真正送到窗口。第三步极其关键:调用lv_display_flush_ready(display),告诉 LVGL 这块区域已经刷完了。

如果你漏了第三步,LVGL 会认为显示驱动还没有准备好接收下一块数据,画面就会卡在某一帧上,看起来像死机。这是所有 LVGL 移植中最高频的错误,没有之一。

我在 v8 时代犯过这个错,当时现象是程序不崩,但屏幕刷一两屏就再也不动了。查了好久才发现是lv_disp_flush_ready没调用。V9 里这个函数改名成了lv_display_flush_ready,原理完全一样。

5.4 鼠标输入:把 SDL 事件翻译成 LVGL 指针事件

模拟器里鼠标就是触摸屏。创建一个 LVGL 输入设备,然后提供一个读取回调。LVGL 的主循环会周期性地调用这个回调,看看有没有新的输入事件。

static void mouse_read_cb(lv_indev_t* indev, lv_indev_data_t* data) { int x = 0, y = 0; Uint32 buttons = SDL_GetMouseState(&x, &y); >lv_indev_t* mouse = lv_indev_create(); lv_indev_set_type(mouse, LV_INDEV_TYPE_POINTER); lv_indev_set_read_cb(mouse, mouse_read_cb);

注意 v8 时代创建输入设备是用lv_indev_drv_register()加一个lv_indev_drv_t结构体,v9 改成了对象式创建。如果你照着 v8 教程抄到这里,编译会报错,这不代表你的配置有问题,只是 API 变了。

5.5 主循环:别让 SDL 事件阻塞 LVGL

主循环是整个模拟器的心脏。大多数人对图形库主循环的理解是"处理事件",但 LVGL 这种非阻塞式 UI 库要求事件处理和 UI 刷新交替进行:

bool running = true; while (running) { SDL_Event e; while (SDL_PollEvent(&e)) { if (e.type == SDL_QUIT) running = false; } lv_timer_handler(); SDL_Delay(5); }

SDL_PollEvent是非阻塞的,它会尽可能多地取事件,取完就返回。处理完事件后调用lv_timer_handler(),LVGL 内部的定时器、动画、重绘都在这个调用里被触发。最后SDL_Delay(5)把帧率控制在 200 FPS 以内,避免空转把 CPU 吃满。

千万不要用SDL_WaitEvent去替代SDL_PollEvent。那样的话,只要鼠标不动、没有任何事件,主循环就阻塞住了,LVGL 的动画和刷新也跟着停掉,画面会卡死。

5.6 完整可运行代码

把前面这些片段组合起来,就是一个完整的 VS2022 + LVGL9 + SDL2 模拟器。我贴一个可以直接编译运行的版本:

#include <SDL.h> #include "lvgl/lvgl.h" #include "lvgl/demos/lv_demos.h" #define HOR_RES 800 #define VER_RES 480 static SDL_Window* sdl_window; static SDL_Renderer* sdl_renderer; static SDL_Texture* sdl_texture; static lv_color_t buf1[HOR_RES * 100]; static lv_color_t buf2[HOR_RES * 100]; static void lvgl_flush_cb(lv_display_t* display, const lv_area_t* area, uint8_t* px_map) { int w = lv_display_get_horizontal_resolution(display); int h = lv_display_get_vertical_resolution(display); SDL_UpdateTexture(sdl_texture, NULL, px_map, w * sizeof(lv_color_t)); SDL_RenderClear(sdl_renderer); SDL_RenderCopy(sdl_renderer, sdl_texture, NULL, NULL); SDL_RenderPresent(sdl_renderer); lv_display_flush_ready(display); } static void mouse_read_cb(lv_indev_t* indev, lv_indev_data_t* data) { int x = 0, y = 0; Uint32 buttons = SDL_GetMouseState(&x, &y); >

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询