LVGL(轻便而多功能的图形库)是一个免费的开放源代码图形库,它提供创建具有易于使用的图形元素,精美的视觉效果和低内存占用的嵌入式GUI所需的一切。

1.官网https://lvgl.io

2.Gihub主页https://github.com/lvgl 在这里可以下载到lvgl的驱动和各板子上的demo。以及windows下和linux下的仿真工程

首先了解下目录构成,lvgl基本构成有lvgl库本身这个也是最核心的东西了,构建的时候也是必须要的了。然后就是lv_examples这个我们移植的时候可以加上,但实际自己的项目中是不需要存在的。lv_drivers这个目录是驱动目录,理论上来说也不是必要的,但是没有驱动的话lvgl显示的东西就不知道在哪了,不可能只显示到内存上而不刷新到LCD或者显示设备上吧。而且ui通常都还需要配备输入设备。所以这个目录包含的就是显示驱动以及输入设备的驱动等。

啰嗦完了开始移植。

1.建个目录  mkdir my_lvgl   cd my_lvgl

2.从github上下载lvgl到本地 https://github.com/lvgl/lvgl 上我们选择最新的释放版本release/v7 我们只下载当前版本来使用。

git clone -b release/v7 --depth 1 https://github.com/lvgl/lvgl.git 下载lvgl核心库

git clone -b release/v7 --depth 1  https://github.com/lvgl/lv_drivers.git 下载驱动

lvgl里面都用一个模板配置拷贝出来稍微修改下就行了。

lvgl模板拷贝出来修改 cp lvgl/lv_conf_template.h ./lv_conf.h

lv_conf.h中 if 0 改为 if 1

最重要的需要配置的有一下几个可以搜索并配置:

这几个就必须要根据板子实际情况修改了

#define LV_HOR_RES_MAX          (480)  //屏幕水平宽度 根据实际使用修改

#define LV_VER_RES_MAX          (320)  //屏幕垂直高度 根据实际使用修改

#define LV_COLOR_DEPTH     16           //LCD 屏幕的像素深度。一般的可能是rgb565 也有是rgb8888的就写 32 根据实际使用修改

#define LV_USE_GPU              1           //这个记得给他置0了一般的板子应该没有gpu

下面这几个可以不动

#define LV_USE_PERF_MONITOR     0  //右下角cpu信息输出。测试可以打开看下

#  define LV_MEM_SIZE    (32U * 1024U) //这里默认的是32K 用作lvgl的动态内存分配。可以根据实际情况修改,但是要大于等于2KB

#define LV_DISP_DEF_REFR_PERIOD      30   //刷新周期 30ms 就是刷新速率问题,看性能设置吧。

#define LV_INDEV_DEF_READ_PERIOD          30//输入设备的扫描时间,就是轮询按键的时间。默认30ms。

#define LV_USE_FILESYSTEM       1    //文件系统不用也可以给置0了

#define LV_USE_DEBUG        1         //debug信息输出可关闭

驱动修改

将lvgl driver模板拷贝出来修改 cp lv_drivers/lv_drv_conf_template.h ./lv_drv_conf.h

修改头 if 0改为 if 1

#  define USE_FBDEV           0    //把这个置位为1

#  define FBDEV_PATH          "/dev/fb0"  //确认是否为你设备的fb

编译输出

mkdir obj 创建一个输出目录用于输出生成的.o文件

在obj目录下创建一个Makefile 毕竟编译方便输入下面语句

CC =  arm-linux-gnueabihf-gcc  #这里使用你板子编译的gcc最好指定有路径的更明确

LVGL_DIR ?= /home/xxx/study/my_lvgl  #创建lvgl的根目录

LVGL_DIR_NAME ?= lvgl

CFLAGS ?= -O3 -g0 -I$(LVGL_DIR)/

LDFLAGS ?= -lm

BIN = lvgl_demo

MAINSRC = ../main.c

include $(LVGL_DIR)/lvgl/lvgl.mk

include $(LVGL_DIR)/lv_drivers/lv_drivers.mk

OBJEXT ?= .o

AOBJS = $(ASRCS:.S=$(OBJEXT))

COBJS = $(CSRCS:.c=$(OBJEXT))

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

SRCS = $(ASRCS) $(CSRCS) $(MAINSRC)

OBJS = $(AOBJS) $(COBJS)

all: default

%.o: %.c

@$(CC)  $(CFLAGS) -c $(INCLUDES) $< -o $@

@echo "CC $

default: $(AOBJS) $(COBJS) $(MAINOBJ)

$(CC) -o $(BIN) $(MAINOBJ) $(AOBJS) $(COBJS) $(LDFLAGS)

cp $(BIN) ../

clean:

rm -f $(BIN) $(AOBJS) $(COBJS) $(MAINOBJ)

为了可以直接在根目录编译,所以在根目录创建一个Makefile加入如下脚本这样就可以直接在根目录进行make了

all:

make  -C obj

clean:

make clean -C obj

添加测试代码

在lvgl根目录下新建main.c加入如下代码。

#include "lvgl/lvgl.h"

#include "lv_drivers/display/fbdev.h"

#define DISP_BUF_SIZE (100 * LV_HOR_RES_MAX) //lvgl用于绘制屏幕的缓冲区。这里用100行像素作为缓冲区这个可以修改。

static lv_obj_t *label;

static void refresh_ui_task(lv_task_t * task)

{

char temp[50];

static int count = 0;

sprintf(temp, "hello why can %d", count++);

lv_label_set_text(label, temp);

}

static void button_event(lv_obj_t * obj, lv_event_t event)

{

if (event == LV_EVENT_CLICKED) {

printf("button event\n");

}

}

static void video_disp_window()

{

lv_obj_t *  this_win = lv_cont_create(lv_scr_act(), NULL);

lv_obj_set_size(this_win, LV_HOR_RES, LV_VER_RES);

lv_obj_t *btn = lv_btn_create(this_win, NULL);

label = lv_label_create(btn, NULL);

lv_label_set_text(label, "hello why can");

lv_obj_set_event_cb(btn, button_event);

lv_task_create(refresh_ui_task, 1000, LV_TASK_PRIO_MID, NULL);

}

int main(int argc, char **argv)

{

lv_init();  //lvgl gui初始化

fbdev_init(); //fb初始化 此函数在 lv_drivers/display/fbdev.c 中,就是打开fb设备映射显存出来使用

static lv_color_t buf[DISP_BUF_SIZE];

static lv_disp_buf_t disp_buf;

lv_disp_buf_init(&disp_buf, buf, NULL, DISP_BUF_SIZE);

lv_disp_drv_t disp_drv;

lv_disp_drv_init(&disp_drv);

disp_drv.buffer = &disp_buf;

disp_drv.flush_cb = fbdev_flush; //fbdev_flush这就是输入显示驱动提供的操作函数 刷新数据到显存的函数。其他非Linux fb移植照葫芦即可。

lv_disp_drv_register(&disp_drv);

video_disp_window();

while (1)

{

lv_task_handler();

usleep(5000);

}

}

根目录make一下 拷贝到板子上试下呗。

然后你会发现任务刷新的label并没有改变。原因就是lvgl还需要提供一个计时的心跳包给他,按照官网的说明是调用lv_tick_inc这个函数来设置lvgl的时钟。官网移植给的建议是linux开个线程来调用这个函数,实际上有另外一种方式就是用用户的时钟源(这里用Linux下的gettimeofday比用usleep好使)。

在lv_conf.h中

LV_TICK_CUSTOM  0   置为1 这个就是使用用户的时钟源使能

#define LV_TICK_CUSTOM_INCLUDE  "Arduino.h" //将这个头文件改为 (gettimeofday 的头文件)

#define LV_TICK_CUSTOM_SYS_TIME_EXPR (millis())   //宏定义声明外部函数,就是提供一个能获取时间的函数给lvgl。这个我们自己写个在main中 写个my_tick() 所以这里替换掉millis()即可。

在 main.c中增加此函数 记得加 这个头文件

uint32_t my_tick(void)

{

static uint64_t start_ms = 0;

struct timeval tv_time;

if(start_ms == 0) {

gettimeofday(&tv_time, NULL);

start_ms = (tv_time.tv_sec * 1000000 + tv_time.tv_usec) / 1000;

}

gettimeofday(&tv_time, 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;

}

至此 make一下拷贝到板子运行即可count就加起来了。

最后再加入个触摸点击

触摸输入在lv_drv_conf.h

#  define USE_EVDEV           0 这个 置为1

#  define EVDEV_NAME   "/dev/input/event0"    //这个就是输入设备了看你自己的是event几了。

在main.c的main函数中加入

evdev_init();

lv_indev_drv_t indev_drv;

lv_indev_drv_init(&indev_drv);

indev_drv.type =LV_INDEV_TYPE_POINTER;

indev_drv.read_cb =evdev_read;

lv_indev_drv_register(&indev_drv);

记得添加头文 #include "lv_drivers/indev/evdev.h"

make一下到板子试试。这样就可以妥妥的运行起来了。

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐