玩转树莓派屏幕之三:lvgl移植到树莓派

一、背景

由于前一篇的文章玩转树莓派屏幕之二:自定义屏幕显示中使用了python绘制图片,再将图片显示到屏幕的方式。只能用于简单显示一些文字,内容不够丰富。

自动动手去从0实现一个显示程序,肯定不显示的,需要了解树莓派嵌入式的底层原理,不是短时间内能够搞定的。我们现在是站在巨人的肩膀上,有很多开源的程序可以直接使用,例如LVGL组件,小巧性能高。

二、配置交叉编译工具

由于树莓派的性能比较弱,直接在树莓派上直接进行编译,效率低而且容易重启(编译中重启N次的经历.....),为了节省时间,提升效率,直接使用交叉编译吧(骚年,不要抱有侥幸的心理....)

1、安装WSL

使用ubuntu系统,工具较为齐全。我采用的是之前已经安装好的ubuntu20,懒得更改了....

2、安装交叉编译工具

更新软件包

首先需要更新一下软件包列表

# 更新软件包列表
sudo apt update
sudo apt upgrade -y
sudo apt install libevdev-dev
sudo apt install python3.8-venv

安装工具链

树莓派通常使用 ARMv6 (如 Pi 1, Zero) 或 ARMv7/ARMv8 (如 Pi 2, 3, 4, 5) 架构。最常用的工具链是 gcc-arm-linux-gnueabihf(用于 ARMv7+ 的 32 位系统)或 gcc-aarch64-linux-gnu(用于 64 位系统)

根据自己树莓派按照OS的操作系统来

安装 32 位 ARM 交叉编译器 (推荐用于大多数树莓派)

sudo apt install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf

安装 64 位 ARM 交叉编译器 (用于 64 位树莓派 OS)

sudo apt install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu

3、获取树莓派的系统根文件系统 (sysroot)

为了编译依赖系统库的程序(如使用 pthread、m、wiringPi 等),你需要将树莓派的 /usr 和 /lib 目录复制到 WSL 中作为 sysroot。我们选择直接从树莓派上复制过来

在树莓派上打包 /usr 和 /lib:

# 在树莓派上执行
sudo tar -czf pi-sysroot.tar.gz /usr /lib

将文件复制到 WSL:

# 例如通过 scp
scp pi@raspberrypi.local:~/pi-sysroot.tar.gz ~/

在 WSL 中解压:

mkdir -p ~/raspberry/sysroot
tar -xzf pi-sysroot.tar.gz -C ~/raspberry/sysroot --strip-components=1

假设我们使用的root用户,绝对路径为/root/raspberry/sysroot

三、编译lvgl demo

树莓派支持FrameBuffer方式进行显示,lvgl提供lv_port_linux demo,编译后可以直接在Linux上进行展示

1、下载源码:

下载源码

git clone https://github.com/lvgl/lv_port_linux.git
cd lv_port_linux/
git submodule update --init --recursive

git submodule update --init --recursive 下载失败,可以直接下载lvgl软件包

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

2、配置toolchain编译链

在项目中新建toolchain.cmake 文件,写入

set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)

set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++)

set(CMAKE_SYSROOT /root/raspberry/sysroot)
set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})

set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

3、设置CLion关联

1)点击 File -> settings -> ToolChains,新增WSL编译环境,其中ToolChains选择 aarch64-linux-gnu-gcc 和 aarch64-linux-gnu-g++
image

2)点击 File -> settings -> CMake,选择刚才增加的toolchains
image

CMake options中新增:

-DCMAKE_TOOLCHAIN_FILE=toolchain.cmake

image

4、编译

选择lvglsim,点击右侧小锤子进行编译:
image

编译结果在编译路径/bin/目录下:
image

4、运行lvglsim可执行文件

设置环境变量

由于使用的默认demo程序,需要调整显示的屏幕分辨率以及设置,需要设置全局变量

export LV_SIM_WINDOW_WIDTH=420
export LV_SIM_WINDOW_HEIGHT=380
export LV_LINUX_FBDEV_DEVICE=/dev/fb0

执行命令

将上一步中编译获取的lvglsim可执行文件传到树莓派上:

./lvglsim

效果展示

shell运行展示结果为:

root@raspberrypi:~/lcd# ./lvglsim
[Warn]  (0.000, +0)  lv_init: Memory integrity checks are enabled via LV_USE_ASSERT_MEM_INTEGRITY which makes LVGL much slower lv_init.c:321
[Warn]  (0.000, +0)  lv_init: Object sanity checks are enabled via LV_USE_ASSERT_OBJ which makes LVGL much slower lv_init.c:325
[Warn]  (0.000, +0)  lv_init: Style sanity checks are enabled that uses more RAM lv_init.c:329
[User]  (4478.457, +4478457)     init_pointer_evdev: Using evdev automatic discovery. evdev.c:160
[User]  (4478.458, +1)   discovery_cb: new 'ABS' device discovered evdev.c:109

展示效果图:
image

四、支持触摸屏:

https://github.com/lvgl/lv_port_linux/issues/43
1、将lv_config.h中的

#define LV_USE_EVDEV 1

2、加入代码:
/dev/input/event0 为触摸屏输入

 lv_evdev_create(LV_INDEV_TYPE_POINTER,"/dev/input/event0");

完整的C的main代码

int main(int argc, char **argv)
{

    configure_simulator(argc, argv);

    /* Initialize LVGL. */
    lv_init();

    // /dev/input/event0
    lv_evdev_create(LV_INDEV_TYPE_POINTER,"/dev/input/event0");

    /* Initialize the configured backend */
    if (driver_backends_init_backend(selected_backend) == -1) {
        die("Failed to initialize display backend");
    }

    /* Enable for EVDEV support */
#if LV_USE_EVDEV
    if (driver_backends_init_backend("EVDEV") == -1) {
        die("Failed to initialize evdev");
    }
#endif

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

    /* Enter the run loop of the selected backend */
    driver_backends_run_loop();

    return 0;
}

参考文章: