X-TRACK 开发指南:基于 Visual Studio 的 LVGL Windows PC 模拟器构建、运行与 HAL 仿真原理

原创2026-10-05 23:58:431,748 阅读
文章标签:智能硬件嵌入式硬件开发

X-TRACK 开发指南:基于 Visual Studio 的 LVGL Windows PC 模拟器构建、运行与 HAL 仿真原理

本文以 Software/X-Track/Simulator/ 目录下的官方 README 为骨架,结合 X-TRACK 仓库中的模拟器工程源码与配置,完整讲解这套基于 Visual Studio 的 LVGL PC 模拟器:如何克隆、构建、运行,以及 X-TRACK 码表应用如何在其上通过 HAL 仿真层实现 GPS 轨迹回放、时钟、电池、SD 卡等外设的“无硬件开发”体验。读完本文,你将掌握模拟器的完整使用流程,并理解其底层驱动与仿真机制,可直接用于 X-TRACK 固件的日常开发与调试。

X-TRACK 是一个支持离线地图与轨迹记录的 GPS 自行车码表项目,其 UI 基于 LVGL 构建。为了让开发者在不焊接硬件、不烧录固件的条件下快速迭代界面与业务逻辑,项目在 Software/X-Track/Simulator/ 目录下内置了一套预配置好的 Visual Studio 工程,把整套 LVGL 码表应用原封不动地搬到了 Windows 桌面窗口里运行。

LVGL PC 模拟器运行界面,展示 LVGL 控件演示页(Controls),右下角实时显示 62 FPS、3% CPU 的性能数据

一、模拟器是什么:一个零依赖的 Visual Studio 工程

按照官方 README 的说明(Software/X-Track/Simulator/README.md),这是一套为在 Windows PC 上体验 LVGL 而预配置的 Visual Studio 工程,其核心特点如下:

  • 依赖极简:整个工程只依赖 Win32 API、C Runtime 与 C++ STL,不需要安装任何第三方 GUI 框架或额外的运行库,克隆后即可编译。
  • 维护版本:工程目前基于 Visual Studio 2019 维护;理论上 Visual Studio 2017 也能编译,但官方不对此版本提供积极支持,因此遇到问题应先在 VS2019 下复现验证。
  • 明确边界:该工程面向 Visual Studio 2019 的 MSVC 工具链,不适用于 Visual Studio Code;如果需要在 VS Code 或其他环境适配 LVGL Windows 应用,请参考 LVGL 官方的 lv_port_windows 移植方案(本文不再赘述)。

版本沿革说明:该模拟器模板前身名为 lv_sim_visual_studio_sdl,后因不再依赖 SDL 而更名为 lv_sim_visual_studio。X-TRACK 仓库将其作为子模块随项目一起维护,因此你在仓库内看到的目录结构即模板与 X-TRACK 定制内容的合集。

支持的功能特性

官方 README 列出了该模拟器模板支持的能力,结合仓库文件可逐条核实:

特性 说明 仓库佐证
仅依赖 Win32 API / C Runtime / C++ STL 无第三方 GUI 依赖,可独立编译 LVGL.Simulator.cpp 仅引入 <Windows.h>、lvgl、win32drv 等模块
原生支持 x86 / x64 / ARM / ARM64 四种架构 × Debug/Release 共 8 个目标 BuildAllTargets.proj 中显式列出 8 组平台配置
支持 VC-LTL 工具链 可编译出接近 MinGW 体积的二进制 Mile.Project.Cpp.VC-LTL.props
Per-monitor DPI Aware 高分屏下窗口按显示器缩放 工程属性清单(LVGL.Simulator.manifest)
支持 Windows 键盘与鼠标滚轮事件 在 HAL 层接入标准输入设备 win32drv 驱动 + lv_win32_add_all_input_devices_to_group() 调用

二、获取源码:克隆与子模块管理

与许多依赖 LVGL 上游仓库的工程一样,模拟器工程通过 git submodule(子模块) 引用 lvgl、lv_drivers、lv_fs_if 等必要仓库。普通 git clone 不会自动拉取子模块,需要额外操作。官方 README 提供了三种方式:

方式一:一步到位克隆(含全部子模块)

git clone --recurse-submodules https://github.com/lvgl/lv_sim_visual_studio.git

方式二:先克隆主仓库,再补拉子模块

git clone https://github.com/lvgl/lv_sim_visual_studio.git
cd lv_sim_visual_studio
git submodule update --init --recursive

方式三:保持克隆与上游同步

在主仓库根目录依次执行两步——先拉主仓库更新(含子模块引用的变化),再让子模块代码跟进:

git pull
git submodule update --init --recursive

如果你 fork 了该仓库,从上游同步更新需要更复杂的流程(涉及上游 remote 与 rebase),本文不再展开。

就 X-TRACK 仓库而言,模拟器依赖的 lvgl、lv_drivers、lv_fs_if 已以子目录形式随仓库一同分发(见 Software/X-Track/Simulator/LVGL.Simulator/ 下的 lvgl/、lv_drivers/、lv_fs_if/),因此克隆 X-TRACK 仓库后即可直接打开工程。

三、构建与运行:从解决方案到启动窗口

3.1 图形界面方式(推荐开发日常使用)

官方 README 给出的标准流程如下:

  1. 用 Visual Studio 2019 打开解决方案文件 LVGL.Simulator.sln;
  2. 在“解决方案资源管理器”中把 LVGL.Simulator 项目设为启动项目;
  3. 点击工具栏上的 “Local Windows Debugger”(本地 Windows 调试器) 按钮;
  4. 工程会被自动编译并在一个 cmd 窗口中启动模拟器窗口。

窗口出现后即可用鼠标模拟触摸/点击、用键盘与滚轮模拟编码器输入,直接操作 X-TRACK 的整套码表 UI。

3.2 命令行方式:一键批量构建全部目标

仓库额外提供了命令行批量构建脚本 BuildAllTargets.cmd,其工作流程如下:

  1. 通过 vswhere 自动定位 Visual Studio 安装目录;
  2. 调用 vcvarsall.bat x86 初始化 MSVC 构建环境;
  3. 执行 MSBuild -m BuildAllTargets.proj,并行构建 BuildAllTargets.proj 中声明的全部 8 个目标:Debug/Release × x86/x64/ARM/ARM64。

该脚本适合 CI 或需要一次性产出多平台二进制(例如为 ARM64 平板准备模拟器版本)的场景。日常单平台调试仍建议直接用 3.1 节的 IDE 流程。

四、入口与主循环:模拟器里跑起来的码表应用

打开 LVGL.Simulator.cpp,可以看到模拟器的完整启动链路(main 函数,L44-L76):

#define SCREEN_HOR_RES  240
#define SCREEN_VER_RES  240

int main()
{
    lv_init();                     // 1. 初始化 LVGL 内核
    lv_fs_if_init();               // 2. 注册文件系统接口(映射到 PC 目录)
    lv_win32_init(..., SW_SHOW, SCREEN_HOR_RES, SCREEN_VER_RES, ...); // 3. 创建 240x240 模拟窗口
    lv_win32_add_all_input_devices_to_group(NULL);   // 4. 键盘/鼠标/滚轮接入 LVGL group

    HAL::HAL_Init();               // 5. 初始化模拟 HAL(蜂鸣器、音频、GPS)
    App_Init();                    // 6. 启动 X-TRACK 码表应用

    while (!lv_win32_quit_signal)  // 7. 主循环
    {
        lv_timer_handler();        //    LVGL 定时器/渲染
        HAL::HAL_Update();         //    模拟外设数据更新(IMU/MAG/音频)
        Sleep(1);
    }

    App_Uninit();                  // 8. 退出清理
    return 0;
}

几个值得注意的实现细节:

  • 窗口分辨率与真机一致:SCREEN_HOR_RES / SCREEN_VER_RES 均为 240,与 X-TRACK 真机屏幕分辨率一致,保证 UI 布局、字体、触控热区在模拟器与真机间完全对齐。
  • 模拟器侧不再运行官方 demo:README 中提到的 lv_demo_widgets 等测试应用是上游模板的默认行为;在 X-TRACK 仓库中,main() 已被替换为 HAL::HAL_Init() + App_Init(),直接启动真实的码表应用(含 Dialplate 表盘、LiveMap 地图、Startup 开机动画、SystemInfos 系统信息等页面,见 Software/X-Track/USER/App/)。如果你想改回官方 demo 或加入自己的测试代码,可在此处替换 App_Init() 为相应 demo 函数。
  • LVGL 配置对齐 PC 平台:模拟器使用独立的 lv_conf.h,其中 LV_TICK_CUSTOM 在 _WIN32 下通过 timeGetTime() 提供毫秒级系统时钟(L102-L105),LV_DISP_DEF_REFR_PERIOD 为 16ms、LV_INDEV_DEF_READ_PERIOD 为 30ms,与真机保持一致以复现相同的刷新节奏。

五、HAL 仿真层:在 PC 上模拟整个码表外设

X-TRACK 的硬件抽象层(HAL)在模拟器中有独立实现,位于 LVGL.Simulator/HAL/,由 HAL.h 统一暴露与真机相同的接口。从 HAL.cpp 可看到初始化与周期更新各包含哪些模块:

void HAL::HAL_Init()   { Buzz_init(); Audio_Init(); GPS_Init(); }
void HAL::HAL_Update() { IMU_Update(); MAG_Update(); Audio_Update(); }

各仿真模块的行为与真机的差异如下:

模块 模拟器行为 实现文件
时钟 Clock 直接读取 PC 系统时间(time()/localtime()),Clock_SetInfo 仅打印设置日志 HAL_Clock.cpp
电池 Power 通过 Win32 GetSystemPowerStatus() 读取 PC 电源状态作为电量与充电状态,电压固定模拟为 3700mV HAL_Power.cpp
SD 卡 恒返回就绪,容量模拟为 32MB 的 SDHC 卡(32 * 1024 MB 即为 32GB 显示值),插入/拔出事件为空实现 HAL_SD_CARD.cpp
编码器 Encoder 空实现(返回 0/False),PC 上的交互由鼠标与键盘经 lv_win32 驱动接管 HAL_Encoder.cpp
GPS 核心仿真:回放 GPX 轨迹文件,详见下文 HAL_GPS.cpp

模拟器上的 SD 卡文件读写并非真的空转:LVGL 文件系统层通过 lv_fs_if 的 PC 驱动把 / 根路径映射到工程根目录(见 lv_fs_if.h 与 lv_fs_pc.c),路径拼接规则为 sprintf(buf, LV_FS_PC_PATH "/%s", path),LV_FS_PC_PATH 默认为 ../../../../(指向仓库根目录)。因此把地图瓦片、SystemSave.json、GPX 轨迹文件放到仓库根目录的对应位置,模拟器就能像读真机 SD 卡一样访问它们——这是无硬件预览离线地图功能的关键前提。

六、GPS 轨迹回放:最值得研究的模拟机制

模拟器最亮眼的仿真当属 GPS 模块。它不再“随机乱动”,而是解析一份 GPX 轨迹文件,按真实轨迹点顺序驱动码表的速度、航向与位置,让 LiveMap 页面能像真实骑行一样画出完整轨迹。

6.1 数据来源与刷新节奏

HAL_GPS.cpp 中的关键定义:

#define CONFIG_TRACK_VIRTUAL_GPX_FILE_PATH    "/TRK_EXAMPLE.gpx"
  • GPS_Init() 通过 lv_fs_open 打开该 GPX 文件,解析成功则置 isVaild = true,并模拟出 10 颗可见卫星(satellites = 10);
  • 随后创建一个 LVGL 定时器,按 CONFIG_GPS_REFR_PERIOD 周期调用 GPS_Update() 推进轨迹点;
  • 刷新周期定义在 Software/X-Track/USER/App/Config/Config.h:真机(ARDUINO)为 1000ms,PC 模拟器为 10ms——即模拟器以 100 倍频率快速“跑完”整条轨迹,方便快速预览全程效果。

6.2 逐点推进与速度、航向计算

GPS_Update()(HAL_GPS.cpp L166-L219)的核心逻辑:

  1. 调用 gpxParser.ReadNext(&point) 读取下一个轨迹点;
  2. 首次读到有效经纬度时直接作为当前定位(isReset 复位逻辑);
  3. 之后每个点计算与上一位置的大圆距离 distanceBetween(),结合时间差 diffTime 换算速度并乘以 3.6 转为 km/h:
    gpsInfo.speed = (float)(distance / diffTime) * 3.6f;
    
  4. 用 courseTo() 依据两点经纬度计算航向角(北为 0°,顺时针),写入 gpsInfo.course;
  5. 同步更新经纬度与海拔(longitude/latitude/altitude);
  6. 读到文件末尾(PARSER_FLAG_EOF)时把文件指针复位到开头、重置 isReset,轨迹循环回放。

时间差有两个来源:若 GPX 点带时间戳,则用 Clock_GetDiffTime() 计算两点真实时间差(difftime/mktime,对应更新日志中“支持获取 GPX 点之间的时间差”);否则回退到固定刷新周期 CONFIG_GPS_REFR_PERIOD / 1000.0 秒。diffTime 过小(< 0.0001)时保留上次速度,避免除零产生 inf(对应更新日志 v1.7 中“修复 diffTime = 0 导致 speed = inf”)。

6.3 默认定位与可配置项

当 GPX 解析失败或尚未定位时,模拟器使用 Config.h 中的默认经纬度作为初始位置:

#define CONFIG_GPS_LONGITUDE_DEFAULT   116.391332f   // 北京天安门附近
#define CONFIG_GPS_LATITUDE_DEFAULT    39.907415f

这两项与 DP_SysConfig 中系统配置的默认经纬度一致(见 DP_SysConfig.cpp),即开机后未定位前 LiveMap 默认显示的位置。

6.4 准备你自己的回放轨迹

想要用真实骑行数据测试,只需:

  1. 将任意 GPX 轨迹文件命名为 TRK_EXAMPLE.gpx;
  2. 放置到 LV_FS_PC_PATH 指向的仓库根目录(与 TRK_EXAMPLE.gpx 对应的实际路径由模拟器文件系统根目录决定,即仓库根目录下的 Software/ 同级的根路径);
  3. 重启模拟器,GPS_Init() 解析成功后码表即开始沿轨迹运动。

轨迹点带时间戳时回放会严格遵循真实时间间隔;不带时间戳时则以 10ms 固定节奏匀速推进。这正是 X-TRACK 更新日志中“HAL_GPS 支持读取 csv 格式文件在 PC 上模拟位置变化”“支持航向和速度模拟”的最终实现形态。

七、版本与同步注意事项

官方 README 的最后一节提醒了子模块版本维护策略,结合仓库现状可总结为以下三点:

  • 子模块引用更新时机:模板仓库会在 lvgl 发布新的主版本后,为子模块引用打上匹配的版本标签并更新;日常的小版本更新也会被跟进(本仓库当前锁定 lvgl v8.1.1-dev,见 lv_conf.h 文件头注释)。
  • 自行拉取子模块修复:如果上游子模块有你需要的新修复,需自行 git submodule update 更新引用;若上游新增/删除了源文件,VS 工程(.vcxproj)通常需要同步调整文件列表,否则会编译失败——这一点从仓库历次“同步 lvgl 主程序 + 调整工程”的提交记录可以佐证。
  • 对本仓库的实操意义:X-TRACK 开发者日常应保持 git pull 后再 git submodule update --init --recursive,确保 lvgl 等子模块与主仓库引用的 commit 一致,避免“主仓库已更新、子模块仍是旧代码”导致的链接或行为差异。

八、小结

这套基于 Visual Studio 的 LVGL PC 模拟器为 X-TRACK 提供了完整的无硬件开发闭环:

  • 工程层面:一个零第三方依赖的 VS2019 解决方案,支持 x86/x64/ARM/ARM64 多平台构建,图形界面与 BuildAllTargets.cmd 命令行两种构建方式并行;
  • 应用层面:App_Init() 直接启动真实码表应用,240×240 窗口与真机分辨率一致,鼠标/键盘/滚轮完整映射输入;
  • 外设层面:HAL 仿真层让时钟、电池、SD 卡、GPS 全部可用 PC 资源或文件模拟——尤其 GPS 通过回放 GPX 轨迹提供真实的速度、航向与位置变化,配合 LV_FS_PC_PATH 的目录映射,离线地图与轨迹记录功能无需任何硬件即可完整预览与调试。

对希望参与 X-TRACK 开发的工程师而言,掌握本模拟器即可在 PC 上完成绝大多数 UI 与业务逻辑的开发验证;只有涉及真实传感器、编码器手感、背光与功耗等硬件耦合环节时,才需要回到 AT32F403A / AT32F435 真机平台(MDK-ARM_F403A、MDK-ARM_F435)验证。

登录后查看全文
X-TRACK