🐣

LvGL 9.3.0 on Wio Terminal

に公開

Wio TerminalLvGL v9.3.0

これまで9.3.0-devだったLvGLですが、この度正式に9.3.0となり、Arduino IDEからも入手できるようになりました。

lvgl 9.3.0 in Arduino IDE

本記事では、Wio TerminalLvGL(v9.3.0)を動かす手順についてまとめます。なお、本記事よりも先行して投稿された記事がございます。参考にさせて頂きました。

https://zenn.dev/akihiro_ya/articles/aaf6776c817398

前提要件

  • Wio Terminalがあること
  • Arduino IDEがあること
  • Arduino IDEからWio Terminalにスケッチを書き込めること
    この要件には、IDEの設定にてhttps://files.seeedstudio.com/arduino/package_seeeduino_boards_index.jsonを指定する必要があります。これにより、次の要件が自動的に満足されるはずです。
  • Arduino IDEの「ライブラリマネージャー」でLvGL(v9.3.0)をインストールしていること
    (上の画像を参照)
  • TFT_eSPIをインストールしていないこと

なお、VSCodeなど、別のIDEからWio Terminalへの書き込みができるのであれば、それでもかまいません。

LvGLの設定

lv_conf_template.hというファイルをコピーし、libraliesフォルダー(Arduino IDEのライブラリーがインストールされる場所)に配置します。この時、ファイル名をlv_conf.hに変更します。

配置例

以降、このlv_conf.hの内容を編集します。

1. 設定ファイルを有効にする

https://github.com/lvgl/lvgl/blob/master/lv_conf_template.h#L15

始めはこの箇所が0になっており、ファイルの内容が全て無効になっています。コメントにある通り、1に変更します。

lv_conf.h (L15)
#if 1 /* Set this to "1" to enable content */

2. TFT_eSPIを有効にする

本記事では、Seeed_Arduino_LCDを併用することでLvGLを動かします。その名前からは分かりにくいものの、TFT_eSPI.hをインクルードして利用します。従って、本家のTFT_eSPIが存在すると、TFT_eSPI.hという同名のファイルが存在することでエラーになります。

https://github.com/lvgl/lvgl/blob/master/lv_conf_template.h#L1258-L1259

0とあるので、1に変更します。

lv_conf.h (L1259)
#define LV_USE_TFT_ESPI         1

正規の設定は以上です。

リンクエラーへの対処

設定を終えたらサンプルプログラムで動作確認したいところですが、現段階ではリンクエラーが起こります。

https://zenn.dev/link/comments/abc7f58a21d631

リンクエラーの例
c:/users/⋯/appdata/local/arduino15/packages/seeeduino/tools/arm-none-eabi-gcc/7-2017q4/bin/../lib/gcc/arm-none-eabi/7.2.1/../../../../arm-none-eabi/bin/ld.exe: error: C:\Users\\AppData\Local\arduino\sketches\2D6F468B41CA3D5D12AB3EBD59EEB1C7\libraries\lvgl\draw\sw\blend\helium\objs.a(lv_blend_helium.S.o): Conflicting CPU architectures 13/0
c:/users/⋯/appdata/local/arduino15/packages/seeeduino/tools/arm-none-eabi-gcc/7-2017q4/bin/../lib/gcc/arm-none-eabi/7.2.1/../../../../arm-none-eabi/bin/ld.exe: failed to merge target specific data of file C:\Users\\AppData\Local\arduino\sketches\2D6F468B41CA3D5D12AB3EBD59EEB1C7\libraries\lvgl\draw\sw\blend\helium\objs.a(lv_blend_helium.S.o)
c:/users/⋯/appdata/local/arduino15/packages/seeeduino/tools/arm-none-eabi-gcc/7-2017q4/bin/../lib/gcc/arm-none-eabi/7.2.1/../../../../arm-none-eabi/bin/ld.exe: error: C:\Users\\AppData\Local\arduino\sketches\2D6F468B41CA3D5D12AB3EBD59EEB1C7\libraries\lvgl\draw\sw\blend\neon\objs.a(lv_blend_neon.S.o): Conflicting CPU architectures 13/0
c:/users/⋯/appdata/local/arduino15/packages/seeeduino/tools/arm-none-eabi-gcc/7-2017q4/bin/../lib/gcc/arm-none-eabi/7.2.1/../../../../arm-none-eabi/bin/ld.exe: failed to merge target specific data of file C:\Users\\AppData\Local\arduino\sketches\2D6F468B41CA3D5D12AB3EBD59EEB1C7\libraries\lvgl\draw\sw\blend\neon\objs.a(lv_blend_neon.S.o)
collect2.exe: error: ld returned 1 exit status

次のフォルダのライブラリlvglバージョン9.3.0-devを使用中:C:\\libraries\lvgl 
exit status 1

Compilation error: exit status 1

原因は、エラーメッセージにある通り二つのアセンブリーファイルです。

https://github.com/lvgl/lvgl/blob/master/src/draw/sw/blend/helium/lv_blend_helium.S

https://github.com/lvgl/lvgl/blob/master/src/draw/sw/blend/neon/lv_blend_neon.S

私の試行錯誤した限りでは、設定でこのエラーを解消することは出来ないだろうと結論付けます。

対処法その一:削除

原因となる二つのファイルを削除・移動・拡張子変更するなどして、コンパイル対象から除外します。これにより、リンクエラーが解消します。どうせWio Terminalでは使われなそうな機能ですから、動作には問題有りません。

対処法その二:編集

https://zenn.dev/link/comments/4368b0ee1a046d

各ファイルに次の四行だけ追加します。

#else
.thumb
.text
nop

具体的には次のようになります。

lv_blend_helium.S
中略
︙
export_set argb8888, argb8888, 32, 32, normal

#else
.thumb
.text
nop
#endif /*LV_USE_DRAW_SW_ASM == LV_DRAW_SW_ASM_HELIUM && defined(__ARM_FEATURE_MVE) && __ARM_FEATURE_MVE && LV_USE_NATIVE_HELIUM_ASM*/


lv_blend_neon.S
中略
︙
export_set argb8888, argb8888, 32, 32, normal

#else
.thumb
.text
nop
#endif /*LV_USE_DRAW_SW_ASM == LV_DRAW_SW_ASM_NEON*/


これで、破壊せずにリンクエラーを解消できました。

動作確認

サンプルプログラムで動作確認したいところですが、そのままでは画面の表示が崩壊します。

https://github.com/lvgl/lvgl/blob/master/examples/arduino/LVGL_Arduino/LVGL_Arduino.ino

崩壊

原因はこの箇所です。

https://github.com/lvgl/lvgl/blob/master/examples/arduino/LVGL_Arduino/LVGL_Arduino.ino#L18-L21

次の通りに編集することで、意図通り表示されます。

#define TFT_HOR_RES   240
#define TFT_VER_RES   320
#define TFT_ROTATION  LV_DISPLAY_ROTATION_270

縦と横が逆になるのは、一体何の不具合なのでしょうか。

プログラム

本記事では、簡単のため次のようにプログラムを修正しました。上述の箇所のみ変更前と後を残しています。

但し、一点変更しています。

https://github.com/lvgl/lvgl/blob/master/examples/arduino/LVGL_Arduino/LVGL_Arduino.ino#L133

この通り、画面にはHello Arduino, I'm LVGL!と表示されるようになっています。これが味気ないので、バージョン情報を含んだ文字列を表示させることにしました。

https://github.com/lvgl/lvgl/blob/master/examples/arduino/LVGL_Arduino/LVGL_Arduino.ino#L79-L80

sample sketch for v9.3.0
#include <lvgl.h>
#include <TFT_eSPI.h>

// before:
// #define TFT_HOR_RES   320
// #define TFT_VER_RES   240
// #define TFT_ROTATION  LV_DISPLAY_ROTATION_0
// after:
#define TFT_HOR_RES   240
#define TFT_VER_RES   320
#define TFT_ROTATION  LV_DISPLAY_ROTATION_270

#define DRAW_BUF_SIZE (TFT_HOR_RES * TFT_VER_RES / 10 * (LV_COLOR_DEPTH / 8))
uint32_t draw_buf[DRAW_BUF_SIZE / 4];

void my_disp_flush( lv_display_t *disp, const lv_area_t *area, uint8_t * px_map) { lv_display_flush_ready(disp); }

static uint32_t my_tick(void) { return millis(); }

void setup() {
    lv_init();

    lv_tick_set_cb(my_tick);

    lv_display_t * disp;
    disp = lv_tft_espi_create(TFT_HOR_RES, TFT_VER_RES, draw_buf, sizeof(draw_buf));
    lv_display_set_rotation(disp, TFT_ROTATION);

    String LVGL_Arduino = "Hello Arduino! ";
    LVGL_Arduino += String('V') + lv_version_major() + "." + lv_version_minor() + "." + lv_version_patch();

    lv_obj_t *label = lv_label_create( lv_screen_active() );
    lv_label_set_text( label, LVGL_Arduino.c_str() );
    lv_obj_align( label, LV_ALIGN_CENTER, 0, 0 );
}

void loop()
{
    lv_timer_handler();
    delay(5);
}

サンプルの正しい動作

v7.0.2との比較

これまで、Wio TerminalではSeeed_Arduino_LvGLを使っていました。その際用いたサンプルプログラムはこちらになります。

https://github.com/lvgl/lv_arduino/blob/master/examples/ESP32_TFT_eSPI/ESP32_TFT_eSPI.ino

異なるリポジトリーを参照していますが、その経緯は以前投稿したこちらの記事をご覧ください。

https://zenn.dev/amenaruya/articles/b3e7a992eec368

さて、こちらも無駄な記述を省いて簡略化しましょう。

sample sketch for v7.0.2
#include <lvgl.h>
#include <TFT_eSPI.h>

TFT_eSPI tft = TFT_eSPI();
static lv_disp_buf_t disp_buf;
static lv_color_t buf[LV_HOR_RES_MAX * 10];

void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p) {
    uint32_t w = (area->x2 - area->x1 + 1);
    uint32_t h = (area->y2 - area->y1 + 1);

    tft.startWrite();
    tft.setAddrWindow(area->x1, area->y1, w, h);
    tft.pushColors(&color_p->full, w * h, true);
    tft.endWrite();

    lv_disp_flush_ready(disp);
}

bool read_encoder(lv_indev_drv_t * indev, lv_indev_data_t * data) {
    static int32_t last_diff = 0;
    int32_t diff = 0;
    int btn_state = LV_INDEV_STATE_REL;

    data->enc_diff = diff - last_diff;;
    data->state = btn_state;

    last_diff = diff;

    return false;
}

void setup() {
    lv_init();

    tft.begin();
    // before
    // tft.setRotation(1);
    // after
    tft.setRotation(3);

    lv_disp_buf_init(&disp_buf, buf, NULL, LV_HOR_RES_MAX * 10);

    lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);
    disp_drv.hor_res = 320;
    disp_drv.ver_res = 240;
    disp_drv.flush_cb = my_disp_flush;
    disp_drv.buffer = &disp_buf;
    lv_disp_drv_register(&disp_drv);

    lv_indev_drv_t indev_drv;
    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_ENCODER;
    indev_drv.read_cb = read_encoder;
    lv_indev_drv_register(&indev_drv);

    lv_obj_t *label = lv_label_create(lv_scr_act(), NULL);
    // before
    // lv_label_set_text(label, "Hello Arduino! (V7.0.X)");
    // after
    String LVGL_Arduino = String::format("Hello Arduino! V%d.%d.%d", LVGL_VERSION_MAJOR, LVGL_VERSION_MINOR, LVGL_VERSION_PATCH);
    lv_label_set_text(label, LVGL_Arduino.c_str());
    lv_obj_align(label, NULL, LV_ALIGN_CENTER, 0, 0);
}

void loop() {
    lv_task_handler();
    delay(5);
}

実際にこれらを実行して分かる違いとして、それぞれの容量があります。

v7.0.2 v9.3.0
バイト数 151672 275736
占有率 \frac{151672}{507904} = 29 \% \frac{275736}{507904} = 54 \%

成果物は殆ど同じものであるにも拘らず、その容量は約1.8倍に増大しています。全体の50\%以上を占有することになるとは驚きです。当然乍ら、コンパイルに掛かる時間も非常に長くなりました。

正に一長一短と言うべきものですが、容量については気を配って開発する必要がありそうです。

Discussion