Infinity ErgoDox の LCD でアニメーションを表示して遊ぶ方法です。
ソースコードはこちら
※もろもろの事情で動作確認は行っていませんが、たぶん動くと思います。
Infinity ErgoDox とは
Input Club 社が販売している ErgoDox です。
LCD が標準で基板に搭載されており、同社提供の Configurator で簡単にキーマップを変更できます。
出荷状態では独自のファームウェアが載っていますが、 QMK Firmware にもプロジェクトがあり、 QMK Firmware に書き換えることもできます。
今回は QMK Firmware に書き換えることを前提とします。
公式のファームウェアに戻すことも可能なので、ご安心ください。
Infinity ErgoDox の LCD でアニメーションしてみる
LCD に表示するデータを用意する
何はともあれ、表示するためのデータがないと始まりません。
LCD の表示領域は 128 x 32 なので、それに合わせて作りましょう。
今回は、みんな大好き(?)パックマンのアニメーションを作っていきます。
イメージとしてはこんな感じです。
雑に自作したところ、画像数は28枚となりました。
これらの画像を LCD で表示するためのデータへと変換します。
LCD に表示するためのデータ形式
まずは、 LCD に表示するためのデータ形式について簡単に説明します。
アニメーションに必要なのは主に2つです。
- 画像のバイナリ配列
- 画像を表示するためのキーフレーム
画像のバイナリ配列
これは、表示したい画像自体のデータです。
例として、 QMK Firmware のロゴのデータを見てみましょう。( quantum\visualizer\resources\lcd_logo.c
にあります)
そのままだと見づらいので少し整形しています。
__attribute__((weak)) const uint8_t resource_lcd_logo[512] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xf8, 0xfe, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x38, 0x38, 0x38, 0x06, 0x29, 0x41, 0x24, 0x52, 0x24, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x38, 0x38, 0x38, 0x09, 0x55, 0x42, 0xaa, 0xaa, 0xaa, 0xa8, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x38, 0x38, 0x38, 0x09, 0x55, 0x82, 0x28, 0xaa, 0xae, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x38, 0x38, 0x38, 0x09, 0x55, 0x43, 0x28, 0xaa, 0xaa, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x38, 0x38, 0x38, 0x0a, 0x55, 0x42, 0x28, 0xaa, 0xaa, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x38, 0x38, 0x38, 0x05, 0x45, 0x42, 0x28, 0x89, 0x4a, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x18, 0x38, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x1c, 0x38, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x0e, 0x38, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x03, 0xff, 0x80, 0x04, 0x45, 0x14, 0xa4, 0x92, 0x83, 0x52, 0x22, 0x22, 0x36, 0x00, 0x00,
0x00, 0x00, 0x38, 0x00, 0x0a, 0xaa, 0xaa, 0xaa, 0xba, 0x84, 0x55, 0x55, 0x57, 0x45, 0x00, 0x00,
0x00, 0x00, 0x38, 0x00, 0x08, 0xaa, 0xaa, 0xaa, 0x92, 0xb2, 0x55, 0x55, 0x42, 0x65, 0x00, 0x00,
0x00, 0x00, 0x38, 0x00, 0x08, 0xaa, 0xaa, 0xaa, 0x92, 0x81, 0x56, 0x65, 0x42, 0x45, 0x00, 0x00,
0x00, 0x00, 0x38, 0x00, 0x0a, 0xaa, 0xaa, 0xaa, 0x92, 0x81, 0x54, 0x45, 0x42, 0x45, 0x00, 0x00,
0x00, 0x00, 0x38, 0x00, 0x04, 0x48, 0xa2, 0x4a, 0x89, 0x06, 0x24, 0x42, 0x41, 0x36, 0x00, 0x00,
0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
8ビットのデータが16列32行並んでいます。バイナリとして考えると、 (8 x 16) x 32 = 128 x 32
ビットと、 LCD の表示サイズと同じになっていることがわかります。
LCD はビットが 1
のピクセルのみ点灯します。
左上から128ビット分描画して、次の行に移ってまた左から128ビット分描画して……、を32行分繰り返しているわけです。
そして、8ビットをひとまとまりとして HEX 値で保持しているわけです。直観的でわかりやすいかと思います。
画像データの保持の仕方がわかったところで、先ほどの画像をすべてこの形式に変換しましょう。
ツールや自分でスクリプト等を組んで行ってもいいです。
例えば、以下のように28枚分の配列を作り、1つのファイルにまとめてしまいます。
const uint8_t pacman_bmp_00[512] = {
0x00, 0x00, ...
.
.
.
};
const uint8_t pacman_bmp_01[512] = {
0x00, 0x00, ...
.
.
.
};
.
.
.
const uint8_t pacman_bmp_27[512] = {
0x00, 0x00, ...
.
.
.
};
キーフレーム
次に、画像を表示するためのキーフレームを作成します。
画像1枚に対して1つキーフレームを定義します。
bool lcd_keyframe_draw_pacman_00(keyframe_animation_t* animation, visualizer_state_t* state) {
(void)state;
gdispClear(White);
gdispGBlitArea(GDISP, 0, 0, LCD_WIDTH, LCD_HEIGHT, 0, 0, LCD_WIDTH, (pixel_t*)pacman_bmp_00);
return false;
};
gdispGBlitArea
関数で描画範囲や描画するデータを指定しています。ここでは、画像データに先ほど作った pacman_bmp_00
を指定しています。
これも先ほどと同様に28枚分作ってファイルにまとめます。
LCD に表示するようにする
これで準備が整いました。 LCD でアニメーションするようにしていきましょう。
なお、 Infinity ErgoDox では一般的な OLED ドライバ API を使いません。
キーボードプロジェクト配下にある、 visualizer.c
で制御します。
各関数についてはコメントで説明がついているので、そんなに迷うことはないと思います。
今回いじるのは以下の関数です。
static keyframe_animation_t lcd_bitmap_animation = {
.num_frames = 1,
.loop = false,
.frame_lengths = {gfxMillisecondsToTicks(0)},
.frame_functions = {lcd_keyframe_display_layer_bitmap},
};
他にも同様の関数がいくつか定義されていますが、これと同じように編集すれば対応できます。
それぞれの設定は以下のようになっています。
項目 | 説明 |
---|---|
.num_frames |
アニメーションのフレーム数 |
.loop |
ループするか |
.frame_lengths |
各キーフレームの表示時間の配列 |
.frame_functions |
キーフレームの配列 |
今回は28個のキーフレームを 200ms ずつ表示して無限ループさせるようにします。
static keyframe_animation_t lcd_bitmap_animation = {
.num_frames = 28,
.loop = true,
.frame_lengths = { gfxMillisecondsToTicks(200), gfxMillisecondsToTicks(200), gfxMillisecondsToTicks(200), gfxMillisecondsToTicks(200), gfxMillisecondsToTicks(200), gfxMillisecondsToTicks(200), gfxMillisecondsToTicks(200), gfxMillisecondsToTicks(200), gfxMillisecondsToTicks(200), gfxMillisecondsToTicks(200), gfxMillisecondsToTicks(200), gfxMillisecondsToTicks(200), gfxMillisecondsToTicks(200), gfxMillisecondsToTicks(200), gfxMillisecondsToTicks(200), gfxMillisecondsToTicks(200), gfxMillisecondsToTicks(200), gfxMillisecondsToTicks(200), gfxMillisecondsToTicks(200), gfxMillisecondsToTicks(200), gfxMillisecondsToTicks(200), gfxMillisecondsToTicks(200), gfxMillisecondsToTicks(200), gfxMillisecondsToTicks(200), gfxMillisecondsToTicks(200), gfxMillisecondsToTicks(200), gfxMillisecondsToTicks(200), gfxMillisecondsToTicks(200) },
.frame_functions = { lcd_keyframe_draw_pacman_00, lcd_keyframe_draw_pacman_01, lcd_keyframe_draw_pacman_02, lcd_keyframe_draw_pacman_03, lcd_keyframe_draw_pacman_04, lcd_keyframe_draw_pacman_05, lcd_keyframe_draw_pacman_06, lcd_keyframe_draw_pacman_07, lcd_keyframe_draw_pacman_08, lcd_keyframe_draw_pacman_09, lcd_keyframe_draw_pacman_10, lcd_keyframe_draw_pacman_11, lcd_keyframe_draw_pacman_12, lcd_keyframe_draw_pacman_13, lcd_keyframe_draw_pacman_14, lcd_keyframe_draw_pacman_15, lcd_keyframe_draw_pacman_16, lcd_keyframe_draw_pacman_17, lcd_keyframe_draw_pacman_18, lcd_keyframe_draw_pacman_19, lcd_keyframe_draw_pacman_20, lcd_keyframe_draw_pacman_21, lcd_keyframe_draw_pacman_22, lcd_keyframe_draw_pacman_23, lcd_keyframe_draw_pacman_24, lcd_keyframe_draw_pacman_25, lcd_keyframe_draw_pacman_26, lcd_keyframe_draw_pacman_27 },
};
これで LCD でアニメーションがされるようになりました。
なお、今回の変更ではレイヤーを変更すると別のアニメーションが実行され、また最初から再生されます。
それを避ける方法もありますが、今回は割愛します。
さて、実は、これではコンパイル時にエラーが発生します。
次はそれを解決しましょう。
最大キーフレーム数を拡張する
実は、 quantum\visualizer\visualizer.h
で最大キーフレーム数が定義されているのですが、16個までとなっています。
今回はキーフレーム数が28あるので最大値を超えてしまい、コンパイルが通りません。
なので、定義を変えてしまいましょう。
// #define MAX_VISUALIZER_KEY_FRAMES 16
# define MAX_VISUALIZER_KEY_FRAMES 32
雑に 32
に変更しました。
これでコンパイルが通るようになります。
LCD でアニメーションを楽しもう!
あとはコンパイルしてファームウェアを焼けば LCD でアニメーションが表示されます。(焼き方は readme を読んでください)
なお、今回作成したコードのヘッダーファイルの設定やインクルード宣言など、基本的なことは省略していますので、適宜追加してください。
ちなみに、私はこんなことをしています。
Infinity ErgoDox の LCD で響ちゃんのスクロールアニメーション pic.twitter.com/xzL0HFnLFL
— アーサー㌠ (@KokToH_kuro) January 25, 2020
響ちゃんかわいい……
参考になったかはわかりませんが、こんな感じで Infinity ErgoDox の LCD で遊べるよ、というお話でした。
皆さんも、好きなキャラを LCD に表示してみたり、好き勝手アニメーションさせてみたり、遊んでみてください。