[自作キーボード]QMKファームウェアでカラーLCDに対応する~アニメーション再生~

目次

Abstract

  • 前回の続きでQuantum Painterについて
  • ST7789のLCD上でアニメーションgifを再生した
    • 再生時のfps周りなど一部課題有り

Introduction

前回は、画像表示を行ったのでアニメーションGIFの再生をやっていきます。

Method

参考:Quantum Painter

手順は画像表示とほぼ同じで、↑のリンクに記載の通りです。

  1. アニメーションGIFを準備する
  2. アニメーションGIFをQGFファイル形式に変換する
  3. QGFファイルをファームウェアに組み込む
  4. アニメーションの再生処理を記述する

0. アニメーションGIFの用意

  • Quantum Painterで再生できる動画はGIF形式のみです
  • アニメーションGIFの作り方は以下のようなものが有るかと思います
    • 動画ファイル(mp4,etc)などから変換する
    • 画像ファイルを複数枚描いて、それを繋げてgifに変換する
  • 今回は30枚の時計の絵を作成してffmpegでアニメーションgifに変換しました

↓ffmpegで30枚の画像から1秒のアニメーションgifを作成する例

$ ffmpeg - r 30 -i %02d.png -filter_complex "[0:v] split [a][b];[a] palettegen [p];[b][p] paletteuse" -r 30 sample.gif

↓作成した画像 (240x240, 1秒ループ, 1秒30コマの時計の絵)

clock 30fps


2. アニメーションGIFをQGFファイル形式に変換する, 3. QGFファイルをファームウェアに組み込む

参考: Quantum Painter CLI Commands

  • 2,3は画像表示と全く同じ手順なので前回記事を参考
  • 今回使うgifは白、黒、青、灰色の4色で十分に表現できるので,色形式は4色パレットのpal4を使いました
    • rgb565,rgb888はおそらく意味ないので使わない方が良いかと
      • 元の動画がgifなので256色以上表現できず、上限でもpal256になるはずなので。。

↓変換例

(venv) user@user:~/qmk$ qmk painter-convert-graphics -f pal4 -i sample.gif
Writing /home/user/qmk/sample.qgf.h...
Writing /home/user/qmk/sample.qgf.c...

4. アニメーションの再生処理を記述する

参考: Animate Image

  • 画像の表示処理から書き換えが必要なのは1文だけです
    • qp_drawimage()を使っていたところをqp_animate()に書き換するだけ

↓keymap.c(1文以外は前回のkeymap.cと全く同じなので省略してます)

~省略~
void keyboard_post_init_user(void)
{
    ~省略~

    // 画像の表示処理
    img_sample = qp_load_image_mem(gfx_sample);
    if(img_sample != NULL)
    {
        qp_animate(lcd, startPos[X], startPos[Y], img_sample); //追記箇所
    }
}

アニメーションの再生処理

  • アニメーションの再生関数は以下の2種類
    • deferred_token qp_animate(painter_device_t device, uint16_t x, uint16_t y, painter_image_handle_t image);
    • deferred_token qp_animate_recolor(painter_device_t device, uint16_t x, uint16_t y, painter_image_handle_t image, uint8_t hue_fg, uint8_t sat_fg, uint8_t val_fg, uint8_t hue_bg, uint8_t sat_bg, uint8_t val_bg)
  • 引数含めて画像の描画関数と使い方は同じです
    • LCDデバイス指定して、表示原点をどこに指定して、どの画像ハンドルを指定するかのみ

5. コンパイルを試す

上記で変更したkeymap.cを使ってコンパイルを実行すると、問題なくコンパイルできました。

注意
手順で説明した時計のgif以外を使った場合はコンパイルエラーになる場合もあります。詳細はAppendix-ファームウェアサイズの上限

Result

  • 作成したファームウェアを書き込んだ結果が以下。
  • 再生はできたけど、絵が崩れている。。。

動画が崩れている原因を調べて対策する

速度関連が問題になっているかと思い以下の2つを試したが、どれも効果がなかった。。

  • ①SPIの速度変更
    • qp_st7789_make_spi_device()の第6引数spi_divisorの値を調節する
  • ②config.hでQuantum Painterの設定、QUANTUM_PAINTER_PIXDATA_BUFFER_SIZEの値を設定してみる
    • QUANTUM_PAINTER_PIXDATA_BUFFER_SIZEは一回のトランザクションでディスプレイに送るデータ量の限界値

ぱっと思いつく修正案が駄目だったので、もう一度崩れた動画を見て何か原因わからないかと見てみると、 時計の針が回転した後の絵が残像みたいになって更新されてない様子。差分だけ更新されて全体が更新されてない?

上記の観点でもう一度QMKのQuantum Painterのページを見てみると、 アニメーションGIFからQGFへの変換コマンドqmk painter-convert-graphicsのところに以下の説明があった。

usage: qmk painter-convert-graphics [-h] [-w] [-d] [-r] -f FORMAT [-o OUTPUT] -i INPUT [-v]

options:
~省略~
  -d, --no-deltas       Disables the use of delta frames when encoding animations.
~省略~

-dオプションつけるとデルタフレームの使用を禁止するとのこと。
デルタフレームはおそらく差分フレームのことと思われるので、アニメーションGIFからQGFへの変換はフレーム間予測使っているようです。(マイコンで動画扱うにはサイズ小さくする必要があるので当然といえば当然ですが。。)今回のケースでは差分フレームが上手く抽出できていないため、本来更新しなければ行けない箇所(時計の針が通った後)が更新されず、残像が残ったようになっていると思われます。

そこで、解決策として、以下のように-dオプションをつけて差分フレームを使用せず毎回全領域を描画するようにgifを変換、生成された~.qqf.c, ~.qgf.hファイルを差し替えてコンパイルしてみたところ、今度は崩れること無く再生できました。

↓変換例

qmk painter-convert-graphics -f pal4 -d -i sample.gif

-dオプションつけて変換したファイルでコンパイルしたビルド生成物を書き込んで見た結果

Discussion

fpsについて

今回使用したgifは30fpsですが、-dオプションを付ける前と付けた後の動画を見比べると、-dオプションをつけた方の動画が15fps程度になってしまってます。-dオプションつけてない(差分フレーム使用)している方では30fps出てるっぽいので、一度に送るデータ量とかが影響しているかと思ってますが詳細は不明でした※。

※config.hでQuantum Painterの設定QUANTUM_PAINTER_PIXDATA_BUFFER_SIZEを増やしてもfpsが向上する気配が無かったです。。。

QGF変換時の-dの使用について

-dオプションつける場合は差分フレームを使用しなくなるため、ファームウェアのサイズは増大してしまいます。なので、-d無しでも綺麗に描画できるのなら基本的につけない方が良いかと思います。今回使ったgifは時計の針が高速に周回しており、差分が上手く取れていなかったものだと思いますので。参考まに-dオプションつけなくても綺麗に描画できたケースをAppendixに乗せておきます。

↓今回作成したgifを-dオプション有り無しで変換した場合のサイズ比較(Arrayはqgfの配列サイズです)

item Array Size [byte] Firmware Size [byte]
-dオプション有り 22,476 112,640
-dオプション有り 151,718 371,200

Conclusion

今回は動画の表示をやってみましたが、動画の再生は基本無限ループしかできないっぽいので用途は少し限られるかも。基本は好みのgifアニメ表示しておくとか、後はローディングアニメーションとか、起動アニメーション的な奴かな。

次回はキーボードとLCD描画の連携とかについて書く予定。



Appendix

別な動画の再生

本編の方では時計のgifの再生のみだったので、適当に作った動画を再生しみました。

  • ① ロゴアニメーション
    • 元動画
      • 240x135, pal16, 5.0sec, 30fps, -dオプション有り


  • ② スタマスのダンス動画キャプチャ
    • 元動画
      • 240x240, pal256, 2.20sec, 10fps, -dオプション無し

-dオプションの件

  • ①の動画は-dつけないと動画が崩れてしまいました
  • ②の方はつけなくても問題なかったです。

ファームウェアサイズについて

  • ②の方は10fps, 2秒のgifをpal256で変換しましたが、ファームウェアサイズが上限の2MB近くなりました。
  • Raspberry Pi Picoのように2MBのフラシュしか持たないボードだと、pal256形式で動画を使うのは現実的じゃないかもしれません。
    • pal256使うなら、フラッシュ16MBとかついてるPico Bootとかを使うことを検討した方が良いかも。。

ファームウェアサイズの上限

今回の手順で使った時計のgifはサイズが小さいので問題になりませんが、元々のgifのサイズが大きい(fpsが高い, 解像度が高い, 時間が長い)やサイズが大きくなる色形式pal256, rgb565などを使用した場合は、ファームウェアサイズが上限を超えてしまいコンパイルエラーになってしまう場合があります。

その場合は、元動画の時間を短くする、解像度を下げる、fpsを下げる、色形式をpal4, pal16とかに変更するなどして、ファームウェアサイズに収まるように調整する必要があります

↓コンパイルエラーの例(リンクしてelfを作るときにflash1領域のサイズを超えている)

(venv) user@user:~/qmk$ qmk compile -kb cepst/rp2040test -km default
~省略~
Linking: .build/cepst_rp2040test_default.elf
 [ERRORS]
 |
 | /usr/lib/gcc/arm-none-eabi/10.3.1/../../../arm-none-eabi/bin/ld: .build/cepst_rp2040test_default.elf section `.rodata' will not fit in region `flash1'
 | /usr/lib/gcc/arm-none-eabi/10.3.1/../../../arm-none-eabi/bin/ld: region `flash1' overflowed by 1555592 bytes
 | collect2: error: ld returned 1 exit status
 |
gmake[1]: *** [builddefs/common_rules.mk:268: .build/cepst_rp2040test_default.elf] Error 1
Make finished with errors
gmake: *** [Makefile:392: cepst/rp2040test:default] Error 1