LVGL (10) 移植流程

目录

1、下载源码

2、目录结构介绍

3、移植前注意事项

4、开始移植

4.1、改文件名字

4.1.1、对接 platform/chip 的部分

4.1.2、LVGL 配置文件

4.2、移植显示驱动

4.3、配置 LVGL

4.3.1、屏幕尺寸的配置

4.3.2、颜色相关配置

4.3.3、内存相关配置

4.3.4、底层的配置

4.3.5、日志

4.3.6、其他(显示帧率等)

4.3.7、编译属性

4.3.8、裁剪组件

4.3.9、开启/关闭 Demo

4.4、加入文件到工程

4.5、适配心跳

4.6、调用处理事务

4.7、LVGL 初始化

4.8、修改栈空间

5、编译

5.1、消除编译 warning

6、结果


基于 LVGL V8 版本

LVGL 的官方文档,大致说了一下如何进行 LVGL 的移植工作,LVGL 的移植主要分为几个步骤:

  1. 下载 LVGL 的源码,gitee 会定期同步 GitHub,Gitee 极速下载/littlevGL
  2. 修改几个源码文件命名;
  3. 修改用于 LVGL 配置的文件;
  4. 移植对接底层驱动送显部分;
  5. 适配 LVGL 心跳 Tick,适配 LVGL 任务处理函数调用,调用 LVGL 初始化函数;
  6. 编译,链接,执行;

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、结果

好吧,跑起来了

 

评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值