Concepts First
- LVGL v8: a mature, stable release of the LVGL graphics library; widgets and drawing APIs are similar to v9, but some interfaces differ (see FAQ).
- Tick differences: the v8 example does not explicitly set a tick callback (
lv_port_disp_initdriveslv_tick_incinternally), while v9 useslv_tick_set_cb. - Display port: the LCD is initialized through
lv_port_disp_init; screen model/pins are configured inlcd_conf_user.h. - Demo switching: benchmark runs by default; widgets, music, stress demos are listed as comments.
Example Overview
This page is based on the lvgl_v8 example in the official Bouffalo SDK (examples/lvgl/lvgl_v8), which demonstrates LVGL v8 running on Ai-M6x:
- Registers a log callback (
lv_log_register_print_cb) and initializes LVGL (lv_init); lv_port_disp_initinitializes the LCD display driver;- Runs the benchmark demo (
lv_demo_benchmark); other demos are listed as comments; - The main loop calls
lv_task_handler()periodically to refresh the screen. - Sibling examples (
examples/lvgl/):lvgl_v9(v9),lvgl_v8_with_osd/lvgl_v9_with_osd(with OSD layer).
Note
The LVGL example depends on an LCD display and its driver. The screen model, pins, and resolution must match your board in lcd_conf_user.h; without a connected display you will not see any picture.
Operation Steps
This page needs a board with an LCD. Open a terminal and enter the SDK LVGL v8 example directory (prerequisite: set up the environment as in Quick Start (Linux) or Windows):
cd examples/lvgl/lvgl_v8Run the build command. The Ai-M62 (BL616) and Ai-M61 (BL618) belong to the same series, so both use bl616:
make CHIP=bl616 BOARD=bl616dkConnect the board with a USB cable, hold the BOOT button (IO2 on the Ai-M61-32S-Kit), briefly press EN/RST to enter download mode, then flash (replace the serial port with the one on your computer):
make flash CHIP=bl616 COMX=/dev/ttyUSB0Open a serial tool (baud rate 2000000). After printing LVGL V8 case and the version, the screen runs the benchmark demo and the serial log ends with lvgl success.
Code Execution Flow
The complete flow from startup to rendering is shown below (loop arrows mean repeated execution):
APIs Used by the Example
lv_log_register_print_cb(cb)
Registers the LVGL log callback, forwarding LVGL internal logs to the serial port.
Parameters:
cb: log callback (lv_log_print_g_cbin the example)
Return: none
lv_init()
Initializes the LVGL core library; must be called before any other LVGL API.
Parameters: none
Return: none
lv_port_disp_init()
Initializes the LVGL display port (LCD driver) and registers the tick increment (lv_tick_inc).
Parameters: none
Return: none
lv_demo_benchmark()
Starts the benchmark demo, drawing various shapes and measuring rendering performance. Other demos (lv_demo_widgets, lv_demo_music, lv_demo_stress, etc.) can be enabled by uncommenting.
Parameters: none
Return: none
lv_task_handler()
LVGL's task handler: advances animations and flushes the display; must be called periodically from the main loop.
Parameters: none
Return: none
Complete Code
The complete source below matches the official example (examples/lvgl/lvgl_v8) verbatim. Collapsed by default, click to expand:
📜 Click to expand lvgl_v8/main.c full code
/**
* @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_LVGL8_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 log cb */
void lv_log_print_g_cb(const char *buf)
{
LOG_RI("[LVGL]%s", buf);
}
void lvgl_main(void *param)
{
/* lvgl init */
lv_log_register_print_cb(lv_log_print_g_cb);
lv_init();
lv_port_disp_init();
#ifdef CONFIG_LVGL8_PORT_INDEV
lv_port_indev_init();
#endif
// lv_disp_set_rotation(NULL, LV_DISP_ROT_90);
lv_demo_benchmark();
// lv_demo_keypad_encoder();
// lv_demo_music();
// lv_demo_stress();
// 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 V8 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
v8 or v9?
For new projects, prefer v9 (LVGL v9 GUI); use v8 if you already have v8 code or depend on older components. Both SDK examples build and run directly.
How is the tick driven
In v8, lv_port_disp_init internally increments the tick with lv_tick_inc; in v9, the example calls lv_tick_set_cb explicitly. When writing your own project, keep the tick increasing, or animations and refreshes will stall.
The screen stays blank
Confirm the board has an LCD connected and that the screen model, resolution, and pins in lcd_conf_user.h match; also check lv_port_disp_init ran (look for LVGL logs on the serial port).
Have questions?
For any other questions, visit the unified Q&A and discussion board: Ai-Thinker Discussions

