LVGL代码移植(裸机+FreeRTOS操作系统+内部SRAM+外部SRAM+内存管理算法+编译错误以及现象显示不正常)
说明
我使用的是正点原子官方的探索者F407ZGT6开发板以及一块我自己之前学习STM32的最小系统板(带屏幕),读者完全可以使用不同的板子或者其他类型的单片机,因为LVGL的移植性非常之高,所以我这里会更突出的进行LVGL的讲解,关于STM32相关的知识,我会讲解的比较简略。
LVGL移植整体流程
移植准备工作
在移植 LVGL 之前,用户必须在实验中选择一个例程作为移植的基础工程,值得注意的是,最终的基础工程中必须包含 LCD 显示驱动 、 触摸屏驱动 以及 基本定时器驱动。 如果需要在FreeRTOS上移植LVGL,那我们还要准备对应的FreeRTOS文件,移植进去我们的基础例程。
这里建议读者使用自己开发板的《内存管理实验》作为基本例程,移植触摸屏、定时器相关的代码进入这个基本工程,至于为什么选择内存管理实验,是因为,我们LVGL的内存管理可以分配在不同的内存,包括内部SRAM、外部SRAM,使用《内存管理实验》这个实验,我们之后可以稍微在源代码里面修改一下,就可以将LVGL的内存分配在不同的地方,方便我们进行观察。
切记,你准备好的基础例程在移植完之后,你要编译一下,看一下有没有报错,这里如果报错的话,要先进行解决,不然到时候移植LVGL的时候,出现报错的话,不好找到错误的原因。
2. 准备 LVGL 源码

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 提供时基
#include "lvgl.h"
void HAL_TIM_PeriodElapsedCallback ( TIM_HandleTypeDef * htim ){if ( htim == (& g_timx_handle )){lv_tick_inc ( 1 ); /* lvgl 的 1ms 心跳 */}}

打开 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 文件
/*** @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
#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设置的初衷就是减少单片机使用门槛,让低性能的单片机也能驱动屏幕。
② 修改 lv_port_indev_template.c 文件
/*** @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
我们这里实际只需要在官方给的文件进行三处地方的修改:
触摸屏初始化:
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 ];}
编写测试代码
#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成功了。
(2) 添加文件路径
(3) 打开 lv_conf.h 文件,找到 LV_USE_DEMO_STRESS 宏定义并设置为 1,开启该实验。
如下图所示:
main函数调用压力测试代码
下载验证
这里官方的示例还有其他的,比如音乐播放器等等,移植思路和这个压力测试的一模一样,不过,不同的官方例程其实对读者的开发板和屏幕有要求,音乐播放器要求的RAM大小和屏幕尺寸的要求较高,读者根据自己的硬件情况进行适配就行。
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*/
3.编写 FreeRTOS 相关代码
新建两个文件:lvgl_demo.c、lvgl_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);
}
}
#ifndef __LVGL_DEMO_H#define __LVGL_DEMO_Hvoid 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,不断的深入讲解。