1. LVGL初探:从认识到了解,再到跑起第一个程序

各位同学好,我是老周。做嵌入式GUI开发有些年头了,从最早的裸机画点、画线,到后来用各种商业GUI库,踩过的坑真不少。今天咱们开始聊LVGL——这个近几年在嵌入式圈子里特别火的开源图形库。

说实话,我第一次接触LVGL是在一个智能家居项目里。当时客户要求界面要“像手机一样流畅”,老板又不想花钱买商业授权。我翻遍了GitHub,最后锁定了LVGL。嗯,这一用就是好几年。

1.1 什么是LVGL?

LVGL的全称是 Light and Versatile Graphics Library,轻量级且多功能的图形库。它专门为嵌入式系统设计,目标是让资源受限的MCU也能跑出漂亮的图形界面。

你想想看,一个只有几百KB Flash、几十KB RAM的单片机,要显示按钮、滑块、图表、动画……听起来是不是有点扯?但LVGL做到了。

核心定位:LVGL不是操作系统,也不是硬件驱动。它是一层图形中间件,跑在RTOS或裸机上,帮你管理屏幕上的所有视觉元素。

我习惯把LVGL比作“嵌入式界的Qt”。它提供了丰富的控件库,你只需要调用API,就能快速搭建出专业级的用户界面。而且它是开源的,MIT许可证,商用也没问题。

1.2 LVGL的发展史

LVGL的故事要从2016年说起。匈牙利开发者Gábor Kiss-Vámosi(网名kisvegabor)在GitHub上发布了第一版,当时还叫LittlevGL。最初只是个小众项目,连文档都不全。

我记得2018年那会儿,我在一个论坛上看到有人讨论LittlevGL,说“这玩意儿能在STM32上跑出60帧”。我当时半信半疑,下载源码试了试……结果真香。

项目的发展有几个关键节点:

时间 版本 重要变化
2016年 v1.0 初始发布,基本控件和绘图功能
2018年 v5.0 引入样式系统,控件数量大幅增加
2019年 v6.0 改名为LVGL,重构API,支持更复杂的动画
2021年 v8.0 重大架构升级,引入lvgl.h统一头文件,性能优化
2023年 v9.0 支持多显示器、GPU加速、更完善的输入设备管理

从v6到v8,API变化很大。我当年从v6迁移到v8,改代码改到怀疑人生。但v8的架构确实更清晰,性能也更好。现在新项目我建议直接上v9,除非你有特殊兼容需求。

1.3 LVGL的应用场景与优势

LVGL能做什么?说白了,只要你的设备需要屏幕交互,它基本都能上。

  • 智能家居面板:温控器、灯光控制、门禁对讲
  • 工业HMI:PLC人机界面、数据采集终端
  • 消费电子:智能手表、健身设备、小家电屏幕
  • 车载仪表:电动车仪表盘、后视镜显示
  • 医疗设备:监护仪、手持诊断工具

它的优势,我总结了几点:

优势 说明
资源占用低 最小配置只需16KB Flash + 8KB RAM
控件丰富 内置30+控件,按钮、滑块、图表、键盘等
样式灵活 支持CSS-like样式,可自定义主题
动画引擎 内置补间动画,无需额外算法
输入设备 支持触摸、按键、编码器、鼠标等
跨平台 可在Windows/Linux/Mac模拟器上调试
开源免费 MIT许可证,商用无限制

我的经验:如果你用的是Cortex-M4以上内核、Flash大于256KB、RAM大于64KB,跑LVGL基本没压力。资源更紧张的话,可以裁剪不需要的控件和功能。

1.4 LVGL的架构概览

在动手之前,咱们先看看LVGL的整体架构。理解了这张图,后面学起来会顺很多。

LVGL 软件架构 用户应用程序(Application) LVGL 核心库 控件管理 | 样式系统 | 事件处理 | 动画引擎 绘图引擎 | 内存管理 | 字体/图片处理 硬件抽象层(HAL / 驱动接口) 硬件平台(MCU + 显示屏 + 触摸/按键) 你只需要写应用层代码和HAL适配,核心库不用动

从上往下看:你的应用程序调用LVGL的API,LVGL核心库负责所有控件的渲染和交互逻辑,硬件抽象层把绘图指令翻译成具体的LCD驱动命令,最后在屏幕上显示出来。

为什么要分层?我在项目中遇到过这种情况:换了一块屏幕,驱动芯片变了。如果代码里到处是直接操作寄存器的语句,那改起来真要命。但LVGL的HAL层把显示和输入设备抽象出来了,换屏只需要改底层驱动,上层代码完全不用动。

1.5 在模拟器上跑第一个LVGL程序

好了,理论说完了,咱们动手。我建议初学者先在PC模拟器上跑LVGL,原因很简单:调试方便,不用反复烧录固件,还能用IDE的断点调试功能。

下面我以Windows + VS Code + SDL2为例,带你走一遍流程。

1.5.1 准备环境

你需要装这几样东西:

  • VS Code:代码编辑器,装好C/C++扩展
  • CMake:构建工具,版本3.10以上
  • MinGW或MSVC:C编译器,我习惯用MinGW
  • SDL2:模拟器依赖的图形后端

注意:SDL2的安装路径不要带中文和空格。我曾经因为路径里有空格,折腾了半小时找不到库文件。

1.5.2 下载源码

从GitHub上克隆LVGL仓库和模拟器项目:

git clone https://github.com/lvgl/lvgl.git
git clone https://github.com/lvgl/lv_port_pc_eclipse.git

或者你直接下载ZIP包也行。我习惯用git,方便后面切换版本。

1.5.3 配置项目

进入 lv_port_pc_eclipse 目录,打开CMakeLists.txt。找到这几行:

# 设置LVGL源码路径
set(LVGL_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../lvgl CACHE PATH "Path to lvgl repository")

确保路径指向你刚才下载的lvgl目录。如果目录结构不一样,手动改一下。

1.5.4 编译运行

在终端里执行:

mkdir build
cd build
cmake ..
make

编译成功后,运行生成的可执行文件。你会看到一个窗口,里面显示LVGL的默认demo界面——一个带按钮和标签的示例。

小技巧:如果窗口显示不出来,检查SDL2的dll是否在可执行文件同目录下。我一般直接把SDL2.dll复制到build目录里。

1.5.5 写一个最简单的程序

咱们不跑demo了,自己写一个。新建 main.c,输入以下代码:

#include "lvgl.h"
#include "lv_drivers/display/monitor.h"
#include "lv_drivers/indev/mouse.h"

int main(void)
{
    /* 初始化LVGL */
    lv_init();

    /* 初始化显示器驱动 */
    monitor_init();

    /* 初始化鼠标驱动 */
    mouse_init();

    /* 设置默认显示设备 */
    lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);
    disp_drv.flush_cb = monitor_flush;
    lv_disp_drv_register(&disp_drv);

    /* 设置默认输入设备 */
    lv_indev_drv_t indev_drv;
    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_POINTER;
    indev_drv.read_cb = mouse_read;
    lv_indev_drv_register(&indev_drv);

    /* 创建一个按钮 */
    lv_obj_t *btn = lv_btn_create(lv_scr_act());
    lv_obj_set_pos(btn, 100, 100);
    lv_obj_set_size(btn, 120, 50);

    /* 在按钮上添加标签 */
    lv_obj_t *label = lv_label_create(btn);
    lv_label_set_text(label, "Hello LVGL!");
    lv_obj_center(label);

    /* 主循环 */
    while(1)
    {
        lv_task_handler();
        SDL_Delay(5);
    }

    return 0;
}

这段代码做了几件事:初始化LVGL、设置显示和输入设备、在屏幕上创建一个按钮、按钮上显示文字。最后进入主循环,不断处理LVGL的任务。

编译运行,你会看到一个窗口,中间有个按钮,上面写着“Hello LVGL!”。点击按钮虽然还没反应,但你已经成功跑起了第一个LVGL程序。

恭喜你!从这一刻起,你已经迈入了嵌入式GUI开发的大门。后面我们会逐步深入,让这个按钮动起来、变漂亮、响应点击事件。

1.6 本章小结

这一章我们聊了LVGL是什么、怎么来的、能干什么、以及怎么在模拟器上跑起来。说白了,LVGL就是一个让单片机也能拥有漂亮界面的开源库。它的架构清晰,资源占用低,社区活跃,是目前嵌入式GUI领域最值得投入时间学习的选择之一。