LVGL v8.2移植到IMX6ULL开发板

news2024/9/20 0:55:02

本文介绍嵌入式轻量化图形库LVGL 8.2移植到Linux开发板IMX6ULL的步骤。

文章目录

  • LVGL简介
  • 移植LVGL的硬件条件
  • 移植准备
    • 1. 源码下载
    • 2. 驱动加载
  • 移植过程
    • 源码修改
      • 1. 修改lv_conf.h
      • 2. 修改lv_drv_conf.h
      • 3. 修改main.c文件
      • 4. 修改Makefile文件
    • 源码编译
    • 效果演示

LVGL简介

LVGL最初是由匈牙利人Gabor Kiss-Vamosi所创建的,目前的更新到了稳定版本V8.3,本文档所移植的版本是V8.2。LVGL的官方文档和Github源码链接如下:

LVGL - Light and Versatile Embedded Graphics Library

LVGL in Github

LVGL是"Light and Versatile Graphics Library"的简称(早年又称之为"LittleVGL",后改名为此),叫做”轻量级多功能图形界面库“,是一种适用于大多数嵌入式设备的图形化界面库。与QT类似,LVGL借用了面向对象的编程思想,但使用的编程语言是C,这使得LVGL编程易于理解与上手。

移植LVGL的硬件条件

大多数嵌入式主板都可以支持LVGL(包括单片机、Arduino、以及Linux开发板等),但是处理器的位数必须是16位及以上

移植准备

1. 源码下载

本文档针对LVGL 8.2版本在Linux IMX6ULL开发板上移植LVGL需要下载的源码如下:

  • lvgl:https://github.com/lvgl/lvgl.git
  • lv_drivers:https://github.com/lvgl/lv_drivers.git
  • lv_port_linux_frame_buffer:https://github.com/lvgl/lv_port_linux_frame_buffer.git

lvgl:包含了LVGL基本的源码,以及官方给出的LVGL demo;

lv_drivers:包含了大多数设备的显示控制器和触摸驱动程序,主要用来指定显示屏使用哪一种驱动框架(包括FB、DRM等驱动程序框架);

lv_port_linux_frame_buffer:主函数文件所在的目录,整个工程的主文件夹,lvgl和lv_drivers都应放在此目录下。

可以在具有代理服务器的情况下克隆上述三个仓库的源码:

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

2. 驱动加载

在使用本文档的教程之前,请确保IMX6ULL已加载FB或DRM驱动

移植过程

源码修改

先将下载好的源码文件夹lvgl和lv_drivers放在lv_port_linux_frame_buffer的路径下;

shallwing@9d57f9229b66:~/lv_port_linux_frame_buffer$ ls
LICENSE  lv_conf.h  lv_drivers/  lv_drv_conf.h  lvgl/  main.c  Makefile  mouse_cursor_icon.c  README.md

下面将对源码中的Makefile、lv_conf.h、main.c、lv_drv_conf.h等文件进行修改,以达到让编译后的程序成功在IMX6ULL上运行的目标。

1. 修改lv_conf.h

打开lv_conf.h,先看到第15行,检查文件是否使能:

/* clang-format off */
#if 1 /*Set it to "1" to enable content*/

使能该文件,应该将#if后面的0改为1。

之后看到第27行,将宏LV_COLOR_DEPTH设置为16

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

这里的宏LV_COLOR_DEPTH表示的是显示屏的颜色深度,由于我们使用的LCD屏幕是RGB565格式的(即表示RGB三个颜色通道分别用5bytes、6bytes、5bytes的空间来存储),所以色深应该设置为16(bytes)。

然后看到48至67行的代码片段:

/*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 (2 * 1024U * 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*/

这一段代码是用来进行显存配置的,将LV_MEM_CUSTOM设置为1,则表示使能显存分配。在开启显存分配之后,系统便可以给LCD屏分配运行显存。

看到第80行至84行的代码:

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

这里设置的是屏幕的刷新时间,单位是毫秒(ms)。我们将其中的30ms改为10ms。

看到86至92行的代码:

/*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()`)*/
#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*/

这里设置的是心跳时间,在主函数文件main.c中有一个custom_tick_get的函数,用于之后的事件响应编程和定时任务编程,若此功能没有使能,则点击屏幕上的组件将没有响应

看到第671行,为了看到移植的效果,我们先使能官方的demo,来检测是否移植成功:

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

2. 修改lv_drv_conf.h

此文件用于配置显示屏所使用的底层驱动,我们使用DRM驱动框架来点亮LCD屏,所以对于此文件的修改,一律是屏蔽FBDEV而使能DRM。

看到第11行,先使能此文件,将“#if 0”改为“#if 1”:

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

看到第318行,屏蔽FBDEV的驱动,将宏USE_FBDEV改为0:

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

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

然后看到337行,使能DRM驱动,将USE_DRM改为1:

/*-----------------------------------------
 *  DRM/KMS device (/dev/dri/cardX)
 *-----------------------------------------*/
#ifndef USE_DRM
#  define USE_DRM           1
#endif

#if USE_DRM
#  define DRM_CARD          "/dev/dri/card0"
#  define DRM_CONNECTOR_ID  -1	/* -1 for the first connected one */
#endif

看到第441行,使能鼠标或者触摸板作为evdev界面,将USE_EVDEV设置为1:

#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         1               /*Scale and offset the touchscreen coordinates by using maximum and minimum values for each axis*/

#  if EVDEV_CALIBRATE
#    define EVDEV_HOR_MIN         0               /*to invert axis swap EVDEV_XXX_MIN by EVDEV_XXX_MAX*/
#    define EVDEV_HOR_MAX      800               /*"evtest" Linux tool can help to get the correct calibraion values>*/
#    define EVDEV_VER_MIN         0
#    define EVDEV_VER_MAX      480
#  endif  /*EVDEV_CALIBRATE*/
#endif  /*USE_EVDEV*/

除此之外,还需要指定evdev设备节点的路径,一般来说,evdev输入设备节点的路径在/dev/input下,对应于event文件,然而/dev/input下有可能有多个event文件,此时我们可以用hexdump命令来检测:

hexdump event1

运行上面的命令之后,再点击LCD屏一下,如果发现终端输出了一大堆十六进制数,则说明LCD的evdev的输入设备节点是它,否则,就换一个event文件进行测试。然后,需要就自己显示屏的分辨率来设置其中的EVDEV_HOR_MAX和EVDEV_VER_MAX,我们的LCD显示屏分辨率是800*480,所以两个宏分别设置为800和480。

evdev是输入设备的配置,只有使能了evdev,触摸屏幕才会有反应

3. 修改main.c文件

在main.c文件里面,我们主要修改其中包含的头文件、使用的驱动类型,以及demo函数等。

先看到文件开头包含的头文件,修改其中包含的驱动头文件,将第3行的"fbdev.h"改为"drm.h":

#include "lvgl/lvgl.h"
#include "lvgl/demos/lv_demos.h"
#include "lv_drivers/display/drm.h"
#include "lv_drivers/indev/evdev.h"
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <sys/time.h>

随后,看到第10行,修改显示缓冲区的大小,即800*480,分辨率的大小:

#define DISP_BUF_SIZE (800 * 480)

看到第18行,修改驱动设备的初始化函数,将fbdev_init()改为drm_init():

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

看到第27至34行的代码部分,这一段是初始化和设置显示驱动的部分:

/*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    = 800;
disp_drv.ver_res    = 480;
lv_disp_drv_register(&disp_drv);

照着我们的实际情况,进行修改与适配,由于LCD屏使用DRM驱动框架,所以将其中的flush_cb由fbdev_flush改为drm_flush,将其中的hor_res改为800,ver_res改为480,其他不改变。

看到代码的第46至50行,我们不使用鼠标作为LCD显示屏的输入设备,所以mouse部分将它注释掉:

#if 0
    /*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*/
#endif

看到第54行,这里面告诉了我们搭建整个LVGL应用程序的函数为lv_demo_widgets,通过查看main.c的头文件就可以知道,这个函数在“lvgl/demos/widgets/lv_demo_widgets.c”中定义。

/*Create a Demo*/
lv_demo_widgets();

4. 修改Makefile文件

打开主文件夹下的Makefile文件,对其进行修改。

看到第4行,将CC编译器修改为自己的交叉编译器,修改如下:

CC = /opt/buildroot/cortexA7/bin/arm-buildroot-linux-gnueabihf-gcc

注释掉第20行,使其不能编译鼠标输入设备的源码:

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

源码编译

源码修改完成之后,直接运行下面的命令进行编译:

make -j48

源码编译中会产生错误:

/home/shallwing/lv_port_linux_frame_buffer/lv_drivers/display/drm.c:28:10: 致命错误:drm_fourcc.h:No such file or directory
   28 | #include <drm_fourcc.h>
      |          ^~~~~~~~~~~~~~
编译中断。
make: *** [Makefile:35: /home/shallwing/lv_port_linux_frame_buffer/lv_drivers/display/drm.o] Error 1

这种错误的原因是没有找到/opt/buildroot下的头文件drm_fourcc.h,通过在/opt/buildroot下用find命令查找此文件,可以得到它的路径为:

./cortexA7/arm-buildroot-linux-gnueabihf/sysroot/usr/include/drm/drm_fourcc.h

所以文件”lv_drivers/display/drm.c“中应该改为:

#include <drm/drm_fourcc.h>

接着再次编译,又报出了"undefined reference"的错误:

drm.c:(.text+0x6c): undefined reference to `drmIoctl'
/opt/buildroot/cortexA7/lib/gcc/arm-buildroot-linux-gnueabihf/9.4.0/../../../..

这是因为系统在编译源码的时候,不会自动加载drm相关的链接器,需要我们自己加载,我们只需要在主Makefile中添加即可,在第8行和第9行之间添加:

LDFLAGS += -ldrm

接下来编译,虽然不会报错,但是会抛出“隐式声明函数custom_tick_get”或变量未使用的警告,这对后面的结果演示影响不大。在编译完成之后,源码主目录下会出现可执行文件demo,这便是最终生成的应用程序,我们接下来通过TFTP把它传到IMX6ULL开发板来进行效果演示。

效果演示

在IMX6ULL上运行demo之后,如果LCD屏出现下面的显示,则说明移植成功:

LVGL官方demo演示

此时IMX6ULL的串口终端还会打印配置信息,这也说明移植成功了:

root@igkboard:~# ./demo 
drm: Found plane_id: 31 connector_id: 35 crtc_id: 33
drm: 800x480 (0mm X 0mm) pixel format RG16
DRM subsystem and buffer mapped successfully

参考文档:
IMX6ULL移植LVGL

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/544326.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

内网渗透之权限维持-自启动映像劫持粘滞键辅助屏保后门WinLogon

0x01权限维持-自启动 以下几种方法都需服务器重启 1、自启动路径加载 C:\Users\Administrator\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup 将木马放到此目录&#xff0c;等待服务器重启即可 2、自启动服务加载 sc create ServiceTest binPath C:\1.exe …

上门家教预约小程序开发 良师就在你身边

社会的发展科技的进步让人们对教育的重视度也逐渐升高&#xff0c;很多家长可以说是为了孩子的教育操碎了心。在学校还好有老师辅导&#xff0c;节假日在家的时候&#xff0c;很多家长自己本身文化知识有限或者工作繁忙没有时间辅导&#xff0c;送去辅导班来回接送又很麻烦&…

LeetCode 116. 填充每个节点的下一个右侧节点指针

116. 填充每个节点的下一个右侧节点指针 描述 给定一个 完美二叉树 &#xff0c;其所有叶子节点都在同一层&#xff0c;每个父节点都有两个子节点。二叉树定义如下&#xff1a; struct Node {int val;Node *left;Node *right;Node *next; }填充它的每个 next 指针&#xff0…

【Win32】资源文件(对话框),逆向对话框回调函数,消息断点(附带恶意软件源码)

之前在学习windows编程的时候已经写过对话框的创建了&#xff0c;其中包括了对话框的分类&#xff0c;原理等等&#xff0c;大家可以去看一下&#xff1a;【windows编程之对话框】对话框原理&#xff0c;对话框的创建。原理今天就讲的不是很多了&#xff0c;直接给大家给出步骤…

inquirer 用户与命令行交互工具

学习脚手架的时候接触到inquirer &#xff0c;用来创建用户与命令行交互工具&#xff0c;使用方式如下&#xff1a; 1、安装 npm i -S inquirer 2、所有type使用范例 var inquirer require(inquirer);const questions [{type: confirm,name: order,message: 您好&#xf…

如何用 Midjourney 绘制你自己的拟人头像?

在元宇宙时代&#xff0c;许多人都使用各种AI应用程序来生成他们自己的头像&#xff0c;其中Midjourney是一个非常流行的选择。然而&#xff0c;成功的先行者可能不愿意透露具体的prompt&#xff08;提示语&#xff09;&#xff0c;因为他们可能担心自己的创意被其他人抄袭。此…

Talk | 北航助理教授孙庆赟 :图学习里的拓扑不均衡问题初探

本期为TechBeat人工智能社区第497期线上Talk&#xff01; 北京时间5月17日(周三)20:00&#xff0c;北京航空航天大学计算机学院 助理教授—孙庆赟的Talk将准时在TechBeat人工智能社区开播&#xff01; 她与大家分享的主题是: “图学习里的拓扑不均衡问题初探 ”&#xff0c;届…

Stream流 - 两个list集合对象属性的合并、对象属性值运算

两个list集合对象属性的合并 合并两个 list&#xff1c;map&#xff1e;, 并将 userId 相同的所有属性合并到一个 map 中 list1中对象的属性&#xff1a;userId、userName list2中对象的属性&#xff1a;userId、gender、age 最总集合中对象的属性&#xff1a;userId、user…

C# | [二进制字符串] 与 [字节数组] 互相转换,一行代码就搞定! - CodePlus系列

C#二进制字符串与字节数组互相转换 文章目录 C#二进制字符串与字节数组互相转换前言示例代码实现思路扩展方法说明引用CodePlus库结束语 前言 开发中有时需要将二进制数据转换为字符串或相反。虽然.NET提供了一些用于二进制数据操作的类库&#xff0c;但是它们的使用有时候会比…

Elasticsearch 安装 X-pack

X-Pack是Elastic Stack扩展功能&#xff0c;提供安全性&#xff0c;警报&#xff0c;监视&#xff0c;报告&#xff0c;机器学习和许多其他功能。 ES7.0之后&#xff0c;默认情况下&#xff0c;当安装Elasticsearch时&#xff0c;会安装X-Pack&#xff0c;无需单独再安装。 1.…

编程测试被候选人吐槽了?原因可能是这些

一位前Facebook Tech Lead曾经说过&#xff1a;面试就好像是在第一次约会的时候&#xff0c;就决定是不是要跟对方结婚。 这虽然是个无奈的笑话&#xff0c;但也真实地反映了技术面试中的一个难题&#xff1a;面试官需要在相当有限的时间里&#xff0c;准确地判断候选人的技术…

什么是自然语言处理的机器翻译?

机器翻译&#xff08;Machine Translation&#xff0c;MT&#xff09;是一种自然语言处理技术&#xff0c;旨在将一种语言的文本自动翻译成另一种语言。机器翻译是自然语言处理领域的重要应用之一&#xff0c;它可以帮助人们在跨语言交流、文档翻译和信息检索等方面更加便捷和高…

Maven聚合和继承,使用IDEA构建聚合与继承工程

文章目录 1 聚合步骤1:创建一个空的maven项目步骤2:将项目的打包方式改为pom步骤3:pom.xml添加所要管理的项目步骤4:使用聚合统一管理项目 2 继承步骤1:创建一个空的Maven项目并将其打包方式设置为pom步骤2:在子项目中设置其父工程步骤3:优化子项目共有依赖导入问题步骤4:优化子…

企业数据治理内训的好处这么多,赶紧安排!

数据治理是确保数据的质量和完整性的一种方法&#xff0c;这对企业非常重要&#xff0c;因为它们需要准确的数据来做出正确的决策。 学习有效地管理和维护数据 通过内部培训&#xff0c;员工可以学习如何有效地管理和维护数据&#xff0c;从而提高数据质量和可靠性。 帮助企业…

SpringAop的实践应用

使用AOP来对前端传来的对象参数进行 BaseDto中属性的填充 这样就不用每次都去UserThreadLocal中拿了再又往BaseDto中放了 Aspect Component public class UserAspect {Pointcut("annotation(org.springframework.web.bind.annotation.RequestMapping) || annotation(o…

测试将被开发、运维替代?我后悔了!?

记得在求职的时候&#xff0c;面试官经常问我&#xff1a;“为什么要选择软件测试工作?”而我也会经常说一堆自己有的没的优势去应付。 工作这么久了&#xff0c;也不再浮躁&#xff0c;静下心来回忆当初选择软件测试工作的历程&#xff0c;也是对自己职业生涯的一次回顾。 …

每日一题162——重塑矩阵

在 MATLAB 中&#xff0c;有一个非常有用的函数 reshape &#xff0c;它可以将一个 m x n 矩阵重塑为另一个大小不同&#xff08;r x c&#xff09;的新矩阵&#xff0c;但保留其原始数据。 给你一个由二维数组 mat 表示的 m x n 矩阵&#xff0c;以及两个正整数 r 和 c &…

Conmi的正确答案——Cordova安装并编译Android应用

系统&#xff1a;debian 11 Cordova版本&#xff1a;11.1.0 Cordova的Android平台&#xff1a;10.1.2 当前安卓最新稳定API&#xff1a;33&#xff08;Android版本列表&#xff09; 1、安装npm&#xff08;cordova是基于nodejs开发的&#xff09; apt install npm -y2、使用n…

OpenCV中的图像处理3.10(八)直方图-寻找、绘制、分析(掩膜)与均衡化

目录 3.10 OpenCV中的直方图3.10.1 直方图--1&#xff1a;寻找、绘制、分析目标理论寻找直方图绘制直方图掩膜的应用其他资源 3.10.2 直方图--2&#xff1a;直方图均衡化目标理论OpenCV中的直方图均衡化CLAHE&#xff08;对比度有限的自适应直方图均衡&#xff09;其他资源 翻译…

【笔试强训选择题】Day16.习题(错题)解析

作者简介&#xff1a;大家好&#xff0c;我是未央&#xff1b; 博客首页&#xff1a;未央.303 系列专栏&#xff1a;笔试强训选择题 每日一句&#xff1a;人的一生&#xff0c;可以有所作为的时机只有一次&#xff0c;那就是现在&#xff01;&#xff01;&#xff01; 文章目录…