[自作キーボード]QMKファームウェアでカラーLCDに対応する~フォント表示~
Abstract
- 前回の続きでQuantum Painterについて
- ST7789のLCDにフォント(テキスト)を表示した
Introduction
前回はST7789のLCDに円や四角形などの簡単な図形を表示するところまでやったので、今回はフォントの表示をやってみます。
Method
フォントの表示処理は↑のQMKのgithubに詳細があります。
今回は、以下の手順で実施していきます。
- フォントファイル(.ttf)を中間ファイル(.png)に変換する
- 中間ファイルをQFFファイル形式※に変換する
- QFFファイルをファームウェアに組み込む
- フォントの表示処理を記述する
※QFFファイル形式: QMK用のフォントデータ。中身はc言語ソースで画像データが配列の形で格納されるっぽい。
1. フォントファイルを中間ファイルに変換する
参考: qmk painter-make-font-image
painter-make-font-image
というコマンドでTTF形式のフォントファイルをPNG形式に変換します- 使い方の詳細は↑のリンクを参照。
- 最低限必要な引数は、入力するTTFフォントファイル名と出力ファイル名
- デフォルトだと変換対象はASCII文字のみです。日本語(ひらがな、カタカナ、漢字)とかが必要であれば
-u
オプションを使う - 引数の説明
-u
- ASCII以外の文字列を使用したい場合に指定する
-s
- フォントのサイズを指定する。デフォルトは12px
- 1.3inch, 240x240のLCDに対して12pxだとかなり小さいので今回は32pxを指定
- 今回は入力には以下のフォントを使用しました。
- 使用したフォント
- JetBrains MonoのRegularのフォント
- 使用したフォント
実行結果
(venv) user@user:~/qmk$ qmk painter-make-font-image -s 32 -o JetBrainsMono-Regular.png -f JetBrainsMono-Regular.ttf
(venv) user@user:~/qmk$
成功すると以下のような中間ファイルが生成されます。
JetBrainsMono-Regular.png
2. 中間ファイルをQFF形式に変換する
参考: qmk painter-convert-font-image
- 上のリンクを参考に中間ファイル(PNG)をQFF形式に変換します
- コマンドの引数は↑のリンクを参照
- 最低限必要なのは入力と出力フォーマットの指定のみ
- 出力フォーマットは白黒で使うならmono4(4階調グレースケール), mono16(16階調グレースケール)辺りで良さそう
- 最低限必要なのは入力と出力フォーマットの指定のみ
実行結果
(venv) user@user:~/qmk$ qmk painter-convert-font-image -i JetBrainsMono-Regular.png -f mono16
Writing /home/user/qmk/JetBrainsMono-Regular.qff.h...
Writing /home/user/qmk/JetBrainsMono-Regular.qff.c...
成功すると~.qff.h, ~.qff.cの1組のCソース・ヘッダーファイルが出力されます
3. QFFファイルをファームウェアに組み込む
生成したフォントファイルを自分のキーボード用ファームの置き場に置く
自分の例だと以下。config.hとかある場所にqffディレクトリを作成してその中に配置しました。(特に指定は無いので、後のパス指定さえちゃんとすれば、どこにおいても問題ないとは思いますが。。)
- qmk_firmware/keyboards/cepst/
- rp2040test // 自分のキーボードファームのソース置き場
- keymaps/
- qff/ <-追加
- JetBrainsMono-Regular.qff.h <-追加
- JetBrainsMono-Regular.qff.c <-追加
- config.h
- etc.
- rp2040test // 自分のキーボードファームのソース置き場
rules.mkに追記
生成したQFFファイルをコンパイル対象に含めるため、rules.mkに追記します。
↓rules.mk
QUANTUM_PAINTER_ENABLE = yes
QUANTUM_PAINTER_DRIVERS += st7789_spi
SRC += qff/JetBrainsMono-Regular.qff.c // <-追記
keymap.cに生成したQFFのヘッダファイルをインクルードする
QFFのヘッダーファイルをkeymap.cでインクルードする
↓keymap.c
#include QMK_KEYBOARD_H
#include <qp.h>
#include "qff/JetBrainsMono-Regular.qff.h" // <-追記
// ~以下省略~
コンパイルを試す
特に問題なくコンパイルできました。この状態でファームウェアにQFF形式のフォントデータが組み込まれており、画像データの分だけファームウェアサイズなども大きくなっていました。
4. フォントの表示処理を記述する
keymap.cにフォントの表示処理を記載する
参考: Font Functions
上記リンクにフォント表示関連のAPIの説明があります。
フォント表示に必要な処理は以下。
- 背景描画
- フォントのロード
- フォントの表示
上記の処理をkeymap.cに追加したコードが以下。
#include QMK_KEYBOARD_H
#include <qp.h>
#include "qff/JetBrainsMono-Regular.qff.h" // <-追記
static painter_device_t lcd;
enum HSL
{
HUE,
SAT,
VAL,
};
const uint8_t black[] = {0, 0, 0};
const uint8_t white[] = {0, 0, 255};
const uint8_t red[] = {0, 255, 127};
const uint8_t blue[] = {170, 255, 127};
enum Position
{
X,
Y,
};
// フォントハンドルの宣言
static painter_font_handle_t fontHandle;
// 表示する文字列の宣言
static const char *text1 = "test";
static const char *text2 = "Hello World!";
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[0] = LAYOUT_ortho_2x3(
KC_A, KC_B, KC_C,
KC_D, KC_E, KC_F
)
};
void keyboard_post_init_user(void)
{
setPinOutput(LCD_BACKLIGHT_PIN);
writePinHigh(LCD_BACKLIGHT_PIN);
lcd = qp_st7789_make_spi_device(LCD_HEIGHT, LCD_WIDTH, SPI_CS_PIN, SPI_MISO_PIN, LCD_RESET_PIN, SPI_DIVISOR, SPI_MODE);
qp_init(lcd, QP_ROTATION_0);
qp_power(lcd, true);
qp_clear(lcd);
/* 描画処理 */
uint16_t startPos[] = {0, 0};
uint16_t endPos[] = {LCD_WIDTH, LCD_HEIGHT};
// 背景描画
qp_rect(lcd, startPos[X], startPos[Y], endPos[X], endPos[Y], white[HUE], white[SAT], white[VAL], true);
// フォント描画処理(追記)
fontHandle = qp_load_font_mem(font_JetBrainsMono_Regular);
if(fontHandle != NULL)
{
// 左上原点に描画
qp_drawtext(lcd, startPos[X], startPos[Y], fontHandle, text1);
// 上下左右中央揃え、フォント色変更して描画
int16_t text2_width = qp_textwidth(fontHandle, text2);
startPos[X] = (LCD_WIDTH / 2) - (text2_width / 2);
startPos[Y]= (LCD_HEIGHT / 2) - (fontHandle->line_height / 2);
qp_drawtext_recolor(lcd, startPos[X], startPos[Y], fontHandle, text2, red[HUE], red[SAT], red[VAL], white[HUE], white[SAT], white[VAL]);
}
}
keymap.cに追加した処理の説明
背景描画
- フォントの描画処理だけを作成すると、下記のようにフォントの描画をしたところ以外の領域がモザイク状になるみたいです
- そのため、一度全画面を
qp_rect()
で塗りつぶす処理を入れたほうが良さそうです
モザイク状に表示される例
フォントのロード処理
- 下記の関数を使ってフォントをロードする
- 引数のbufferはフォントデータを指定する
- 2で変換した~.qff.cにフォントデータ配列が下記のように定義されているので、その配列の変数名を指定すれば良い
- 下記の例だと, “font_JetBrainsMono_Regular"を指定する
- 2で変換した~.qff.cにフォントデータ配列が下記のように定義されているので、その配列の変数名を指定すれば良い
↓qmk_firmware/keyboards/cepst/rp2040test/qff/JetBrainsMono-Regular.qff.c
#include <qp.h>
const uint32_t font_JetBrainsMono_Regular_length = 16089;
// clang-format off
const uint8_t font_JetBrainsMono_Regular[16089] = {
0x00, 0xFF, 0x14, 0x00, 0x00, 0x51, 0x46, 0x46, 0x01, 0xD9, 0x3E, 0x00, 0x00, 0x26, 0xC1, 0xFF,
0xFF, 0x23, 0x01, 0x00, 0x00, 0x02, 0x00, 0x01, 0xFF, 0x01, 0xFE, 0x1D, 0x01, 0x00, 0x13, 0x00,
~~省略
フォントの描画処理
- フォントの色を指定しない
qp_drawtext()
とフォントの色を指定するqp_drawtext_recolor()
の2種類がある- int16_t qp_drawtext(painter_device_t device, uint16_t x, uint16_t y, painter_font_handle_t font, const char *str)
- int16_t qp_drawtext_recolor(painter_device_t device, uint16_t x, uint16_t y, painter_font_handle_t font, const char *str, uint8_t hue_fg, uint8_t sat_fg, uint8_t val_fg, uint8_t hue_bg, uint8_t sat_bg, uint8_t val_bg)
- 引数は図形描画の関数と同様に表示デバイスと表示座標を指定する
qp_drawtext_recolor()
の方はフォントの背景色と文字色をHSV形式で指定することができる
(補足)描画するテキストの長さと高さを図る方法
表示座標を計算する際にテキストの長さと高さがほしい場合は以下の方法で取得可能
- 長さを取得する方法
qp_textwidth()
- 高さw取得する方法
fontHandle->line_height
qp_load_font_mem()
で得られるフォントハンドルからアクセス演算子で取得できる
Result
上記で変更したrules.mk, keymap.cを使ってコンパイルを実行し、作成したファームウェアを書き込んだ結果が以下。無事にテキストが表示できた。
※LCDの下部の黒っぽい線はLCD自体の傷です。。。
Conclusion
今回はLCDにフォントを表示してみました。フォントが表示できるとレイヤー情報、入力しているキー、打件数とか色々なパラメータを動的に表示できるようになるので楽しくなりますね。
ただ、quantum painterでのフォントの組み込みはフォントサイズ一つにつき、一つのファイルを組み込む必要があるようなので、大きい文字と小さい文字を表示したいみたいな場合だと、複数のフォントファイル(~.qff.c)をファームウェアに組み込む必要が出てくるのが少しめんどくさいです。。
次回はLCDに画像を表示したいと思います。
Appendix
ASCII以外の文字列(日本語等)をLCDに表示する場合
qmk painter-make-font-image
, qmk painter-convert-font-image
で-u
オプションを使うことで日本語(ひらがな、カタカナ、漢字)を含めたUnicode文字をQFFファイルに入れ込むことができます。最終的にファームウェアに入れ込む際には必要な文字列だけ指定する形となってます。
下記の例では、-n
オプションを併用してASCII文字を除いて、「ハロー・ワールド」という文字列のみを含んだフォントファイルを生成している。
(venv) user@user:~/qmk$ qmk painter-make-font-image -s 16 -f NotoSansJP-ExtraBold.ttf -n -u "ハロー・ワールド" -o NotoSansJP-ExtraBold_16px_ja.png
(venv) user@user:~/qmk$ qmk painter-convert-font-image -i NotoSansJP-ExtraBold_16px_ja.png -f mono16 -n -u "ハロー・ワールド"
Writing /home/user/qmk/NotoSansJP-ExtraBold_16px_ja.qff.h...
Writing /home/user/qmk/NotoSansJP-ExtraBold_16px_ja.qff.c...
生成されたpngファイル
上記の日本語フォントファイル(~ja.qff.h, ~ja.qff.c)を使って日本語フォントを表示させた例