04.M5Tab5でLVGL9テスト


04.M5Tab5でLVGL9テスト

M5Stack Tab5にLVGL v9をインストールし、LVGL_Arduino.inoを修正したAkihiroさんのスケッチを元に、日本語表示とサンプルスケッチも一部確認します。

LVGLの公式ドキュメント

v9.4 (最新) https://docs.lvgl.io/master/
v9.3 https://docs.lvgl.io/9.3/

LVGLのVerについて

Arduino-IDEのライブラリマネージャーでの最新はv9.3.0です。

1. メジャーVerとは
例:v5.0.0, v6.0.0
互換性の無いAPIの変更
v8.0 発表2021/6
v9.0 発表2024/1 約2年半後
2-3年で最新Verとは互換性がなくなってしまう。

2. マイナーVerとは
例:v6.1.0, v6.2.0
下位互換
サポートは発表後1年
v8.4は、2024/3発表で、2025/3にサポート終了しています。
現在のサポートはv9.3以降です。

3. パッチVerとは
例:v6.1.1, v6.1.2
バグ修正

LVGLのセットアップ

これからの作成は、現在例が色々あるLVGL v8よりも最新のLVGL v9を選びました。

LVGLのインストール

Arduino-IDE > ライブラリマネージャー > lvglで検索 > lvgl by kisvegabor 9.3.0 (最新)をインストール

設定

1. lv_conf_template.hをリネームしてlibrarisホルダにcopy
Arduino-IDE > ファイル > 基本設定... > "スケッチブックの場所"

c:\Users\.....\Documents\Arduino
より、以下をエクスプローラーで開きます
(続けて)\libraries\lvgl\lv_conf_template.h
そしてこのファイルを
(同じく)\libraries\lv_conf_template.h
にコピーし、リネームして
(同じく)\libraries\lv_conf.h
にします。(lvglのホルダが見れる場所)

2. lv.conf.hを編集
上記ファイルの15行目"#if 0"を"#if 1"に変更し保存
"1"にしないと、このファイルの内容を修正時に有効になりません。

3. サンプルスケッチを使用する場合
lvgl/examples を
lvgl/src/examples にcopyする。

スケッチのVer

・Arduino-IDE 2.3.6
・LVGL 9.3.0
・M5Unified 0.2.7
・M5GFX 0.2.9
新しくても古くてもVer違いで動作しなくなることがあります。

スケッチの内容について

スケッチ例での
Arduino-IDE > ファイル > スケッチ例 > (カスタムライブラリのスケッチ例) lvgl > arduino > LVGL_Arduino
これは、LVGL初めてではうまく動作しなかったので別を探します。

参考は、「LVGL V9をM5StackCore2で動かしてみる」
https://zenn.dev/akihiro_ya/articles/25f37c3075f4bc
です。これは、LVGL_Arduino.inoを元にしてM5用に書換えたスケッチです。

参考からの変更点

1. 画面変更

M5Stack Tab5の画面サイズは1280x720pxです。
さらに、右手で電池部を握るのが楽なので
M5.Display.setRotation(3);
を使用します。LVGLには文字の回転は無いようです。(たぶん)

2. 日本語表示

上で修正した"lv.conf.h"の581行目から下にいろいろなfontの設定があります。

589行目 #define LV_FONT_MONTSERRAT_14 1→0
602行目 #define LV_FONT_MONTSERRAT_40 0→1
縦14pxはTab5では小さすぎるの40pxに変更します。

614行目 #define LV_FONT_SOURCE_HAN_SANS_SC_16_CJK 0→1
この特殊fontも使用可とし、日本語表示します。
CJKは、中国・日本・韓国語のフォントです。
C:\Users\.....\Documents\Arduino\libraries\lvgl\src\font\lv_font_source_han_sans_sc_16_cjk.c
に、内蔵文字がそのファイルの最初に記載されています。ただ、縦16pxと小さい文字なのでそれをズーム拡大して表示しますが、多少読みづらくなってしまいます。

632行目 #define LV_FONT_DEFAULT &lv_font_montserrat_40
で、_14→_40 デフォルトは英文用40pxとします。(とりあえず)

今回のスケッチでは、M5GFXのM5.Display.printは使用していません。

3. サンプルの確認

いくつかはコメントを外す事により簡単に確認できます。
修正前のスケッチでは、日本語表示のみします。サンプルの確認をする場合は、日本語表示の部分のスケッチ5行をコメントとし(あると重なって表示をしてしまいます)、スケッチ上の
lv_example_...();
のコメントを1つ外して実行します。
サンプルが表示できることを確認し、それぞれやその他のサンプルは深く修正していません。

スケッチ


// LVGL_Arduino.inoが元の
// https://zenn.dev/akihiro_ya/articles/25f37c3075f4bc を修正
// M5.Display.printを使用せず、LVGLにて1行表示 一部サンプルも表示可能
// Arduino-IDE 2.3.6
// LVGL 9.3.0
// M5Unified 0.2.7
// M5GFX 0.2.9
#include <lvgl.h>                  // LVGL 9.3.0を使用
#include <M5Unified.h>             // M5統合ライブラリ SDが先に
#include <examples/lv_examples.h>  // 例を使用
// "lvgl/examples" を "lvgl/src/examples" にcopyしておく
//#include <demos/lv_demos.h>  // デモを使用 未確認
// "lvgl/demos" を "lvgl/src/demos" にcopyしておく 未確認
#define TFT_HOR_RES 720   // 画面幅 Tab5
#define TFT_VER_RES 1280  // 画面高さ
#define DRAW_BUF_SIZE (TFT_HOR_RES * TFT_VER_RES / 10 * (LV_COLOR_DEPTH / 8))
//LVGLはこのバッファに描画を行う、画面サイズの1/10バイト
uint32_t draw_buf[DRAW_BUF_SIZE / 4];  //

#if LV_USE_LOG != 0  // ログを表示するなら デフォルトは0
void my_print(lv_log_level_t level, const char *buf) {
  //
  LV_UNUSED(level);     // 未使用の変数?
  Serial.println(buf);  // bufをシリアルモニタに表示
  Serial.flush();       // 送信完まで待つ
}
#endif


void my_disp_flush(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map) {
  //レンダリングされた画像をdisplayにcopyする自作関数
  uint32_t width = (area->x2 - area->x1 + 1);   //
  uint32_t height = (area->y2 - area->y1 + 1);  //
  lv_draw_sw_rgb565_swap(px_map, width * height);
  // RGB565バッファの上位バイトと下位バイトを入れ替えます(バッファへのポインタ,ピクセル数)
  M5.Display.pushImageDMA<uint16_t>(area->x1, area->y1, width, height, (uint16_t *)px_map);
  // DMAを使用して画像を効率的に表示(x0,y0,w,h,画像データへのポインタ、通常は16bit RGB565)
  lv_display_flush_ready(disp);  // 書込完でdisplayドライバから呼出す
}


void my_touchpad_read(lv_indev_t *indev, lv_indev_data_t *data) {
  // タッチを取得する自作関数
  lgfx::touch_point_t tp;                   //
  M5.update();                              // touch状態更新
  if (M5.Display.getTouch(&tp)) {           // タッチパネルの情報を取得
    data->point.x = tp.x;                   //
    data->point.y = tp.y;                   //
    data->state = LV_INDEV_STATE_PRESSED;   // 入力デバイスが押された状態
  } else {                                  // タッチしていなければ
    data->state = LV_INDEV_STATE_RELEASED;  // 入力デバイスが解放の状態
  }
}

static uint32_t my_tick() {  // 経過時間の自作関数
  return millis();           // Arduinoのmillis()を使用
}

void setup() {
  M5.begin();                     // M5初期化
  Serial.begin(115200);           // シリアルモニタの通信速度
  M5.Display.setRotation(3);      // LVGLに文字の回転は無いので
  String LVGL_Arduino = "LVGL ";  // 表示用文字変数
  LVGL_Arduino += String('V') + lv_version_major() + "." + lv_version_minor() + "." + lv_version_patch();
  Serial.println(LVGL_Arduino);  // LVGL V9.3.0 とシリアルモニタに表示
  lv_init();                     // LVGLライブラリを初期化
  lv_tick_set_cb(my_tick);       // 経過時間(mS)を取得するためのコールバック関数

#if LV_USE_LOG != 0  // logを表示するなら
  // C:\Users\.....\Documents\Arduino\libraries\lv_conf.h の278行目の #defineで0
  lv_log_register_print_cb(my_print);  //デバッグ用レジスタプリント
#endif

  lv_display_t *disp;                                  //
  disp = lv_display_create(TFT_HOR_RES, TFT_VER_RES);  // 指定解像度で新しいディスプレイを作成
  lv_display_set_flush_cb(disp, my_disp_flush);
  // レンダリングされたイメージをdisplayにcopyするために呼出されるコールバックを設定
  lv_display_set_buffers(disp, draw_buf, nullptr, sizeof(draw_buf), LV_DISPLAY_RENDER_MODE_PARTIAL);
  // displayのバッファを設定(displayへのポインタ,バッファ1,バッファ2,buf_size,レンダリングモード)
  lv_display_set_rotation(disp, LV_DISPLAY_ROTATION_90);  // 0と90のみ動作?
  lv_indev_t *indev = lv_indev_create();                  // 入力デバイスindevの作成
  lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);        //タッチはPOINTERタイプ
  lv_indev_set_read_cb(indev, my_touchpad_read);
  // 入力デバイスのdataをindevに読込むためのコールバック関数を設定 タッチしてるか タッチx,y値

  // C:\Users\.....\Documents\Arduino\libraries\lvgl\src\examples より
  // 以下のコメントを1つずつ外して確認
  //lv_example_anim_1();         // スライドon/offボタン 画面上部に状態をtxt表示
  //lv_example_anim_2();         // 左右に動くボール 大きさも変わる
  //lv_example_anim_3();         // スライドバー2本でグラフ変化
  //lv_example_anim_4();         // 上記1の表示が1次止まる
  //lv_example_anim_timeline_1();// 四角が徐々に小さくなる 下のスライドバーで途中状態で止められる
  //lv_example_event_bubble();// ボタンが3行3列表示 スクロールして29まである 押すと青→赤に
  //lv_example_event_button();// ボタン状態を表示 クリック,離した,longクリック
  //lv_example_event_click(); // 中央にボタン クリックでボタン表示の数字が1加算
  //lv_example_event_draw();  // 中央に表示の円のサイズが大きくなったり小さくなったりする
  //lv_example_event_streak();// ボタンの連続クリック数を画面上に表示
  //lv_example_get_started_1();// 薄い紺の地 中央に白文字
  //lv_example_get_started_2();// ボタン基本
  //lv_example_get_started_3();// 角ボタンとRのついたボタン
  //lv_example_get_started_4();// スライドボタン 上部に0-100の表示
  //lv_example_grad_1();// 白から赤までのグラディション
  //lv_example_grad_2();// 1行が画面からはみ出て、手でスクロール
  //lv_example_grad_3();// 上と同じ NGか?
  //lv_example_grad_4();// 上と同じ NGか?
  //lv_example_flex_1();// 横スクロールをして10個のボタン、縦スクロールも
  //lv_example_flex_2();// 2行3列の四角、スクロールして8個まで表示
  //lv_example_flex_3();// 画面に表示 動作わからない?????
  //lv_example_flex_4();// 画面に表示 動作わからない?????
  //lv_example_flex_5();// 四角内の数字とボタンが揺れている
  //lv_example_flex_6();// 2行3列の四角 中は右から左に数字 スクロールして20個の四角
  //lv_example_grid_1();// 3x3個のボタン
  //lv_example_grid_2();// 四角が重なり合っている NGか?????
  //lv_example_grid_3();// 3x3の四角 動作わからない?????
  //lv_example_grid_4();// 上と同じ
  //lv_example_grid_5();// 3x3の四角 上下に揺れている
  //lv_example_grid_6();// 3x3の四角 右から数字

  //   \libs\..... にて
  //lv_example_barcode_1();
  //lv_example_bmp_1();// エラー
  //lv_example_ffmpeg_1();
  //lv_example_ffmpeg_2();
  //lv_example_freetype_1();
  //lv_example_freetype_2();
  //lv_example_freetype_3();
  //lv_example_gif_1();
  //lv_example_libjpeg_turbo_1();
  //lv_example_libpng_1();
  //lv_example_lodepng_1();
  //lv_example_qrcode_1();
  //lv_example_rlottie_1();
  //lv_example_rlottie_2();
  //lv_example_rlottie_approve();
  //lv_example_svg_1();
  //lv_example_tiny_ttf_1();// エラー
  //lv_example_tiny_ttf_2();// エラー
  //lv_example_tiny_ttf_3();// エラー
  //lv_example_tjpgd_1();

  //   \others (13フォルダ) にて
  // 略

  //   \porting\osal にて
  //lv_example_osal();// 画面中央にR付青図形

  //   \porting にて
  //lv_example_scroll_1();// 2つのボタンとスクロール
  //lv_example_scroll_2();// ボタン 横スクロール 1つずつ移動
  //lv_example_scroll_3();// +ボタンで項目追加
  //lv_example_scroll_4();// 四角内のtxtを横スクロール
  //lv_example_scroll_5();// エラー?????
  //lv_example_scroll_6();// NG????? 画面の4割ぐらいが白
  //lv_example_scroll_7();// チェックボタン それ以外もある?????
  //lv_example_scroll_8();// ボタン横スクロールと縦スクロール
  //lv_example_style_1();// ボタンらしき四角
  //lv_example_style_2();// 四角内に灰から青へグラディション
  //lv_example_style_3();// 四角が飛び出ているように右辺と下辺が青
  //lv_example_style_4();// 灰色四角の周りに青い線
  //lv_example_style_5();// 灰色四角の周りに青いにじみ
  //lv_example_style_6();// 画面半分づつ白黒 中央に図形 NGか?????
  //lv_example_style_7();// 円状のスクロール設定
  //lv_example_style_8();// 四角内にアンダーライン付文字
  //lv_example_style_9();// 白地に灰色のレ点
  //lv_example_style_10();// 白四角をタッチで赤茶色、離すと白
  //lv_example_style_11();// 青ボタンと黄ボタン 影付き
  //lv_example_style_12();// 橙四角 4辺黄緑
  //lv_example_style_13();// 赤と水色のスライド設定 タッチで赤が太くなる
  //lv_example_style_14();// 青のボタンと緑のボタン
  //lv_example_style_15();// ボタンが斜め
  //lv_example_style_16();// NG?????
  //lv_example_style_17();// NG?????
  //lv_example_style_18();// NG?????
  //lv_example_style_19();// 青ボタン スライド設定 黄ボタン

  //   \widgets (33フォルダ) にて
  // 略

  //lv_demo_widgets(); lv_conf.hでデモを有効にする。例:LV_USE_DEMOS_WIDGETS 未確認

  // サンプルを表示する時はした5行をコメントにする
  lv_obj_t *label = lv_label_create(lv_screen_active());  // ラベルオブジェクトを作成
  lv_obj_set_style_text_font(label, &lv_font_source_han_sans_sc_16_cjk, 0);
  // フォント 16px cjk
  lv_label_set_text(label, "皆様こんにちは LVGL");  // ラベルに新しいtxtを設定
  lv_obj_set_style_transform_zoom(label, 1024, 0);  // ズーム4(=1024/256)倍
  lv_obj_align(label, LV_ALIGN_CENTER, -200, 0);    // 親の位置(中心)からx,y移動(obj,位置,x,y)
  // 無いと左上に表示 次の描画サイクルで表示
  Serial.println("Setup 完");  // シリアルモニタに表示
}

void loop() {
  //M5.update();       // いるか?
  lv_timer_handler();  // 数mSごとに定期的にLVGLを呼出す
  delay(5);            // 5mS待つ(応答性を維持するmax値)
}
* flash memory(6.5Mbyte)のうち、スケッチが14%使用。RAM(327kbyte)のうち、global変数が84%使用、local変数で51kbyte使用可能。(1000byte=1kbyteで計算)