Skip to main content

LVGL User Guide

In this article, we will provide a detailed explanation of the basic concepts of the Framebuffer (FB) device and the LittlevGL (LVGL) graphics user interface library. By using the Luckfox Pico Pro/Max development board and the Pico-LCD-1.3 display, we will demonstrate how to configure the FB device, create applications using the LVGL library, and finally display the interface on the LCD.

Quick Start

  1. Hardware Setup

    • Luckfox Pico Pro/Max *1
    • Pico-LCD-1.3 *1
  2. Download the Zip Archive

    • Download link: LVGL.zip

    • Contents of the zip archive:

      luaCopy codeLVGL/
      ├── image ------------------------------- Image files
      ├── lvgl_demo --------------------------- Project directory
      ├── demo -------------------------------- Executable file
      └── rv1106g-luckfox-pico-pro-max.dts ---- Device tree file
  3. Burn the Image

  4. Run the Demo

    Copy the demo from the zip archive to the development board and run the program:

    chmod +x demo
    ./demo
  5. Modify the Program

    • Modify the Makefile

      Copy the lvgl_demo to the virtual machine and modify the <SDK Directory\> in the Makefile to your SDK path, e.g., /home/luckfox/luckfox-pico/

      CC = <SDK Directory>/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-gcc
    • Compile the project

      Execute the make command in the command line to compile the project

      make
    • Run the program

      After compiling, an executable file demo will be generated in the /build/bin directory. Copy this file to the development board to run it directly.

1. Basic Concepts

Framebuffer (FB) Device

The Framebuffer (FB) device is a hardware device used for graphical display. It provides a frame buffer for drawing graphics and displaying them directly on the screen. The use of FB devices typically involves configuring display resolution, color depth, and transferring drawn graphics data to the display device.

LittlevGL (LVGL)

LVGL (LittlevGL) is a lightweight, open-source embedded graphics library that focuses on providing a flexible and easy-to-use solution for graphical user interfaces (GUI). With LVGL, developers can implement rich graphical interface designs, including common GUI controls such as buttons, sliders, text boxes, as well as features like animations and touch interactions. As an open-source library, LVGL is widely used in various embedded systems and is loved by developers for its lightweight characteristics. LVGL supports various display devices, including Framebuffer, allowing developers to easily create beautiful user interfaces on embedded systems.

2. Pin Configuration

Ensure that all pins communicating with the LCD function properly; this is a basic requirement for driving the LCD. Therefore, we need to configure the pin functions in the device tree file. Before that, we need to determine the pins to which the Pico-LCD-1.3 is connected to the Luckfox Pico Pro/Max. Below, we illustrate this with the example of the GP2 pin for the joystick's upward direction on the Pico-LCD-1.3. It corresponds to pin number 4 on the Luckfox Pico Pro/Max, with the GPIO pin name being GPIO1_C7_d and the pin number being 55.

2.1 Interface Diagram

Pico-LCD-1.3
image

Luckfox Pico Pro/Max

2.2 Modify the Device Tree

You can see that the default function of GPIO1_C7_d is PWM. In the program, we want to determine the key status by reading the level of this pin, so we need to configure this pin as a general IO.

  1. Device tree file path:

    <SDK Directory>/sysdrv/source/kernel/arch/arm/boot/dts/rv1106g-luckfox-pico-pro-max.dts
  2. Define GPIO

    Defining a GPIO usually requires adding two code snippets. Note that the key is active at a low level and requires pull-up. The following is an example showing how to add the definition of the GPIO1_C7_d pin in the device tree.

    The code snippet to be added is as follows:

    /{
    gpio1pc7:gpio1pc7 {
    compatible = "regulator-fixed";
    pinctrl-names = "default";
    pinctrl-0 = <&gpio1_pc7>;
    regulator-name = "gpio1_pc7";
    regulator-always-on;
    };
    };

    &pinctrl {
    gpio1-pc7 {
    gpio1_pc7:gpio1-pc7 {
    rockchip,pins = <1 RK_PC7 RK_FUNC_GPIO &pcfg_pull_up>;
    };
    };
    };
  3. Comment on the pin's peripheral functions

    Commenting on the peripheral functions of the pin can be achieved by commenting out the corresponding peripheral node in the device tree. The following is an example showing how to disable the PWM function of the GPIO1_C7_d pin in the device tree.

  4. Configure the remaining pins sequentially

    Continue to improve pin configurations such as LCD_BL, LCD_DC, LCD_RES, and buttons.

3. Configure the FB Device

3.1 Modify Content

ItemFile Path
Kernel Device Tree<SDK Directory>/sysdrv/source/kernel/arch/arm/boot/dts/rv1106g-luckfox-pico-pro-max.dts
FB Driver Support<SDK Directory>/sysdrv/source/kernel/arch/arm/configs/luckfox_rv1106_linux_defconfig

3.2 Kernel Device Tree

  1. File path

    <SDK Directory>/sysdrv/source/kernel/arch/arm/boot/dts/rv1106g-luckfox-pico-pro-max.dts
  2. Modify the device tree file

    Note: Comment out the original SPI0 configuration first, then add the following configuration:

    &spi0 {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&spi0m0_cs0 &spi0m0_pins>;

    st7789v@0{
    status = "okay";
    compatible = "sitronix,st7789v";
    reg = <0>;
    spi-max-frequency = <20000000>;
    fps = <30>;
    buswidth = <8>;
    debug = <0x7>;
    led-gpios = <&gpio2 RK_PB0 GPIO_ACTIVE_LOW>;//BL
    dc = <&gpio2 RK_PB1 GPIO_ACTIVE_HIGH>; //DC
    reset = <&gpio1 RK_PC3 GPIO_ACTIVE_LOW>; //RES
    };
    };

    &pinctrl {
    spi0 {
    /omit-if-no-ref/
    spi0m0_pins: spi0m0-pins {
    rockchip,pins =
    /* spi0_clk_m0 */
    <1 RK_PC1 4 &pcfg_pull_none>,
    /* spie_miso_m0 */
    // <1 RK_PC3 6 &pcfg_pull_none>,
    /* spi_mosi_m0 */
    <1 RK_PC2 6 &pcfg_pull_none>;
    };
    };
    };

3.3 FB Driver Support

  1. File path

    <SDK Directory>/sysdrv/source/kernel/arch/arm/configs/luckfox_rv1106_linux_defconfig
  2. Compile the driver

    Add the following content:

    CONFIG_SPI_MASTER=y
    CONFIG_FB=y

3.4 FB Device Verification

  1. Check if the fb driver is loaded correctly

    After completing the above modifications, recompile and burn the image. After power-on, you can use the dmesg command to check if the fb driver is loaded correctly:

    # dmesg | grep fb_
    [ 0.432873] fb_st7789v spi0.0: fbtft_write_reg8_bus8: e1 d0 05 0a 09 08 05 2e 43 45 0f 16 16 2b 33
    [ 0.432898] fb_st7789v spi0.0: fbtft_write_spi(len=1): e1
    [ 0.432972] fb_st7789v spi0.0: fbtft_write_spi(len=14): d0 05 0a 09 08 05 2e 43 45 0f 16 16 2b 33
    [ 0.433078] fb_st7789v spi0.0: fbtft_register_backlight(): led pin not set, exiting.
    [ 0.433515] graphics fb0: fb_st7789v frame buffer, 240x320, 150 KiB video memory, 4 KiB buffer memory, fps=30, spi0.0 at 20 MHz
  2. Test for screen corruption:

    cat /dev/urandom > /dev/fb0
  3. Test for screen clear:

    cat /dev/zero > /dev/fb0

    If the screen corruption and screen clearing functions work correctly, it means that the driver can be loaded successfully.

4. Porting LVGL

4.1 Required Resources

Resource NameRepository AddressDescription
lvglhttps://github.com/lvgl/lvgl.gitSource code of LVGL graphics controls and a few examples
lv_drivershttps://github.com/lvgl/lv_drivers.gitDriver interface source code for driving LVGL graphics interface
lv_demoshttps://github.com/lvgl/lv_demos.gitLVGL examples
lv_port_linux_frame_bufferhttps://github.com/lvgl/lv_port_linux_frame_buffer.gitInterface for adapting to Linux systems with Framebuffer

4.2 Fetch Resources

Create a folder in the root directory to store the official source code:

luckfox@luckfox:~$ mkdir lvgl
luckfox@luckfox:~$ cd lvgl

Fetch the resources:

git clone -b v8.1.0 https://github.com/lvgl/lvgl.git
git clone -b v8.1.0 https://github.com/lvgl/lv_drivers.git
git clone -b v8.1.0 https://github.com/lvgl/lv_demos.git
git clone --branch release/v8.2 --single-branch https://github.com/lvgl/lv_port_linux_frame_buffer.git

After successful fetching, create a project directory in the root directory and create a project:

luckfox@luckfox:~$ mkdir lvgl_project
luckfox@luckfox:~$ cd lvgl_project/
luckfox@luckfox:~/lvgl_project$ mkdir project_01
luckfox@luckfox:~/lvgl_project$ cd project_01/

4.3 Copy Files

  • Copy the lvgl and lv_drivers directories from the root directory
  • Copy main.c and Makefile from lv_port_linux_frame_buffer
  • Copy lv_conf_template.h from lvgl and rename it to lv_conf.h
  • Copy lv_drv_conf_template.h from lv_drivers and rename it to lv_drv_conf.h
cp -r ~/lvgl/lvgl ./
cp -r ~/lvgl/lv_drivers ./
cp ~/lvgl/lvgl/lv_conf_template.h ./lv_conf.h
cp ~/lvgl/lv_drivers/lv_drv_conf_template.h ./lv_drv_conf.h
cp ~/lvgl/lv_port_linux_frame_buffer/main.c ./
cp ~/lvgl/lv_port_linux_frame_buffer/Makefile ./

Check the project_01 project directory:

luckfox@luckfox:~/lvgl_project/project_01$ ls -l
total 64
-rw-rw-r-- 1 luckfox luckfox 29023 1227 19:14 lv_conf.h
drwxrwxr-x 12 luckfox luckfox 4096 1227 19:14 lv_drivers
-rw-rw-r-- 1 luckfox luckfox 15184 1227 19:14 lv_drv_conf.h
drwxrwxr-x 12 luckfox luckfox 4096 1227 19:14 lvgl
-rw-rw-r-- 1 luckfox luckfox 2350 1227 19:14 main.c
-rw-rw-r-- 1 luckfox luckfox 2321 1227 19:14 Makefile

4.4 Modify Files

lv_conf.h

  1. Enable

    Change #if 0 to #if 1 at the beginning

    /* clang-format off */
    #if 1 /*Set it to "1" to enable content*/
  2. Allocate VRAM

    Enable LV_MEM_CUSTOM and choose to allocate VRAM manually

    /*1: use custom malloc/free, 0: use the built-in `lv_mem_alloc()` and `lv_mem_free()`*/
    #define LV_MEM_CUSTOM 1
  3. Refresh time

    Change the original 30ms to 10ms

    /*Default display refresh period. LVG will redraw changed areas with this period time*/
    #define LV_DISP_DEF_REFR_PERIOD 10 /*[ms]*/

    /*Input device read period in milliseconds*/
    #define LV_INDEV_DEF_READ_PERIOD 10 /*[ms]*/
  4. TICK Configuration

    Enable LV_TICK_CUSTOM and choose to customize the Tick timer configuration function in the application

    Original content:

    #define LV_TICK_CUSTOM 0
    #if LV_TICK_CUSTOM
    #define LV_TICK_CUSTOM_INCLUDE "Arduino.h" /*Header for the system time function*/
    #define LV_TICK_CUSTOM_SYS_TIME_EXPR (millis()) /*Expression evaluating to current system time in ms*/
    #endif /*LV_TICK_CUSTOM*/

    Change to:

    #define LV_TICK_CUSTOM 1
    #if LV_TICK_CUSTOM
    #define LV_TICK_CUSTOM_INCLUDE <stdint.h> /*Header for the system time function*/
    #define LV_TICK_CUSTOM_SYS_TIME_EXPR (custom_tick_get()) /*Expression evaluating to current system time in ms*/
    #endif /*LV_TICK_CUSTOM*/

lv_drv_conf.h

  1. Enable

    Change #if 0 to #if 1 at the beginning

    /* clang-format off */
    #if 1 /*Set it to "1" to enable the content*/
  2. Support devices

    Enable USE_FBDEV to support Framebuffer devices

    /*-----------------------------------------
    * Linux frame buffer device (/dev/fbx)
    *-----------------------------------------*/
    #ifndef USE_FBDEV
    # define USE_FBDEV 1
    #endif

    #if USE_FBDEV
    # define FBDEV_PATH "/dev/fb0"
    #endif

Makefile

Replace the original content of the Makefile, and modify <SDK Directory\> in the Makefile to your SDK path, e.g., /home/luckfox/luckfox-pico/

#
# Makefile
#
CC = <SDK Directory>/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-gcc
LVGL_DIR_NAME ?= lvgl
LVGL_DIR ?= .

WARNINGS := -Wall -Wshadow -Wundef -Wmissing-prototypes -Wno-discarded-qualifiers -Wall -Wextra -Wno-unused-function -Wno-error=strict-prototypes -Wpointer-arith \
-fno-strict-aliasing -Wno-error=cpp -Wuninitialized -Wmaybe-uninitialized -Wno-unused-parameter -Wno-missing-field-initializers -Wtype-limits -Wsizeof-pointer-memaccess \
-Wno-format-nonliteral -Wno-cast-qual -Wunreachable-code -Wno-switch-default -Wreturn-type -Wmultichar -Wformat-security -Wno-ignored-qualifiers -Wno-error=pedantic \
-Wno-sign-compare -Wno-error=missing-prototypes -Wdouble-promotion -Wclobbered -Wdeprecated -Wempty-body -Wtype-limits -Wshift-negative-value -Wstack-usage=2048 \
-Wno-unused-value -Wno-unused-parameter -Wno-missing-field-initializers -Wuninitialized -Wmaybe-uninitialized -Wall -Wextra -Wno-unused-parameter \
-Wno-missing-field-initializers -Wtype-limits -Wsizeof-pointer-memaccess -Wno-format-nonliteral -Wpointer-arith -Wno-cast-qual -Wmissing-prototypes \
-Wunreachable-code -Wno-switch-default -Wreturn-type -Wmultichar -Wno-discarded-qualifiers -Wformat-security -Wno-ignored-qualifiers -Wno-sign-compare -std=c99
CFLAGS ?= -O3 -g0 -I$(LVGL_DIR)/ $(WARNINGS)
LDFLAGS ?= -lm
BIN = demo
BUILD_DIR = ./build
BUILD_OBJ_DIR = $(BUILD_DIR)/obj
BUILD_BIN_DIR = $(BUILD_DIR)/bin

prefix ?= /usr
bindir ?= $(prefix)/bin

#Collect the files to compile
MAINSRC = $(wildcard ./*.c)

include $(LVGL_DIR)/lvgl/lvgl.mk
include $(LVGL_DIR)/lv_drivers/lv_drivers.mk

# CSRCS +=$(LVGL_DIR)/mouse_cursor_icon.c

OBJEXT ?= .o

AOBJS = $(ASRCS:.S=$(OBJEXT))
COBJS = $(CSRCS:.c=$(OBJEXT))

MAINOBJ = $(MAINSRC:.c=$(OBJEXT))

SRCS = $(ASRCS) $(CSRCS) $(MAINSRC)
OBJS = $(AOBJS) $(COBJS) $(MAINOBJ)
TARGET = $(addprefix $(BUILD_OBJ_DIR)/, $(patsubst ./%, %, $(OBJS)))

## MAINOBJ -> OBJFILES

all: default

$(BUILD_OBJ_DIR)/%.o: %.c
@mkdir -p $(dir $@)
@$(CC) $(CFLAGS) -c $< -o $@
@echo "CC $<"

default: $(TARGET)
@mkdir -p $(dir $(BUILD_BIN_DIR)/)
$(CC) -o $(BUILD_BIN_DIR)/$(BIN) $(TARGET) $(LDFLAGS)

clean:
rm -rf $(BUILD_DIR)

install:
install -d $(DESTDIR)$(bindir)
install $(BUILD_BIN_DIR)/$(BIN) $(DESTDIR)$(bindir)

uninstall:
$(RM) -r $(addprefix $(DESTDIR)$(bindir)/,$(BIN))

main.c

  1. Comment out header files

    // #include "lvgl/demos/lv_demos.h"
    // #include "lv_drivers/indev/evdev.h"
  2. Modify screen resolution

    #define DISP_BUF_SIZE (240 * 240)
    ...
    disp_drv.hor_res = 240;
    disp_drv.ver_res = 240;
  3. Comment out the following code

    No external input device, comment out the following code:

    evdev_init();
    static lv_indev_drv_t indev_drv_1;
    lv_indev_drv_init(&indev_drv_1); /*Basic initialization*/
    indev_drv_1.type = LV_INDEV_TYPE_POINTER;
    /*This function will be called periodically (by the library) to get the mouse position and state*/
    indev_drv_1.read_cb = evdev_read;
    lv_indev_t *mouse_indev = lv_indev_drv_register(&indev_drv_1);

    Mouse style not ported, comment out the following code:

    /*Set a cursor for the mouse*/
    LV_IMG_DECLARE(mouse_cursor_icon)
    lv_obj_t * cursor_obj = lv_img_create(lv_scr_act()); /*Create an image object for the cursor */
    lv_img_set_src(cursor_obj, &mouse_cursor_icon); /*Set the image source*/
    lv_indev_set_cursor(mouse_indev, cursor_obj); /*Connect the image object to the driver*/

    Not using official demo, comment out the following code:

    /*Create a Demo*/
    lv_demo_widgets();

4.5 Compile and Run

After modifying the above files, enter the make command in the command line to compile the project. After compiling, an executable file demo will be generated in the /build/bin directory. The compiled directory structure is as follows:

project_01/
├── build
│   ├── bin
│ │ └── demo
│   └── obj
│ ├── lv_drivers
│ ├── lvgl
│ └── ...
├── lv_conf.h
├── lv_drivers
├── lv_drv_conf.h
├── lvgl
├── main.c
└── Makefile

You only need to copy demo to the development board to run the program. You can later modify the main.c file and add files according to your requirements to achieve the desired display effect.

4.6 Official Demo

If you need to use the examples provided by the official, follow these steps:

  1. Copy files

    • Copy the lv_demos directory from the root directory

    • Copy lv_demo_conf_template.h from lv_demos and rename it to lv_demo_conf.h

      cp -r ~/lvgl/lv_demos ./
      cp ~/lvgl/lv_demos/lv_demo_conf_template.h ./lv_demo_conf.h
  2. Modify lv_demo_conf.h

    • Enable

      /* clang-format off */
      #if 1 /*Set it to "1" to enable the content*/
    • Enable widget demo

      /*Show some widget*/
      #define LV_USE_DEMO_WIDGETS 1
  3. Modify lv_conf.h

    Enable LV_FONT_MONTSERRAT_12 and LV_FONT_MONTSERRAT_16

    #define LV_FONT_MONTSERRAT_12 1
    #define LV_FONT_MONTSERRAT_16 1
  4. Makefile

    include $(LVGL_DIR)/lvgl/lvgl.mk
    include $(LVGL_DIR)/lv_drivers/lv_drivers.mk
    #Add lv_demo.mk:
    include $(LVGL_DIR)/lv_demos/lv_demo.mk
  5. Modify main.c

    • Add header files

      #include "lv_demos/lv_demo.h"
    • Call interface functions

      lv_demo_widgets();

      while(1) {
      lv_timer_handler();
      usleep(5000);
      }

5. Example Program

5.1 Project Directory

After burning the image and creating the project files, you can start modifying the program yourself. Here, we use the Luckfox Pico Pro/Max development board and Pico-LCD-1.3 display screen to implement interface display. The modified project directory is as follows:

lvgl_demo/
├── cat.c -------------------- File obtained after converting the image
├── galaxy.c ----------------- File obtained after converting the image
├── Luckfox.c ---------------- File obtained after converting the image
├── saint.c ------------------ File obtained after converting the image
├── main.c ------------------- Main program file
├── Debug.h ------------------ Debug information header file
├── DEV_Config.c ------------- GPIO control interface file
├── DEV_Config.h ------------- GPIO control interface header file
├── sysfs_gpio.c ------------- sysfs GPIO control implementation file
├── sysfs_gpio.h ------------- sysfs GPIO control header file
└── ...

5.2 Display Custom Images

  1. Convert online

    Online Image Converter

  2. Convert images

    Note that for PNG images, choose the CF_TRUE_COLOR_ALPHA color format, and for JPG images, choose CF_TRUE_COLOR. After conversion, copy the C file to the project directory

  3. Modify main.c

    Add declarations

    LV_IMG_DECLARE(Luckfox);  

    Display images

    lv_obj_t *scr = lv_disp_get_scr_act(NULL);
    lv_obj_t *img = lv_img_create(scr);
    lv_show_img(img,Luckfox);
    lv_obj_center(img);

5.3 Main Program

main.c

#include "lvgl/lvgl.h"
#include "DEV_Config.h"
#include "lv_drivers/display/fbdev.h"
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <sys/time.h>

#define DISP_BUF_SIZE (240 * 240)

/*Image declare*/
LV_IMG_DECLARE(Luckfox);
LV_IMG_DECLARE(cat);
LV_IMG_DECLARE(galaxy);
LV_IMG_DECLARE(saint);

void fbdev_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_p);

/**
* A meter with multiple arcs
*/

void lv_show_img(lv_obj_t * img,const lv_img_dsc_t img_dat){
lv_obj_clean(img);
lv_img_set_src(img, &img_dat);
lv_obj_center(img);
}

int main(void)
{
/*LittlevGL init*/
lv_init();

/*Linux frame buffer device init*/
fbdev_init();

/*A small buffer for LittlevGL to draw the screen's content*/
static lv_color_t buf[DISP_BUF_SIZE];

/*Initialize a descriptor for the buffer*/
static lv_disp_draw_buf_t disp_buf;
lv_disp_draw_buf_init(&disp_buf, buf, NULL, DISP_BUF_SIZE);

/*Initialize and register a display driver*/
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.draw_buf = &disp_buf;
disp_drv.flush_cb = fbdev_flush;
disp_drv.hor_res = 240;
disp_drv.ver_res = 240;
lv_disp_drv_register(&disp_drv);

/*Initialize pin*/
DEV_ModuleInit();

/*Show an image*/
lv_obj_t *scr = lv_disp_get_scr_act(NULL);
lv_obj_t *img = lv_img_create(scr);
lv_show_img(img,Luckfox);
lv_obj_center(img);

/*Create a cursor*/
lv_obj_t *cursor = lv_img_create(scr);
lv_img_set_src(cursor, LV_SYMBOL_GPS);
lv_obj_set_pos(cursor, 70, 120);
int x=70,y=120,move=0;

/*Handle LitlevGL tasks (tickless mode)*/
while(1) {
lv_timer_handler();
usleep(5000);

/*Key*/
if(GET_KEYA == 0){
lv_show_img(img,Luckfox);
}
else if(GET_KEYB == 0){
lv_show_img(img,cat);
}
else if(GET_KEYX == 0){
lv_show_img(img,galaxy);
}
else if(GET_KEYY == 0){
lv_show_img(img,saint);
}

/*Joystick*/
if(GET_KEY_UP == 0){
x += 1;
if(x > 226)x = 226;
move =1;
}
else if(GET_KEY_DOWN == 0){
x -= 1;
if(x < 0)x = 0;
move =1;
}
else if(GET_KEY_LEFT == 0){
y -= 1;
if(y < 0)y = 0;
move =1;
}
else if(GET_KEY_RIGHT == 0){
y += 1;
if(y > 224)y = 224;
move =1;
}
else if(GET_KEY_PRESS == 0){
x = 70;
y = 120;
move =1;
}
if(move == 1){
lv_obj_set_pos(cursor, x, y);
move = 0;
}
}

return 0;
}

/*Set in lv_conf.h as `LV_TICK_CUSTOM_SYS_TIME_EXPR`*/
uint32_t custom_tick_get(void)
{
static uint64_t start_ms = 0;
if(start_ms == 0) {
struct timeval tv_start;
gettimeofday(&tv_start, NULL);
start_ms = (tv_start.tv_sec * 1000000 + tv_start.tv_usec) / 1000;
}

struct timeval tv_now;
gettimeofday(&tv_now, NULL);
uint64_t now_ms;
now_ms = (tv_now.tv_sec * 1000000 + tv_now.tv_usec) / 1000;

uint32_t time_ms = now_ms - start_ms;
return time_ms;
}

Code analysis:

Image Declarations

Sequentially declare the images that will be used in the program

/*Image declare*/
LV_IMG_DECLARE(Luckfox);
LV_IMG_DECLARE(cat);
LV_IMG_DECLARE(galaxy);
LV_IMG_DECLARE(saint);

Pin Initialization

Call the interface to initialize the control pins of the LCD

/*Initialize pin*/
DEV_ModuleInit();

Display Images

This code segment is used to display an image in the LVGL graphics interface and center the image on the currently active screen.

/*Show an image*/
lv_obj_t *scr = lv_disp_get_scr_act(NULL);
lv_obj_t *img = lv_img_create(scr);
lv_show_img(img,Luckfox);
lv_obj_center(img);
  • lv_disp_get_scr_act(NULL): Get the currently active screen object. lv_obj_t *scr points to this screen object.
  • lv_img_create(scr): Create an image object on the screen object. lv_obj_t *img points to the created image object.
  • lv_show_img(img, Luckfox): Display the image named Luckfox on the created image object.
  • lv_obj_center(img): Center the image object on the screen.

When the user presses buttons A, B, X, and Y separately, control the LCD to display different images.

/*Key*/
if(GET_KEYA == 0){
lv_show_img(img,Luckfox);
}
else if(GET_KEYB == 0){
lv_show_img(img,cat);
}
else if(GET_KEYX == 0){
lv_show_img(img,galaxy);
}
else if(GET_KEYY == 0){
lv_show_img(img,saint);
}

Display Cursor

The purpose of this code is to create a cursor object, set the position and image source of the cursor.

/*Create a cursor*/
lv_obj_t *cursor = lv_img_create(scr);
lv_img_set_src(cursor, LV_SYMBOL_GPS);
lv_obj_set_pos(cursor, 70, 120);
int x=70,y=120,move=0;
  • lv_img_create(scr): Creates an image object on the screen. lv_obj_t *cursor points to the created image object, which will be used as a cursor.
  • lv_img_set_src(cursor, LV_SYMBOL_GPS): Sets the image source of the cursor to the GPS symbol. LV_SYMBOL_GPS is a GPS symbol defined in the LVGL library.
  • lv_obj_set_pos(cursor, 70, 120): Sets the position of the cursor to (70, 120).
  • int x = 70, y = 120, move = 0;: Defines three variables representing the initial horizontal coordinate x, vertical coordinate y, and the movement flag move of the cursor.

This code segment enables the control of the cursor position on the screen using a joystick. When the user operates the joystick, the x and y values change based on the direction. The move flag is then set to 1. Subsequently, when the move flag is 1, the cursor position is updated, achieving the effect of moving the cursor.

/*Joystick*/
if(GET_KEY_UP == 0){
x += 1;
if(x > 226)x = 226;
move =1;
}
else if(GET_KEY_DOWN == 0){
x -= 1;
if(x < 0)x = 0;
move =1;
}
else if(GET_KEY_LEFT == 0){
y -= 1;
if(y < 0)y = 0;
move =1;
}
else if(GET_KEY_RIGHT == 0){
y += 1;
if(y > 224)y = 224;
move =1;
}
else if(GET_KEY_PRESS == 0){
x = 70;
y = 120;
move =1;
}
if(move == 1){
lv_obj_set_pos(cursor, x, y);
move = 0;
}

5.4 Implementation Results

  1. Hardware Connection
  2. Display Effect