桜のはなびらブログロゴ

桜のはなびらブログ

Raspberry Pi 2でI2C接続、SSD1306採用のグラフィック有機ELディスプレイを使う

どうもお盆休み9連休のSakura87です。家の片付けと法事で大体潰れました。
ということで久しぶりのラズパイ記事です!なんと2年ぶり?くらいになります。
今回はこの有機ELディスプレイを操ってみたいと思います。

プロジェクタースクリーン?アレは書く事がなさ過ぎて没になりました。まぁ画用紙で作ったスクリーン(自称)に比べたら良い感じでした。

なぜ今更ラズパイ2なのか?それは持っているからだ。今度4が日本で発売されたら買い換える予定。

使用するもの

  • Raspberry Pi本体
    なんでも良いだろうけど性能的には2B以降を推奨。
    2B以下だとプログラムの修正が必要かも。
  • 0.96インチ 128×64ドット有機ELディスプレイ(OLED)
    今回は秋月電子で売っていたこちらにしました。ICがSSD1306のものであれば同様に処理できるはず。
  • 接続するための配線類
    なんでもいいです。ラズパイで上記有機ELなら、両方雌のジャンパ線があれば良いんじゃないですかね。最悪半田付けすれば何とでもなりますし。

購入した有機ELディスプレイ正面。実はこの有機ELディスプレイは1年以上前に買って積んでいたものなので外見は若干の仕様変更があるかもしれません。

裏面。ちょっとぼけてますが、アドレスは0x78に設定されていました。
ここで0x78ですが、Raspberry Piで認識されるのは1ビット右シフトした3Cになるようです。

今回はこの有機ELディスプレイにモノクロビットマップとテキストファイルを表示してみたいと思います。

有機ELの制御

それでは早速有機ELディスプレイの制御を行っていきたいと思います。

データの転送方法

I2C接続でデータを転送します。I2Cですので以下の図のSDA.1/SCL.1に液晶のSDA/SCLを接続して後は3.3V出力とGNDをつなげば物理接続はOK。

正しく接続されていれば、Raspberry Pi側はこうなっているはずです。

(VCCが3.0Vになっていますが3.3Vの間違いです。)
GNDは図解しやすいようにここから取りましたが、SCL.1の向かいのピンからとっても良いです。

Raspberry Pi側の設定は以下の通りです。

  1. raspi-configにてRaspberry PiのI2Cを有効にする
  2. i2c-toolsとlibi2c-devをインストールする

最新のRaspbianでフルインストールした場合は1番のみで良かったです。
前回がもう5年近く前なので最新の操作方法を以下に記しておきます。

I2Cの有効化

端末を起動して以下のコマンドを入力してラズベリーパイの設定を開く

5 Interfacing Options を選択

P5 I2C を選択

<はい>を選択

上のメッセージがThe ARM I2C interface is enabledである事を確認し、<了解>を選択。

<Finish>で終了。

次に以下のコマンドを入力してi2c-toolsをインストールします。

インストールが完了したらlibi2c-devをインストールします。

以上2つのコマンドを実行する事でRaspberry PiでI2Cが使えるようになります。
実行結果は以下の通りでした(こちらの環境ではすでにどちらも最新版が入っていました。)

今回はWiringPiおよび特別なライブラリは使用しません。(自作のFONTXライブラリは使いますが)

あとはRaspberry PiのI2Cは標準で100kbpsですので/boot/config.txtに以下の行を追加してI2Cの速度を400kbpsに上げておきましょう。

これを追加すると(実際に転送速度が体感速くなっているし)転送速度が上がっている…はず!

ここまで設定すれば、以下のコマンドを実行してデバイスIDが取得する事ができる。

すると以下のようなデータが返ってくるので、30行のC列に「3c」と表示されていればOK。

 転送するデータの仕様

転送データの仕様は秋月にある仕様書の8章に詳細に書いていてI2C関連は19ページの8.1.5から、転送仕様はFigure8-7で図解されていますが。基本的なフォーマットはRaspberry Pi側がやってくれるので、基本的には転送するデータの仕様を伝えるコントロールバイトと実際に転送するコマンド等のデータを管理しておけば良いです。

コントロールバイトの仕様は以下の通りです。

76543210
CoD/CCo:Continuation Bit:
1:1バイトコマンド
0:2バイト以上のコマンド
——————————————
D/C:Data/Command Selection Bit:
0:Command
1:Data

Coに1を立てると1バイトのコマンドとして認識されます。2バイト以上のコマンドやデータは0に設定するようです。
別にどちらでも0でよいようですが、セオリーとしてはこうするようです、
D/Cに1を立てるとデータ、0にするとコマンドとして認識されます。という事でコントロールバイトは以下のようになります。

データ内容
0x002バイト以上のコマンドデータ
0x40画面に表示する画像データ
0x801バイトのコマンドデータ

このデータを転送後、転送したいコマンドやデータを転送すればデータやコマンドが転送できます。

コマンドは一度に必要分転送しても特に問題ないようでした。

データ転送プログラム

さて、以上で前準備は終わりました。続いてデータ転送プログラムを書いていきます。
今回も例に漏れずC言語で(Pythonは嫌いなので)

I2Cデバイスの初期化

まずI2Cデバイスを初期化します。今までI2C通信はWiringPiを使っていましたが、中身を見てみると大したことがなかったので今回は個別の命令に切り出して見ました。

なんと必要なのはこれだけでした。
デバイスをファイルとしてOpenしてioctlでデバイスを取得。その戻り値を今後I2Cデバイスとして使用すればOKみたいです。

これならデバイス名さえ分かればほとんどのLinux/Unix系オペレーションシステムで同じ方法が使えると思います。

これを利用するには以下のインクルードが必要です。

なお、上記のインクルードには他の部分に必要なインクルードも含まれています。
今回のプログラムは以上のインクルードがあれば動作します。

上記プログラム中の「I2CDevicePath」にI2Cデバイスのファイルパスを設定します。
ファイルパスは「ls /dev/i2c*」コマンドを打てば出てきます。自分の環境では「/dev/i2c-1」でした。Raspberry Pi 2B以降であれば同じのようです。

このコードを一番最初に実行する事でI2Cデバイスの初期化が行えます。

I2Cデバイスへのデータの転送

I2Cデバイスにデータを転送するのは1行あればで可能です。

[I2C_Device]の所に先ほどI2Cデバイスを初期化した際に戻ってきたInt型の変数を指定します。
[Data]に転送するデータのポインタを渡します。
[DataLength]に転送するデータの大きさを指定します。

ちなみにこの有機ELの場合、コマンドはある程度一括して転送可能のようですが、コマンド系(0x00、0x80で送信するデータ列)とデータ(0x40で送信する実際に表示するビットマップデータ)は別に送る必要があるみたいです。(仕様書には一括転送出来そうな感じに書いてあるのですが)

今回は読み込みは使用しないのですが。ここの方法は前回温度計をいじった時と同じですのでそちらを参考に。

Raspberry Pi 2 で I2C接続の温湿度センサーを使用する(HDC1000)

コマンドは大したことがないので適当に変数作って転送させれば良いと思います。
データ転送コマンドはLSB→MSBの順で描画されるのでWindows系をメインで作成されたデータだとMSBとLSBを入れ替える必要があるので、関数を作りました。

大したことをしていないので細かい説明はコメントを見ていただくとして、まず行列の範囲を設定してビットを反転させ、それらのデータを転送しています。

有機ELの初期化

基本的な制御用コマンドができあがったら、有機ELの初期化関数を作ります。
有機ELの初期化は仕様書の最後にチャートがあります。()内はコマンド。16進数2桁0xは省略
特に記載がないものは1バイトコマンド。

  1. SET MUX Ratio(A8)
    横方向の解像度を設定
    2バイトコマンド。
    2バイト目が設定値で0x00から0x3F(63)がの範囲。この数値が直接画面サイズとして使用される。
    この有機ELなら通常0x3Fでよい。
  2. Set Display Offset(D3)
    ディスプレイの縦方向(64pxのほう)のオフセット。1px単位。指定したバイト分オフセットされて循環する。
    2バイトコマンド。
    2バイト目が設定値で0x00から0x3Fが範囲。通常0x00でよい
  3. Det Display Start Line(40-7F)
    ディスプレイの縦方向のスタート位置。1px単位。
    0x40が0pxで0x7F(0x40+0x3F)が63。通常初期化時は0x40でよい。
  4. Set Segment re-map(A0/A1)
    セグメントを左右(長いほう)どちらの方から書き始めるか。
    A0が左から右、A1が右から左
  5. Set COM Output Scan Direction(C0/C8)
    COMをページの上下どちらから描画し始めるか
    C0が上から下、C8が下から上。なおビットの反転はされない模様。
  6. Set COM Pins hardware configuration(DA)
    2バイトコマンド
    よく分からんがCOMのデータをどのように描画するか。という事らしい。
    取説の10.1.18に内容が書かれているのだけどいまいちよく分からん。
    説明書の説明では0x02が設定値だが、0x12に設定したものが正常に表示できた。
  7. Set Contrast Control(81)
    2バイトコマンド
    画面の明るさ(0x00~0xFF 0~255)
    当然大きくなる方が明るくなるが、自分が入手した個体は最大~最小はあまり変わらなかった。
    まぁ初期値の0x7Fで問題になる事はない。
  8. Disable Entire Dispaly On(A4)
    画面の描画をオン。
    これを受けるとRAMのデータを画面に転送するようになる。
    コマンドA5は逆に画面にRAMのデータを転送しなくなる。A5を転送した後画面を描画してA4を転送すれば画面がチラチラしない。
  9. Set Normal Display(A6)
    ビットの表示方法を選択。
    A6が通常の点灯方法(1が点灯で0が消灯)A7がその逆である。
    有機ELは点灯で電力を消費し、寿命が短いので黒背景になるように使うのが良い。
  10. Set Osc Frequency(D5)
    2バイトコマンド。
    ディスプレイクロックとオシレーターの設定。
    メーカー指定値の0x80で良いと思う。
  11. Enable chargepump regulator(8D)
    2バイトコマンド。
    チャージポンプレギュレーターの設定。
    こちらもメーカー指定値の0x14でOK。
  12. Display On(AF)
    ディスプレイ表示をオン。
    AEでディスプレイオフ(スリープする時とか使う。)

以上が仕様書の最後に書いてある初期化方法で、あとはどこかのタイミングで以下の3つを設定してあげれば転送できます。

  • Set Memory Addressing Mode(20)
    2バイトコマンド
    メモリアドレスモード。送られたデータをどのような規則に従って描画するかを決定。
    00:横(列)→縦(行)方向で循環
    指定範囲を列方向で書き込んだ後、行の方向に移動する
    01:縦(行)→横(列)方向で循環
    00の逆。
    この2つが以下の21・22コマンドで描画範囲が設定できるモード。
    10:ページアドレスモード
    B0~B7コマンドを転送してページアドレス(縦 狭いほう)を指定するモード。
    00~0Fで下限スタートコラム、10~1Fで上限スタートコラムを指定して範囲指定するのかな?
    よく分からないので制御できなかった。仕様書の説明だとこれでいいはずだけど。
  • Set Column Address(21)
    3バイトコマンド
    描画する列(長いほう)のアドレスの範囲を設定する。
    コマンド,開始位置,終了位置 の順で3バイト構成。開始終了はそれぞれ0x00~0x7F(0~127)
    ここで指定した横方向のエリア内および下のコマンドで指定した縦方向のエリア内でデータが循環する。
  • Set Page Address(22)
    3バイトコマンド
    描画する行(短いほう)のアドレスの範囲を設定する。
    要領は上のコマンドと同じ。指定値が0~7というだけ。

Set Memory Addressing Modeは今回はすべて同じでかまわないので上記の初期化コマンドの中に入れてしまいました。

Set Column AddressとSet Page Addressはデータを転送する時に設定するのが一番やりやすいので上記のsend_dataコマンドに入れ込みました。

以上の条件でコマンドを組み立てると以下のようになりました。

これを一度に転送してやると有機ELが使えるようになります。転送コマンドはこの変数を転送するのですが。変数は一度初期化すれば良いのでグローバルで持っておく。よって転送コマンドは非常にシンプルなものになります。

基本的にはwriteコマンドでコマンドデータを転送してあげれば良い。

これでこの有機ELを使う一通りのコマンドは用意できた。

これだけでも十分使えるが転送時に画面の描画を切った方がすっきりするので以下の関数を追加で作成した。

beginDraw関数を実行すれば画面の更新がオフになり、endDrawで描画される。
これを利用すればある程度更新中の画面が隠せる。

さて、ここまで出来れば後は表示したいデータを送るだけなので。ここで終了……でもよいのだが。せっかくなので画像表示とテキストファイル表示を行ってみたい。

画像の転送

今回は64×128のモノクロビットマップを転送しようと思う。
正直Raspberry Piで制御するのであれば、何らかの画像処理ライブラリでメモリに画像として保存してそれを受け取るのが手っ取り早いと思われるので真っ先に紹介する。

画像を64×128にしたのは液晶のRAM構造に合わせるため。RAM構造が縦(短いほう)が1バイト単位で区切られているのでモノクロビットマップと相性が良い。さらにBMP形式は4バイトの境界に合わせる必要があるが、横64でも128でも4バイトで割り切れるので気にする必要がないという利点がある。

処理は教科書にも載ってるような基本的なファイルIOなのでいきなり正解コードを出す。

argv[2]は引数の2番目を拾ってきている。
ここに読み込むBMPファイルを指定する。

プログラムが実行されると、ファイルを開いて、10バイトシーク。そこからInt型として4バイト読み込む。
この4バイトがヘッダーのサイズ(画像データまでのバイトオフセット)なので、先頭からこのサイズ分オフセットをして、1024バイト(128×8バイト)読み込んで先ほど組み立てたsend_dataを使い転送しているだけ。

どうせこの後プログラムは終了するので開放する必要はないが、一応ファイルクローズして終了。

これを実行して何らかの画像を表示すると以下のようになります。

画像であれば事前に90度回転させておくなり好きにして処理が出来るので、表示する項目が決まっている温湿度などの表示であれば画像化して用意しておいて条件に合った画像を転送させるのが簡単で良いでしょう。

テキストデータの表示

テキストデータは文字通り文字コードの集合体なので、何らかの方法で文字コードから文字パターンに変換してあげる必要があります。そこで今回は以前電光掲示板で使用したFONTXのライブラリを使用して表示したいと思います。

FONTX関係の説明はArduinoで使った時に詳細な説明を行っていますのでそちらを。

今回使用するFONTXライブラリはラズパイ2資料にあります。

このライブラリのZIPを解凍して「fontxpi.h」をインクルードすれば良いです。

上のインクルードパスは実際にこのヘッダファイルを解凍した所に指定します。
解凍して出てきたフォルダをそのままソースファイルと同じ階層に置いた場合は上記のコードそのままです。

テキストファイルを読み込んで表示する処理はこんな感じです。

  1. テキストファイルを開く
  2. 1バイト読み込む 読み込めなかったら終了 ※
    読み込めない=ファイル終端に到達と判断する。
  3. 全角半角をチェック。
    読み込んだ1バイトが全角文字のコードであるかを確認する。
  4. 全角文字であればさらに1バイト読み込む。
  5. 全角文字であれば追加の1バイトと元々の1バイトを結合する。
  6. KanjiReadX関数でフォントパターンを読み込む
  7. 読み込んだフォントパターンを転送する
  8. ※に戻る

これにさらにこのフォントファイルを有機ELの使用にあわせて修正を加えてやる必要があります。
修正する内容は以下の通り

  • Y方向を上下反転させる
    BMP画像が下→上に記録されているから。それにあわせる。
  • さらにX方向が2バイト以上の場合はX方向も左右反転させる
  • そしてビットも左右反転させて転送

読み込むフォントファイルの作成

フォントファイルは既存のFONTXファイルを引っ張ってくるなり、オープンソース系のフォントを変換する等で対応します。
今回はBDFファイルが存在する東雲フォントを変換ツールで変換して使用しました。(ツールの紹介等はArduinoの方でやってますのでそちらで。

変換なんてめんどくせえよ!という方はこの辺のフォントが良いんじゃないかと思います。

  • ぱうフォント公式ミラーサイト
    FONTXファイルあり 16×16 視認性○
    フリーフォント 商用利用×?
  • Izumi Bitmap Font
    FONTXファイルあり 16×16 視認性○
    フォントの中では比較的新しめでJIS X 0213:2004対応版もあり。
    パブリックドメイン
  • 美咲フォント
    FONTXファイルあり 8×8ドット 視認性△
    いつも使ってる美咲フォントにもFONTX版がありますが、最新の書式にするにはBDF等からの変換が必要。

上2つのフォントはもう15年近くまえのものになりますがまぁ使えます。上2つが視認性が良いですが、この液晶サイズで使うならIzumi Bitmapの方が良いかなと思います。

なお、フォントファイルはバイトの境界で穴埋めされますが、今回のプログラムは14や20といった中途半端なフォントサイズのフォントは考慮しておりませんので、表示はされますがその分空白が生まれます。

フォントファイルはプログラムと同じ階層に、全角が「FONTX-J.FNT」半角が「FONTX-A.FNT」というファイル名に変更して配置するか、fontxpi.hを直接編集するもしくはソースコードの方に追加して以下のデファインを変更します。

ソースコードに追加する場合はfontxpi.hのインクルードの「前」に追加してください。

↑ぱうフォントを読み込むように設定した例。

フォント転送コアプログラムの作成

という事で以上の条件を元にフォントを転送するコマンドを作ります。最後のビット左右反転はsend_dataでやってあるのでそれ以外の処理をします。

KanjiReadX関数の戻り値を分割させています。使用するフォントが全角8×8であれば半角は4×8でどちらも8×8として出てきますので計算する必要はないです。16×16はバイト数が変わってくるので要計算。まぁ汎用性を持たせるなら計算します。
計算内容は上3つがXドット数Yドット数、データサイズの抜き出し。下二つがXYそれぞれのバイト数の計算。

Xのドット数はXバイト数の計算にのみ使用しています。Yのバイト数は現状回転などを行わないので不要です。
よってこの二つは統合・削除できますが一応残してあります。

これらの情報を元に、文字データの上下反転とX方向の反転をしている箇所がこれです。
今回は別に元のデータが変わっても問題ないのでバッファ変数を1バイト作っておけば良いのでしょうが。念のため別の変数にメモリを確保してそちらに転送しています。ついでにネガポジ反転も織り込んである。
計算が終わったらデータを転送して終了。

という事でこれをsend_font_dataとして作成します。

ファイルを読み込んでフォントを表示する部分

それではファイルを読み込んで文字コードを解析、フォントデータを取りだして表示する部分を作ります。

最後に示す全体のコードではDefineの有無で縦書き・横書きを切り替えられるようにしていますが、これは横書き用のコードです。
まずテキストファイルから1バイト読み込みます。この読み込んだコードの文字コードによって全角・半角を判定し判定結果を保存。

次に最初に読み込んだ1バイトを文字コード用の変数に代入します。

もし全角文字の場合は追加で1文字読み込んで元々読み込んだ1バイトを8bitシフトしてOR演算であわせる。

その後は読み込んでただ転送して転送後は描画位置を転送したフォントサイズ分開始位置を移動させて終了。

という事でこれでShift-JIS形式のテキストファイルを指定すれば読み込まれて有機EL上に表示されます。

例えば以下の文字をテキストファイルに保存して読み込ませるとこうなります。

上記のコードであれば半角全角対応しており、画面端に到達したら改行されます。
ただし改行コードなどは一切関知しません。試してませんが空白として認識されるかと。

終わりに

という事でグラフィック型有機ELディスプレイを動かしてみました。

この有機ELに搭載されたSSD1306は調べてみると結構多くのSSD。特に128×64の有機ELであれば日本で入手可能なほとんどの有機ELに採用されているみたいなので使用する幅が広がりますね。今回は実証なのであまり複雑な処理は入れていませんけど、基本は押さえているはずなので、各々工夫してみてください。

次回ですがスマートフォンを買い換えたのでそちらのレビューを書こうと思います。
ただし1つ前の機種になるのであまり凝ったものは書きません。

全体のソースコード

以下が全体のソースコード。コメントをたっぷり入れたので詳細はコメントを見てください。
コンパイルは単純にgccでソースファイル名と出力ファイル名を指定すればOK。
ちなみに今回は必要な処理がわかりやすいように最低限のコードのみに絞っているためエラー処理はしていないので適宜追加するようにしてください。

以上のデータを次のコマンドでコンパイルした場合

tを第一引数に、その後にファイル名を付けるとテキストファイルとして該当のファイルを読み込んで画面に表示します。

iを第一引数にするとビットマップファイルとして画像を表示します。

それでは。

コメントを残す

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

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください

関連記事