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 (128×128 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、结果

    好吧,跑起来了

     

    物联沃分享整理
    物联沃-IOTWORD物联网 » LVGL移植流程:10步实现

    发表评论