目录
基于 LVGL V8 版本
LVGL 的官方文档,大致说了一下如何进行 LVGL 的移植工作,LVGL 的移植主要分为几个步骤:
- 下载 LVGL 的源码,gitee 会定期同步 GitHub,Gitee 极速下载/littlevGL
- 修改几个源码文件命名;
- 修改用于 LVGL 配置的文件;
- 移植对接底层驱动送显部分;
- 适配 LVGL 心跳 Tick,适配 LVGL 任务处理函数调用,调用 LVGL 初始化函数;
- 编译,链接,执行;
1、下载源码
这部分不多说了,直接下载最新的 LVGL 的源代码,现在最新的是 v8.3,在 LVGL 源码目录下的 lvgl.h 中:
/***************************
* CURRENT VERSION OF LVGL
***************************/
#define LVGL_VERSION_MAJOR 8
#define LVGL_VERSION_MINOR 3
#define LVGL_VERSION_PATCH 0
#define LVGL_VERSION_INFO "dev"
2、目录结构介绍
LVGL Project 包含了如下目录
|-- demos :LVGL 的 demo 应用,包含了 stress 压力测试,widgets 测试,等;
|-- docs :LVGL 的官方说明文档 Markdown;
|-- env_support:现目前支持的一些环境,包含 cmake,cmsis-pack,rt-thread;
|-- examples:一些 LVGL 的使用示例;
|-- scripts:脚本;
|-- src:LVGL 的源码实现目录;
|-- tests:LVGL 的自测代码;
3、移植前注意事项
在移植 LVGL 的时候,对软硬件有一些最低要求:
- 16、32 或 64 位微控制器或处理器
- 建议使用 >16 MHz 时钟速度
- 闪存/ROM: > 64 kB 用于非常重要的组件 (> 建议使用 180 kB)
- RAM:
- 静态 RAM 使用量:~2 kB,取决于使用的功能和对象类型
- 堆: > 2kB (> 建议使用 8 kB)
- 动态数据(堆): > 2 KB (> 如果使用多个对象,建议使用 16 kB). 在 lv_conf.h 文件中配置 LV_MEM_SIZE 生效。
- 显示缓冲区:> “水平分辨率”像素(推荐 >10 × 10ד 水平分辨率”)
- MCU或外部显示控制器中的一个帧缓冲区
- C99 或更新的编译器
我们在移植的时候,需要注意上面的部分内容,一方面对编译器的有要求 (C99 以上),另一方面是针对 memory 的要求;
有的移植手册说要 Keil AC6 编译器,注意这不是必要条件;
4、开始移植
这里,我是使用 STM32F407ZGT6 + ST7735S (128x128 RGB565) 进行移植;
4.1、改文件名字
4.1.1、对接 platform/chip 的部分
在源码目录下,进入到 lvgl/examples/porting 目录:
这些文件带了 port 字眼,都是用来对接特定平台/芯片的内容,其中 disp 的部分对接的是显示部分,fs 对接文件系统部分,indev 对接输入设备(触摸,键盘,鼠标);
值得注意的是,如果我们只做显示,那么对接 lv_port_disp_template 即可;
我们将其改名,去掉 template:
这一步改名不是必须的,只是为了整体好看的效果而已;
当然,你这里改了 .c 和 .h,之前 .c 文件中引用的(include) .h 文件的名字,也需要同步改
4.1.2、LVGL 配置文件
在 LVGL 源码目录,有一个 lv_conf_template.h 文件,就在整个源码的一级目录下,我们打开看看:
/**
* @file lv_conf.h
* Configuration file for v8.3.0-dev
*/
/*
* 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 0 /*Set it to "1" to enable content*/
这个文件的注释看到,它明确的告诉大家,首先需要将这个文件改名(或者 Copy)为名字为 lv_conf.h 的文件;
其次,在宏定义中,将 #if 0 改为 #if1,即使能这个文件下面的内容,用于编译;
因为在 LVGL 的核心实现中,都是使用 #include "lv_conf.h",所以 lv_conf_template.h 文件改名为 lv_conf.h 是必须要做的;
4.2、移植显示驱动
在之前改过名字的 lv_port_disp.H文件中,定义我们 LCD 屏幕的尺寸:
#define ST7735S_WIDTH 128
#define ST7735S_HEIGHT 128
修改 lv_conf.h 中,关于屏幕尺寸的定义:
#define MY_DISP_HOR_RES 128
#define MY_DISP_VER_RES 128
#define LV_VER_RES_MAX 128
在 lv_port_disp.c 文件中,开始移植:
首先打开文件中的宏定义:
源码如下:
void lv_port_disp_init(void)
{
/*-------------------------
* Initialize your display
* -----------------------*/
disp_init();
/*-----------------------------
* Create a buffer for drawing
*----------------------------*/
/**
* LVGL requires a buffer where it internally draws the widgets.
* Later this buffer will passed to your display driver's `flush_cb` to copy its content to your display.
* The buffer has to be greater than 1 display row
*
* There are 3 buffering configurations:
* 1. Create ONE buffer:
* LVGL will draw the display's content here and writes it to your display
*
* 2. Create TWO buffer:
* LVGL will draw the display's content to a buffer and writes it your display.
* You should use DMA to write the buffer's content to the display.
* It will enable LVGL to draw the next part of the screen to the other buffer while
* the data is being sent form the first buffer. It makes rendering and flushing parallel.
*
* 3. Double buffering
* Set 2 screens sized buffers and set disp_drv.full_refresh = 1.
* This way LVGL will always provide the whole rendered screen in `flush_cb`
* and you only need to change the frame buffer's address.
*/
/* Example for 1) */
static lv_disp_draw_buf_t draw_buf_dsc_1;
static lv_color_t buf_1[MY_DISP_HOR_RES * 10]; /*A buffer for 10 rows*/
lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, NULL, MY_DISP_HOR_RES * 10); /*Initialize the display buffer*/
// /* Example for 2) */
// static lv_disp_draw_buf_t draw_buf_dsc_2;
// static lv_color_t buf_2_1[MY_DISP_HOR_RES * 10]; /*A buffer for 10 rows*/
// static lv_color_t buf_2_2[MY_DISP_HOR_RES * 10]; /*An other buffer for 10 rows*/
// lv_disp_draw_buf_init(&draw_buf_dsc_2, buf_2_1, buf_2_2, MY_DISP_HOR_RES * 10); /*Initialize the display buffer*/
// /* Example for 3) also set disp_drv.full_refresh = 1 below*/
// 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]; /*A screen sized buffer*/
// static lv_color_t buf_3_2[MY_DISP_HOR_RES * MY_DISP_VER_RES]; /*Another screen sized buffer*/
// lv_disp_draw_buf_init(&draw_buf_dsc_3, buf_3_1, buf_3_2, MY_DISP_VER_RES * LV_VER_RES_MAX); /*Initialize the display buffer*/
/*-----------------------------------
* Register the display in LVGL
*----------------------------------*/
static lv_disp_drv_t disp_drv; /*Descriptor of a display driver*/
lv_disp_drv_init(&disp_drv); /*Basic initialization*/
/*Set up the functions to access to your display*/
/*Set the resolution of the display*/
disp_drv.hor_res = ST7735S_WIDTH;
disp_drv.ver_res = ST7735S_HEIGHT;
/*Used to copy the buffer's content to the display*/
disp_drv.flush_cb = disp_flush;
/*Set a display buffer*/
disp_drv.draw_buf = &draw_buf_dsc_1;
/*Required for Example 3)*/
//disp_drv.full_refresh = 1
/* Fill a memory array with a color if you have GPU.
* Note that, in lv_conf.h you can enable GPUs that has built-in support in LVGL.
* But if you have a different GPU you can use with this callback.*/
//disp_drv.gpu_fill_cb = gpu_fill;
/*Finally register the driver*/
lv_disp_drv_register(&disp_drv);
}
1. 首先 LVGL 提供了一个叫做 disp_init(); 的函数,默认情况下是空实现,移植的时候,咱们可以在里面做我们和芯片Platform 显示相关的初始化部分;
2. 接着,调用 lv_disp_draw_buf_init 来进行绘制 buffer 的初始化,这里 LVGL 支持了 3 中 buffer 选择,具体的介绍可以阅读 LVGL 官方文档介绍,这里我们使用第一种最简单的,所以把其他两种的初始化全部屏蔽掉;
3. 初始化 disp 的宽高
disp_drv.hor_res = ST7735S_WIDTH;
disp_drv.ver_res = ST7735S_HEIGHT;
4. 对接底层和芯片平台相关的刷图接口:
disp_drv.flush_cb = disp_flush;
这个接口是需要我们自己根据芯片和 LCD 来实现的接口,它将像素数据通过芯片的驱动刷新到指定的屏幕,我们 STM32F407 + ST7735S 芯片的送显,使用过 STM32 的 SPI 接口,将数据传输到 ST7735S 上的 RAM,ST7735S 通过自刷新,将数据刷新到他的 panel 上;
extern void LCD_DrawPoint(uint16_t x,uint16_t y,uint16_t color);
/*Flush the content of the internal buffer the specific area on the display
*You can use DMA or any hardware acceleration to do this operation in the background but
*'lv_disp_flush_ready()' has to be called when finished.*/
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
/*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/
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)*/
// 画点函数,例如常见的一些屏用的是16位颜色,你把16位数据输出到屏幕即可
LCD_DrawPoint(x, y, color_p->full);
color_p++;
}
}
// LCD_Fill(area->x1, area->y1, area->x2, area->y2, *((uint16_t *)color_p));
/*IMPORTANT!!!
*Inform the graphics library that you are ready with the flushing*/
lv_disp_flush_ready(disp_drv);
}
可以看到,disp_flush 的部分,传入了表征一块待绘制区域的 x y 坐标,以及要绘制的颜色;
这里,我们调用 LCD_DrawPoint(x, y, color_p->full); 函数,来完成通过 SPI 传输到 LCD 的过程;(驱动这部分不多讲,不在本节的讨论范围之内);
void LCD_DrawPoint(u16 x,u16 y,u16 color)
{
LCD_Address_Set(x,y,x,y);//设置光标位置
LCD_WriteData_16Bit(color);
}
显示部分对接完成!
4.3、配置 LVGL
LVGL 支持很多配置,它的配置文件都被罗列在之前被改名为 lv_conf.h 的文件中;
有几个配置需要注意一下:
4.3.1、屏幕尺寸的配置
#if 1 /*Set it to "1" to enable content*/
#ifndef LV_CONF_H
#define LV_CONF_H
#include <stdint.h>
// 屏幕尺寸需要配置
#define MY_DISP_HOR_RES 128
#define MY_DISP_VER_RES 128
#define LV_VER_RES_MAX 128
4.3.2、颜色相关配置
/*====================
COLOR SETTINGS 颜色相关的配置
*====================*/
// 颜色的配置,这里我们使用 RGB565。所以配置为 16
/*Color depth: 1 (1 byte per pixel), 8 (RGB332), 16 (RGB565), 32 (ARGB8888)*/
#define LV_COLOR_DEPTH 16
/*Swap the 2 bytes of RGB565 color. Useful if the display has an 8-bit interface (e.g. SPI)*/
#define LV_COLOR_16_SWAP 0
/*Enable more complex drawing routines to manage screens transparency.
*Can be used if the UI is above another layer, e.g. an OSD menu or video player.
*Requires `LV_COLOR_DEPTH = 32` colors and the screen's `bg_opa` should be set to non LV_OPA_COVER value*/
#define LV_COLOR_SCREEN_TRANSP 0
/* Adjust color mix functions rounding. GPUs might calculate color mix (blending) differently.
* 0: round down, 64: round up from x.75, 128: round up from half, 192: round up from x.25, 254: round up */
#define LV_COLOR_MIX_ROUND_OFS (LV_COLOR_DEPTH == 32 ? 0: 128)
/*Images pixels with this color will not be drawn if they are chroma keyed)*/
#define LV_COLOR_CHROMA_KEY lv_color_hex(0x00ff00) /*pure green*/
4.3.3、内存相关配置
内存相关的配置,为了如何使用动态内存分配/释放的策略;
如果你打算自己写内存管理,那么需要定义 LV_MEM_CUSTOM 为 1,否则,那么使用 LVGL 的基于 TLSF 的内存管理算法;
#if 1 /*Set it to "1" to enable content*/
/*=========================
MEMORY SETTINGS 内存相关的配置,如果没有定义 LV_MEM_CUSTOM,那么代表动态内存分配释放,使用 LVGL 那套内存管理算法
默认情况下 LVGL 使用 TLSF 内存管理算法,LV_MEM_SIZE 指定了 LVGL 管理的内存大小;这个需要配置
*=========================*/
/*1: use custom malloc/free, 0: use the built-in `lv_mem_alloc()` and `lv_mem_free()`*/
#define LV_MEM_CUSTOM 0
#if LV_MEM_CUSTOM == 0
/*Size of the memory available for `lv_mem_alloc()` in bytes (>= 2kB)*/
// 此内存用于 LVGL 的动态申请/释放内存,这里我们配置的 48KB 的全局数组给 LVGL 使用
#define LV_MEM_SIZE (48U * 1024U) /*[bytes]*/
/*Set an address for the memory pool instead of allocating it as a normal array. Can be in external SRAM too.*/
#define LV_MEM_ADR 0 /*0: unused*/
/*Instead of an address give a memory allocator that will be called to get a memory pool for LVGL. E.g. my_malloc*/
#if LV_MEM_ADR == 0
//#define LV_MEM_POOL_INCLUDE your_alloc_library /* Uncomment if using an external allocator*/
//#define LV_MEM_POOL_ALLOC your_alloc /* Uncomment if using an external allocator*/
#endif
#else /*LV_MEM_CUSTOM*/
#define LV_MEM_CUSTOM_INCLUDE <stdlib.h> /*Header for the dynamic memory function*/
#define LV_MEM_CUSTOM_ALLOC malloc
#define LV_MEM_CUSTOM_FREE free
#define LV_MEM_CUSTOM_REALLOC realloc
#endif /*LV_MEM_CUSTOM*/
/*Number of the intermediate memory buffer used during rendering and other internal processing mechanisms.
*You will see an error log message if there wasn't enough buffers. */
// 用于渲染和 LVGL 内部处理机制的 buffer 个数,如果配置不够的话,LVGL 会打印 ERROR 信息
// 这个其实是一个 lv_mem_buf_arr_t[LV_MEM_BUF_MAX_NUM] 结构的数组个数;
#define LV_MEM_BUF_MAX_NUM 16
/*Use the standard `memcpy` and `memset` instead of LVGL's own functions. (Might or might not be faster).*/
#define LV_MEMCPY_MEMSET_STD 0
4.3.4、底层的配置
配置了多长时间刷新一次显示,和读取一次 input 的值进行处理;这里我们让他维持默认的值即可;
/*====================
HAL SETTINGS HAL 层的配置
*====================*/
/*Default display refresh period. LVG will redraw changed areas with this period time*/
// 这个参数决定了多久处理一起屏幕刷新,默认情况是 30ms
#define LV_DISP_DEF_REFR_PERIOD 30 /*[ms]*/
/*Input device read period in milliseconds*/
// 这个参数决定了多久处理一起input,默认情况是 30ms
#define LV_INDEV_DEF_READ_PERIOD 30 /*[ms]*/
/*Use a custom tick source that tells the elapsed time in milliseconds.
*It removes the need to manually update the tick with `lv_tick_inc()`)*/
#define LV_TICK_CUSTOM 0
#if LV_TICK_CUSTOM
#define LV_TICK_CUSTOM_INCLUDE "Arduino.h" /*Header for the system time function*/
#define LV_TICK_CUSTOM_SYS_TIME_EXPR (millis()) /*Expression evaluating to current system time in ms*/
#endif /*LV_TICK_CUSTOM*/
/*Default Dot Per Inch. Used to initialize default sizes such as widgets sized, style paddings.
*(Not so important, you can adjust it to modify default sizes and spaces)*/
/*
* LV_DPI_DEF 注意这里,虽然LVGL的作者说这个没这么重要,但他会严重影响到LVGL的动画效果
* 你应该进行DPI的手动计算,例如128x128分辨率1.44英寸的屏幕,那么 DPI = ((√128*128) / 1.44) ≈ 89
*/
#define LV_DPI_DEF 89 /*[px/inch]*/
4.3.5、日志
可以通过 LV_USE_LOG 设置为 1,来打开 LVGL 的日志打印,LVGL 日志分为了:
- TRACE
- INFO
- WARN
- ERROR
- USER
- NONE
这几种类型,通过定义 LV_LOG_LEVEL 来指定日志等级;
并且如果你已经适配好了 printf 函数,并在你的平台上可以通过 printf 能够打印出串口数据,那么定义 LV_LOG_PRINTF 为 1,来使用 printf 函数打印 LVGL 的日志;否则,就要通过调用 lv_log_register_print_cb 来注册适合你的平台的串口打印日志的接口
/*-------------
* Logging
*-----------*/
/*Enable the log module*/
#define LV_USE_LOG 1
#if LV_USE_LOG
/*How important log should be added:
*LV_LOG_LEVEL_TRACE A lot of logs to give detailed information
*LV_LOG_LEVEL_INFO Log important events
*LV_LOG_LEVEL_WARN Log if something unwanted happened but didn't cause a problem
*LV_LOG_LEVEL_ERROR Only critical issue, when the system may fail
*LV_LOG_LEVEL_USER Only logs added by the user
*LV_LOG_LEVEL_NONE Do not log anything*/
#define LV_LOG_LEVEL LV_LOG_LEVEL_INFO
/*1: Print the log with 'printf';
*0: User need to register a callback with `lv_log_register_print_cb()`*/
#define LV_LOG_PRINTF 1
/*Enable/disable LV_LOG_TRACE in modules that produces a huge number of logs*/
#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.3.6、其他(显示帧率等)
包含了帧率监控,内存监控等配置;
/*-------------
* Others
*-----------*/
/*1: Show CPU usage and FPS count*/
#define LV_USE_PERF_MONITOR 1
#if LV_USE_PERF_MONITOR
#define LV_USE_PERF_MONITOR_POS LV_ALIGN_BOTTOM_RIGHT
#endif
/*1: Show the used memory and the memory fragmentation
* Requires 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: Draw random colored rectangles over the redrawn areas*/
#define LV_USE_REFR_DEBUG 0
/*Change the built in (v)snprintf functions*/
#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
/*Garbage Collector settings
*Used if lvgl is bound to higher level language and the memory is managed by that language*/
#define LV_ENABLE_GC 0
#if LV_ENABLE_GC != 0
#define LV_GC_INCLUDE "gc.h" /*Include Garbage Collector related things*/
#endif /*LV_ENABLE_GC*/
4.3.7、编译属性
LVGL 给很多变量和函数增加了编译的属性,比如你支持 MPU 等,或者在你编译、链接的适合,希望一些函数,或者数据被 Layout 到你指定的地方 (Linker),或者你的编译器支持一些属性,那么可以配置他们:
/*=====================
* COMPILER SETTINGS
*====================*/
/*For big endian systems set to 1*/
#define LV_BIG_ENDIAN_SYSTEM 0
/*Define a custom attribute to `lv_tick_inc` function*/
#define LV_ATTRIBUTE_TICK_INC
/*Define a custom attribute to `lv_timer_handler` function*/
#define LV_ATTRIBUTE_TIMER_HANDLER
/*Define a custom attribute to `lv_disp_flush_ready` function*/
#define LV_ATTRIBUTE_FLUSH_READY
/*Required alignment size for buffers*/
#define LV_ATTRIBUTE_MEM_ALIGN_SIZE 1
/*Will be added where memories needs to be aligned (with -Os data might not be aligned to boundary by default).
* E.g. __attribute__((aligned(4)))*/
#define LV_ATTRIBUTE_MEM_ALIGN
/*Attribute to mark large constant arrays for example font's bitmaps*/
#define LV_ATTRIBUTE_LARGE_CONST
/*Compiler prefix for a big array declaration in RAM*/
#define LV_ATTRIBUTE_LARGE_RAM_ARRAY
/*Place performance critical functions into a faster memory (e.g RAM)*/
#define LV_ATTRIBUTE_FAST_MEM
/*Prefix variables that are used in GPU accelerated operations, often these need to be placed in RAM sections that are DMA accessible*/
#define LV_ATTRIBUTE_DMA
/*Export integer constant to binding. This macro is used with constants in the form of LV_<CONST> that
*should also appear on LVGL binding API such as Micropython.*/
#define LV_EXPORT_CONST_INT(int_value) struct _silence_gcc_warning /*The default value just prevents GCC warning*/
/*Extend the default -32k..32k coordinate range to -4M..4M by using int32_t for coordinates instead of int16_t*/
#define LV_USE_LARGE_COORD 0
4.3.8、裁剪组件
LVGL 内建了很多组件,什么 label、button,line,calendar,chart,list 等等,你可以按照你的需要进行裁剪,默认情况下,这些组件都被包含,也就是都 define 为 1 了:
/*==================
* WIDGET USAGE
*================*/
/*Documentation of the widgets: 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 /*Requires: lv_label*/
#define LV_USE_IMG 1 /*Requires: lv_label*/
#define LV_USE_LABEL 1
#if LV_USE_LABEL
#define LV_LABEL_TEXT_SELECTION 1 /*Enable selecting text of the label*/
#define LV_LABEL_LONG_TXT_HINT 1 /*Store some extra info in labels to speed up drawing of very long texts*/
#endif
#define LV_USE_LINE 1
#define LV_USE_ROLLER 1 /*Requires: lv_label*/
#if LV_USE_ROLLER
#define LV_ROLLER_INF_PAGES 7 /*Number of extra "pages" when the roller is infinite*/
#endif
#define LV_USE_SLIDER 1 /*Requires: lv_bar*/
#define LV_USE_SWITCH 1
#define LV_USE_TEXTAREA 1 /*Requires: lv_label*/
#if LV_USE_TEXTAREA != 0
#define LV_TEXTAREA_DEF_PWD_SHOW_TIME 1500 /*ms*/
#endif
#define LV_USE_TABLE 1
/*==================
* EXTRA COMPONENTS
*==================*/
/*-----------
* Widgets
*----------*/
#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
/*A line text can contain maximum num of span descriptor */
#define LV_SPAN_SNIPPET_STACK_SIZE 64
#endif
4.3.9、开启/关闭 Demo
还记得吗,之前介绍目录的时候,介绍了 LVGL 的 demo,移植完毕后,可以把 LVGL 的 demo 加进来编译跑,在 lv_conf.h 中需要打开对应的宏,才可以编译并且跑 demo 哟:
/*===================
* DEMO USAGE
====================*/
/*Show some widget. It might be required to increase `LV_MEM_SIZE` */
#define LV_USE_DEMO_WIDGETS 0
#if LV_USE_DEMO_WIDGETS
#define LV_DEMO_WIDGETS_SLIDESHOW 0
#endif
/*Demonstrate the usage of encoder and keyboard*/
#define LV_USE_DEMO_KEYPAD_AND_ENCODER 0
/*Benchmark your system*/
#define LV_USE_DEMO_BENCHMARK 1
/*Stress test for LVGL*/
#define LV_USE_DEMO_STRESS 0
/*Music player demo*/
#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
4.4、加入文件到工程
把下载的 LVGL 中的 src 目录,拷贝到自己的工程中,并加入编译(我知道这是一个很痛苦的过程,但是为了跑起来,没办法),记住是所有文件全部加入,不要偷懒;
4.5、适配心跳
LVGL 的任务都是基于时间的,包含绘制,响应触摸等等;所以呢,我们需要给它一个时间基准;
LVGL 提供了一个 lv_tick_inc 的函数,我们需要在系统中去周期性调用他,告诉 LVGL 的时间基准;可以用硬件 Timer,也可以使用 SystemTick;
给他的精度是 ms,也就是如果你定义了一个 1ms 的 Timer(或者 SystemTick),那么超时的时候,就调用 lv_tick_inc(1),如果你定义的是 2ms 的,那么就是 lv_tick_inc(2),以此类推;
这里我使用的是 SystemTick,定义的 1ms 的精度,所以呢,就在 ISR 中调用即可:
/**
* @brief This function handles SysTick Handler.
* @param None
* @retval None
*/
void SysTick_Handler(void)
{
lv_tick_inc(1);
}
4.6、调用处理事务
LVGL 是基于 Timer 的事务处理,但是 LVGL 的事务处理,是基于不断的调用它的 handler 进行的,在 handler 中,不断的比较某些任务是否超时(比如 30ms 一次的绘制),然后调用相关的处理函数;
所以我们需要定期的调用 LVGL 的处理事务的函数:lv_timer_handler();
在裸机上,我在 while 1 中调用它:
while(1)
{
lv_timer_handler();
LVGL_Delay();
}
4.7、LVGL 初始化
最后,在 main 函数中,调用 LVGL 的初始化:
void LVGL_CentralButton(void)
{
lv_obj_t *btn = lv_btn_create(lv_scr_act());
lv_obj_align(btn, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_height(btn, 30);
lv_obj_t *label;
label = lv_label_create(btn);
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
lv_label_set_text(label, "LVGL");
static lv_style_t style_btn;
lv_style_init(&style_btn);
lv_style_set_radius(&style_btn, 10);
lv_style_set_border_color(&style_btn, lv_color_white());
lv_style_set_border_opa(&style_btn, LV_OPA_30);
lv_obj_add_style(btn, &style_btn, LV_STATE_DEFAULT);
}
void LVGL_Init(void)
{
lv_init(); // lvgl初始化,如果这个没有初始化,那么下面的初始化会崩溃
lv_port_disp_init(); // 显示器初始化
// lv_port_indev_init(); // 输入设备初始化(如果没有实现就注释掉)
// lv_port_fs_init(); // 文件系统设备初始化(如果没有实现就注释掉)
}
int main(void)
{
LogInit()
BspLedInit();
LCD_ST7735S_Init();
LVGL_Init();
LVGL_CentralButton();
SystemTickInit();
while(1)
{
lv_timer_handler();
LVGL_Delay();
}
}
4.8、修改栈空间
还记得 LVGL 的最低要求配置吗,我们需要根据要求来修改堆栈空间:
5、编译
在 Keil 中的 Options->C/C++ 勾上 C99 Mode;
一切准备就绪,编译(有点久),下载 OK ;
5.1、消除编译 warning
你会发现很多编译的 warning,在 Options->C/C++ 的 Misc Controls 中加入
--diag_suppress=188 --diag_suppress=111 --diag_suppress=550
即可屏蔽掉一些错误;
6、结果
好吧,跑起来了