M5PaperでLVGL

前回 M5Core2でLVGLを動かすことができました。

いよいよ本命のM5PaperでLVGLを使ってみたいと思います。

前回のサンプルコードをM5Paperに合わせてちょっとだけ変更して動かしてみます。(M5Paper: LVGL Sample (with LGFX))

f:id:hollyhockberry:20211010193005j:plain

フォントサイズもそのままなので小さいですが、描画自体はできました。
ただ、描画切り替え時のちらつきが目立ちます。気になりますがとりあえず先に進みます。

ボタンとタッチイベント

タッチイベントの取得とWidgetsのイベントハンドリングも実験してみます。

タッチイベントを取得するには、コールバック関数の実装と登録が必要です。

// コールバック
void my_touchpad_read(lv_indev_drv_t*, lv_indev_data_t* data) {
  M5.TP.update();
  if (!M5.TP.isFingerUp()) {
    const tp_finger_t finger = M5.TP.readFinger(0);
    data->state = LV_INDEV_STATE_PR;
    data->point.x = finger.x;
    data->point.y = finger.y;
  } else {
    data->state = LV_INDEV_STATE_REL;
  }
}

void setup() {
    :
  // 登録
  static lv_indev_drv_t indev_drv;
  ::lv_indev_drv_init(&indev_drv);
  indev_drv.type = LV_INDEV_TYPE_POINTER;
  indev_drv.read_cb = my_touchpad_read;
  ::lv_indev_drv_register(&indev_drv);
    :
}

後は、オブジェクト(Widget)を配置すればLVGLのハンドラがイベントをハンドリングしてくれます。

LVGL公式ドキュメントのQuick overviewで紹介されていたボタンのサンプルコードを流用してボタンを配置してみます。(ボタンのサイズと位置だけ変更しました)

タッチはうまいこと反応してますが、やたらとチカチカする上に、たまにボタンが消えちゃってます。

やたらとチカチカするのは、イベント時のボタンがアニメーションしてるので書き換えが頻繁に入っているからでしょう。消えちゃうのは描画の指定が悪いのでしょうか・・?

といろいろ考えてると、またもフォローをいただきました。

なるほど・・・書き換え中の上書きが発生していそうですね。

  • LVGLのレンダリングについて
  • オブジェクト(さしあたってはButton)のデザイン変更方法

LVGLのドキュメントを読んで、この辺りがわかれば良くなりそうですかね。

バッファ上書き抑止

ドキュメントを読み進めると、Portingの章でバッファフラッシュのコールバックについての記述がありました。

LVGL might render the screen in multiple chunks and therefore call flush_cb multiple times. To see if the current one is the last chunk of rendering use lv_disp_flush_is_last(&disp_drv). Display interface — LVGL documentation

レンダリングの最後はlv_disp_flush_is_last()で判定できるようです。レンダリングの開始を知る方法が見当たらなかったので、適宜判定が必要ですね。(startWrite()endWrite()は対になっている必要があります)

void my_disp_flush(...) {
  if (gfx.getStartCount() <= 0) {
    gfx.startWrite();
  }
  
  〜バッファの書き込み処理〜

  if (lv_disp_flush_is_last(disp)) {
    gfx.endWrite();
  }
  ::lv_disp_flush_ready(disp);
}

もしくは、SPI占有しちゃって問題なければsetup()時にstartWrite()しちゃって、レンダリングの最後にendWrite()じゃなくてdisplay()でも良いかもしれません。

ボタン押下時アニメーション

オブジェクトの外観は、lv_style_tという構造体を作成してスタイルを設定できるようです。

docs.lvgl.io

ドキュメントによると、スタイルのコンセプトはCSSに大きく影響を受けているそうです。

lv_style_t型のオブジェクトがクラスに相当し、単一もしくは複数のスタイルをオブジェクトに割り当てることができます。

オブジェクトは 通常(LV_STATE_DEFAULT)、チェック(LV_STATE_CHECKED)などの状態を持っており、それぞれ毎にスタイルを割り当てることができます。

スタイルのプロパティは色々用意されていて、トランジションも駆使すれば凝ったデザインのオブジェクトができそうです。

今回は極力簡素な振る舞いに変更したいので、

  • 通常時(LV_STATE_DEFAULT)は黒ボーダーの白背景
  • 押下時(LV_STATE_PRESSED)は黒背景

というスタイルを設定します。

通常時のスタイル設定
// スタイルのオブジェクト(自動変数にしたらダメです)
lv_style_t style_btn;

::lv_style_init(&style_btn);
// 角の丸め
::lv_style_set_radius(&style_btn, 10);
// 背景の透明度(LV_OPA_COVER: 不透明)
::lv_style_set_bg_opa(&style_btn, LV_OPA_COVER);
// 背景色
::lv_style_set_bg_color(&style_btn, ::lv_color_white());
// ボーダーの色
::lv_style_set_border_color(&style_btn, ::lv_color_black());
// ボーダーの透明度
::lv_style_set_border_opa(&style_btn, LV_OPA_COVER);
// ボーダーの太さ
::lv_style_set_border_width(&style_btn, 2);
押下時のスタイル設定
lv_style_t style_btn_pressed;
::lv_style_init(&style_btn_pressed);
::lv_style_set_bg_color(&style_btn_pressed, ::lv_color_black());
ラベルのスタイル設定

ボタンの子要素にラベルがいますが、標準のフォントサイズだと小さいので、大きめのフォントを設定します。

組み込みのフォントは lv_conf.hで選択できます。
標準では14ptのフォントしか有効になっていないので、42ptのフォントを有効にしましょう。

lv_conf.hの260行付近にある使用フォントの定義を0から1に変更します。

  (略)
#define LV_FONT_MONTSERRAT_12    0
#define LV_FONT_MONTSERRAT_14    1
#define LV_FONT_MONTSERRAT_16    0
#define LV_FONT_MONTSERRAT_18    0
  (略)
#define LV_FONT_MONTSERRAT_40    0
#define LV_FONT_MONTSERRAT_42    1
#define LV_FONT_MONTSERRAT_44    0
  (略)

これでlv_font_montserrat_42が実体化されるので、スタイルに設定可能となります。

lv_style_t style_label;

::lv_style_init(&style_label);
::lv_style_set_text_font(&style_label, &lv_font_montserrat_42);
スタイルの割り当て

ボタンに通常時と押下時のスタイルを割り当てます。

lv_obj_t * btn = ::lv_btn_create(::lv_scr_act());
// 全てのスタイルを削除
::lv_obj_remove_style_all(btn);
// 通常のスタイル
::lv_obj_add_style(btn, &style_btn, LV_STATE_DEFAULT);
// 押下時のスタイル
::lv_obj_add_style(btn, &style_btn_pressed, LV_STATE_PRESSED);

ボタン内部のラベルにもスタイルを割り当てます。

lv_obj_t * label = ::lv_label_create(btn);
::lv_obj_add_style(label, &style_label, LV_STATE_DEFAULT);

ラベルはフォントの指定を上書きするだけなので、元のスタイルは削除せずにそのままです。

完成

実際に動作させた様子を動画でご確認ください。

ボタンのトランジションを無しにしたので書き換えは1度で完了しています。
また、押下時の背景を黒一色にしたので、書き換え時の反転が目立たなくなっています。狙い通りの結果が出て良かったです。

まとめ

カレンダー表示を横着したいという欲求でLVGLを触り始めましたが、オブジェクトのスタイル変更までできるようになりました。

カレンダーのオブジェクトが希望通りの仕様かはこれから調べないとわかりませんが、それ以外のGUIはLVGLで構築するのも良さそうですね。

例によってソースコードGithubにて公開しています。

github.com