LVGL 的移植

  1. 1. 将 LVGL 添加至项目
  2. 2. 修改配置文件
  3. 3. 初始化 LVGL,添加显示及输入驱动函数
  4. 4. Demo

最近了解了一下开源图形库 LVGL,现在记录一下移植的过程。其实 LVGL 移植挺方便的,仅需实现两个函数。

将 LVGL 添加至项目

首先,在 GitHub Release 中下载最新版本的源代码。下载完成后,将解压出的文件夹复制到工程目录的一个文件夹中(这里以Middlewares/lvgl/为例)。

此处的 MakefileSTM32CubeMX 生成。在 Makefile 中添加:

1
2
3
4
5
6
7
8
9
# LVGL Makefiles
LVGL_DIR = Middlewares
LVGL_DIR_NAME = lvgl

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

C_SOURCES += $(CSRCS)

C_INCLUDES += -IMiddlewares

修改配置文件

lv_conf_template.h文件复制到Middlewares/文件夹下,重命名为lv_conf.h,随后打开文件,将#if 0改为#if 1

修改屏幕大小,色深等参数:

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
/* Maximal horizontal and vertical resolution to support by the library.*/
#define LV_HOR_RES_MAX (240)
#define LV_VER_RES_MAX (320)

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

/* 如果使用了 RTOS,需修改内存管理函数 */
/* 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 used by `lv_mem_alloc` in bytes (>= 2kB)*/
# define LV_MEM_SIZE (32U * 1024U)

/* Complier prefix for a big array declaration */
# define LV_MEM_ATTR

/* Set an address for the memory pool instead of allocating it as an array.
* Can be in external SRAM too. */
# define LV_MEM_ADR 0

/* Automatically defrag. on free. Defrag. means joining the adjacent free cells. */
# define LV_MEM_AUTO_DEFRAG 1
#else /*LV_MEM_CUSTOM*/
# define LV_MEM_CUSTOM_INCLUDE <rtthread.h> /*Header for the dynamic memory function*/
# define LV_MEM_CUSTOM_ALLOC rt_malloc /*Wrapper to malloc*/
# define LV_MEM_CUSTOM_FREE rt_free /*Wrapper to free*/
#endif /*LV_MEM_CUSTOM*/

/* 重定义获取时基函数,如果不重定义,需手动调用 lv_tick_inc 更新时基 */
/* 1: use a custom tick source.
* It removes the need to manually update the tick with `lv_tick_inc`) */
#define LV_TICK_CUSTOM 1
#if LV_TICK_CUSTOM == 1
#define LV_TICK_CUSTOM_INCLUDE "rtthread.h" /*Header for the system time function*/
#define LV_TICK_CUSTOM_SYS_TIME_EXPR (rt_tick_get()) /*Expression evaluating to current system time in ms*/
#endif /*LV_TICK_CUSTOM*/

初始化 LVGL,添加显示及输入驱动函数

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
#include <lvgl/lvgl.h>

/* LVGL 缓冲区 */
static lv_disp_buf_t disp_buf;
static lv_color_t buf_1[LV_HOR_RES_MAX * 10];
/* LVGL 显示驱动 */
static lv_disp_drv_t disp_drv;
/* LVGL 显示器 */
static lv_disp_t *disp;
/* LVGL 输入驱动 */
static lv_indev_drv_t indev_drv;

/* 初始化 LVGL */
lv_init();
/* 初始化相关外设及屏幕 */
LCD_Init();
/* 初始化缓冲区和驱动 */
lv_disp_buf_init(&disp_buf, buf_1, NULL, LV_HOR_RES_MAX * 10);
lv_disp_drv_init(&disp_drv);
disp_drv.buffer = &disp_buf;
disp_drv.flush_cb = disp_flush;
disp = lv_disp_drv_register(&disp_drv);
/* 注册触摸屏驱动 */
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = Input_Read;
__unused lv_indev_t *indev = lv_indev_drv_register(&indev_drv);

/**
* @brief LCD 输出驱动
*
* @param disp_drv 显示驱动
* @param area 刷新区域
* @param color_p 颜色缓冲区指针
*/
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
/* The most simple case (but also the slowest) to put all pixels to the screen one-by-one */
/* 可使用 DMA 或 GPU 来加速 */
int32_t x;
int32_t y;
for(y = area->y1; y <= area->y2; y++) {
for(x = area->x1; x <= area->x2; x++) {
/* 显示一个像素,例如: */
put_px(x, y, *color_p);
color_p++;
}
}

/* IMPORTANT!!!
* Inform the graphics library that you are ready with the flushing*/
lv_disp_flush_ready(disp_drv);
}

/**
* @brief LVGL 输入驱动函数
*
* @param drv 驱动结构体
* @param data 数据结构体
* @return true 缓冲区中有数据
* @return false 缓冲区中无数据
*/
bool Input_Read(lv_indev_drv_t *drv, lv_indev_data_t *data) {
/* 此处用的是正点原子提供的驱动库 */
/* 转换坐标 */
Convert_Pos();
/* 返回坐标 */
data->point.x = Pen_Point.X0;
data->point.y = Pen_Point.Y0;
data->state = HAL_GPIO_ReadPin(TPEN_GPIO_Port, TPEN_Pin) ? LV_INDEV_STATE_REL : LV_INDEV_STATE_PR;
/* 因为没有 buffer,所以始终返回 false */
return false;
}

需要周期调用lv_task_handler()以处理输入事件和刷新屏幕。

Demo

这里提供了一个 LVGL 官方的demo,可以测试一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
lv_obj_t *btn = lv_btn_create(lv_scr_act(), NULL);   // Add a button the current screen
lv_obj_set_pos(btn, 10, 10); // Set its position
lv_obj_set_size(btn, 100, 50); // Set its size
lv_obj_set_event_cb(btn, btn_event_cb); // Set button callback
lv_obj_t *label_title = lv_label_create(btn, NULL); // Add a label to the button

/**
* @brief 按钮回调函数
*
* @param obj 按钮对象
* @param event 事件
*/
void btn_event_cb(lv_obj_t *obj, lv_event_t event) {
if (event == LV_EVENT_CLICKED) {
lv_label_set_text(label_title, "clicked");
}
}

本网站所有文章除特别声明外,均采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。