LVGL移植到IMX6ULL笔记

所需库文件及版本问题

所需库文件及版本如下:

库文件 版本 仓库地址 描述
lv_port_linux_frame_buffer release/v8.2 https://github.com/lvgl/lv_port_linux.git 适配有frame buffer的linux系统的接口
lv_drivers release/v8.2(49c4b17) https://github.com/lvgl/lv_drivers.git 包含了驱动LVGL图形界面的驱动接口源代码
lvgl release/v8.2.0(0b5a1d4) https://github.com/lvgl/lvgl.git LVGL图形库源代码,包含所需demo
注意一定要版本对应,不然会出现各种稀奇古怪的问题,可以使用下面的方法下载特定版本:
  1. 选择对应版本后,选择Download ZIP

  2. 使用git命令克隆特定版本

    先git整个仓库

    1
    git clone https://github.com/lvgl/lvgl.git

    然后切换到对应版本

    1
    git checkout v8.2.0

移植文件

将上述下载到的lv_drivers lvgl 和 lv_port_linux_frame_buffer中 main.c和 Makefile文件的 放在同一目录下,比如我放在了新建的lvgl_demo目录下。
将lvgl_demo/lvgl/lv_conf_template.h复制到lvgl_demo/下并且改名位lvgl_demo/lv_conf.h
将lvgl_demo/lv_drivers/lv_drv_conf_template.h复制到lvgl_demo/下并且改名位lvgl_demo/lv_drv_conf.h
此时查看

1
2
book@100ask:~/Desktop/lvgl_demo2$ ls
lv_conf.h lv_drivers lv_drv_conf.h lvgl main.c Makefile

此时目录结构为:

1
2
3
4
5
6
7
lvgl_demo
├── lvgl
├── lv_drivers
├── lv_conf.h
├── lv_drv_conf.h
├── main.c
└── Makefile

二级目录使用tree -L 2查看我的lvgl_demo目录结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
lvgl_demo
├── lv_conf.h
├── lv_drivers
│   ├── CMakeLists.txt
│   ├── display
│   ├── docs
│   ├── gtkdrv
│   ├── indev
│   ├── library.json
│   ├── LICENSE
│   ├── lv_drivers.mk
│   ├── lv_drv_conf_template.h
│   ├── README.md
│   ├── sdl
│   ├── wayland
│   ├── win32drv
│   ├── win_drv.c
│   ├── win_drv.h
│   └── win_drv.o
├── lv_drv_conf.h
├── lvgl
│   ├── CMakeLists.txt
│   ├── component.mk
│   ├── demos
│   ├── docs
│   ├── env_support
│   ├── examples
│   ├── idf_component.yml
│   ├── Kconfig
│   ├── library.json
│   ├── library.properties
│   ├── LICENCE.txt
│   ├── lv_conf_template.h
│   ├── lvgl.h
│   ├── lvgl.mk
│   ├── README.md
│   ├── README_zh.md
│   ├── SConscript
│   ├── scripts
│   ├── src
│   └── tests
├── main.c
└── Makefile

修改配置文件

修改lv_drv_conf.h

  1. 使能驱动
    #if 1 /*Set it to "1" to enable the content*/改为#if 1 /*Set it to "1" to enable the content*/
  2. 使能frame buffer设备,将USE_FBDEV的值改为1
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    /*-----------------------------------------
    * Linux frame buffer device (/dev/fbx)
    *-----------------------------------------*/
    #ifndef USE_FBDEV
    # define USE_FBDEV 1
    #endif

    #if USE_FBDEV
    # define FBDEV_PATH "/dev/fb0"
    #endif
  3. 使能USE_EVDEV,将/dev/input/event0改为/dev/input/event1,这里是使触摸设备生效
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    /*-------------------------------------------------
    * Mouse or touchpad as evdev interface (for Linux based systems)
    *------------------------------------------------*/
    #ifndef USE_EVDEV
    # define USE_EVDEV 1
    #endif

    #ifndef USE_BSD_EVDEV
    # define USE_BSD_EVDEV 0
    #endif

    #if USE_EVDEV || USE_BSD_EVDEV
    # define EVDEV_NAME "/dev/input/event1" /*You can use the "evtest" Linux tool to get the list of devices and test them*/
    # define EVDEV_SWAP_AXES 0 /*Swap the x and y axes of the touchscreen*/

    # define EVDEV_CALIBRATE 0 /*Scale and offset the touchscreen coordinates by using maximum and minimum values for each axis*/

修改lv_conf.h

  1. 使能,这里和上述步骤相同
    #if 1 /*Set it to "1" to enable the content*/改为#if 1 /*Set it to "1" to enable the content*/
  2. 修改显存大小
    可以使能LV_MEM_CUSTOM自己分配也可以自动分配,我选择的是自己分配显存:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    /*1: use custom malloc/free, 0: use the built-in `lv_mem_alloc()` and `lv_mem_free()`*/
    #define LV_MEM_CUSTOM 1
    #if LV_MEM_CUSTOM == 0
    /*Size of the memory available for `lv_mem_alloc()` in bytes (>= 2kB)*/
    #define LV_MEM_SIZE (48U * 1024U) /*[bytes]*/

    /*Set an address for the memory pool instead of allocating it as a normal array. Can be in external SRAM too.*/
    #define LV_MEM_ADR 0 /*0: unused*/
    /*Instead of an address give a memory allocator that will be called to get a memory pool for LVGL. E.g. my_malloc*/
    #if LV_MEM_ADR == 0
    //#define LV_MEM_POOL_INCLUDE your_alloc_library /* Uncomment if using an external allocator*/
    //#define LV_MEM_POOL_ALLOC your_alloc /* Uncomment if using an external allocator*/
    #endif

    #else /*LV_MEM_CUSTOM*/
    #define LV_MEM_CUSTOM_INCLUDE <stdlib.h> /*Header for the dynamic memory function*/
    #define LV_MEM_CUSTOM_ALLOC malloc
    #define LV_MEM_CUSTOM_FREE free
    #define LV_MEM_CUSTOM_REALLOC realloc
    #endif /*LV_MEM_CUSTOM*/
  3. 刷新时间
    可修改可以不修改,记录说明这里可以按需修改
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    /*====================
    HAL SETTINGS
    *====================*/

    /*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的配置
    这里选择自己定义一个Tick定时器配置函数,更改后的配置如下:
    1
    2
    3
    4
    5
    6
    7
    8
    /*Use a custom tick source that tells the elapsed time in milliseconds.
    *It removes the need to manually update the tick with `lv_tick_inc()`)*/
    uint32_t custom_tick_get(void);
    #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*/
  5. LV_COLOR_DEPTH设置
    1
    2
    3
    4
    5
    6
    7
    8
    9
    /*====================
    COLOR SETTINGS
    *====================*/

    /*Color depth: 1 (1 byte per pixel), 8 (RGB332), 16 (RGB565), 32 (ARGB8888)*/
    #define LV_COLOR_DEPTH 32

    /*Swap the 2 bytes of RGB565 color. Useful if the display has an 8-bit interface (e.g. SPI)*/
    #define LV_COLOR_16_SWAP 0
    颜色深度设置为32位,容易遗忘的设置,否则lvgl颜色显示不正常
  6. 使能相应的demo模板
    这里使用的是LV_USE_DEMO_WIDGETS
    1
    2
    3
    4
    5
    6
    7
    8
    9
     /*===================
    * DEMO USAGE
    ====================*/

    /*Show some widget. It might be required to increase `LV_MEM_SIZE` */
    #define LV_USE_DEMO_WIDGETS 1
    #if LV_USE_DEMO_WIDGETS
    #define LV_DEMO_WIDGETS_SLIDESHOW 0
    #endif

修改main.c

  1. 修改显示器分辨率
    1
    2
    disp_drv.hor_res    = 1024;
    disp_drv.ver_res = 600;
  2. 注释鼠标输入
    1
    2
    3
    4
    5
    /*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*/
  3. 注释其他不需要的地方,防止编译不通过,比如:// #include "lvgl/demos/lv_demos.h"

到这里main函数就修改完了,注意到main.c中就有custom_tick_get实现,其他可以按需修改
完整的main.c代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include "lvgl/lvgl.h"
// #include "lvgl/demos/lv_demos.h"
#include "lv_drivers/display/fbdev.h"
#include "lv_drivers/indev/evdev.h"
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <sys/time.h>

// #define DISP_BUF_SIZE (128 * 1024)
#define DISP_BUF_SIZE (1024 * 600)

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 = 1024;
disp_drv.ver_res = 600;
lv_disp_drv_register(&disp_drv);

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);

/* Initialize and register a display input driver */
lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv); /*Basic initialization*/

indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = evdev_read;
lv_indev_t * my_indev = lv_indev_drv_register(&indev_drv);

/*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*/


/*Create a Demo*/
// lv_demo_widgets();
lv_demo_music();

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

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;
}

Makefile配置

  1. 修改了交叉编译工具链为:CC := arm-buildroot-linux-gnueabihf-gcc.

使用echo $CROSS_COMPILE可以查看交叉编译链是什么

  1. 将鼠标样式的连接源文件注释掉# CSRCS +=$(LVGL_DIR)/mouse_cursor_icon.c
    完整的Makefile如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    #
    # Makefile
    #
    # CC ?= gcc
    CC := arm-buildroot-linux-gnueabihf-gcc
    LVGL_DIR_NAME ?= lvgl
    LVGL_DIR ?= ${shell pwd}
    CFLAGS ?= -O3 -g0 -I$(LVGL_DIR)/ -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
    LDFLAGS ?= -lm
    BIN = demo


    #Collect the files to compile
    MAINSRC = ./main.c

    include $(LVGL_DIR)/lvgl/lvgl.mk
    include $(LVGL_DIR)/lv_drivers/lv_drivers.mk
    # include $(LVGL_DIR)/lv_demos/lv_demos.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 -> OBJFILES

    all: default

    %.o: %.c
    @$(CC) $(CFLAGS) -c $< -o $@
    @echo "CC $<"

    default: $(AOBJS) $(COBJS) $(MAINOBJ)
    $(CC) -o $(BIN) $(MAINOBJ) $(AOBJS) $(COBJS) $(LDFLAGS)
    mkdir -p $(LVGL_DIR)/obj $(LVGL_DIR)/bin
    mv *.o $(LVGL_DIR)/obj/
    mv $(BIN) $(LVGL_DIR)/bin/

    clean:
    rm -f $(BIN) $(AOBJS) $(COBJS) $(MAINOBJ) ./bin/* ./obj/*

运行和结果

  1. 使用make编译后,将生成demo可执行文件。然后通过mount挂载,将编译好的demo放到开发板的/mnt/目录下

  2. 开发板上切换到/mnt目录,执行.demo运行

  3. 结果

    image-20241225194021333

    image-20241225194042463

    image-20241225194058499

    image-20241225194220788

注意事项

1.注意版本一致,不然容易出现各种各样稀奇古怪的问题,有可能无法编译。我甚至遇到过编译成功之后出来的可执行文件是64位的,而开发板是32位的,导致无法运行。
2.修改配置或者源码之后,如果不是预期的效果,可以试试先make clean,然后再make。

参考链接

【嵌入式Linux应用开发】1. 移植LVGL到Linux开发板