当前位置: 首页 > news >正文

LVGL代码移植(裸机+FreeRTOS操作系统+内部SRAM+外部SRAM+内存管理算法+编译错误以及现象显示不正常)

说明

我使用的是正点原子官方的探索者F407ZGT6开发板以及一块我自己之前学习STM32的最小系统板(带屏幕),读者完全可以使用不同的板子或者其他类型的单片机,因为LVGL的移植性非常之高,所以我这里会更突出的进行LVGL的讲解,关于STM32相关的知识,我会讲解的比较简略。

LVGL移植整体流程 

移植准备工作  

1. 准备基础工程
在移植 LVGL 之前,用户必须在实验中选择一个例程作为移植的基础工程,值得注意的是,最终的基础工程中必须包含 LCD 显示驱动 触摸屏驱动 以及 基本定时器驱动。 如果需要在FreeRTOS上移植LVGL,那我们还要准备对应的FreeRTOS文件,移植进去我们的基础例程。

 

这里建议读者使用自己开发板的《内存管理实验》作为基本例程,移植触摸屏、定时器相关的代码进入这个基本工程,至于为什么选择内存管理实验,是因为,我们LVGL的内存管理可以分配在不同的内存,包括内部SRAM、外部SRAM,使用《内存管理实验》这个实验,我们之后可以稍微在源代码里面修改一下,就可以将LVGL的内存分配在不同的地方,方便我们进行观察。 

切记,你准备好的基础例程在移植完之后,你要编译一下,看一下有没有报错,这里如果报错的话,要先进行解决,不然到时候移植LVGL的时候,出现报错的话,不好找到错误的原因。 

2. 准备 LVGL 源码 

LVGL 官方的 GitHub 仓库 ( https://github.com/lvgl/lvgl/ )中可以下载源码,解压 lvgl-maser.zip 压缩包后,即可得到如下图所示的文件和文件夹:

3. 把上图中的 lv_conf_template.h 文件改名为 lv_conf.h

4. 打开 lv_conf.h 文件,修改条件编译指令。

修改前:

#if 0 /*Set it to "1" to enable content*/

修改后:

#if 1 /* #if 0 修改成 

5. 精简 LVGL 源码

除了 examples 文件夹、 src 文件夹、 lv_conf_template.h lvgl.h 文件,其他的文件和文件夹
均与移植无关,我们可以将它们删除,这样即可得到 LVGL 的精简源码,如下图所示(保留了demos 文件夹):

由上图可知, demos 文件夹没有被删除,该文件夹中存放的是 LVGL 官方的演示例程,后
续我们将移植其中的一些示例。 接下来我们打开上图中的 examples 文件夹,仅保留其中的 porting 文件夹,其他的文件和文件夹皆可删除 ,删减后如下图所示:

裸机移植LVGL 

 我这里的例程使用的是正点原子的基本例程,工程目录为:

Middlewares文件夹在不同的例程里面可能不一样,你可以存放在你自己喜欢的文件夹下面,不过为了方便跟着我学习,可以自己建一个Middlewares文件夹,然后在其目录下添加对应的子文件,如图所示: 

我们将我们移植好触摸屏、定时器的基本例程名字改为LVGL例程1无操作系统移植,把精简后的 LVGL 源码(图 2.1.3 中的文件和文件夹)复制到《LVGL 例程 1 无操作系统移植》的Middlewares/LVGL/GUI/lvgl 路径下,如下图所示:

打开《 LVGL移植课堂代码1 》工程,编译。然后添加这几个工程分组:

添加LVGL相关的.c文件到相应分组,如下: 

添加头文件路径 :

开启C99模式

我们然后编译一下看一下, 这里虽然会有很多警告,但是并不会进行报错,我们就可以进行下一步的移植。

修改工程文件

1. LVGL 提供时基

打开 Drivers/BSP 分组中的 btim.c 文件(这里读者变成自己工程的定时器文件),声明 LVGL 的头文件,如下源码所示:
#include "lvgl.h"  
在该文件下的 HAL_TIM_PeriodElapsedCallback 函数中添加以下源码:
void HAL_TIM_PeriodElapsedCallback ( TIM_HandleTypeDef * htim )
{
        if ( htim == (& g_timx_handle ))
        {
        lv_tick_inc ( 1 ); /* lvgl 1ms 心跳 */
        }
}
上述源码中,定时器中断回调函数调用了 LVGL lv_tick_inc 函数,该函数可以让 LVGL内部的时基参数加 1 (入口参数为 1 的情况下), 这里要注意,你定时器设置多少毫秒进入一次中断,那么, lv_tick_inc();函数的入口参数就设置为多少,不然时基就不准确。

接下来我们打开 main.c 文件,在 main 函数中,对基本定时器进行初始化,并设置中断周期为 1ms,这里不同板子的设置是不一样的,读者根据定时器的主频自行进行修改。

2. 配置显示屏、触摸输入驱动

打开 Middlewares/lvgl/examples/porting 分组中的 lv_port_disp_template.c/h(显示屏相关)

lv_port_indev_template.c/h (触摸输入相关)文件,将这 4 个文件中的条件编译指令 #if 0 都修改成#if 1 ,如下源码所示:

修改前:

#if 0 /* lv_port_disp_template.c/h lv_port_indev_template.c/h */ 

修改后:

#if 1 /* #if 0 修改成#if 1 */ 

① 修改 lv_port_disp_template.c 文件

该文件用于配置显示屏,它可以将用户的底层显示驱动与 LVGL 的显示驱动衔接起来。我们打开官方提供的 lv_port_disp_template.c 文件,包含 LCD 驱动头文件,然后初始化 LCD 以及配置打点函数,修改后的源码如下:
/*** @file lv_port_disp_templ.c**//*Copy this file as "lv_port_disp.c" and set this value to "1" to enable content*/
#if 1/**********************      INCLUDES*********************/
#include "lv_port_disp_template.h"
#include "../../lvgl.h"
/* 导入lcd驱动头文件 */
#include "./BSP/LCD/lcd.h"/**********************      DEFINES*********************/
#define USE_SRAM        0       /* 使用外部sram为1,否则为0 */
#ifdef USE_SRAM
#include "./MALLOC/malloc.h"
#endif#define MY_DISP_HOR_RES (800)   /* 屏幕宽度 */
#define MY_DISP_VER_RES (480)   /* 屏幕高度 *//***********************      TYPEDEFS**********************//***********************  STATIC PROTOTYPES**********************/
/* 显示设备初始化函数 */
static void disp_init(void);/* 显示设备刷新函数 */
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p);
/* GPU 填充函数(使用GPU时,需要实现) */
//static void gpu_fill(lv_disp_drv_t * disp_drv, lv_color_t * dest_buf, lv_coord_t dest_width,
//        const lv_area_t * fill_area, lv_color_t color);/***********************  STATIC VARIABLES**********************//***********************      MACROS**********************//***********************   GLOBAL FUNCTIONS**********************/
/*** @brief       LCD加速绘制函数* @param       (sx,sy),(ex,ey):填充矩形对角坐标,区域大小为:(ex - sx + 1) * (ey - sy + 1)* @param       color:要填充的颜色* @retval      无*/
void lcd_draw_fast_rgb_color(int16_t sx, int16_t sy,int16_t ex, int16_t ey, uint16_t *color)
{uint16_t w = ex-sx+1;uint16_t h = ey-sy+1;lcd_set_window(sx, sy, w, h);uint32_t draw_size = w * h;lcd_write_ram_prepare();for(uint32_t i = 0; i < draw_size; i++){lcd_wr_data(color[i]);}
}/*** @brief       初始化并注册显示设备* @param       无* @retval      无*/
void lv_port_disp_init(void)
{/*-------------------------* 初始化显示设备* -----------------------*/disp_init();/*-----------------------------* 创建一个绘图缓冲区*----------------------------*//*** LVGL 需要一个缓冲区用来绘制小部件* 随后,这个缓冲区的内容会通过显示设备的 `flush_cb`(显示设备刷新函数) 复制到显示设备上* 这个缓冲区的大小需要大于显示设备一行的大小** 这里有3中缓冲配置:* 1. 单缓冲区:*      LVGL 会将显示设备的内容绘制到这里,并将他写入显示设备。** 2. 双缓冲区:*      LVGL 会将显示设备的内容绘制到其中一个缓冲区,并将他写入显示设备。*      需要使用 DMA 将要显示在显示设备的内容写入缓冲区。*      当数据从第一个缓冲区发送时,它将使 LVGL 能够将屏幕的下一部分绘制到另一个缓冲区。*      这样使得渲染和刷新可以并行执行。** 3. 全尺寸双缓冲区*      设置两个屏幕大小的全尺寸缓冲区,并且设置 disp_drv.full_refresh = 1。*      这样,LVGL将始终以 'flush_cb' 的形式提供整个渲染屏幕,您只需更改帧缓冲区的地址。*//* 单缓冲区示例) */static lv_disp_draw_buf_t draw_buf_dsc_1;
#if USE_SRAMstatic lv_color_t buf_1 = mymalloc(SRAMEX, MY_DISP_HOR_RES * MY_DISP_VER_RES);              /* 设置缓冲区的大小为屏幕的全尺寸大小 */lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, NULL, MY_DISP_HOR_RES * MY_DISP_VER_RES);     /* 初始化显示缓冲区 */
#elsestatic lv_color_t buf_1[MY_DISP_HOR_RES * 10];                                              /* 设置缓冲区的大小为 10 行屏幕的大小 */lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, NULL, MY_DISP_HOR_RES * 10);                  /* 初始化显示缓冲区 */
#endif/* 双缓冲区示例) */
//    static lv_disp_draw_buf_t draw_buf_dsc_2;
//    static lv_color_t buf_2_1[MY_DISP_HOR_RES * 10];                                            /* 设置缓冲区的大小为 10 行屏幕的大小 */
//    static lv_color_t buf_2_2[MY_DISP_HOR_RES * 10];                                            /* 设置另一个缓冲区的大小为 10 行屏幕的大小 */
//    lv_disp_draw_buf_init(&draw_buf_dsc_2, buf_2_1, buf_2_2, MY_DISP_HOR_RES * 10);             /* 初始化显示缓冲区 *//* 全尺寸双缓冲区示例) 并且在下面设置 disp_drv.full_refresh = 1 */
//    static lv_disp_draw_buf_t draw_buf_dsc_3;
//    static lv_color_t buf_3_1[MY_DISP_HOR_RES * MY_DISP_VER_RES];                               /* 设置一个全尺寸的缓冲区 */
//    static lv_color_t buf_3_2[MY_DISP_HOR_RES * MY_DISP_VER_RES];                               /* 设置另一个全尺寸的缓冲区 */
//    lv_disp_draw_buf_init(&draw_buf_dsc_3, buf_3_1, buf_3_2, MY_DISP_HOR_RES * MY_DISP_VER_RES);/* 初始化显示缓冲区 *//*-----------------------------------* 在 LVGL 中注册显示设备*----------------------------------*/static lv_disp_drv_t disp_drv;                  /* 显示设备的描述符 */lv_disp_drv_init(&disp_drv);                    /* 初始化为默认值 *//* 建立访问显示设备的函数  *//* 设置显示设备的分辨率* 这里为了适配正点原子的多款屏幕,采用了动态获取的方式,* 在实际项目中,通常所使用的屏幕大小是固定的,因此可以直接设置为屏幕的大小 */disp_drv.hor_res = lcddev.width;disp_drv.ver_res = lcddev.height;/* 用来将缓冲区的内容复制到显示设备 */disp_drv.flush_cb = disp_flush;/* 设置显示缓冲区 */disp_drv.draw_buf = &draw_buf_dsc_1;/* 全尺寸双缓冲区示例)*///disp_drv.full_refresh = 1/* 如果您有GPU,请使用颜色填充内存阵列* 注意,你可以在 lv_conf.h 中使能 LVGL 内置支持的 GPUs* 但如果你有不同的 GPU,那么可以使用这个回调函数。 *///disp_drv.gpu_fill_cb = gpu_fill;/* 注册显示设备 */lv_disp_drv_register(&disp_drv);
}/***********************   STATIC FUNCTIONS**********************//*** @brief       初始化显示设备和必要的外围设备* @param       无* @retval      无*/
static void disp_init(void)
{/*You code here*/lcd_init();         /* 初始化LCD */lcd_display_dir(1); /* 设置横屏 */
}/*** @brief       将内部缓冲区的内容刷新到显示屏上的特定区域*   @note      可以使用 DMA 或者任何硬件在后台加速执行这个操作*              但是,需要在刷新完成后调用函数 'lv_disp_flush_ready()'** @param       disp_drv    : 显示设备*   @arg       area        : 要刷新的区域,包含了填充矩形的对角坐标*   @arg       color_p     : 颜色数组** @retval      无*/
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{/* LVGL 官方给出的一个打点刷新屏幕的例子,但这个效率是最低效的 *///    int32_t x;
//    int32_t y;
//    for(y = area->y1; y <= area->y2; y++) {
//        for(x = area->x1; x <= area->x2; x++) {
//            /*Put a pixel to the display. For example:*/
//            /*put_px(x, y, *color_p)*/
//            color_p++;
//        }
//    }//    /* 在指定区域内填充指定颜色块 */
//    lcd_color_fill(area->x1, area->y1, area->x2, area->y2, (uint16_t *)color_p);lcd_draw_fast_rgb_color(area->x1,area->y1,area->x2,area->y2,(uint16_t*)color_p);/* 重要!!!* 通知图形库,已经刷新完毕了 */lv_disp_flush_ready(disp_drv);
}/* 可选: GPU 接口 *//* 如果你的 MCU 有硬件加速器 (GPU) 那么你可以使用它来为内存填充颜色 */
/*** @brief       使用 GPU 进行颜色填充*   @note      如有 MCU 有硬件加速器 (GPU),那么可以用它来为内存进行颜色填充** @param       disp_drv    : 显示设备*   @arg       dest_buf    : 目标缓冲区*   @arg       dest_width  : 目标缓冲区的宽度*   @arg       fill_area   : 填充的区域*   @arg       color       : 颜色数组** @retval      无*/
//static void gpu_fill(lv_disp_drv_t * disp_drv, lv_color_t * dest_buf, lv_coord_t dest_width,
//                    const lv_area_t * fill_area, lv_color_t color)
//{
//    /*It's an example code which should be done by your GPU*/
//    int32_t x, y;
//    dest_buf += dest_width * fill_area->y1; /*Go to the first line*///    for(y = fill_area->y1; y <= fill_area->y2; y++) {
//        for(x = fill_area->x1; x <= fill_area->x2; x++) {
//            dest_buf[x] = color;
//        }
//        dest_buf+=dest_width;    /*Go to the next line*/
//    }
//}#else /*Enable this file at the top*//*This dummy typedef exists purely to silence -Wpedantic.*/
typedef int keep_pedantic_happy;
#endif
由上述源码可知,配置 LVGL 显示屏驱动的步骤可分为以下 7 步:
(1) 调用函数 disp_init 初始化 LCD 驱动
(2) 调用函数 lv_disp_draw_buf_init 初始化缓冲区(最低标准:显示屏的宽度分配率*10)。
(3) 调用函数 lv_disp_drv_init 初始化显示驱动。
(4) 设置显示的高度与宽度
(5) 注册显示驱动回调(打点函数)
(6) 设置显示驱动的绘画缓冲区。
(7) 调用 lv_disp_drv_register 函数注册显示驱动到 LVGL 列表中。
在整个配置的过程中,实际上我们只需要提供两个函数:LCD 初始化函数和 LCD 填充函
数。如果想设置屏幕的方向,则调用 lcd_display_dir 函数即可。
注意: 如果需要使用 DMA2D 外设,请打开 lv_conf.h 文件,将 LV_USE_GPU_STM32_D
MA2D 宏定义置 1 ,并在 LV_GPU_DMA2D_CMSIS_INCLUDE 宏定义中添加 MCU 的头文件路
径。值得注意的是,不同 MCU 所对应的头文件不同,比如:
#include stm32f429xx.h
#include stm32h743xx.h
#include stm32h767xx.h
#include stm32f750xx.h
#include stm32h750xx.h

选用哪个头文件,和你使用的STM32型号有关系,如果你使用的不是STM32系列的单片机,那么请你调用对应型号单片机的头文件,包括到DMA2D外设。 这里有个注意点,使用到DMA2D外设的话,屏幕是有要求的,不能是MCU的屏幕,只可以是RGB的屏幕,因为MCU的屏幕里面内置了GPU,根本不需要用到DMA2D外设,不过现在主流都是用RGB屏幕,毕竟现在技术不断发展,单片机性能不断变强,MCU设置的初衷就是减少单片机使用门槛,让低性能的单片机也能驱动屏幕。

        配置完以上步骤之后,当 LVGL 调用  lcd_color_fill  函数绘制图形时,如果识别到显示屏是RGB,则会调用 ltdc_color_fill 函数,该函数即可实现 DMA2D传输(阻塞的方式)。如果用户想要更加优异的传输性能,可以使用 DMA2D 中断的方法来刷新显示屏,这里其实是属于STM32的知识,我们关注点是LVGL,这里不过多展开。

② 修改 lv_port_indev_template.c 文件  

该文件用于配置输入设备,例如:触摸屏、鼠标、键盘、编码器、按键等,它可以将用户的底层输入设备驱动与 LVGL 的输入驱动衔接起来。这里我们使用触摸屏作为输入设备,具体的配置源码如下所示:
/*** @file lv_port_indev_templ.c**//*Copy this file as "lv_port_indev.c" and set this value to "1" to enable content*/
#if 1/**********************      INCLUDES*********************/
#include "lv_port_indev_template.h"
#include "../../lvgl.h"/* 导入驱动头文件 */
#include "./BSP/TOUCH/touch.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"/**********************      DEFINES*********************//***********************      TYPEDEFS**********************//***********************  STATIC PROTOTYPES**********************//* 触摸屏 */
static void touchpad_init(void);
static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
static bool touchpad_is_pressed(void);
static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y);/* 鼠标 */
//static void mouse_init(void);
//static void mouse_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
//static bool mouse_is_pressed(void);
//static void mouse_get_xy(lv_coord_t * x, lv_coord_t * y);/* 键盘 */
//static void keypad_init(void);
//static void keypad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
//static uint32_t keypad_get_key(void);/* 编码器 */
//static void encoder_init(void);
//static void encoder_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
//static void encoder_handler(void);/* 按钮 */
//static void button_init(void);
//static void button_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
//static int8_t button_get_pressed_id(void);
//static bool button_is_pressed(uint8_t id);/***********************  STATIC VARIABLES**********************/
lv_indev_t * indev_touchpad;    // 触摸屏
//lv_indev_t * indev_mouse;       // 鼠标
//lv_indev_t * indev_keypad;      // 键盘
//lv_indev_t * indev_encoder;     // 编码器
//lv_indev_t * indev_button;      // 按钮/* 编码器相关 */
//static int32_t encoder_diff;
//static lv_indev_state_t encoder_state;/***********************      MACROS**********************//***********************   GLOBAL FUNCTIONS**********************//*** @brief       初始化并注册输入设备* @param       无* @retval      无*/
void lv_port_indev_init(void)
{/*** * 在这里你可以找到 LittlevGL 支持的出入设备的实现示例:*  - 触摸屏*  - 鼠标 (支持光标)*  - 键盘 (仅支持按键的 GUI 用法)*  - 编码器 (支持的 GUI 用法仅包括: 左, 右, 按下)*  - 按钮 (按下屏幕上指定点的外部按钮)**  函数 `..._read()` 只是示例*  你需要根据具体的硬件来完成这些函数*/static lv_indev_drv_t indev_drv;/*------------------* 触摸屏* -----------------*//* 初始化触摸屏(如果有) */touchpad_init();/* 注册触摸屏输入设备 */lv_indev_drv_init(&indev_drv);indev_drv.type = LV_INDEV_TYPE_POINTER;indev_drv.read_cb = touchpad_read;indev_touchpad = lv_indev_drv_register(&indev_drv);/*------------------* 鼠标* -----------------*//* 初始化鼠标(如果有) */
//    mouse_init();/* 注册鼠标输入设备 */
//    lv_indev_drv_init(&indev_drv);
//    indev_drv.type = LV_INDEV_TYPE_POINTER;
//    indev_drv.read_cb = mouse_read;
//    indev_mouse = lv_indev_drv_register(&indev_drv);/* 设置光标,为了简单起见,现在设置为一个 HOME 符号 */
//    lv_obj_t * mouse_cursor = lv_img_create(lv_scr_act());
//    lv_img_set_src(mouse_cursor, LV_SYMBOL_HOME);
//    lv_indev_set_cursor(indev_mouse, mouse_cursor);/*------------------* 键盘* -----------------*///    /* 初始化键盘(如果有) */
//    keypad_init();//    /* 注册键盘输入设备 */
//    lv_indev_drv_init(&indev_drv);
//    indev_drv.type = LV_INDEV_TYPE_KEYPAD;
//    indev_drv.read_cb = keypad_read;
//    indev_keypad = lv_indev_drv_register(&indev_drv);//    /* 接着你需要用 `lv_group_t * group = lv_group_create()` 来创建组
//     * 用 `lv_group_add_obj(group, obj)` 往组中添加物体
//     * 并将这个输入设备分配到组中,以导航到它:
//     * `lv_indev_set_group(indev_keypad, group);` *//*------------------* 编码器* -----------------*///    /* 初始化编码器(如果有) */
//    encoder_init();//    /* 注册编码器输入设备 */
//    lv_indev_drv_init(&indev_drv);
//    indev_drv.type = LV_INDEV_TYPE_ENCODER;
//    indev_drv.read_cb = encoder_read;
//    indev_encoder = lv_indev_drv_register(&indev_drv);//    /* 接着你需要用 `lv_group_t * group = lv_group_create()` 来创建组
//     * 用 `lv_group_add_obj(group, obj)` 往组中添加物体
//     * 并将这个输入设备分配到组中,以导航到它:
//     * `lv_indev_set_group(indev_keypad, group);` *//*------------------* 按钮* -----------------*///    /* 初始化按钮(如果有) */
//    button_init();//    /* 注册按钮输入设备 */
//    lv_indev_drv_init(&indev_drv);
//    indev_drv.type = LV_INDEV_TYPE_BUTTON;
//    indev_drv.read_cb = button_read;
//    indev_button = lv_indev_drv_register(&indev_drv);//    /* 为按钮分配屏幕上的点
//     * 以此来用按钮模拟点击屏幕上对应的点 */
//    static const lv_point_t btn_points[2] = {
//            {10, 10},   /*Button 0 -> x:10; y:10*/
//            {40, 100},  /*Button 1 -> x:40; y:100*/
//    };
//    lv_indev_set_button_points(indev_button, btn_points);
}/***********************   STATIC FUNCTIONS**********************//*------------------* 触摸屏* -----------------*//*** @brief       初始化触摸屏* @param       无* @retval      无*/
static void touchpad_init(void)
{/*Your code comes here*/tp_dev.init();/* 电阻屏坐标矫正 */if (key_scan(0) == KEY0_PRES)           /* KEY0按下,则执行校准程序 */{lcd_clear(WHITE);                   /* 清屏 */tp_adjust();                        /* 屏幕校准 */tp_save_adjust_data();}
}/*** @brief       图形库的触摸屏读取回调函数* @param       indev_drv   : 触摸屏设备*   @arg       data        : 输入设备数据结构体* @retval      无*/
static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{static lv_coord_t last_x = 0;static lv_coord_t last_y = 0;/* 保存按下的坐标和状态 */if(touchpad_is_pressed()){touchpad_get_xy(&last_x, &last_y);data->state = LV_INDEV_STATE_PR;} else{data->state = LV_INDEV_STATE_REL;}/* 设置最后按下的坐标 */data->point.x = last_x;data->point.y = last_y;
}/*** @brief       获取触摸屏设备的状态* @param       无* @retval      返回触摸屏设备是否被按下*/
static bool touchpad_is_pressed(void)
{/*Your code comes here*/tp_dev.scan(0);if (tp_dev.sta & TP_PRES_DOWN){return true;}return false;
}/*** @brief       在触摸屏被按下的时候读取 x、y 坐标* @param       x   : x坐标的指针*   @arg       y   : y坐标的指针* @retval      无*/
static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y)
{/*Your code comes here*/(*x) = tp_dev.x[0];(*y) = tp_dev.y[0];
}/*------------------* 鼠标* -----------------*//*** @brief       初始化鼠标* @param       无* @retval      无*/
//static void mouse_init(void)
//{
//    /*Your code comes here*/
//    tp_dev.init();
//    /* 电阻屏如果发现显示屏XY镜像现象,需要坐标矫正 */
//    if (0 == (tp_dev.touchtype & 0x80))
//    {
//        tp_adjust();
//        tp_save_adjust_data();
//    }
//}/*** @brief       图形库的鼠标读取回调函数* @param       indev_drv   : 鼠标设备*   @arg       data        : 输入设备数据结构体* @retval      无*/
//static void mouse_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
//{
//    /* 获取当前的 x、y 坐标 */
//    mouse_get_xy(&data->point.x, &data->point.y);//    /* 获取是否按下或释放鼠标按钮 */
//    if(mouse_is_pressed()) {
//        data->state = LV_INDEV_STATE_PR;
//    } else {
//        data->state = LV_INDEV_STATE_REL;
//    }
//}/*** @brief       获取鼠标设备是否被按下* @param       无* @retval      返回鼠标设备是否被按下*/
//static bool mouse_is_pressed(void)
//{
//    /*Your code comes here*/
//    tp_dev.scan(0);
//    
//    if (tp_dev.sta & TP_PRES_DOWN)
//    {
//        return true;
//    }
//    
//    return false;
//}/*** @brief       当鼠标被按下时,获取鼠标的 x、y 坐标* @param       x   : x坐标的指针*   @arg       y   : y坐标的指针* @retval      无*/
//static void mouse_get_xy(lv_coord_t * x, lv_coord_t * y)
//{
//    /*Your code comes here*///    (*x) = tp_dev.x[0];
//    (*y) = tp_dev.y[0];
//}/*------------------* 键盘* -----------------*////**
// * @brief       初始化键盘
// * @param       无
// * @retval      无
// */
//static void keypad_init(void)
//{
//    /*Your code comes here*/
//}///**
// * @brief       图形库的键盘读取回调函数
// * @param       indev_drv : 键盘设备
// *   @arg       data      : 输入设备数据结构体
// * @retval      无
// */
//static void keypad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
//{
//    static uint32_t last_key = 0;/* 这段代码是 LVGL 给出的例子,这里获取坐标好像是多余的 *//*Get the current x and y coordinates*/mouse_get_xy(&data->point.x, &data->point.y);//    /* 获取按键是否被按下,并保存键值 */
//    uint32_t act_key = keypad_get_key();
//    if(act_key != 0) {
//        data->state = LV_INDEV_STATE_PR;//        /* 将键值转换成 LVGL 的控制字符 */
//        switch(act_key) {
//        case 1:
//            act_key = LV_KEY_NEXT;
//            break;
//        case 2:
//            act_key = LV_KEY_PREV;
//            break;
//        case 3:
//            act_key = LV_KEY_LEFT;
//            break;
//        case 4:
//            act_key = LV_KEY_RIGHT;
//            break;
//        case 5:
//            act_key = LV_KEY_ENTER;
//            break;
//        }//        last_key = act_key;
//    } else {
//        data->state = LV_INDEV_STATE_REL;
//    }//    data->key = last_key;
//}///**
// * @brief       获取当前正在按下的按键
// * @param       无
// * @retval      0 : 按键没有被按下
// */
//static uint32_t keypad_get_key(void)
//{
//    /*Your code comes here*///    return 0;
//}/*------------------* 编码器* -----------------*////**
// * @brief       初始化编码器
// * @param       无
// * @retval      无
// */
//static void encoder_init(void)
//{
//    /*Your code comes here*/
//}///**
// * @brief       图形库的编码器读取回调函数
// * @param       indev_drv : 编码器设备
// *   @arg       data      : 输入设备数据结构体
// * @retval      无
// */
//static void encoder_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
//{//    data->enc_diff = encoder_diff;
//    data->state = encoder_state;
//}///**
// * @brief       在中断中调用此函数以处理编码器事件(旋转、按下)
// * @param       无
// * @retval      无
// */
//static void encoder_handler(void)
//{
//    /*Your code comes here*///    encoder_diff += 0;
//    encoder_state = LV_INDEV_STATE_REL;
//}/*------------------* 按钮* -----------------*////**
// * @brief       初始化按钮
// * @param       无
// * @retval      无
// */
//static void button_init(void)
//{
//    /*Your code comes here*/
//}///**
// * @brief       图形库的按钮读取回调函数
// * @param       indev_drv : 按钮设备
// *   @arg       data      : 输入设备数据结构体
// * @retval      无
// */
//static void button_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
//{//    static uint8_t last_btn = 0;//    /* 获取被按下按钮的ID */
//    int8_t btn_act = button_get_pressed_id();//    if(btn_act >= 0) {
//        data->state = LV_INDEV_STATE_PR;
//        last_btn = btn_act;
//    } else {
//        data->state = LV_INDEV_STATE_REL;
//    }//    /* 保存最后被按下按钮的ID */
//    data->btn_id = last_btn;
//}///**
// * @brief       获取被按下按钮的ID
// * @param       无
// * @retval      被按下按钮的ID
// */
//static int8_t button_get_pressed_id(void)
//{
//    uint8_t i;//    /* 检查那个按键被按下(这里给出的示例适用于两个按钮的情况) */
//    for(i = 0; i < 2; i++) {
//        /* 返回被按下按钮的ID */
//        if(button_is_pressed(i)) {
//            return i;
//        }
//    }//    /* 没有按钮被按下 */
//    return -1;
//}///**
// * @brief       检查指定ID的按钮是否被按下
// * @param       无
// * @retval      按钮是否被按下
// */
//static bool button_is_pressed(uint8_t id)
//{//    /*Your code comes here*///    return false;
//}#else /*Enable this file at the top*//*This dummy typedef exists purely to silence -Wpedantic.*/
typedef int keep_pedantic_happy;
#endif
由上述源码可知,配置 LVGL 的触摸驱动分为以下 5 个步骤:
(1) 调用 touchpad_init 函数初始化触摸设备。
(2) 调用函数 lv_indev_drv_init 初始化输入设备。
(3) 设置设备的的类型。
(4) 设置触摸回调函数,该函数用于获取触摸屏的坐标。
(5) 调用函数 lv_indev_drv_register 注册输入设备。

我们这里实际只需要在官方给的文件进行三处地方的修改:

触摸屏初始化:

static void touchpad_init ( void )
{
        /*Your code comes here*/
        tp_dev . init ();
        /* 电阻屏坐标矫正 */
        if (key_scan( 0 ) == KEY0_PRES) /* KEY0 按下 , 则执行校准程序 */
        {
        lcd_clear(WHITE); /* 清屏 */
        tp_adjust(); /* 屏幕校准 */
        tp_save_adjust_data();
        }
}

检测屏幕是否被摁下:

static bool touchpad_is_pressed ( void )
{
        /*Your code comes here*/
        tp_dev . scan ( 0 );
        if ( tp_dev . sta & TP_PRES_DOWN )
        {
        return true ;
        }
        return false ;
}

返回按下的坐标:

static void touchpad_get_xy ( lv_coord_t * x , lv_coord_t * y )
{
        /*Your code comes here*/
        (* x ) = tp_dev . x [ 0 ];
        (* y ) = tp_dev . y [ 0 ];
}

编写测试代码

main.c 中编写测试代码,检测 LVGL 是否移植成功,具体源码如下:

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/KEY/key.h"
#include "./BSP/SRAM/sram.h"
#include "./BSP/TIMER/btim.h"/* LVGL */
#include "lvgl.h"
#include "lv_port_indev_template.h"
#include "lv_port_disp_template.h"
#include "lv_demo_stress.h"int main(void)
{HAL_Init();                         /* 初始化HAL库 */sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */delay_init(168);                    /* 延时初始化 */usart_init(115200);                 /* 串口初始化为115200 */led_init();                         /* 初始化LED */key_init();                         /* 初始化按键 */sram_init();                        /* SRAM初始化 */btim_timx_int_init(10-1,8400-1);    /* 初始化定时器 */lv_init();                          /* lvgl系统初始化 */lv_port_disp_init();                /* lvgl显示接口初始化,放在lv_init()的后面 */lv_port_indev_init();               /* lvgl输入接口初始化,放在lv_init()的后面 */lv_obj_t *label = lv_label_create(lv_scr_act());lv_label_set_text(label,"Hello KUrumi!!!");lv_obj_center(label);/while (1){lv_task_handler();delay_ms(5);}
}

如果屏幕显示对应字符串,那么说明我们移植LVGL成功了。

移植官方例程
在精简 LVGL 源码的时候,我们保留了 demos 文件夹,该文件夹中存放的就是 LVGL 的官方示例。由于 demos 文件夹中存在多个官方例程,不同的例程对硬件的要求有所不同,这里以官方的压力测试为例,为大家介绍官方示例的移植流程,具体步骤如下:
(1) demos 文件夹复制到 Middlewares/LVGL/GUI_APP 路径下,如下图所示:

(2) 添加文件路径

(3) 打开 lv_conf.h 文件,找到 LV_USE_DEMO_STRESS 宏定义并设置为 1,开启该实验。

(4) 创建 Middlewares/LVGL/GUI_APP 分组,往其中添加 demos/stress  路径下的全部 .c 文件

 如下图所示:

main函数调用压力测试代码

下载验证

这里官方的示例还有其他的,比如音乐播放器等等,移植思路和这个压力测试的一模一样,不过,不同的官方例程其实对读者的开发板和屏幕有要求,音乐播放器要求的RAM大小和屏幕尺寸的要求较高,读者根据自己的硬件情况进行适配就行。 

LVGL带操作系统移植

1. 移植 FreeRTOS
LVGL 移植操作系统的步骤并不复杂,我们只需要把上一章中移植好的工程换成任务或者线程的形式即可。
 

其实将FreeRTOS移植到工程里面非常的简单,我们只需要将FreeRTOS的源文件,移植到我们的工程组里面,不过有一点需要注意,我们内存管理算法以及内核适配文件有点不同,我们内存管理算法一遍采用heap4,而内核文件就要根据我们STM32内核的型号进行选择了,我们使用的是STM32F4系列的,是Cortex-M4的内核,所以我们要在ARM_CM4F文件夹下面选择port.c文件。 

这里有一点特别需要注意,不然你FreeRTOS移植很可能出现问题,读者一般都是用的某个厂商的开发板或者移植的是某个厂商的代码,我这里移植的是正点原子的代码,由于厂商写的代码适配性很高,通过修改特定的宏,就可以实现对应的功能,不过,只要你懂FreeRTOS,那么移植就是简简单单。

2. LVGL 提供时基

在裸机移植的时候,我们使用基本定时器为 LVGL 提供时基,而当有了系统之后,提供时 基的方式就有了第二种选择。需要使用 RTOS 提供时基。

我们首先打开 lv_conf.h 文件,把 LV _TICK_CUSTOM 宏定义置 1。
然后设置 LV_TICK_CUSTOM_INCLUDE 和 LV_TICK_CUSTOM_SYS_TIME_EXPR 配置项。
/* 使用自定义 tick 源,以毫秒为单位告诉运行时间。它不需要手动更新 `lv_tick_inc()函数` */
#define LV_TICK_CUSTOM 1
#if LV_TICK_CUSTOM
#define LV_TICK_CUSTOM_INCLUDE "FreeRTOS.h" /* 系统时间函数头 */
/* 计算系统当前时间的表达式(以毫秒为单位) */#define LV_TICK_CUSTOM_SYS_TIME_EXPR (xTaskGetTickCount()) 
#endif /*LV_TICK_CUSTOM*/
由上述源码可知,我们使用 FreeRTOS xTaskGetTickCount 函数来为 LVGL 提供时基。
注意: FreeRTOSConfig.h 文件中,必须把 configTICK_RATE_HZ 宏定义设置为 1000 (没有改动的情况下,默认就是 1000 )。

3.编写 FreeRTOS 相关代码

新建两个文件:lvgl_demo.clvgl_demo.h,并将它们存放在 User 目录下,如下图所示:

这两个文件用于存放 RTOS 相关的代码,具体源码如下:

#include "lvgl_demo.h"
#include "./BSP/LED/led.h"
#include "FreeRTOS.h"
#include "task.h"

#include "lvgl.h"
#include "lv_port_disp_template.h"
#include "lv_port_indev_template.h"
#include "lv_demo_stress.h"


/******************************************************************************************************/
/*FreeRTOS配置*/

/* START_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define START_TASK_PRIO     1           /* 任务优先级 */
#define START_STK_SIZE      128         /* 任务堆栈大小 */
TaskHandle_t StartTask_Handler;         /* 任务句柄 */
void start_task(void *pvParameters);    /* 任务函数 */

/* LV_DEMO_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define LV_DEMO_TASK_PRIO   3           /* 任务优先级 */
#define LV_DEMO_STK_SIZE    1024        /* 任务堆栈大小 */
TaskHandle_t LV_DEMOTask_Handler;       /* 任务句柄 */
void lv_demo_task(void *pvParameters);  /* 任务函数 */

/* LED_TASK 任务 配置
 * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
 */
#define LED_TASK_PRIO       4           /* 任务优先级 */
#define LED_STK_SIZE        128         /* 任务堆栈大小 */
TaskHandle_t LEDTask_Handler;           /* 任务句柄 */
void led_task(void *pvParameters);      /* 任务函数 */
/******************************************************************************************************/

/**
 * @brief       lvgl_demo入口函数
 * @param       无
 * @retval      无
 */
void lvgl_demo(void)
{
    lv_init();                                          /* lvgl系统初始化 */
    lv_port_disp_init();                                /* lvgl显示接口初始化,放在lv_init()的后面 */
    lv_port_indev_init();                               /* lvgl输入接口初始化,放在lv_init()的后面 */

    xTaskCreate((TaskFunction_t )start_task,            /* 任务函数 */
                (const char*    )"start_task",          /* 任务名称 */
                (uint16_t       )START_STK_SIZE,        /* 任务堆栈大小 */
                (void*          )NULL,                  /* 传递给任务函数的参数 */
                (UBaseType_t    )START_TASK_PRIO,       /* 任务优先级 */
                (TaskHandle_t*  )&StartTask_Handler);   /* 任务句柄 */

    vTaskStartScheduler();                              /* 开启任务调度 */
}

/**
 * @brief       start_task
 * @param       pvParameters : 传入参数(未用到)
 * @retval      无
 */
void start_task(void *pvParameters)
{
    pvParameters = pvParameters;
    
    taskENTER_CRITICAL();           /* 进入临界区 */

    /* 创建LVGL任务 */
    xTaskCreate((TaskFunction_t )lv_demo_task,
                (const char*    )"lv_demo_task",
                (uint16_t       )LV_DEMO_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )LV_DEMO_TASK_PRIO,
                (TaskHandle_t*  )&LV_DEMOTask_Handler);

    /* LED测试任务 */
    xTaskCreate((TaskFunction_t )led_task,
                (const char*    )"led_task",
                (uint16_t       )LED_STK_SIZE,
                (void*          )NULL,
                (UBaseType_t    )LED_TASK_PRIO,
                (TaskHandle_t*  )&LEDTask_Handler);

    taskEXIT_CRITICAL();            /* 退出临界区 */
    vTaskDelete(StartTask_Handler); /* 删除开始任务 */
}

/**
 * @brief       LVGL运行例程
 * @param       pvParameters : 传入参数(未用到)
 * @retval      无
 */
void lv_demo_task(void *pvParameters)
{
    pvParameters = pvParameters;
    
    lv_demo_stress();       /* 测试的demo */
    
    while(1)
    {
        lv_timer_handler(); /* LVGL计时器 */
        vTaskDelay(5);
    }
}

/**
 * @brief       led_task
 * @param       pvParameters : 传入参数(未用到)
 * @retval      无
 */
void led_task(void *pvParameters)
{
    pvParameters = pvParameters;
    
    while(1)
    {
        LED0_TOGGLE();
        vTaskDelay(1000);
    }
}

lvgl_demo.c 文件的代码实现可分为 4 个步骤:
(1) 包含头文件;
(2) 定义 FreeRTOS 相关变量以及宏定义;
(3) 编写 LVGL 接口函数 lvgl_demo ,在其中初始化 LVGL 、显示设备以及输入设备,并创
建开始任务;
(4) 在开始任务中创建 lvgl 示例任务以及 led 任务,调用音乐播放器相关的函数。
注意: 如果使用的是其他的官方例程,请根据实际的例程来包含头文件以及调用 demo
数,上述代码中以压力测试为例。
2. lvgl_demo.h
这个文件很简单,只是声明了 LVGL 的接口函数
#ifndef __LVGL_DEMO_H
#define __LVGL_DEMO_H
void lvgl_demo ( void );
#endif

3. 调用接口函数

接下来只需要在 main.c 文件中调用 LVGL 的接口函数 lvgl_demo 即可,具体源码如下所示:

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/KEY/key.h"
#include "./BSP/SRAM/sram.h"
#include "./MALLOC/malloc.h"

#include "lvgl_demo.h"


int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */
    delay_init(168);                    /* 延时初始化 */
    usart_init(115200);                 /* 串口初始化为115200 */
    led_init();                         /* 初始化LED */
    key_init();                         /* 初始化按键 */
    sram_init();                        /* SRAM初始化 */
    my_mem_init(SRAMIN);                /* 初始化内部SRAM内存池 */
    my_mem_init(SRAMEX);                /* 初始化外部SRAM内存池 */

    lvgl_demo();                        /* 运行FreeRTOS例程 */
}

4.下载验证  

FreeRTOS,在移植这里特别需要注意的就是时基问题,这个时候,LVGL的时基就由FreeRTOS的内置函数提供了。

lv_conf.h

接下来我会通过介绍这个头文件,来大概帮助大家了解LVGL,比如时基获取、内存管理算法、控件使能......,lv_confg.h具体源码如下所示:

/*** @file lv_conf.h* Configuration file for v8.2.0*//** Copy this file as `lv_conf.h`* 1. simply next to the `lvgl` folder* 2. or any other places and*    - define `LV_CONF_INCLUDE_SIMPLE`*    - add the path as include path*//* clang-format off */
#if 1 /*Set it to "1" to enable content*/#ifndef LV_CONF_H
#define LV_CONF_H#include <stdint.h>/*********************************************************************************颜色设置***********************************************************************************//* 颜色深度: 1(每像素1字节), 8(RGB332), 16(RGB565), 32(ARGB8888) */
#define LV_COLOR_DEPTH                      16/* 交换2字节的RGB565颜色。如果显示有8位接口(例如SPI) */
#define LV_COLOR_16_SWAP                    0/* 1: 启用屏幕透明.* 对OSD或其他有重叠的gui很有用.* 要求' LV_COLOR_DEPTH = 32 '颜色和屏幕的样式应该被修改: `style.body.opa = ...`*/
#define LV_COLOR_SCREEN_TRANSP              0/* 调整颜色混合功能四舍五入。gpu可能会以不同的方式计算颜色混合。* 0:取整,64:从x.75取整,128:从half取整,192:从x.25取整,254:从half取整 */
#define LV_COLOR_MIX_ROUND_OFS              (LV_COLOR_DEPTH == 32 ? 0: 128)/* 如果使用色度键,将不会绘制这种颜色的图像像素) */
#define LV_COLOR_CHROMA_KEY                 lv_color_hex(0x00ff00)         /* 纯绿 *//*********************************************************************************内存设置***********************************************************************************//* 0: 使用内置的 `lv_mem_alloc()` 和 `lv_mem_free()`*/
#define LV_MEM_CUSTOM                       0
#if LV_MEM_CUSTOM == 0/* `lv_mem_alloc()`可获得的内存大小(以字节为单位)(>= 2kB) */#define LV_MEM_SIZE                     (46U * 1024U)          /*[字节]*//* 为内存池设置一个地址,而不是将其作为普通数组分配。也可以在外部SRAM中。 */#define LV_MEM_ADR                      0     /*0: 未使用*//* 给内存分配器而不是地址,它将被调用来获得LVGL的内存池。例如my_malloc */#if LV_MEM_ADR == 0//#define LV_MEM_POOL_INCLUDE your_alloc_library  /* 如果使用外部分配器,取消注释 *///#define LV_MEM_POOL_ALLOC   your_alloc          /* 如果使用外部分配器,取消注释 */#endif#else       /*LV_MEM_CUSTOM*/#define LV_MEM_CUSTOM_INCLUDE <stdlib.h>   /* 动态内存函数的头 */#define LV_MEM_CUSTOM_ALLOC   malloc#define LV_MEM_CUSTOM_FREE    free#define LV_MEM_CUSTOM_REALLOC realloc
#endif     /*LV_MEM_CUSTOM*//* 在渲染和其他内部处理机制期间使用的中间内存缓冲区的数量。* 如果没有足够的缓冲区,你会看到一个错误日志信息. */
#define LV_MEM_BUF_MAX_NUM                  16/* 使用标准的 `memcpy` 和 `memset` 代替LVGL自己的函数。(可能更快,也可能不会更快) */
#define LV_MEMCPY_MEMSET_STD                0/*********************************************************************************HAL 设置***********************************************************************************//* 默认的显示刷新周期。LVGL使用这个周期重绘修改过的区域 */
#define LV_DISP_DEF_REFR_PERIOD             4      /*[ms]*//* 输入设备的读取周期(以毫秒为单位) */
#define LV_INDEV_DEF_READ_PERIOD            4     /*[ms]*//* 使用自定义tick源,以毫秒为单位告诉运行时间。它不需要手动更新 `lv_tick_inc()` */
#define LV_TICK_CUSTOM                      1
#if LV_TICK_CUSTOM#define LV_TICK_CUSTOM_INCLUDE          "FreeRTOS.h"                /* 系统时间函数头 */#define LV_TICK_CUSTOM_SYS_TIME_EXPR    (xTaskGetTickCount())       /* 计算系统当前时间的表达式(以毫秒为单位) */
#endif   /*LV_TICK_CUSTOM*//* 默认每英寸的点数量。用于初始化默认大小,例如小部件大小,样式填充。* (不是很重要,你可以调整它来修改默认大小和空格) */
#define LV_DPI_DEF                          130     /*[px/inch]*//*********************************************************************************特征选项***********************************************************************************/
/*-------------* 1. 绘制*-----------*//* 启用复杂的绘制引擎* 需要绘制阴影,梯度,圆角,圆,弧,斜线,图像转换或任何遮罩 */
#define LV_DRAW_COMPLEX                     1
#if LV_DRAW_COMPLEX != 0/* 允许缓冲一些阴影计算* LV_SHADOW_CACHE_SIZE为最大的缓冲大小,缓冲大小为 `阴影宽度 + 半径`* 将会有 LV_SHADOW_CACHE_SIZE^2 的内存开销 */#define LV_SHADOW_CACHE_SIZE            0/* 设置最大缓存循环数据的数量。* 保存1/4圆的周长用于抗锯齿* 半径*每个圆使用4个字节(保存最常用的半径)* 0:禁用缓存 */#define LV_CIRCLE_CACHE_SIZE            4#endif /*LV_DRAW_COMPLEX*//* 默认图像缓存大小。图像缓存保持图像打开。* 如果只使用内置的图像格式,缓存没有真正的优势。(即没有添加新的图像解码器)* 复杂的图像解码器(如PNG或JPG)缓存可以保存连续打开/解码的图像。然而,打开的图像可能会消耗额外的RAM。* 0:禁用缓存 */
#define LV_IMG_CACHE_DEF_SIZE               0/* 每个坡度允许停车的数目。增加这个值以允许更多停站。* 每个额外的停止增加(sizeof(lv_color_t) + 1)字节 */
#define LV_GRADIENT_MAX_STOPS               2/* 默认梯度缓冲区大小。* 当LVGL计算梯度“地图”,它可以将它们保存到缓存,以避免再次计算它们。* LV_GRAD_CACHE_DEF_SIZE设置缓存的大小(以字节为单位)。* 如果缓存太小,地图只会在需要绘制的时候被分配。* 0表示没有缓存*/
#define LV_GRAD_CACHE_DEF_SIZE              0/* 允许抖动渐变(在有限的颜色深度显示上实现视觉平滑的颜色渐变)* LV_DITHER_GRADIENT意味着分配对象渲染表面的一条或两条线* 内存消耗的增加是(32位*对象宽度)加上24位*对象宽度如果使用错误扩散 */
#define LV_DITHER_GRADIENT                  0
#if LV_DITHER_GRADIENT/* 增加了错误扩散抖动的支持。* 错误扩散抖动得到了更好的视觉效果,但在绘制时意味着更多的CPU和内存消耗。内存消耗增加(24位*对象的宽度) */#define LV_DITHER_ERROR_DIFFUSION       0
#endif/* 为旋转分配的最大缓冲区大小。仅在显示驱动程序中启用软件旋转时使用 */
#define LV_DISP_ROT_MAX_BUF                 (10*1024)/*-------------* 2. GPU*-----------*//* 使用STM32的DMA2D(又名Chrom Art) GPU */
#define LV_USE_GPU_STM32_DMA2D              0
#if LV_USE_GPU_STM32_DMA2D/* 必须定义包括目标处理器的CMSIS头的路径如。“stm32f769xx.h”或“stm32f429xx.h”*/#define LV_GPU_DMA2D_CMSIS_INCLUDE
#endif/* 使用NXP的PXP GPU iMX RTxxx平台 */
#define LV_USE_GPU_NXP_PXP                  0
#if LV_USE_GPU_NXP_PXP/*1:为PXP (lv_gpu_nxp_pxp_osa.c)添加默认的裸代码和FreeRTOS中断处理例程*,在lv_init()时自动调用lv_gpu_nxp_pxp_init()。注意符号SDK_OS_FREE_RTOS*必须定义以便使用FreeRTOS OSA,否则选择裸金属实现。*0: lv_gpu_nxp_pxp_init()必须在lv_init()之前手动调用*/#define LV_USE_GPU_NXP_PXP_AUTO_INIT    0
#endif/* 使用NXP的VG-Lite GPU iMX RTxxx平台 */
#define LV_USE_GPU_NXP_VG_LITE              0/* 使用SDL渲染器API */
#define LV_USE_GPU_SDL                      0
#if LV_USE_GPU_SDL#define LV_GPU_SDL_INCLUDE_PATH <SDL2/SDL.h>/* 纹理缓存大小,默认8MB */#define LV_GPU_SDL_LRU_SIZE (1024 * 1024 * 8)/* 自定义混合模式的蒙版绘制,如果你需要链接旧SDL2库禁用 */#define LV_GPU_SDL_CUSTOM_BLEND_MODE (SDL_VERSION_ATLEAST(2, 0, 6))
#endif/*-------------* 3. 日志*-----------*//* 启用日志模块 */
#define LV_USE_LOG                          0
#if LV_USE_LOG/*应该添加多重要的日志:*LV_LOG_LEVEL_TRACE       大量的日志给出了详细的信息*LV_LOG_LEVEL_INFO        记录重要事件*LV_LOG_LEVEL_WARN        如果发生了一些不想要的事情但没有引起问题,则记录下来*LV_LOG_LEVEL_ERROR       只有在系统可能出现故障时才会出现关键问题*LV_LOG_LEVEL_USER        仅用户自己添加的日志*LV_LOG_LEVEL_NONE        不要记录任何内容*/#define LV_LOG_LEVEL LV_LOG_LEVEL_WARN/*1: 使用'printf'打印日志;*0: 用户需要用' lv_log_register_print_cb() '注册回调函数 */#define LV_LOG_PRINTF                   0/* 在产生大量日志的模块中启用/禁用LV_LOG_TRACE */#define LV_LOG_TRACE_MEM                1#define LV_LOG_TRACE_TIMER              1#define LV_LOG_TRACE_INDEV              1#define LV_LOG_TRACE_DISP_REFR          1#define LV_LOG_TRACE_EVENT              1#define LV_LOG_TRACE_OBJ_CREATE         1#define LV_LOG_TRACE_LAYOUT             1#define LV_LOG_TRACE_ANIM               1#endif  /*LV_USE_LOG*//*-------------* 4. 断言*-----------*//* 如果操作失败或发现无效数据,则启用断言。* 如果启用了LV_USE_LOG,失败时会打印错误信息*/
#define LV_USE_ASSERT_NULL                  1   /* 检查参数是否为NULL。(非常快,推荐) */
#define LV_USE_ASSERT_MALLOC                1   /* 检查内存是否分配成功。(非常快,推荐) */
#define LV_USE_ASSERT_STYLE                 0   /* 检查样式是否正确初始化。(非常快,推荐) */
#define LV_USE_ASSERT_MEM_INTEGRITY         0   /* 关键操作完成后,请检查“lv_mem”的完整性。(慢)*/
#define LV_USE_ASSERT_OBJ                   0   /* 检查对象的类型和存在(例如,未删除)。(慢) *//* 当assert发生时,添加一个自定义处理程序,例如重新启动MCU */
#define LV_ASSERT_HANDLER_INCLUDE           <stdint.h>
#define LV_ASSERT_HANDLER while(1);         /* 停止在默认情况下 *//*-------------* 5. 其他*-----------*//* 1:显示CPU使用率和FPS */
#define LV_USE_PERF_MONITOR                 0
#if LV_USE_PERF_MONITOR#define LV_USE_PERF_MONITOR_POS LV_ALIGN_BOTTOM_RIGHT
#endif/* 1:显示使用的内存和内存碎片* 要求LV_MEM_CUSTOM = 0*/
#define LV_USE_MEM_MONITOR                  0
#if LV_USE_MEM_MONITOR#define LV_USE_MEM_MONITOR_POS LV_ALIGN_BOTTOM_LEFT
#endif/* 1:在重新绘制的区域上绘制随机的彩色矩形 */
#define LV_USE_REFR_DEBUG                   0/* 改变内置的(v)snprintf函数 */
#define LV_SPRINTF_CUSTOM                   0
#if LV_SPRINTF_CUSTOM#define LV_SPRINTF_INCLUDE  <stdio.h>#define lv_snprintf         snprintf#define lv_vsnprintf        vsnprintf
#else   /*LV_SPRINTF_CUSTOM*/#define LV_SPRINTF_USE_FLOAT            0
#endif  /*LV_SPRINTF_CUSTOM*/#define LV_USE_USER_DATA                    1/* 垃圾收集器设置* 如果lvgl绑定到高级语言,并且内存由该语言管理时使用*/
#define LV_ENABLE_GC                        0
#if LV_ENABLE_GC != 0#define LV_GC_INCLUDE "gc.h"                           /* 包括垃圾收集器相关的东西 */
#endif /*LV_ENABLE_GC*//*********************************************************************************编译器设置***********************************************************************************/
/* 对于设置为1的大端序系统 */
#define LV_BIG_ENDIAN_SYSTEM                0/* 为' lv_tick_inc '函数定义一个自定义属性 */
#define LV_ATTRIBUTE_TICK_INC/* 为' lv_timer_handler '函数定义一个自定义属性 */
#define LV_ATTRIBUTE_TIMER_HANDLER/* 为' lv_disp_flush_ready '函数定义一个自定义属性 */
#define LV_ATTRIBUTE_FLUSH_READY/* 缓冲区所需的对齐大小 */
#define LV_ATTRIBUTE_MEM_ALIGN_SIZE         1/* 将被添加到需要对齐内存的地方(默认情况下-Os数据可能不会对齐到边界)。* 如__attribute__((对齐(4))) */
#define LV_ATTRIBUTE_MEM_ALIGN/* 属性来标记大型常量数组,例如字体的位图 */
#define LV_ATTRIBUTE_LARGE_CONST/* RAM中大数组声明的编译器前缀 */
#define LV_ATTRIBUTE_LARGE_RAM_ARRAY/* 将性能关键功能放入更快的内存中(例如RAM) */
#define LV_ATTRIBUTE_FAST_MEM/* 在GPU加速操作中使用的前缀变量,通常需要放置在DMA可访问的RAM段中 */
#define LV_ATTRIBUTE_DMA/* 导出整型常量到绑定。该宏与LV_<CONST> that形式的常量一起使用* 也应该出现在LVGL绑定API,如Micropython。*/
#define LV_EXPORT_CONST_INT(int_value) struct _silence_gcc_warning /* 默认值只是防止GCC警告 *//* 扩展默认值-32k..32k坐标范围到-4M..使用int32_t而不是int16_t作为坐标 */
#define LV_USE_LARGE_COORD                  0/*********************************************************************************字库设置***********************************************************************************/
/* 蒙特塞拉特字体的ASCII范围和一些符号使用bpp = 4* https://fonts.google.com/specimen/Montserrat */
#define LV_FONT_MONTSERRAT_8                0
#define LV_FONT_MONTSERRAT_10               0
#define LV_FONT_MONTSERRAT_12               1
#define LV_FONT_MONTSERRAT_14               1
#define LV_FONT_MONTSERRAT_16               1
#define LV_FONT_MONTSERRAT_18               0
#define LV_FONT_MONTSERRAT_20               0
#define LV_FONT_MONTSERRAT_22               1
#define LV_FONT_MONTSERRAT_24               0
#define LV_FONT_MONTSERRAT_26               0
#define LV_FONT_MONTSERRAT_28               0
#define LV_FONT_MONTSERRAT_30               0
#define LV_FONT_MONTSERRAT_32               1
#define LV_FONT_MONTSERRAT_34               0
#define LV_FONT_MONTSERRAT_36               0
#define LV_FONT_MONTSERRAT_38               0
#define LV_FONT_MONTSERRAT_40               0
#define LV_FONT_MONTSERRAT_42               0
#define LV_FONT_MONTSERRAT_44               0
#define LV_FONT_MONTSERRAT_46               0
#define LV_FONT_MONTSERRAT_48               0/* 展示特色 */
#define LV_FONT_MONTSERRAT_12_SUBPX         0
#define LV_FONT_MONTSERRAT_28_COMPRESSED    0  /* bpp = 3 */
#define LV_FONT_DEJAVU_16_PERSIAN_HEBREW    0  /* 希伯来语,阿拉伯语,波斯语以及它们的各种形式 */
#define LV_FONT_SIMSUN_16_CJK               0  /* 1000个最常见的CJK自由基s *//* 像素完美的单空间字体 */
#define LV_FONT_UNSCII_8                    0
#define LV_FONT_UNSCII_16                   0/* 可选声明自定义字体在这里。* 你也可以使用这些字体作为默认字体,它们将是全球可用的。* 如。#define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(my_font_1) LV_FONT_DECLARE(my_font_2) */
#define LV_FONT_CUSTOM_DECLARE/* 始终设置默认字体 */
#define LV_FONT_DEFAULT                     &lv_font_montserrat_14/* 启用处理大字体和/或带有大量字符的字体。* 限制取决于字体大小,字体面和bpp。* 编译器错误将被触发,如果字体需要它 */
#define LV_FONT_FMT_TXT_LARGE               0/* 启用/禁用对压缩字体的支持。 */
#define LV_USE_FONT_COMPRESSED              0/* 使亚像素渲染 */
#define LV_USE_FONT_SUBPX                   0
#if LV_USE_FONT_SUBPX/* 设置显示的像素顺序。RGB通道的物理顺序。与“正常”字体无关。 */#define LV_FONT_SUBPX_BGR               0  /* 0: RGB;1: BGR秩序 */
#endif/*********************************************************************************文本设置***********************************************************************************/
/*** 为字符串选择字符编码* IDE或编辑器应该具有相同的字符编码* - LV_TXT_ENC_UTF8* - LV_TXT_ENC_ASCII*/
#define LV_TXT_ENC LV_TXT_ENC_UTF8/* 可以在这些字符上中断(换行)文本 */
#define LV_TXT_BREAK_CHARS                  " ,.;:-_"/* 如果一个单词至少有这么长,就会在“最美”的地方断裂* 要禁用,设置一个值<= 0 */
#define LV_TXT_LINE_BREAK_LONG_LEN          0/* 在一个长单词中,在停顿之前放置一行的最小字符数。* 取决于LV_TXT_LINE_BREAK_LONG_LEN。 */
#define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN  3/* 在一个长单词中,在停顿后插入一行的最小字符数。* 取决于LV_TXT_LINE_BREAK_LONG_LEN。*/
#define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN 3/* 用于信令文本重新上色的控制字符。 */
#define LV_TXT_COLOR_CMD                    "#"/* 支持双向文本。允许混合从左到右和从右到左的文本。* 方向会根据Unicode双向算法进行处理:* https://www.w3.org/International/articles/inline-bidi-markup/uba-basics*/
#define LV_USE_BIDI                         0
#if LV_USE_BIDI/* 设置默认方向。支持的值:*`LV_BASE_DIR_LTR` 从左到右*`LV_BASE_DIR_RTL` 从右到左*`LV_BASE_DIR_AUTO` 检测文本基本方向 */#define LV_BIDI_BASE_DIR_DEF LV_BASE_DIR_AUTO
#endif/* 支持阿拉伯语/波斯处理* 在这些语言中,字符应该根据其在文本中的位置被替换为其他形式 */
#define LV_USE_ARABIC_PERSIAN_CHARS         0/*********************************************************************************控件设置***********************************************************************************/
/* 小部件的文档:https://docs.lvgl.io/latest/en/html/widgets/index.html */#define LV_USE_ARC                          1#define LV_USE_ANIMIMG                      1#define LV_USE_BAR                          1#define LV_USE_BTN                          1#define LV_USE_BTNMATRIX                    1#define LV_USE_CANVAS                       1#define LV_USE_CHECKBOX                     1#define LV_USE_DROPDOWN                     1   /* 依赖: lv_label */#define LV_USE_IMG                          1   /* 依赖: lv_label */#define LV_USE_LABEL                        1
#if LV_USE_LABEL#define LV_LABEL_TEXT_SELECTION         1  /* 启用标签的选择文本*/#define LV_LABEL_LONG_TXT_HINT          1  /* 在标签中存储一些额外的信息,以加快绘制非常长的文本 */
#endif#define LV_USE_LINE                         1#define LV_USE_ROLLER                       1  /* 依赖: lv_label */
#if LV_USE_ROLLER#define LV_ROLLER_INF_PAGES             7  /* 当滚筒无限时,额外的“页数” */
#endif#define LV_USE_SLIDER                       1  /* 依赖: lv_bar*/#define LV_USE_SWITCH                       1#define LV_USE_TEXTAREA                     1  /* 依赖: lv_label*/
#if LV_USE_TEXTAREA != 0#define LV_TEXTAREA_DEF_PWD_SHOW_TIME   1500    /*ms*/
#endif#define LV_USE_TABLE                        1/*********************************************************************************特别功能***********************************************************************************/
/*-----------* 1. 控件*----------*/
#define LV_USE_CALENDAR                     1
#if LV_USE_CALENDAR#define LV_CALENDAR_WEEK_STARTS_MONDAY  0#if LV_CALENDAR_WEEK_STARTS_MONDAY#define LV_CALENDAR_DEFAULT_DAY_NAMES {"Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"}#else#define LV_CALENDAR_DEFAULT_DAY_NAMES {"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"}#endif#define LV_CALENDAR_DEFAULT_MONTH_NAMES {"January", "February", "March",  "April", "May",  "June", "July", "August", "September", "October", "November", "December"}#define LV_USE_CALENDAR_HEADER_ARROW    1#define LV_USE_CALENDAR_HEADER_DROPDOWN 1
#endif  /*LV_USE_CALENDAR*/#define LV_USE_CHART                        1#define LV_USE_COLORWHEEL                   1#define LV_USE_IMGBTN                       1#define LV_USE_KEYBOARD                     1#define LV_USE_LED                          1#define LV_USE_LIST                         1#define LV_USE_MENU                         1#define LV_USE_METER                        1#define LV_USE_MSGBOX                       1#define LV_USE_SPINBOX                      1#define LV_USE_SPINNER                      1#define LV_USE_TABVIEW                      1#define LV_USE_TILEVIEW                     1#define LV_USE_WIN                          1#define LV_USE_SPAN                         1
#if LV_USE_SPAN/* 一个行文本可以包含最大数量的span描述符 */#define LV_SPAN_SNIPPET_STACK_SIZE      64
#endif/*-----------* 2. 主题*----------*//* 一个简单,令人印象深刻和非常完整的主题 e*/
#define LV_USE_THEME_DEFAULT                1
#if LV_USE_THEME_DEFAULT/* 0:光模式;1:黑暗的模式 */#define LV_THEME_DEFAULT_DARK           0/* 1:按下使能生长 */#define LV_THEME_DEFAULT_GROW           1/* 默认转换时间[ms] */#define LV_THEME_DEFAULT_TRANSITION_TIME 80
#endif /*LV_USE_THEME_DEFAULT*//* 一个非常简单的主题,这是一个自定义主题的良好起点 */
#define LV_USE_THEME_BASIC                  1/* 专为单色显示器设计的主题 */
#define LV_USE_THEME_MONO                   1/*-----------* 3. 布局*----------*//* 类似于CSS中的Flexbox布局 */
#define LV_USE_FLEX                         1/* 类似于CSS中的网格布局 */
#define LV_USE_GRID                         1/*---------------------* 4. 第三方库*--------------------*//* 用于通用api的文件系统接口* 为该API启用设置驱动程序号 *//* STDIO */
#define LV_USE_FS_STDIO            0
#if LV_USE_FS_STDIO#define LV_FS_STDIO_LETTER      '\0'        /* 设置一个可访问驱动器的大写字母(例如。“A”) */#define LV_FS_STDIO_PATH        ""          /* 设置工作目录。文件/目录路径将被追加到它 */#define LV_FS_STDIO_CACHE_SIZE  0           /* >0在lv_fs_read()中缓存这个字节数 */
#endif/* POSIX */
#define LV_USE_FS_POSIX             0
#if LV_USE_FS_POSIX#define LV_FS_POSIX_LETTER      '\0'        /* 设置一个可访问驱动器的大写字母(例如。“A”) */#define LV_FS_POSIX_PATH        ""          /* 设置工作目录。文件/目录路径将被追加到它 */#define LV_FS_POSIX_CACHE_SIZE  0           /* >0在lv_fs_read()中缓存这个字节数 */
#endif/* WIN32 */
#define LV_USE_FS_WIN32             0
#if LV_USE_FS_WIN32#define LV_FS_WIN32_LETTER      '\0'        /* 设置一个可访问驱动器的大写字母(例如。“A”) */#define LV_FS_WIN32_PATH        ""          /* 设置工作目录。文件/目录路径将被追加到它 */#define LV_FS_WIN32_CACHE_SIZE  0           /* >0在lv_fs_read()中缓存这个字节数 */
#endif/* FATFS */
#define LV_USE_FS_FATFS             0
#if LV_USE_FS_FATFS#define LV_FS_FATFS_LETTER      '\0'        /* 设置一个可访问驱动器的大写字母(例如。“A”) */#define LV_FS_FATFS_CACHE_SIZE  0           /* >0在lv_fs_read()中缓存这个字节数 */
#endif/* PNG译码器库 */
#define LV_USE_PNG                          0/* BMP 译码器库 */
#define LV_USE_BMP                          0/* JPG +分割JPG解码器库。* Split JPG是为嵌入式系统优化的自定义格式 */
#define LV_USE_SJPG                         0/* GIF译码器库 */
#define LV_USE_GIF                          0/* QR译码器库 */
#define LV_USE_QRCODE                       0/* FreeType库 */
#define LV_USE_FREETYPE                     0
#if LV_USE_FREETYPE/* FreeType用于缓存字符[bytes]的内存(-1:没有缓存) */#define LV_FREETYPE_CACHE_SIZE          (16 * 1024)#if LV_FREETYPE_CACHE_SIZE >= 0/* 1:位图cache使用sbit cache, 0:位图cache使用图像cache* sbit缓存:对于小的位图(字体大小< 256),它的内存效率更高*如果字体大小>= 256,必须配置为图像缓存 */#define LV_FREETYPE_SBIT_CACHE      0/* 由这个缓存实例管理的打开的FT_Face/FT_Size对象的最大数量。*/(0:使用系统默认值) */#define LV_FREETYPE_CACHE_FT_FACES  0#define LV_FREETYPE_CACHE_FT_SIZES  0#endif
#endif/* Rlottie 库 */
#define LV_USE_RLOTTIE                      0/* FFmpeg库的图像解码和播放视频* 支持所有主要的图像格式,所以不启用其他图像解码器与它t*/
#define LV_USE_FFMPEG                       0
#if LV_USE_FFMPEG/* 将输入信息转储到stderr */#define LV_FFMPEG_AV_DUMP_FORMAT        0
#endif/*-----------* 5. 其他*----------*//* 1:启用API对对象进行快照 */
#define LV_USE_SNAPSHOT                     1/* 1:使能Monkey测试 */
#define LV_USE_MONKEY                       0/* 1:启用网格导航 */
#define LV_USE_GRIDNAV                      0/*********************************************************************************实例***********************************************************************************/
/* 允许用库构建示例 */
#define LV_BUILD_EXAMPLES                   1/*===================* 演示使用====================*//* 显示了一些部件。可能需要增加“LV_MEM_SIZE” */
#define LV_USE_DEMO_WIDGETS                 0
#if LV_USE_DEMO_WIDGETS
#define LV_DEMO_WIDGETS_SLIDESHOW           0
#endif/* 演示编码器和键盘的用法 */
#define LV_USE_DEMO_KEYPAD_AND_ENCODER      0/* 基准系统 */
#define LV_USE_DEMO_BENCHMARK               0/* LVGL压力测试 */
#define LV_USE_DEMO_STRESS                  1/* 音乐播放器的演示 */
#define LV_USE_DEMO_MUSIC                   0
#if LV_USE_DEMO_MUSIC
# define LV_DEMO_MUSIC_SQUARE               0
# define LV_DEMO_MUSIC_LANDSCAPE            0
# define LV_DEMO_MUSIC_ROUND                0
# define LV_DEMO_MUSIC_LARGE                0
# define LV_DEMO_MUSIC_AUTO_PLAY            0
#endif/*--END OF LV_CONF_H--*/#endif /*LV_CONF_H*/#endif /*End of "Content enable"*/

这个注释是由正点原子翻译的,我认为他翻译的很好,我这里说几个最为重要的,。

1、颜色设置

我们根据我们屏幕的颜色深度,来定义#define LV_COLOR_DEPTH                 16,如果你移植完LVGL之后,屏幕花白或者不亮,并且你用的是spi屏幕,那么你就定义LV_COLOR_16_SWAP为1,并且把颜色深度变为8看一下。

2、内存设置

 ***********************************************************************************/

/* 0: 使用内置的 `lv_mem_alloc()` 和 `lv_mem_free()`*/
#define LV_MEM_CUSTOM                       0
#if LV_MEM_CUSTOM == 0
    /* `lv_mem_alloc()`可获得的内存大小(以字节为单位)(>= 2kB) */
    #define LV_MEM_SIZE                     (46U * 1024U)          /*[字节]*/

    /* 为内存池设置一个地址,而不是将其作为普通数组分配。也可以在外部SRAM中。 */
    #define LV_MEM_ADR                      0     /*0: 未使用*/
    /* 给内存分配器而不是地址,它将被调用来获得LVGL的内存池。例如my_malloc */
    #if LV_MEM_ADR == 0
        //#define LV_MEM_POOL_INCLUDE your_alloc_library  /* 如果使用外部分配器,取消注释 */
        //#define LV_MEM_POOL_ALLOC   your_alloc          /* 如果使用外部分配器,取消注释 */
    #endif

#else       /*LV_MEM_CUSTOM*/
    #define LV_MEM_CUSTOM_INCLUDE <stdlib.h>   /* 动态内存函数的头 */
    #define LV_MEM_CUSTOM_ALLOC   malloc
    #define LV_MEM_CUSTOM_FREE    free
    #define LV_MEM_CUSTOM_REALLOC realloc
#endif     /*LV_MEM_CUSTOM*/

/* 在渲染和其他内部处理机制期间使用的中间内存缓冲区的数量。
 * 如果没有足够的缓冲区,你会看到一个错误日志信息. */
#define LV_MEM_BUF_MAX_NUM                  16

/* 使用标准的 `memcpy` 和 `memset` 代替LVGL自己的函数。(可能更快,也可能不会更快) */
#define LV_MEMCPY_MEMSET_STD                0
 

这块就是LVGL有关内存管理的配置部分,我们先看一下LVGL有关内存的部分:

我们这里配置的是LVGL管理的内存。通过特定的宏,我们可以决定,LVGL管理内存是在外部SRAM或者内部SRAM、LVGL内置的内存管理算法还是自研的内存管理算法、LVGL内存管理的大小(一般40KB就很够用了),我们这里最好使用默认的设置,默认是使用内部SRAM,速度够快,不然LVGL运行的效果很差。

3、HAL 设置

 ***********************************************************************************/
 
/* 默认的显示刷新周期。LVGL使用这个周期重绘修改过的区域 */
#define LV_DISP_DEF_REFR_PERIOD             4      /*[ms]*/

/* 输入设备的读取周期(以毫秒为单位) */
#define LV_INDEV_DEF_READ_PERIOD            4     /*[ms]*/

/* 使用自定义tick源,以毫秒为单位告诉运行时间。它不需要手动更新 `lv_tick_inc()` */
#define LV_TICK_CUSTOM                      1
#if LV_TICK_CUSTOM
    #define LV_TICK_CUSTOM_INCLUDE          "FreeRTOS.h"                /* 系统时间函数头 */
    #define LV_TICK_CUSTOM_SYS_TIME_EXPR    (xTaskGetTickCount())       /* 计算系统当前时间的表达式(以毫秒为单位) */
#endif   /*LV_TICK_CUSTOM*/


/* 默认每英寸的点数量。用于初始化默认大小,例如小部件大小,样式填充。
 * (不是很重要,你可以调整它来修改默认大小和空格) */
#define LV_DPI_DEF                          130     /*[px/inch]*/
 

这里主要通过修改宏配置LVGL刷新周期以及时基获取方式,自带的或者外部的。

4、日志


/* 启用日志模块 */
#define LV_USE_LOG                          0
#if LV_USE_LOG

    /*应该添加多重要的日志:
    *LV_LOG_LEVEL_TRACE       大量的日志给出了详细的信息
    *LV_LOG_LEVEL_INFO        记录重要事件
    *LV_LOG_LEVEL_WARN        如果发生了一些不想要的事情但没有引起问题,则记录下来
    *LV_LOG_LEVEL_ERROR       只有在系统可能出现故障时才会出现关键问题
    *LV_LOG_LEVEL_USER        仅用户自己添加的日志
    *LV_LOG_LEVEL_NONE        不要记录任何内容*/
    #define LV_LOG_LEVEL LV_LOG_LEVEL_WARN

    /*1: 使用'printf'打印日志;
     *0: 用户需要用' lv_log_register_print_cb() '注册回调函数 */
    #define LV_LOG_PRINTF                   0

    /* 在产生大量日志的模块中启用/禁用LV_LOG_TRACE */
    #define LV_LOG_TRACE_MEM                1
    #define LV_LOG_TRACE_TIMER              1
    #define LV_LOG_TRACE_INDEV              1
    #define LV_LOG_TRACE_DISP_REFR          1
    #define LV_LOG_TRACE_EVENT              1
    #define LV_LOG_TRACE_OBJ_CREATE         1
    #define LV_LOG_TRACE_LAYOUT             1
    #define LV_LOG_TRACE_ANIM               1

#endif  /*LV_USE_LOG*/

 通过修改宏,可以通过printf来打印对应日志,方便我们进行调试。

5. 断言

/* 如果操作失败或发现无效数据,则启用断言。
 * 如果启用了LV_USE_LOG,失败时会打印错误信息*/
#define LV_USE_ASSERT_NULL                  1   /* 检查参数是否为NULL。(非常快,推荐) */
#define LV_USE_ASSERT_MALLOC                1   /* 检查内存是否分配成功。(非常快,推荐) */
#define LV_USE_ASSERT_STYLE                 0   /* 检查样式是否正确初始化。(非常快,推荐) */
#define LV_USE_ASSERT_MEM_INTEGRITY         0   /* 关键操作完成后,请检查“lv_mem”的完整性。(慢)*/
#define LV_USE_ASSERT_OBJ                   0   /* 检查对象的类型和存在(例如,未删除)。(慢) */

/* 当assert发生时,添加一个自定义处理程序,例如重新启动MCU */
#define LV_ASSERT_HANDLER_INCLUDE           <stdint.h>
#define LV_ASSERT_HANDLER while(1);         /* 停止在默认情况下 */

如果存在特定的错误,我们可以设置 LV_ASSERT_HANDLER,进行处理。

6、其他

/* 1:显示CPU使用率和FPS */
#define LV_USE_PERF_MONITOR                 0
#if LV_USE_PERF_MONITOR
    #define LV_USE_PERF_MONITOR_POS LV_ALIGN_BOTTOM_RIGHT
#endif

/* 1:显示使用的内存和内存碎片
 * 要求LV_MEM_CUSTOM = 0*/
#define LV_USE_MEM_MONITOR                  0
#if LV_USE_MEM_MONITOR
    #define LV_USE_MEM_MONITOR_POS LV_ALIGN_BOTTOM_LEFT
#endif

/* 1:在重新绘制的区域上绘制随机的彩色矩形 */
#define LV_USE_REFR_DEBUG                   0

/* 改变内置的(v)snprintf函数 */
#define LV_SPRINTF_CUSTOM                   0
#if LV_SPRINTF_CUSTOM
    #define LV_SPRINTF_INCLUDE  <stdio.h>
    #define lv_snprintf         snprintf
    #define lv_vsnprintf        vsnprintf
#else   /*LV_SPRINTF_CUSTOM*/
    #define LV_SPRINTF_USE_FLOAT            0
#endif  /*LV_SPRINTF_CUSTOM*/

#define LV_USE_USER_DATA                    1

/* 垃圾收集器设置
 * 如果lvgl绑定到高级语言,并且内存由该语言管理时使用*/
#define LV_ENABLE_GC                        0
#if LV_ENABLE_GC != 0
    #define LV_GC_INCLUDE "gc.h"                           /* 包括垃圾收集器相关的东西 */
#endif /*LV_ENABLE_GC*/

我们可以通过设置一下某个宏,屏幕会打印一些其他项,比如:FPS、CPU使用率......

7.实例

/* 允许用库构建示例 */
#define LV_BUILD_EXAMPLES                   1

/*===================
 * 演示使用
 ====================*/

/* 显示了一些部件。可能需要增加“LV_MEM_SIZE” */
#define LV_USE_DEMO_WIDGETS                 0
#if LV_USE_DEMO_WIDGETS
#define LV_DEMO_WIDGETS_SLIDESHOW           0
#endif

/* 演示编码器和键盘的用法 */
#define LV_USE_DEMO_KEYPAD_AND_ENCODER      0

/* 基准系统 */
#define LV_USE_DEMO_BENCHMARK               0

/* LVGL压力测试 */
#define LV_USE_DEMO_STRESS                  1

/* 音乐播放器的演示 */
#define LV_USE_DEMO_MUSIC                   0
#if LV_USE_DEMO_MUSIC
# define LV_DEMO_MUSIC_SQUARE               0
# define LV_DEMO_MUSIC_LANDSCAPE            0
# define LV_DEMO_MUSIC_ROUND                0
# define LV_DEMO_MUSIC_LARGE                0
# define LV_DEMO_MUSIC_AUTO_PLAY            0
#endif

当我们需要使用到官方的示例的时候,我们需要修改某个示例的宏,我们这里使用压力测试,所有#define LV_USE_DEMO_STRESS    1。

我这里只介绍了lv_conf.h的某些内容,实际上还有一些我没介绍到,之后不断的学习之后,读者就会慢慢理解了。 

编译错误以及现象显示不正常 

编译错误

我这里只介绍一种特殊的编译错误,报错提示内存不足:

解决方法:

1、修改lvgl可用内存的大小,在lv_conf.h中修改LV_MEM_SIZE的值,不要设置的太大。一般20-40KB就行了。

2、增大STM32的空间,在stm32f407xx.s(我这里用的是STM32F407的板子)文件中修改,修改堆和栈的大小,修改后的样子:

现象显示不正常  

移植好后编译下载发现屏幕显示是乱的,这是lv_port_disp.c里的disp_flush函数修改错误导致的。我开始按照正点原子的例程在移植到我其他的板子,我发现屏幕是花屏,但是触摸我看到由现象,然后我自己也很蒙蔽,我就看到有位博主的解决方法,我们开始为了显示的速率,就删掉了for循环进行一个个打点的方法,而是使用一片区域进行填充,如果读者发现花屏,不妨试一下将区域填充变回for循环一个个颜色打点试一下。

总结 

一系列步骤做完之后,相信读者对LVGL有了个大概了解了,我们之后将围绕LVGL,不断的深入讲解。


http://www.mrgr.cn/news/52394.html

相关文章:

  • 18. 位运算
  • torch-npu的配置+yolo
  • 十月编程语言排行榜~
  • 【数据结构】之链表详解
  • 【AscendC】算子调用API的对齐问题
  • 下面四款2024年首推的免费的录屏工具,哪个是你的首选??
  • 【HuggingFace 如何上传数据集 (2) 】国内网络-稳定上传图片、文本等各种格式的数据
  • 【Qt】信号和槽——信号和槽的概念、信号和槽的使用、信号和槽的优缺点、自定义信号和槽、信号和槽的断开
  • 群晖前面加了雷池社区版,安装失败,然后无法识别出用户真实访问IP
  • C++ IO多路复用 poll模型
  • 【Linux系统编程】环境基础开发工具使用
  • 编写Python 自动化安装openGauss 数据库方法和代码 (2)
  • QtModel
  • FreeRTOS - 队列
  • 【日志】编辑器开发——修复根据Excel表格数据生成Json文件和配置表代码报错
  • Oracle实际需要用到但常常被忽略的函数
  • Dbeaver24.2.2安装和使用教程(免费的数据库管理工具)
  • iptables 内网端口映射
  • 【C语言教程】【常用类库】(十七)国际化与本地化 - <locale.h>
  • linux命令之lspci用法