OLED ( 有機EL ) SSD1306 に16×16ドットのフリーの日本語漢字、東雲フォントを表示させてみました

ESP8266 ( ESP-WROOM-02 )

7.東雲フォントの解説

ビットマップフォント版ではBDF形式のファイルになっています。
美咲フォントでは表示するビット列をそのまま16進数で区切りなく羅列されてますが、東雲フォントではそのバイト列をテキスト形式にして記録されており、インデックスも組み込まれています。
バイナリエディタで開くとこんな感じになります。

美咲フォントではバイナリエディタで開いて、Shift_JIS表示ビューを見てもどの文字だかサッパリわかりませんが、BDF形式だとテキスト形式で記録されており、Shift_JISビューやASCIIビューで容易に分かるようになってます。
STARTCHARと表示されているところにJISコードが出ているのでどの文字だかすぐに分かります。
ただ、この難点は実際のデータバイトがテキスト形式というところが少々厄介です。
Shift_JISビューのBITMAP後にピリオドがあり、その後から実際のフォントデータです。
上図でいうと ”あ” という文字ならば、

0200.0200.0260.1f80.・・・・・.0000

となっていますが、実際のファイルに書き込まれているバイト列は

0x30, 0x32, 0x30, 0x30 ・・・・

となっています。 これを本来のバイト数値に変換しなければなりません。
0はASCIIコードの16進数で0x30ですから、それをゼロにするには
0x30-0x30=0x00
となるわけです。テキストの0~9の数値は実際のバイト列から0x30を引けば実際のバイト数値を導き出せます。
しかし、a~fのテキストは異なります。
”a” という文字はASCIIコードで0x61です。
16進数のaは10進数で10 ですから、
0x61-0x61+10 = 10
となります。”f” ならば
0x66-0x61+10 = 16
となって0~f までの16進数を数値化するのに2通りの計算をしなければなりません。
こうやって本来のバイト列を抽出するのが厄介なわけです。

テキスト数値をバイト数値に変換して上位バイト、下位バイトに分けて2進数表記にしてやり、2進数の1の数値が実際に光るドットとすると下図のようにビットマップができあがります。

これを自作ライブラリで計算させてやってます。
因みに、JIS 13区の丸囲み文字はありませんのでご了承ください。

余談ですが、東雲フォントはJIS X 0208並びに配置されていて、実は、UTF8からJIS変換テーブルを新たに作らなければいけないかと思っていました。
しかし、よく考えると、JIS並びもShift_JIS並びも同じだったので、Shift_JIS変換テーブルがあれば問題ないことが分かったわけです。
今回、文字コードを勉強し直そうを思っていたら、以下の本を見つけました。
とても理解が深まったので、ぜひお勧めの一冊ですよ

8.サンプルスケッチの解説

【ソースコード】 (※無保証 ※PCの場合、ダブルクリックすればコード全体を選択できます)

/* OLED(有機EL) SSD1306にフリーの日本語フォント、東雲フォント(16x16)を表示させるサンプルスケッチ
 *  予めSPIFFSファイルシステムやOTAを使ってフォントファイル等をフラッシュにアップロードしておくこと
 *  This library
 *  UTF8toSJIS.h
 *  OLED_SSD1306.h
 *  ShinonomeFONTread.h
 *  is what Mgo-tec has created.
 */
#include <Wire.h>
#include <UTF8toSJIS.h>
#include <OLED_SSD1306.h>
#include <ShinonomeFONTread.h>

UTF8toSJIS u8ts;
OLED_SSD1306 oled;
ShinonomeFONTread SFR;

const char* UTF8SJIS_file = "/Utf8Sjis.tbl"; //UTF8 Shift_JIS 変換テーブルファイル名を記載しておく
const char* ZenkakuFontFile = "/shnmk16.bdf"; //全角フォントファイル名を定義
const char* HalfFontFile = "/shnm8x16r.bdf"; //半角フォントファイル名を定義

const uint8_t OLED_Adress = 0x3C; //OLEDのI2Cアドレス

char* c[4] = {"フリー日本語漢字","16x16ドット です。","全角-半角混在OK!","半角カタカナもOK!!"};
//Char型ならばポインタ配列にする必要あり。String型でも良い

uint8_t sj_txt[32]; //Shift_JISコード
uint16_t sj_length; //Shift_JISコードの長さ
uint8_t font_buf[32][16]; //東雲フォントデータ
uint8_t tmp_buf1[16],tmp_buf2[16]; //フォントデータ90度回転後の一時データ

uint8_t cp; //ある一文字のShift_JISコードバイト数
uint16_t i, j;
int16_t Rotation = 90; //90度回転させる

void setup() {
  Wire.begin(); // I2C initialise the connection
  Wire.setClock(400000L); //クロックはMax 400kHz

  oled.Ini_OLED_SSD1306(OLED_Adress, 127); //Contrast 0-255 default 127

  delay(300);
  
  for(j=0; j<4; j++){
    u8ts.UTF8_to_SJIS_str_cnv(UTF8SJIS_file, c[j], sj_txt, &sj_length);
    i=0;
    while(i<sj_length){
      cp=SFR.SjisToShinonome16FontRead(ZenkakuFontFile, HalfFontFile, 0, 0, sj_txt[i], sj_txt[i+1], font_buf[i], font_buf[i+1]);
      i=i+cp;
    }
    for(i=0; i<16; i++){
      oled.Dot_8X16_Rotation(90, font_buf[i], tmp_buf1, tmp_buf2);
      oled.OLED_8x16_Display_Out(OLED_Adress, 120-8*i, 6-j*2, tmp_buf1, tmp_buf2);
    }
  }
}

void loop() {
}

 

●9行:
これはArduino標準で入っているI2C通信用ライブラリです。

●24行:
45行目の関数で使用するchar型文字列ですが、この関数ではString型定義です。でも、char型をポインタ文字列にすると問題なく使えるので使ってみました。
String型だと配列にできないので、char型のポインタ配列にすると、forループ内で使うことが出来るので都合が良いのです。

●40行:
SSD1306の初期化関数で、ver1.5で追加しました。コントラストをここで設定できます。
0~255 でデフォルトは127です。でも127~255はあまり変わらないかも・・・。

●45行:
スケッチ上に書き込んだ日本語文字コードはUTF8なので、この関数でShift_JISに変換します。全角は2バイト、半角は1バイトです。

●48行:
Shift_JISコードから東雲フォントデータを抽出する関数です。
0, 0 という数値は文字の方向と角度を入力するのですが、まだ関数が未完成ですのでゼロを入力しておいてください。
font_buf[i] が上位バイト、 fontbuf[i+1] が下位バイトです。
美咲フォントよりも4倍の計算が必要なので、ここで一気に読み込んでSRAMバッファに格納しておきます。メモリを食うのは仕方ないです。
cp は全角ならば2、半角ならば1を返します。

●51-54行:
ここで、先ほど読み込んだフォントをバッファから取り出し、一気にI2C通信へ吐き出します。
52行目ではSSD1306の座標の関係上どうしても90度回転させなければならないのでビット操作する関数を使います。
まず、回転させなければ、このように表示されます。

つまり、ビットを縦方向に表示してしまうんです。
しかも、このOLEDの縦方向はページ単位となっていて、8bit描いたら次のビットは同じページ内に縦方向に描いていくんです。
ですから、下図のように90度回転させるとページをまたいでしまうんです。


そこで、上位バイトと下位バイトで分けて、それぞれのバイト列を2分割してページに割り当てなければなりません。
そこで、font_buf[i] で半角バイトだけをtmp_buf1,  tmp_buf2 と分けて取り出します。
そうしたら、53行目の関数で半角バイト分ずつI2Cに送信してあげれば、都合よく全角半角を表示できるわけです。
おかげで、計算が多くなってしまいますが、今のところ私の頭ではここまでしかできませんでした。
もっと高速で処理する方法があれば教えていただきたいです。

以上です。
16×16フォントを表示させるだけで新たな使用法が増えて、記事を書く手間が倍増で疲れ果ててしまいました。

次回はこれを使ってWeb連動電光掲示板を解説したいと思います。

では、また・・・。

コメント

  1. 隣人 より:

    こんにちは。楽しく参考にさせてもらっています。
    まずは全てを真似してみようということでESP-WROOM-02 開発ボードをいくつか購入しIDEのバージョンなど諸々をそろえてやってみました。

    ところが、なんと、「6.SPIFFSファイルシステムとOTAでESP8266フラッシュにファイルをアップロードする」がUSB経由で成功しました。

    で、確認したところ、ESP-WROOM-02 開発ボードのリビジョンによる違いがあるようです。

    「Rev.2」と記されているESP-WROOM-02 開発ボードでは成功しますが、表記の無いものは失敗します。

    リビジョンの違いが仕様面でどのような違いがあるのか把握できていませんが、ご参考まで。

    • mgo-tec mgo-tec より:

      隣人さん

      当ブログをご覧いただきありがとうございます。

      とても有益な情報ですね。
      私の方でも、ハードがRev.2と刻印されているものはArduino IDE ver1.6.10で問題なくシリアルUSBでSPIFFSの3Mアップロードできました!!!
      どうやらハードの問題だったようですね。
      ライブラリの問題かと思って諦めてました。
      近いうちに当ブログでもこの情報をアップしたいと思います。
      貴重な情報ありがとうございました!!!

  2. Youtaka7 より:

    こんにちは。楽しく拝見しています。ESPr DeveloperのRev.3とIDE1.6.12で追加ボードマネージャをstagingにして全てUSB経由で実行したところ、上手くできましたのでお知らせ致します。因みに私はWindow7 ,64bitのOSで使っています。

    • mgo-tec mgo-tec より:

      Youtaka7さん

      当ブログをご覧いただきありがとうございます。

      これって、SPIFFSアップロードの件ということで良いのでしょうか?

      SPIFFSならば、当方の環境では、staging版、stable版の両方ともダメでした。
      環境は、Windows10, 64bit, です。
      Arduino15フォルダを削除してもダメでした。
      GPIO 0, 2, 15 を外してもダメでした。
      シリアルの転送速度を115200, 921600 で試しましたがダメでした。
      スケッチのアップロードは問題なくOKのですが・・・。

      Win7 でOKならばWin10でもできると思うのですが、不思議ですね。
      何が違うのかサッパリ分かりません。
      とりあえず、一手間かかりますが、OTAでアップロードしています。

      • Youtaka7 より:

        お世話になっています。もちろんSPIFFSの件です。他のRev.3でも上手くいきました。ただ、無印のものでは、SPIFFS Upload failedになり上手くいきません。なお、私はRev.2は持っていません。Revによる違いは良く分かりません。
        Switch sience さんに聞いて見ようと思っています。

        • mgo-tec mgo-tec より:

          なるほど、Rev.3 でもうまくいったというのは不思議ですね。
          ESPr Developer の回路図を見ると、トランジスタをうまく使って自動的にGPIO #0 のHIGH LOW を切り替えているようですが、私もそこはあまり詳しく解析していません。
          今はOTAでアップロードできるので、無理して解析しなくてもイイかな・・・、と思ってました。
          もし、スイッチサイエンスさんに聞いて何か分かりましたら、ぜひ教えていただけると幸いです。
          情報ありがとうございます。

タイトルとURLをコピーしました