桜のはなびらどっとねっとロゴ

桜のはなびらどっとねっと

Arduino Unoで漢字を扱う

P1010168

※2つの記事を統合しました。

はじめに

Arduinoでグラフィック液晶などを利用する場合に漢字を表示したい。漢字はなくともせめてひらがなだけでも…。と思う時もあるかもしれません。そこで今回はArduinoで文字コードから漢字パターンを取得してみたいと思います。

必要なもの

  • SDカードシールド
  • FONTX形式のフォントファイル
    ネット上にフリーの既存フォントが多く存在していますし。エディタや変換ツールも存在します。
    例:ELMソフトウェア FONTXエディタ (Windows用)
    ホビーユースでは気にする必要はないでしょうが、他人の作ったフォントを流用したり、変換する場合はフォントファイルのライセンスにご注意ください。

漢字を表示する方法について

Androidは基本的には英文をメインに使います。
ですので使う文字といえばASCIIコード前半の文字のみを使う場合が多く。この場合はFONTを使うよりもパターンを書いてしまったほうが早いのですが。やはり日本人としては漢字を、せめてひらがなやカタカナくらいは使いたいと思うことも多いと思います。

正直カタカナだけでいいのであればキャラクタ液晶を使ったほうが手っ取り早いし簡単です。
しかし、何かの図形や画像などを同時に処理をしたいときはフォントを使う必要があります。表示される文字が固定されている場合はそちらも画像で作ってもいいのでしょうが。例えば何かの情報や不定な文字列を表示しようとするとやはりフォントを使う必要があります。

Arduinoで使える文字コードについて

基本的にはUTF-8をメインで使います。

ただし、UTF-8では扱いにくいのでShift-JISやJISコードを使用したいところであります。UTF-8以外を使う場合、メニューを別ファイルにして読み込むか、”\”を付けて文字コードを個別に指定してあげる必要があります。もしくは文字コードを自由に扱える環境を親とし、Arduinoを子として利用し、シリアル通信で送ってやるという方法を取る必要があります。

シリアル通信で漢字を扱う

ハードウェアの前にシリアル通信で漢字を扱ってみたいと思います。

といっても、Arduinoのスケッチの文字コードはUTF-8であり、特に何らかの制限をしているわけではないので。単純にパソコンとの通信でArduinoからのデータ転送時に日本語を表示したいだけであればパソコン側でシリアルモニタではなく端末エミュレータを使用し、文字コードをUTF-8に変更してやるだけでとりあえず表示することはできます。

arkanji3 arkanji4
シリアルモニタでの表示 端末エミュレータを使用

理由は知りませんが。Arduinoのシリアルモニタのエンコードは「Shift_JIS」になっているので、UTF-8のままだと文字化けしてしまいます。というわけでシリアルモニタで漢字を表示してやるならShift_JISで文字コードを転送してしまえばOKという事になります。

文字コードがUTF-8なArduinoスケッチでShift_JISを表示するにはエスケープ文字を使います。

例えば「桜」という漢字のShift_JISコードは「0x8DF7」となるのでこれを1バイトずつエスケープ文字を入れて表記することでShift_JISの文字コードで文字を表示できます。

表示結果:
arkanji5

ただし、毎回エスケープ文字を入れてコードを書くのは少々面倒だし、実際のところArduinoIDEのシリアルモニタは性能が良くないので、外部の端末エミュレータを使用した方がよいと思います。

外部の端末エミュレータを使用する場合の注意点は、端末側で繋ぎっぱなしにしているとArduinoIDEからプログラミングの書き込みができなくなるので、必ずプログラムの転送前には接続を切っておく必要があるというところくらいです。標準のシリアルモニタが転送前に毎回閉じるのもこの辺が理由。

使うフォントデータについて

使うフォントデータはFONTX形式を使用します。これは1991年にIBM DOS上の日本語環境改善策として、日本の有志により作成されたフォントドライバで。このソフトウェア自体はDOS/V専用で今となっては無用のものとなりましたが。そのフォントドライバで扱われていたフォントファイルは、形式が単純で軽く、様々なフォントサイズのフォントが扱えるため当時のコンピュータやそれ以下の処理性能しか無いマイコンなどでフォントを扱う場合にもってこいのフォント形式なので最近人気が再燃しているようです。

FONTXファイルのフォーマット

基本的に文字コードはShift_JISです。

1バイト目から6バイトがファイルシグネチャ「FONTX2」が入っています。
6バイト目から14バイト目までの8バイトにフォント名が入っています。

この2つはマイコンにてフォントを扱う場合においては気にする必要はないでしょうし、扱うメモリも惜しいですので気にせずに14バイトシークしてしまいましょう。(若しくは最初から削っておくか)

14バイト以降は「フォント幅・フォント高さ・文字コードフラグ」の順で記録されていて各データのデータサイズは1バイトとなっております。

17バイト目はフォントの種類によって違い。半角文字用のフォント(コードフラグ0)の場合はここからフォントイメージが始まります。全角文字用のフォント(フォントフラグ1)の場合は17バイト目にはコードブロックの総数が格納れています。

  • コードブロックとは・・・
    日本語の文字コードは2バイト文字で65536文字格納することが可能ですが。実際に利用されるのは7000文字程度なので、正直いって殆どの領域が無駄領域となっています。(ただしこれは一般的なフォントファイルはJIS第2水準漢字まで採用していることが多いためであり。Shift_JISの規格上はもう少し文字数は多いです。)そのため、FONTXでは、そのフォントが文字コードのどこからどこまでを収録しているものなのかを格納しておく領域が存在します。これをコードブロックと呼んでおります。

半角フォントの場合はここからフォントデータが始まりますが。全角フォントの場合は先程説明したコードブロックのデータが続いております。2バイトコードですので開始コードと終了コード、計4バイトのデータがコードブロックの総数分記録されています。

つまりこのフォントファイルのヘッダは以下のとおりとなっています。

fontxfmt

フォントイメージのについて

フォントデータはBMP等と同様に横8ドット分が1バイトに収まっていて、例えば12ドットや10ドットのような8の倍数でない場合には8ビット(1バイト)単位で穴埋めされています。

fontxfmtb

イメージまでのオフセット計算方法

それでは、以上のフォーマットで目的の文字パターンが何処に格納されているのかをどうやって知ればいいのかというと。オフセットを計算すればいいわけですが。半角フォントと全角フォントで計算方法が微妙に違います。

なお、フォントのデータサイズの計算方法は以下のとおりです。(16×16ドットの場合)

  1. (幅 + 7) ÷ 8 × 高さ
  2. (16 + 7) ÷ 8 × 16
  3. 23÷8×16
  4. ×16
  5. =32バイト

※ 端数切り捨て(int型で計算する)

半角フォントの場合の計算式

  • ヘッダサイズ + 文字コード×フォントサイズ

8×16ドットのフォントファイルで、「S」の文字までのオフセットを計算する場合は、
1フォントあたり16バイト、Sの文字コードは0x53(83)ヘッダーサイズは17バイトなので。

  1. 17+83×16 = 1345バイト

となります。

全角フォントの場合の計算式

  • ヘッダサイズ+4×Cb+該当コードまでの文字数×フォントサイズ

8×8ドットのフォントファイルで。「さ」の文字までのオフセットを計算する場合は、
1フォント8バイト、「さ」の文字コードは82B3となります。コードブロックの総数はフォントの収録内容によって違いますが。例えば何時も利用している美咲フォントのサイトにあるFONTX2形式のファイルの場合は92個のブロックに分かれています。

次に該当コードまでの文字数の計算方法ですが。

  • フォントブロックの終了コード-フォントブロックの開始コード+1

で1ブロックあたりの文字数を計算して、該当文字コードがその範囲にない場合は次のブロックに進み、該当の文字コードが含まれるまで計算結果を累積していきます。

「さ」の文字は82B3なので、11ブロック目に存在し、10ブロック目までに209文字存在することになり、11ブロック目の開始位置は829Fなので手前に20文字存在することになり、足すと229文字目が「さ」が格納されている文字になります。

これを元に計算すると

  • 18+4×92+229×8
  • 18+368+1832 = 2218バイト

となります。

つまり上で説明したフォント形式の場合、半角の「S」なら1345バイト目から16バイト抜き出し、全角「さ」なら2218バイト目から8バイト抜き出せば目当ての文字が得られるということです。

コーディング

まずSDカードを使用するので標準ライブラリをインクルードし、defineでフォントファイルのファイル名を指定します。ファイルはSDカード直下に記録しています。

次に上の説明を実現し、パターンを取得する関数を作ります。
関数の詳しい説明はコメントに書いてあるのでそれを読んでください。

この関数の動作は

  1. 受け取った文字コードの数値により使用フォントを決定
    256以下なら半角用フォント、それ以上なら全角用フォントを呼び出す
  2. フォントファイルを14バイトシーク
  3. ヘッダである文字サイズ縦・横、文字コードを取り出す
  4. 取り出した文字サイズからデータサイズを計算する
  5. フォントタイプによって条件を分岐する
    半角フォントならオフセットを計算。
    全角フォントの場合は、ブロック総数のデータを取得し該当文字が格納されているブロックが見つかるまでブロック内の文字数を控えつつループ、見つかったらオフセットを計算。
  6. 計算されたオフセット分ファイルをシークする
  7. そこからフォントのデータサイズ分フォントデータを配列に読み出す
  8. フォントファイルを閉じる
  9. 戻り値を返して完了。
    戻り値はフォントが見つからない場合は0を返す。
    見つかった場合はunsigned longのデータに「フォント幅(1)」「フォント高さ(1)」「データサイズ(2)」を連結させて返す。( )内はバイト数
    例えば24×18ドットのフォントの場合はデータサイズは54バイトで「0x18120036」が戻り値となり、これをビット演算で切り出すことで目的の値が取得できる。

という動作になっている。これを使うためには1つのunsigned int(short)型変数と1つのunsigned char型配列、戻り値を格納するunsigned long型が必要となります。

例えば

という変数を確保した場合

  • ret = KanjiReadX( kanji , pat );

記述すれば使用することが出来ます。

なお、Arduinoのシリアル通信は1バイトずつ取得できますので、文字コードの受信は以下のとおりに行います。

最初に8bitシフトして1バイト目を取得します。
そのコードが2バイト文字を扱う範囲にあるかどうかを確認し、2バイト文字の上位バイトだった場合は、2バイト目を続けて取得します。もし1バイト文字と判断された場合は、最初にシフトした8bitを戻して終了します。

戻り値ですが、フォント幅を取得する場合は24bitシフトして0xFFでマスクをして抜き出します。
フォントの高さは16bitシフトしてマスクします。 データサイズを取得する場合はビットシフトは不要で0xFFFFのマスクをかけるだけで取得できます。

実行結果

例えば最後に掲載しているサンプルコードを実行して「桜」の文字を送信すると以下のようになるはずです。
(文字コードはShift_JISです。)

arfontx2

終わりに

Arduinoで使う場合は漢字ROMを使う以外では最良の方法だと思います。
Raspberry Piに関しても、多分この方法のほうが早いです。

というわけで今回はフォントファイルから漢字パターンを取得する方法をご紹介しました。
これを使えば8×8ピクセル以外に12×12や16×16、もっと大きなフォントも使えるので例えばカラー液晶などに転送する場合はこっちの方がいいでしょうね。

なお、この文字コードは縦はドット分そのままですが、横が8ドット単位で格納されています。
14ドットなどの場合は2pxの空きとなるので逆に見やすいですが。10pxや12pxの場合はちょっと文字間が開きすぎなので、文字を転送し終えたあと例えば12ドットなら4ドット分戻って次の文字を転送すると穴埋め分をかき消すことができると思います。

それでは。

サンプルコード

改版履歴

2016年8月22日 2つの記事を統合・加筆しました。

コメントを残す

メールアドレスが公開されることはありません。