OLEDと東雲フォントで日本語表示

2025年4月22日火曜日

【STM32】 OLED

t f B! P L

SSD1309で日本語表示

Asciiコード、ひらがな、カタカナ、JIS第一水準までの漢字を表示できます。


(伸ばし棒「ー」をフォントとして入れていないのでハイフン「-」で代用しています)

各種要件

実装対象:STM32G070CBT6
開発環境:STM32CubeIDE

ハードウェア

実装対象として、これまで使ってきたG0シリーズの中でもFlash領域が大きいG070シリーズを使いました。
Flashサイズが128KBとかなり巨大なので今回の日本語フォント(約3000文字)も問題なく実装できました。

もしさらに小さいFlashサイズのマイコンを使う場合は外部にメモリを確保する必要があります。
1Mビット(128KB)のEEPROMです。
エイブリック製なこともあり日本語のデータシートで読みやすいです。

4Mビット(512KB)のフラッシュメモリです。
SPI接続で高速な転送が期待できます。

ソフトウェア

STM32CubeIDEですが、Eclipseベースとなっている点が重要です。
このおかげでソースコード上での文字コードを任意のものに変更できます。
今回は変換が容易なEUC-JPを使っています。

UTF-8,UTF-16

現在の主流となっている文字コードです。
日本語に限らず、世界中の言語・文字が含まれています。
素晴らしいことですが、マイコンのような限られたメモリ領域では厳しいです。

Shift-Jis

日本語を使う環境では依然影響力がある文字コードだと思います。
しかし文字コードが綺麗に連続していない事に気づき、EUCに変更しました。

EUC-JP

Shift-Jisと比べ、文字コードが連続、規則的に増加しておりプログラム上で扱うのがもっとも容易だったので今回採用しました。

プログラム概要

参考ライブラリ:ssd1306-stm32HAL
使用フォント:東雲フォント12
ビットマップ→16進数変換:FontEdit

プログラム作成、フォントの利用、変換に使わせていただきました。
この場を借りて感謝申し上げます。

東雲フォントはJIS X規格で文字フォントが収録されています。
JISとUTF-8は根本的に並び方が違うので、漢字を扱う場合は変換テーブルを用意する必要があります。
今回はEUCコードを使っていることもあり、プログラムには含んでいませんがUTF-8からEUCコードを探すコードも作成しています。

フォントデータの用意

マイコン側で容易に扱えるようにフォントデータを編集しました。
これらの作業はすべてvscode上で行っています。

テキストの編集

FontEditで生成される配列データは1文字ずつの配列となっています。
これがJISコードと共に並んでいるので必要な文字のみを抽出します。

さらにこの1文字ずつの配列を1つの配列にまとめます。
今回はvscode上で正規表現等を用いて1つの配列にまとめました。
(この辺りはほぼAI頼みでした。便利)
またこの時、ひらがな・カタカナ・漢字と種別ごとに別の配列としています。

ビット幅の変更

今回使用した東雲フォントは縦12*横12のフォントです。
SSD130xでは1バイトを送信すると縦に情報が反映されます。

例:0x1F(0b0001 1111)を送信した場合
下記のように描画されることを期待しますが…




実際には下記のように描画されます。




この為ビット列の変換が必要になります。
12ビットを1ラインとする東雲フォントを変換するので1文字のデータを8ビット*24個から16ビット*24個への変換。
さらに二次元配列の形で出力する作業を行いました(bit8to16bit.c)

こうして変換したフォントデータ配列へのアクセス方法は下記のようになります。
(EUC_GetCharacter関数)

ひらがな

JIS2420~2473、EUCは0xA4A1~A4F3。
文字コードが0xA4であればひらがなと判定。
ひらがな用の配列Kana12X12配列には文字コード2バイト-0xA4A1をしてアクセスします。

例えば「ぁ」であれば0xA1A1で、配列[0]にあるのでこれでアクセス可能です。

フォント要素文字EUC
[0][0]~[11]A4A0
[1][0]~[11]A4A1
............
[84][0]~[11]A4F2
[85][0]~[11]A4F3

カタカナ

JIS2520~2576、EUCは0xA5A1~A5F6。
文字コードが0xA5であればカタカナと判定。
あとの流れはひらがなと同様です。

漢字

JIS3020~4F70。EUCは0xB0A0~0xCFD3。
漢字はJISの区をまたぐため、単純に連番でアクセスはできません。
16区末尾のB0FEから163(0xA3)個目が17区先頭のB1A1です。
EUCコードはこの間隔が常に一定であり、区内で数値が飛ぶことはありません。
(Shift-Jisの場合は区内でも飛ぶので処理が増えます)

このため区ごとの間隔は162(0xA2)になります。
あとは描画する文字が漢字コード内で何番目の区にあるかを特定できれば計算ができます。
16区(0xB0~)を始点0として、末尾の47区(0xCF)を終点31とします。
漢字コードの上位バイトから0xB0を引き、間隔0xA2をかければ適切な配列にアクセスできます。

フォント要素文字EUC
[0][0]~[11]B0A1
[1][0]~[11]B0A2
............
[93][0]~[11]B0FD
[94][0]~[11]B0FE
[95][0]~[11]B1A1
[96][0]~[11]B1A2

例:「機」であれば…
0xB5A1。上位バイトからB0を引くと5。16区を0とすると+5で21区の文字とわかる。

Asciiコード

0x20~0x7EまでのAsciiコードも含んでいます。
EUCコードは上位・下位バイト共にAsciiコードを含まない(0xA0以上しかない)ので共存が可能です。
ただしAsciiコードは1バイトで認識されるので適切にアドレスを管理する必要はあります。

UTFとEUC

ひらがな、カタカナはどちらも連続した配置になっているので文字サイズの違い(UTFは3バイト、EUCは2バイト)だけ考慮すれば問題ありません。
しかし、漢字に関しては大きく異なります。

先述の通り東雲フォントはJIS規格に沿っており、さらにJISとUTFは根本的に並び方、収録している文字数が異なります。
そのため両者を変換するのは困難(不可能?)で、今回はシンプルにループでUTF→EUC→JISと探る方式にしました。


UTFとEUCのペアからなる構造体をJIS16~47区の役3000文字分の配列として用意します。
例えば「亜」であれば「.euc = 0xB0A1」「.utf = 0xE4BA9C」のような形です。

これでUTF-8であっても今回用意したフォント配列にアクセスできますが、当然ながら問題もあります。
まずEUC2バイトとUTF4バイトの構造体はパディング無しとして6バイト。
これが3000個近くあるのでそれだけで18KBも使います。ローエンドのマイコンならもう何も書けなくなるくらい巨大です。

次に該当するUTFコードが見つかるまでひたすらループするしかないという点です。
JIS第一水準最後の文字「腕」を探す場合は3000回ループを回してようやく見つかることになります。


実際にビルドした後のFlash領域の状態が上の画像です。
ここまで来ると外付けのFlash等を用いた方が精神衛生上良さそうです。

OLED側のプログラム作成

今回使用したSSD1309はSPI接続ですが、SSD1306と大きな差はありません。
参考にしたライブラリではOLEDの画面サイズ分のフレームバッファを作成し、一気に転送していました。
おおむねこれを踏襲しつつ、処理の短縮の為更新された領域のみ転送するようにしています。

またさきほども述べたように、SSD130xではビット列が縦に並ぶので予め変換した文字データを作成し、単なるメモリコピーで済むようにしています。

GitHubリンク

vscodeでのファイル(フォントデータの編集)およびターゲットのSTM32でのファイルです。
注意点としてstm32フォルダにあるソースファイルはUTF-8になっているのでこのままでは使えせん。
EUC文字コードでファイルを作成、コードをコピペして使う。
あるいはutfフォルダにあるUTF-8用のソースを試してみてください。

Translate

検索

QooQ