2. 开发环境搭建:从零开始,让代码跑起来
说实话,很多初学者在环境搭建这一步就被劝退了。我当年第一次搞嵌入式 HMI 开发,光配交叉编译链就折腾了两天。嗯,今天咱们把这层窗户纸捅破,你跟着我一步步来,半小时内就能看到第一个“Hello World”在屏幕上亮起来。
2.1 选哪个框架?Qt for MCU 还是 LVGL?
这是你面临的第一个选择题。我直接给你结论:
| 对比项 | Qt for MCU | LVGL |
|---|---|---|
| 授权方式 | 商业授权(贵) | MIT 开源(免费) |
| 资源占用 | RAM ≥ 256KB,Flash ≥ 2MB | RAM ≥ 8KB,Flash ≥ 64KB |
| 开发语言 | C++ / QML | C(纯 C) |
| 学习曲线 | 陡峭(需要懂 Qt 生态) | 平缓(上手快) |
| 适合场景 | 高端仪表、车载、工业 HMI | 中低端 MCU、小屏、快速原型 |
我个人习惯用 LVGL 做教学,原因很简单:免费、轻量、社区活跃。你拿它练手,成本几乎为零。等后面做产品级项目时,再根据硬件选型决定是否切到 Qt。
2.2 安装 LVGL 模拟器环境(基于 SDL2)
咱们先从 PC 模拟器开始。为什么?因为每次改代码都烧录到开发板上,太慢了。在 PC 上跑模拟器,改完立刻看到效果,效率翻倍。
步骤一:安装 SDL2 库
LVGL 的模拟器依赖 SDL2 做显示和输入。不同系统命令不一样:
- Ubuntu/Debian:
sudo apt install libsdl2-dev - macOS:
brew install sdl2 - Windows: 去 libsdl.org 下载开发库,解压到项目目录
步骤二:克隆 LVGL 模拟器项目
git clone https://github.com/lvgl/lv_port_pc_eclipse.git
cd lv_port_pc_eclipse
这个项目已经帮你配好了 LVGL 源码和 SDL2 驱动。你只需要编译就行。
步骤三:编译并运行
mkdir build && cd build
cmake ..
make -j4
./demo
如果一切顺利,你会看到一个灰色窗口弹出来,里面有几个 LVGL 的示例控件。恭喜,模拟器环境搭好了!
2.3 配置交叉编译工具链(ARM GCC)
模拟器跑通了,接下来要让代码真正在 MCU 上跑。这需要交叉编译——说白了,就是在 PC 上编译出 ARM 芯片能执行的机器码。
下载 ARM GCC 工具链
我推荐用 ARM 官方维护的 GNU Arm Embedded Toolchain:
- 下载地址:
developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain - 选择 gcc-arm-none-eabi 版本(注意是 none-eabi,不是 linux-gnu)
解压后,把 bin 目录加到系统 PATH 里。验证一下:
arm-none-eabi-gcc --version
# 输出类似:arm-none-eabi-gcc (GNU Tools for Arm Embedded Processors) 10.3.1 20210621
arm-none-eabi-,这意味着它生成的是裸机代码,没有操作系统依赖。如果你的 MCU 跑 FreeRTOS 或 RT-Thread,依然用这个工具链。
配置 CMake 交叉编译文件
在 LVGL 项目中,创建一个 arm-gcc.cmake 文件:
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(CMAKE_C_COMPILER arm-none-eabi-gcc)
set(CMAKE_CXX_COMPILER arm-none-eabi-g++)
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
编译时指定这个文件:
cmake -DCMAKE_TOOLCHAIN_FILE=arm-gcc.cmake ..
make
2.4 搭建 QEMU 模拟器(可选但推荐)
如果你手头没有开发板,QEMU 可以模拟一个 ARM 开发板环境。我经常用它做快速验证。
安装 QEMU
sudo apt install qemu-system-arm
准备一个裸机程序
以 STM32F407 为例,QEMU 支持 netduinoplus2 开发板模型。你需要准备:
- 启动文件
startup_stm32f407.s - 链接脚本
stm32f407.ld - 主程序
main.c
编译后生成 main.elf,然后用 QEMU 运行:
qemu-system-arm -M netduinoplus2 -kernel main.elf -nographic
-serial stdio 参数,可以把 MCU 的串口输出重定向到终端。这样你就能看到 printf 打印了。
2.5 第一个“Hello World”程序
环境都搭好了,咱们写个最简单的 LVGL 程序,在屏幕上显示“Hello World”。
代码示例
#include "lvgl.h"
#include "lv_drivers/display/sdl.h"
int main(void)
{
/* 初始化 LVGL */
lv_init();
/* 初始化 SDL 显示驱动 */
sdl_init();
/* 创建一个标签控件 */
lv_obj_t *label = lv_label_create(lv_scr_act());
lv_label_set_text(label, "Hello World!");
lv_obj_center(label);
/* 主循环 */
while(1) {
lv_task_handler();
SDL_Delay(5);
}
return 0;
}
编译运行
mkdir build && cd build
cmake ..
make
./hello_world
你会看到窗口中央显示“Hello World!”。嗯,第一个 HMI 程序跑起来了。
lv_label_create 在屏幕上创建一个标签对象,lv_label_set_text 设置显示内容,lv_obj_center 让它居中。主循环里 lv_task_handler() 负责处理 LVGL 的内部任务,比如刷新显示、处理输入。
2.6 本章知识体系总览
下面这张图帮你理清整个环境搭建的脉络:
2.7 常见问题与避坑指南
- SDL2 窗口黑屏: 检查是否调用了
sdl_init(),以及主循环里有没有SDL_Delay()。我曾经忘了加延时,CPU 跑满导致窗口无响应。 - 交叉编译链接报错: 确认链接脚本里的 Flash 和 RAM 地址与你的 MCU 型号匹配。STM32F407 的 Flash 起始地址是 0x08000000,RAM 是 0x20000000。
- QEMU 无法启动: 试试加
-d in_asm参数查看反汇编输出,看程序是不是卡在启动文件里了。
好了,环境搭好了,第一个程序也跑起来了。接下来你就可以开始真正设计仪表界面了。记住,开发环境就像工具箱,工具顺手了,活才能干得快。