概念先知道
- LVGL:一个开源的嵌入式图形库(LittlevGL),用 C 语言编写,专为 MCU 设计,提供控件(按钮、滑块、图表等)和绘图能力。
- v9 与 v8:LVGL v9 是最新大版本,API 与 v8 有差异(例如 tick 设置改用
lv_tick_set_cb、旋转枚举改名);两者在 SDK 中各有独立例程。 - 显示驱动(lv_port_disp):LVGL 不直接操作屏幕,而是通过显示驱动接口把绘图缓冲送到 LCD。SDK 例程在
lv_port_disp_init()里完成屏幕初始化。 - 任务循环:LVGL 需要周期性调用
lv_task_handler()来刷新界面;例程在 FreeRTOS 任务或主循环中不断调用它。
例程功能简介
本页对应博流官方 SDK 的 lvgl_v9 例程(examples/lvgl/lvgl_v9),演示 LVGL v9 在 Ai-M6x 上运行:
- 初始化 LVGL(
lv_init),注册 tick 回调(lv_tick_set_cb)与日志回调; lv_port_disp_init初始化 LCD 显示驱动;- 运行 benchmark 演示(
lv_demo_benchmark),评估 MCU 渲染性能;其余演示(widgets、music、scroll、stress 等)以注释形式列出,取消注释即可切换; - 主循环周期调用
lv_task_handler()持续刷新画面。 - 同族例程(
examples/lvgl/):lvgl_v8(v8 版本)、lvgl_v9_with_osd/lvgl_v8_with_osd(带 OSD 图层版本)。
注意
LVGL 例程依赖 LCD 显示屏与对应驱动(lcd.h / lv_port_disp)。不同开发板屏幕型号、引脚和分辨率可能不同,需要按板子的 lcd_conf_user.h 配置核对;没有接屏的板子无法看到画面。
操作步骤
本页需要一块带屏幕的开发板(例程通过 LCD 接口驱动显示屏)。在终端进入 SDK 的 LVGL v9 例程目录(前提:已按快速开始(Linux)或Windows搭建好环境):
cd examples/lvgl/lvgl_v9执行编译命令。Ai-M62(BL616)与 Ai-M61(BL618)同属一个系列,统一填写引脚最少的 bl616 即可:
make CHIP=bl616 BOARD=bl616dk用 USB 线连接开发板,按住 BOOT 键(Ai-M61-32S-Kit 为 IO2)不放、短按 EN/RST 进入下载模式,然后执行烧录(把串口号换成实际值):
make flash CHIP=bl616 COMX=/dev/ttyUSB0打开串口助手(波特率 2000000)。程序打印 LVGL V9 case 和 LVGL 版本号后,屏幕开始运行 benchmark 性能测试(lv_demo_benchmark),不断绘制不同图形并统计渲染帧率/耗时;串口打印 lvgl success。
代码执行流程
例程从启动到画面刷新的完整流程如下(图中的循环箭头表示反复执行):
例程调用的 API 介绍
lv_tick_set_cb(cb)
注册毫秒 tick 回调,LVGL 用它计时动画与刷新。例程传入 lv_get_time_ms_cb(内部调用 bflb_mtimer_get_time_ms)。
参数:
cb:返回当前毫秒数的函数
返回值:无
lv_demo_benchmark()
启动 benchmark 演示,循环绘制各类图形并统计渲染耗时,用于评估设备图形性能。其他演示(lv_demo_widgets、lv_demo_music、lv_demo_stress 等)取消注释即可切换。
参数:无
返回值:无
完整代码
以下为 lvgl_v9/main.c 完整源码,与官方示例(examples/lvgl/lvgl_v9)一致,默认折叠,点击展开:
📜 点击展开 lvgl_v9/main.c 完整代码
/**
* @file main.c
* @brief
*
* Copyright (c) 2021 Bouffalolab team
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
*
*/
#include "board.h"
#include "bflb_gpio.h"
#include "bflb_l1c.h"
#include "bflb_mtimer.h"
#include "lcd.h"
#if defined(CONFIG_FREERTOS)
#include <FreeRTOS.h>
#include "task.h"
#endif
#include "lvgl.h"
#include "lv_port_disp.h"
#ifdef CONFIG_LVGL9_PORT_INDEV
#include "lv_port_indev.h"
#endif
#include "demos/lv_demos.h"
#define DBG_TAG "MAIN"
#include "log.h"
#if defined(CONFIG_FREERTOS)
static TaskHandle_t lvgl_handle;
#endif
/* lvgl tick cb */
uint32_t lv_get_time_ms_cb(void)
{
return (uint32_t)bflb_mtimer_get_time_ms();
}
#if (LV_USE_LOG)
/* lvgl log cb */
void lv_log_print_cb(lv_log_level_t level, const char *buf)
{
switch (level) {
case LV_LOG_LEVEL_TRACE:
case LV_LOG_LEVEL_INFO:
LOG_RI("[LVGL] %s", buf);
break;
case LV_LOG_LEVEL_WARN:
case LV_LOG_LEVEL_USER:
LOG_RW("[LVGL] %s", buf);
break;
case LV_LOG_LEVEL_ERROR:
LOG_RE("[LVGL] %s", buf);
break;
default:
LOG_RW("[LVGL] %s", buf);
break;
}
}
#endif
void lvgl_main(void *param)
{
/* lvgl init */
lv_init();
/* tick cb */
lv_tick_set_cb(lv_get_time_ms_cb);
#if (LV_USE_LOG)
/* log cb */
lv_log_register_print_cb(lv_log_print_cb);
#endif
/* lcd init */
lv_port_disp_init();
#ifdef CONFIG_LVGL9_PORT_INDEV
lv_port_indev_init();
#endif
// lv_disp_set_rotation(NULL, LV_DISP_ROTATION_90);
lv_demo_benchmark();
// lv_demo_flex_layout();
// lv_demo_keypad_encoder();
// lv_demo_multilang();
// lv_demo_music();
// lv_demo_render(LV_DEMO_RENDER_SCENE_FILL, LV_OPA_50);
// lv_demo_scroll();
// lv_demo_stress();
// lv_demo_transform();
// lv_demo_widgets();
lv_task_handler();
LOG_I("lvgl success\r\n");
while (1) {
lv_task_handler();
bflb_mtimer_delay_us(10);
}
}
int main(void)
{
board_init();
LOG_I("LVGL V9 case\r\n");
LOG_I("LVGL VER: %d.%d.%d\r\n", LVGL_VERSION_MAJOR, LVGL_VERSION_MINOR, LVGL_VERSION_PATCH);
#if defined(CONFIG_FREERTOS)
xTaskCreate(lvgl_main, (char *)"test_task", 2048, NULL, configMAX_PRIORITIES - 2, &lvgl_handle);
vTaskStartScheduler();
#else
lvgl_main(NULL);
#endif
}FAQ
屏幕没有画面
先确认开发板确实接了 LCD 屏,且 lcd_conf_user.h 中的屏幕型号、分辨率、引脚与板子一致;再确认编译时 CONFIG_FREERTOS 相关配置正常(例程默认 FreeRTOS 任务方式运行 LVGL)。
如何切换成其他演示
把 lvgl_main 中的 lv_demo_benchmark() 注释掉,取消注释想要的演示(如 lv_demo_widgets()),重新编译烧录即可。
v9 与 v8 的 API 有什么区别
主要差异在命名与部分初始化方式:v9 用 lv_tick_set_cb 设置 tick、旋转枚举为 LV_DISP_ROTATION_*;v8 用 lv_tick_inc 驱动 tick、旋转枚举为 LV_DISP_ROT_*。写新代码建议直接用 v9。
遇到问题?
如有其他问题,请到统一的提问与讨论区:Ai-Thinker Discussions

