LVGL 通用移植指南 (MCU & Linux)

本指南总结了将 LVGL 移植到任何嵌入式系统(无论是裸机 MCU 还是运行 Linux 的 SoC)的通用流程。


1. 核心源码集成

无论是什么平台,第一步都是将 LVGL 源码加入工程。

  • 获取源码: 从 GitHub 克隆或下载特定版本的 LVGL。
  • 添加路径: 确保编译器能够搜索到 lvgl.h 所在的根目录。
  • 编译配置:
    • Linux/Makefile: 包含 lvgl.mk
    • MCU/IDE: 手动将 src 目录下所有源文件加入编译列表,并添加头文件包含路径。

2. 配置文件 (lv_conf.h)

这是 LVGL 的“大脑”,决定了库的功能和行为。

  1. 创建文件: 复制 lv_conf_template.h 并更名为 lv_conf.h
  2. 基本配置:
    • #if 0 修改为 #if 1
    • 颜色深度: 设置 LV_COLOR_DEPTH (通常 MCU 为 16,Linux 为 32)。
    • 内存管理:
      • MCU: 设置 LV_MEM_SIZE 分配静态堆。
      • Linux/OS: 建议设置 LV_MEM_CUSTOM 1 直接使用系统的 malloc/free
  3. Tick 接口:
    • LVGL 需要一个毫秒级的计时器来处理动画和刷新。
    • 方式 A (推荐): 开启 LV_TICK_CUSTOM 1 并提供一个获取系统时间的函数。
    • 方式 B: 在硬件中断(如 SysTick)中手动调用 lv_tick_inc(1)

3. 显示驱动移植 (Display Interface)

LVGL 不直接操作硬件,它通过你提供的回调函数进行绘图。

  1. 初始化硬件: 初始化 LCD 控制器、SPI/I2C 总线或映射 Framebuffer。
  2. 注册驱动:
    • 定义 lv_disp_draw_buf_t 并分配显存缓冲区(可以是单缓冲或双缓冲)。
    • 定义 lv_disp_drv_t 结构体。
    • 实现 flush_cb 回调函数: 这是核心,LVGL 会传入坐标和像素数据,你需要将其拷贝到屏幕上。
    • 调用 lv_disp_drv_register()

4. 输入设备移植 (Input Interface)

  1. 初始化硬件: 初始化触摸芯片、按键 GPIO 或打开 evdev 设备。
  2. 注册驱动:
    • 定义 lv_indev_drv_t 结构体。
    • 设置设备类型(LV_INDEV_TYPE_POINTER 触摸/鼠标,或 LV_INDEV_TYPE_KEYPAD 按键)。
    • 实现 read_cb 回调函数: LVGL 会定期调用此函数,你需要返回当前坐标和按键状态(按下或释放)。
    • 调用 lv_indev_drv_register()

5. 主循环与任务处理

LVGL 是非阻塞的任务系统,需要你在主循环中不断轮询。

int main() {
    lv_init();               // 1. 初始化内核
    hal_init();              // 2. 初始化你的显示和输入驱动
    
    ui_init();               // 3. 创建你的 UI 界面

    while(1) {
        lv_timer_handler();  // 4. 处理定时器、刷新屏幕、读取输入
        usleep(5000);        // 5. 适当延时 (MCU 使用 Delay)
    }
}

6. MCU 与 Linux 的主要区别

特性MCU (裸机/RTOS)Linux (SoC)
显存通常在内部 RAM 或外部 SDRAM通过 /dev/fb0 进行内存映射 (mmap)
输入SPI/I2C 读取触摸 IC 寄存器读取 /dev/input/eventX 标准接口
内存静态数组分配动态内存分配 (malloc)
文件系统FatFS / LittleFS标准 POSIX (fopen/fread)
多线程需注意临界区保护需注意互斥锁 (Mutex)

7. 移植清单 (Checklist)

  • lv_conf.h 是否已启用并配置了颜色深度?
  • lv_tick_incLV_TICK_CUSTOM 是否正常工作?
  • flush_cb 是否能将像素正确显示在屏幕上?
  • read_cb 是否能正确上报坐标?
  • 主循环是否在持续调用 lv_timer_handler()