LittlevGL 图形库的移植


首先使用git获取到源代码和例子代码
git clone https://github.com/littlevgl/lvgl.git
git clone https://github.com/littlevgl/lv_examples.git

也有其他项目,可以自己看看,下载需要的。
https://github.com/littlevgl

切换到发布稳定版本的tag,目前的版本是 6.0,所以
git checkout v6.0

移植:
编译器 Keil MDK 4.74
MCU    Cortex M3

1,把 lv_conf_template.h 复制一份为 lv_conf.h,放在上级目录。
形成这个目录结构
lv_conf.h
lvgl/
如果不想那样,也可以把 lv_conf.h 拷贝到自己的项目目录下
编译器定义变量 LV_CONF_INCLUDE_SIMPLE 就可以了。

以下3个变量必须配置
#define LV_HOR_RES_MAX          (320)
#define LV_VER_RES_MAX          (240)
#define LV_COLOR_DEPTH           16

2,src 目录下的各个子目录都是源代码,所有加入编译

3,程序架构:

void DisplayThread(void)
{
    /* Step1 */
    lv_init();

    /* Step 2 */
    lv_port_disp_init();

    /* Step 3 */
    lv_port_indev_init();

    /* Step 4 */
    demo_create();

    while (true) {
        DelayMs(10);
        
        /* Step 5 */
        lv_tick_inc(10);
        lv_task_handler();
    }
}

这是基于RTOS的线程的 LittlevGL 的调用流程,步骤如下:
1,调用图形库的初始化 lv_init。
2,调用 lv_port_disp_init 初始化显示芯片和接口,下面详细介绍。
3,调用 lv_port_indev_init 初始化输入设备和接口,例如触摸屏,下面详细介绍
4,设计具体的GUI界面,例如我们调用官方的demo程序 demo_create。
5,在一个循环中调用库自己的处理函数,lv_tick_inc 是图形库的tick函数,你这个循环延时了多久就传入多久
例如我这个循环是 10ms的,所以这里传10。这样做比较简单,当然也可以在 SysTick_Handler 里面调用。

4,显示驱动的移植
在 lvgl/port/lv_port_disp_template.c 文件就是显示需要的接口文件

static lv_disp_buf_t disp_buf_2;
static lv_color_t buf2_1[LV_HOR_RES_MAX * 10];
static lv_color_t buf2_2[LV_HOR_RES_MAX * 10];
    
    disp_init();
    lv_disp_buf_init(&disp_buf_2, buf2_1, buf2_2, LV_HOR_RES_MAX * 10);
    lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);
    disp_drv.flush_cb = disp_flush;
    disp_drv.buffer = &disp_buf_2;
    lv_disp_drv_register(&disp_drv);

1)首先做 disp_init 硬件初始化。
2)使用1个或者2个buffer作为显示缓冲,这个buffer要求是多行像素点,例如
LV_HOR_RES_MAX * 10 是 10个像素点行。lvgl比较灵活,如果缓冲不够,那么只需要
传入1个缓冲就可以,如果传入2个缓冲,则可以使用双缓冲机制,就是一个缓冲送到显示芯片
另外一个缓冲可以继续处理。

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*/
    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++;
        }
    }

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

然后需要实现一个刷新数据的函数,传入的 color_p 是各个颜色点数据,只需要实现一个 put_px
把某个坐标的颜色填进去就可以了。当然,这种实现是比较低效率的。可以自由发挥。

注意:
液晶屏,左上角是坐标原点(0,0),如果不是,则需要转换。


5,触摸屏驱动的移植
在 lvgl/port/lv_port_indev_template.c 就是输入设备的模版
其中触摸屏是属于 

    /*Initialize your touchpad if you have*/
    touchpad_init();

    /*Register a touchpad input device*/
    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);

流程也是那样,首先初始化触摸板。
然后注册进去系统。
static bool 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;

    /*Save the pressed coordinates and the state*/
    if(touchpad_is_pressed()) {
        touchpad_get_xy(&last_x, &last_y);
        data->state = LV_INDEV_STATE_PR;
    } else {
        data->state = LV_INDEV_STATE_REL;
    }

    /*Set the last pressed coordinates*/
    data->point.x = last_x;
    data->point.y = last_y;

    /*Return `false` because we are not buffering and no more data to read*/
    return false;
}
然后就是实现这个一个函数,如果触摸板有按下,那么读取相应的坐标返回。
注意
触摸屏,左上角是坐标原点(0,0),如果不是,则需要转换。

6,如果驱动没有问题,运行起来之后,就可以玩玩官方的延时程序了。
下载了 lv_examples 仓库的话,可以看看里面有很多例子。


遇到的问题,编译报错
..\..\gui\lvgl\src\lv_font\lv_font_roboto_16.c(1944): 
error:  #69: integer conversion resulted in truncation

由于默认的是 roboto_16 所以只有这个加入编译,其实 roboto_12,roboto_22 一样是有问题的。
/src/lv_font/lv_font_fmt_txt.h 里面 lv_font_fmt_txt_dsc_t 定义

这里莫名其妙,看了很久没有发现问题,应该是 Keil 的bug 吧。
对位域的处理有问题

    /*Scale kern values in 12.4 format*/
//    uint16_t kern_scale;

    /*Number of cmap tables*/
    uint16_t cmap_num       :10;

    /*Bit per pixel: 1, 2, 4 or 8*/
    uint16_t bpp            :3;

    /*Type of `kern_dsc`*/
    uint16_t kern_classes   :1;

    /*
     * storage format of the bitmap
     * from `lv_font_fmt_txt_bitmap_format_t`
     */
    uint16_t bitmap_format  :2;

    //xxxx
    uint16_t kern_scale;
    
    
我将 kern_scale放到后面就可以编译通过了。这里没有理由。

    /*Scale kern values in 12.4 format*/
    uint16_t kern_scale;

    /*Number of cmap tables*/
    uint16_t cmap_num;

    /*Bit per pixel: 1, 2, 4 or 8*/
    uint16_t bpp;

    /*Type of `kern_dsc`*/
    uint16_t kern_classes;

    /*
     * storage format of the bitmap
     * from `lv_font_fmt_txt_bitmap_format_t`
     */
    uint16_t bitmap_format;
    
或者不要位域,定义为这样也可以

GCC 应该是可以编译通过的,所以我只能怀疑是 Keil 的问题了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值