LVGL学习笔记1 - 准备

31 篇文章 86 订阅

目录

1. 下载LVGL源代码

2. 平台

3. 导入到工程

3.1 配置头文件

3.2 src文件夹

4. 移植

4.1 显示接口部分

4.1.1 disp_init

4.1.2 lv_port_disp_init

4.1.3 disp_flush

4.2 IPA部分

4.2.1 lv_draw_gd32_ipa_init

4.2.2 lv_draw_gd32_ipa_blend_fill

4.2.3 lv_draw_gd32_ipa_blend_map

4.2.3 lv_gpu_gd32_ipa_wait_cb

4.3 tick

5. 初始化


LVGL是Light and Versatile Graphics Library(轻量级通用型图形库)的简称,遵循MIT开源许可协议。

LVGL的官网地址如下:

LVGL - Light and Versatile Embedded Graphics Library

LVGL中文资料:

http://lvgl.z.net

1. 下载LVGL源代码

源代码在Github上。

LVGL部分:

GitHub - lvgl/lvgl: Powerful and easy-to-use embedded GUI library with many widgets, advanced visual effects (opacity, antialiasing, animations) and low memory requirements (16K RAM, 64K Flash).https://github.com/lvgl/lvgl

驱动部分(这部分和外部驱动芯片有关,例如ST7565之类的,而GD32F450自带驱动,所以实际不需要):

GitHub - lvgl/lv_drivers: TFT and touch pad drivers for LVGL embedded GUI libraryhttps://github.com/lvgl/lv_drivers

2. 平台

平台1:

GD的开发板GD32450I_EVAL,即GD32GD32F450 + 4.3寸480x272。

编译环境是MDK + GCC。

平台2:

Visual Studio模拟器,下载地址:

GitHub - lvgl/lv_port_win_visual_studio: LVGL Windows Simulator Visual Studio Edition

Visual Studio的版本要求是2019版及以上,下载后打开工程LVGL.Simulator.sln,将默认的平台改为X64(根据自己的Windows配置修改),编译后可以运行看看效果。如果只用这个模拟器学习,可以不看后面的GD32F450的移植。

3. 导入到工程

3.1 配置头文件

根目录下的lv_conf_template.h和lvgl.h拷贝到工程中,并将lv_conf_template.h改名为lv_conf.h。

lv_conf.h是会随项目情况改动到的,放在项目相关的目录内,这里做个另外的lv_config.h,里面只是include真正的lv_config.h文件。而lvgl.h不会更改,而且和代码内的包含路径有关,可以保留在原来的相对路径内。

 其中lv_config.h的内容:

#ifndef LV_CONF_H
#define LV_CONF_H

#include "..\..\include.h"

#endif

其中include.h中会include真正的配置文件。

把配置文件中的配置打开,默认有一个宏定义设置为0的,改成1。

3.2 src文件夹

把文件加入MDK的工程,draw文件夹里面还有一些文件夹,可以先不加,和平台移植有关。

4. 移植

draw文件夹中有部分代码和平台有关,这里拷贝分和平台有关的代码,例如stm32_dma2d这个文件夹并改名字为gd32_ipa,里面的文件名改为lv_gpu_gd323_ipa.c和lv_gpu_gd323_ipa.h,将所有的stm32和dma2d的字符串改为gd32和ipa。

搜索LV_USE_GPU_STM32_DMA2D,参照改一个LV_USE_GPU_GD32_IPA,将lv_gpu_gd323_ipa.c中的函数内有关STM32的部分删除。

4.1 显示接口部分

在之前下载的lvgl文件夹里面\examples\porting文件夹下有对应的硬件接口文件lv_port_disp_template.c和lv_port_disp_template.h。这个文件主要要实现函数disp_init、lv_port_disp_init和disp_flush。

4.1.1 disp_init

显示初始化,主要是硬件配置和TLI接口初始化。

要使用到SDRAM,初始化好TLI接口,并且定义好一个Layer。

定义指针变量pTFTBuf指向SDRAM,因为这里定义格式为RGB565,大小为TFT_WIDTH * TFT_HEIGHT * 2字节。

#define GUI_TFT_BUF_START               EXMC_SDRAM_ADDR0
#define GUI_TFT_BUF_LEN                 (TFT_WIDTH * TFT_HEIGHT * sizeof(lv_color_t))
uint16_t *pTFTBuf = (uint16_t *)GUI_TFT_BUF_START;

选择Layer 0作为显示层,初始化这个层。

    tliLayer_t layer;
    layer.alpha = 0xFF;
    layer.bufAddr = (uint32_t)pTFTBuf;
    layer.defalutColor = 0x000000FF;//0x00FFFFFF;
    layer.format = FORMAT_RGB565;
    layer.x = 0;
    layer.y = 0;
    layer.w = TFT_WIDTH;
    layer.h = TFT_HEIGHT;
    tliLayerInit(0, layer, 1);

4.1.2 lv_port_disp_init

这个函数里面需要选择1种显示缓冲,文件提供了3种方式的缓冲方式,方式1/2/3对应的刷新速度分别是慢/中/快,而RAM占用是少/中/多。这里选择方式3,分配的空间放在SDRAM中(这里注意,如果RAM不够会导致Hard Fault中断,我选择方式2会在第四次调用disp_flush后出错)。

#define GUI_DISP_BUF1_START             (GUI_TFT_BUF_START + GUI_TFT_BUF_LEN)
#define GUI_DISP_BUF1_LEN               GUI_TFT_BUF_LEN
#define GUI_DISP_BUF2_START             (GUI_DISP_BUF1_START + GUI_DISP_BUF1_LEN)
#define GUI_DISP_BUF2_LEN               GUI_TFT_BUF_LEN

static lv_disp_draw_buf_t draw_buf_dsc;
static lv_color_t *dispbuf_1 = (lv_color_t *)GUI_DISP_BUF1_START;
static lv_color_t *dispbuf_2 = (lv_color_t *)GUI_DISP_BUF2_START;

lv_disp_draw_buf_init(&draw_buf_dsc, dispbuf_1, dispbuf_2, GUI_DISP_BUF1_LEN);   /*Initialize the display buffer*/

4.1.3 disp_flush

对于GD32F450的TLI来说,flush就是对Layer对应的buffer更新数据。

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;
    uint16_t *pTFTBuf = (uint16_t *)GUI_TFT_BUF_START;
    //Printf("draw:(%d, %d) - (%d, %d)\n", area->x1, area->y1, area->x2, area->y2);
    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)*/
            *(uint16_t *)(pTFTBuf + y * TFT_WIDTH + x) = (*color_p).full;
            color_p++;
        }
    }

    /*IMPORTANT!!!
     *Inform the graphics library that you are ready with the flushing*/
    lv_disp_flush_ready(disp_drv);
}

4.2 IPA部分

4.2.1 lv_draw_gd32_ipa_init

初始化ipa,并设置颜色模式。

void lv_draw_gd32_ipa_init(void)
{
    RCU_AHB1EN |= ((uint32_t)1 << 23);
    //IPA Reset
    RCU_AHB1RST |= ((uint32_t)1 << 23);
    RCU_AHB1RST &= ~((uint32_t)1 << 23);
    
    /*set output colour mode*/
    IPA_CTL |= ((uint32_t)1 << 2);
    IPA_DPCTL = LV_IPA_COLOR_FORMAT;
}

4.2.2 lv_draw_gd32_ipa_blend_fill

这个函数的作用是用指定的颜色刷新显存。函数原型:

static void lv_draw_gd32_ipa_blend_fill(lv_color_t * dest_buf, lv_coord_t dest_stride, 
    const lv_area_t * fill_area, lv_color_t color)

参数含义:

dest_buf - 显存地址

dest_stride - 显存地址偏移量

fill_area - 填充的区域,长方形(x1,y1)- (x2,  y2)

color - 填充的颜色

static void lv_draw_gd32_ipa_blend_fill(lv_color_t * dest_buf, lv_coord_t dest_stride, const lv_area_t * fill_area,
                                           lv_color_t color)
{
    /*Simply fill an area*/
    int32_t area_w = lv_area_get_width(fill_area);
    int32_t area_h = lv_area_get_height(fill_area);
    invalidate_cache();

    IPA_CTL |= ((uint32_t)1 << 2);  //Stop IPA
    
    IPA_CTL &= ~((uint32_t)0x3 << 16);
    IPA_CTL |= ((uint32_t)0x3 << 16);    //Specific color fill
    
    IPA_DMADDR = (uint32_t)dest_buf;
    IPA_DPV = color.full;
    IPA_DLOFF = dest_stride - area_w;
    IPA_IMS = (area_w << 16) | (area_h << 0);
    
    IPA_CTL |= ((uint32_t)1 << 0);
}

4.2.3 lv_draw_gd32_ipa_blend_map

这个函数的作用是指定buffer更新显存。

static void lv_draw_gd32_ipa_blend_map(
    lv_color_t * dest_buf, 
    const lv_area_t * dest_area, 
    lv_coord_t dest_stride,
    const lv_color_t * src_buf, 
    lv_coord_t src_stride, 
    lv_opa_t opa)

参数含义:

dest_buf - 显存地址

dest_area - 填充的区域

dest_stride - 显存地址偏移量

src_buf - 源数据地址

dest_stride - 源数据地址偏移量

opa - alpha值

static void lv_draw_gd32_ipa_blend_map(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride,
                                          const lv_color_t * src_buf, lv_coord_t src_stride, lv_opa_t opa)
{
    /*Simple copy*/
    int32_t dest_w = lv_area_get_width(dest_area);
    int32_t dest_h = lv_area_get_height(dest_area);
    invalidate_cache();
    
    if(opa >= LV_OPA_MAX) {
        IPA_CTL |= ((uint32_t)1 << 2);  //Stop IPA
    
        IPA_CTL &= ~((uint32_t)0x3 << 16);
        IPA_CTL = ((uint32_t)0x0 << 16);    
        
        IPA_FPCTL = LV_IPA_COLOR_FORMAT;
        IPA_FMADDR = (uint32_t)src_buf;
        IPA_FLOFF = src_stride - dest_w;
        IPA_DMADDR = (uint32_t)dest_buf;
        IPA_DLOFF = dest_stride - dest_w;
        IPA_IMS = (dest_w << 16) | (dest_h << 0);

        IPA_CTL |= ((uint32_t)1 << 0); //Start IPA
    }
    else {
        IPA_CTL |= ((uint32_t)1 << 2);  //Stop IPA
    
        IPA_CTL &= ~((uint32_t)0x3 << 16);
        IPA_CTL |= ((uint32_t)0x2 << 16);  
        IPA_BPCTL = LV_IPA_COLOR_FORMAT;
        IPA_BMADDR = (uint32_t)dest_buf;
        IPA_BLOFF = dest_stride - dest_w;
        IPA_FPCTL = (uint32_t)LV_IPA_COLOR_FORMAT
                         /*alpha mode 2, replace with foreground * alpha value*/
                         | (2 << 16)
                         /*alpha value*/
                         | (opa << 24);
        IPA_FMADDR = (uint32_t)src_buf;
        IPA_FLOFF = src_stride - dest_w;
        IPA_DMADDR = (uint32_t)dest_buf;
        IPA_DLOFF = dest_stride - dest_w;
        IPA_IMS = (dest_w << 16) | (dest_h << 0);
        
        IPA_CTL |= ((uint32_t)1 << 0); //Start IPA
    }
}

4.2.3 lv_gpu_gd32_ipa_wait_cb

这个函数的作用是等待渲染结束。

void lv_gpu_gd32_ipa_wait_cb(lv_draw_ctx_t * draw_ctx)
{
    lv_disp_t * disp = _lv_refr_get_disp_refreshing();
    if(disp->driver && disp->driver->wait_cb) {
        while(IPA_CTL & 0x01) {
            disp->driver->wait_cb(disp->driver);
        }
    }
    else {
        while(IPA_CTL & 0x01);
    }
    lv_draw_sw_wait_for_finish(draw_ctx);
}

4.3 tick

LVGL 需要一个系统滴答来了解动画和其他任务(例如输入设备读取)所用的时间,所以需要在一个定时器中调用lv_tick_inc。

lv_tick_inc(TIMER_MS);

定时调用这个函数即可,实现方式可以随意,裸奔的方式可以在system tick中调用。

5. 初始化

在使用其他LVGL的API前必须调用lv_init(),然后调用lv_port_disp_init即可。

    lv_init();
    lv_port_disp_init();

这时候屏幕应该是没有显示(如果把pTFTBuf 的数据全部改成0xF800,即红色,可以看到屏幕为红色,LVGL并没有起作用)。

在非OS应用中,需要在主循环中添加lv_task_handler();而在OS应用中,应该是在一个任务循环中添加这个函数的调用。

这是屏幕会显示白屏,添加一段测试程序(在lv_port_disp_init()后添加即可)。

lv_obj_t *rect = lv_obj_create(lv_scr_act());
lv_obj_set_size(rect, LV_PCT(20), LV_PCT(20));
lv_obj_align(rect, LV_ALIGN_CENTER, 0, 0);

即画一个矩形。

模拟器显示效果:

  • 2
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值