一、环境说明
1.1 硬件环境
- 开发板:MYD-YT153MX-MINI (全志 T153 SoC)
- 显示屏:HDMI 输出,分辨率 1920x1080
- 连接方式:开发板通过 HDMI 连接到显示器
1.2 软件环境
- 宿主机:Ubuntu 20.04 (虚拟机)
- 交叉编译工具链:arm-buildroot-linux-gnueabihf
- LVGL 版本:9.3.0
二、交叉编译环境搭建
2.1 安装交叉编译工具链
移步https://bbs.elecfans.com/jishu_2511633_1_1.html详细了解。
2.2 配置环境变量
创建环境脚本 ~/opt/T153-env.sh:
#!/bin/sh
export PATH=$HOME/t153/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin:$PATH
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
export CC=arm-linux-gnueabihf-gcc
export AR=arm-linux-gnueabihf-ar
export STRIP=arm-linux-gnueabihf-strip
echo "✅ T153 交叉编译环境已加载"
使环境脚本可执行:
chmod +x ~/opt/T153-env.sh
三、LVGL 9.3.0 源码准备
3.1 创建项目目录
cd ~
mkdir -p t153/lvgl9_project
cd t153/lvgl9_project
3.2 下载 LVGL 9.3.0
wget https://github.com/lvgl/lvgl/archive/refs/tags/v9.3.0.zip -O lvgl.zip
unzip lvgl.zip
mv lvgl-9.3.0 lvgl
rm lvgl.zip
git clone https://github.com/lvgl/lv_drivers.git
四、编写显示驱动
4.1 创建 Framebuffer 驱动文件
创建 lv_port_disp.c:
#include "lvgl/lvgl.h"
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <linux/fb.h>
#include <string.h>
#define FB_DEVICE "/dev/fb0"
static int fb_fd = -1;
static uint8_t *fb_buf = NULL;
static struct fb_var_screeninfo vinfo;
static struct fb_fix_screeninfo finfo;
static int fb_bpp;
static void fb_flush(lv_display_t * display, const lv_area_t * area, uint8_t * buf)
{
if (fb_fd < 0 || fb_buf == NULL) {
lv_display_flush_ready(display);
return;
}
int32_t x1 = area->x1;
int32_t y1 = area->y1;
int32_t x2 = area->x2;
int32_t y2 = area->y2;
if (x2 < 0 || y2 < 0 || x1 >= vinfo.xres || y1 >= vinfo.yres) {
lv_display_flush_ready(display);
return;
}
if (x1 < 0) x1 = 0;
if (y1 < 0) y1 = 0;
if (x2 >= vinfo.xres) x2 = vinfo.xres - 1;
if (y2 >= vinfo.yres) y2 = vinfo.yres - 1;
int32_t w = x2 - x1 + 1;
int32_t h = y2 - y1 + 1;
uint32_t line_len = finfo.line_length;
uint32_t bpp = fb_bpp;
for (int32_t y = 0; y < h; y++) {
uint8_t *src = buf + (y * w * bpp);
uint8_t *dst = fb_buf + ((y1 + y) * line_len) + (x1 * bpp);
memcpy(dst, src, w * bpp);
}
lv_display_flush_ready(display);
}
lv_display_t * lv_display_create_from_fb(uint32_t hor_res, uint32_t ver_res)
{
fb_fd = open(FB_DEVICE, O_RDWR);
if (fb_fd < 0) {
printf("Cannot open %s\\n", FB_DEVICE);
return lv_display_create(hor_res, ver_res);
}
if (ioctl(fb_fd, FBIOGET_VSCREENINFO, &vinfo) < 0) {
printf("Cannot get vinfo\\n");
close(fb_fd);
fb_fd = -1;
return lv_display_create(hor_res, ver_res);
}
if (ioctl(fb_fd, FBIOGET_FSCREENINFO, &finfo) < 0) {
printf("Cannot get finfo\\n");
close(fb_fd);
fb_fd = -1;
return lv_display_create(hor_res, ver_res);
}
fb_bpp = vinfo.bits_per_pixel / 8;
printf("FB: %dx%d, %dbpp\\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);
fb_buf = (uint8_t *)mmap(0, finfo.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, 0);
if (fb_buf == MAP_FAILED) {
printf("mmap failed\\n");
close(fb_fd);
fb_fd = -1;
fb_buf = NULL;
return lv_display_create(hor_res, ver_res);
}
printf("FB mapped OK\\n");
memset(fb_buf, 0, finfo.smem_len);
lv_display_t *disp = lv_display_get_default();
if (disp == NULL) {
printf("Creating new display\\n");
disp = lv_display_create(hor_res, ver_res);
}
static uint8_t buf[800*480*4];
static uint8_t buf2[800*480*4];
lv_display_set_buffers(disp, buf, buf2, sizeof(buf), LV_DISPLAY_RENDER_MODE_PARTIAL);
lv_display_set_flush_cb(disp, fb_flush);
lv_display_set_color_format(disp, LV_COLOR_FORMAT_ARGB8888);
printf("Display ready\\n");
return disp;
}
五、编写应用程序
5.1 创建 main.c
#include "lvgl/lvgl.h"
#include <unistd.h>
#include <time.h>
#include <stdio.h>
#include <string.h>
extern lv_display_t * lv_display_create_from_fb(uint32_t hor_res, uint32_t ver_res);
static void timer_callback(lv_timer_t *timer)
{
time_t now = time(NULL);
struct tm *tm_info = localtime(&now);
char time_str[32];
strftime(time_str, sizeof(time_str), "%H:%M:%S", tm_info);
lv_label_set_text((lv_obj_t *)lv_timer_get_user_data(timer), time_str);
}
int main(int argc, char **argv)
{
(void)argc;
(void)argv;
printf("=== LVGL 9.3.0 T153 Demo ===\\n");
lv_init();
lv_display_t * disp = lv_display_create_from_fb(1920, 1080);
lv_display_set_default(disp);
printf("显示创建完成\\n");
lv_obj_t *scr = lv_screen_active();
lv_obj_set_style_bg_color(scr, lv_color_hex(0x0a2a59), LV_PART_MAIN);
lv_obj_t *title = lv_label_create(scr);
lv_label_set_text(title, "MYD-YT153 LVGL 9.3 Demo");
lv_obj_set_style_text_color(title, lv_color_white(), 0);
lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 20);
lv_obj_t *btn = lv_btn_create(scr);
lv_obj_set_size(btn, 180, 60);
lv_obj_align(btn, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_style_bg_color(btn, lv_color_hex(0x4CAF50), LV_PART_MAIN);
lv_obj_t *btn_label = lv_label_create(btn);
lv_label_set_text(btn_label, "Hello T153!");
lv_obj_center(btn_label);
lv_obj_t *clock_label = lv_label_create(scr);
lv_obj_set_style_text_color(clock_label, lv_color_hex(0x4CAF50), 0);
lv_obj_align(clock_label, LV_ALIGN_BOTTOM_MID, 0, -20);
lv_timer_t *timer = lv_timer_create(timer_callback, 1000, clock_label);
printf("UI 创建完成,开始刷新\\n");
while (1) {
lv_timer_handler();
usleep(5000);
}
return 0;
}
六、创建 CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(lvgl_t153 C)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_COMPILER /home/lugl/t153/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/arm-linux-gnueabihf-gcc)
set(CMAKE_AR /home/lugl/t153/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/arm-linux-gnueabihf-ar)
set(CMAKE_RANLIB /home/lugl/t153/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/arm-linux-gnueabihf-ranlib)
set(CMAKE_STRIP /home/lugl/t153/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/arm-linux-gnueabihf-strip)
set(CMAKE_C_FLAGS "-O2 -DLV_CONF_INCLUDE_SIMPLE -static")
set(CMAKE_EXE_LINKER_FLAGS "-static")
set(LVGL_SRC ${CMAKE_SOURCE_DIR}/lvgl/src)
file(GLOB_RECURSE LVGL_ALL_SOURCES ${LVGL_SRC}/*.c)
add_library(lvgl STATIC ${LVGL_ALL_SOURCES})
target_include_directories(lvgl PUBLIC
${CMAKE_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/lvgl
${CMAKE_SOURCE_DIR}/lvgl/src
)
add_executable(my_lvgl_app main.c lv_port_disp.c)
target_include_directories(my_lvgl_app PRIVATE
${CMAKE_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/lvgl
${CMAKE_SOURCE_DIR}/lvgl/src
)
target_link_libraries(my_lvgl_app lvgl m pthread dl)
add_custom_command(TARGET my_lvgl_app POST_BUILD
COMMAND ${CMAKE_STRIP} my_lvgl_app
)
七、编译
7.1 执行编译
cd ~/t153/lvgl9_project
source ~/opt/T153-env.sh
rm -rf build
mkdir build
cd build
cmake ..
make -j4
7.2 验证编译结果
file my_lvgl_app
八、部署到开发板
8.1 启动 HTTP 服务器
在 Ubuntu 上启动 HTTP 服务器:
cd ~/t153/lvgl9_project/build
python3 -m http.server 8888
8.2 在开发板上下载运行
通过串口或 SSH 连接到 T153 开发板:
wget -O /root/my_lvgl_app http://<Ubuntu-IP>:8888/my_lvgl_app
chmod +x /root/my_lvgl_app
./my_lvgl_app
预期输出:
=== LVGL 9.3.0 T153 Demo ===
FB: 1920x1080, 32bpp
FB mapped OK
显示创建完成
UI 创建完成,开始刷新
九、调试经验总结
9.1 LVGL 9.x 与 8.x 的主要区别
- 显示驱动 API 变化
- 8.x:
lv_disp_drv_t + lv_disp_drv_register()
- 9.x:
lv_display_t + lv_display_create() + lv_display_set_buffers()
- 输入设备 API 变化
- 8.x:
lv_indev_drv_t + lv_indev_drv_register()
- 9.x:
lv_indev_create() + lv_indev_set_read_cb()
- 屏幕获取
- 8.x:
lv_scr_act()
- 9.x:
lv_screen_active()
- 定时器 user_data
- 8.x:
timer->user_data
- 9.x:
lv_timer_get_user_data(timer)
9.2 常见问题及解决方案
问题1: 编译报错 "lv_disp_drv_t 未定义"
原因:lv_drivers 库与 LVGL 9.x 不兼容
解决:自己编写显示驱动,不使用 lv_drivers
问题2: 段错误 (Segmentation Fault)
可能原因:
- 缓冲区设置过大
- 渲染模式设置错误
- Framebuffer mmap 失败
解决方案:
- 减小缓冲区大小
- 使用
LV_DISPLAY_RENDER_MODE_PARTIAL 模式
- 检查 Framebuffer 设备是否正确打开
问题3: 只显示一行然后黑屏
原因:flush 回调中的数据拷贝逻辑有问题
解决:
- 确认
vinfo.bits_per_pixel 正确
- 确认
finfo.line_length 正确使用
- 使用单缓冲区模式测试
问题4: 编译时找不到头文件
解决:在 CMakeLists.txt 中正确添加头文件路径:
target_include_directories(lvgl PUBLIC
${CMAKE_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/lvgl
${CMAKE_SOURCE_DIR}/lvgl/src
)
实现效果

9.3 性能优化建议
- 使用 DRM 驱动 :相比 Framebuffer,DRM 支持双缓冲,渲染效率更高
- 启用 G2D 硬件加速 :T153 内置 G2D 2D 图形加速器
- 调整缓冲区大小 :根据实际需求平衡内存使用和性能
- 使用 DMA-BUF :可实现零拷贝显示
十、参考资料
- LVGL 官方文档: https://docs.lvgl.io/
- LVGL GitHub: https://github.com/lvgl/lvgl
- 全志 T153 SDK 文档
- Linux Framebuffer 开发文档