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 的問題了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章