コントロールバイトの Co bit の解釈が間違えておりました。 スケッチを修正しました。 (2018/03/12) また、コントロールバイトのCo bit についての解釈を修正しましたので、以下の記事をご参照ください。 有機EL ( OLED ) SSD1306 を再検証してみました ( I2C 通信用 )
こんばんは。
前回の記事に引き続き、再検証した I2C通信の小型 有機EL ディスプレイ ( OLED ) SSD1306 で、グラフィカルな点、線、四角形、円を描いてみたいと思います。
Adafruit さんや、他の流通しているライブラリは一切使いません。
Arduino core for ESP32 および ESP8266 標準の Wire ライブラリのみ使います。
プロのプログラマーのようなコードとはいきませんが、アマチュアの私なりに試行錯誤してみました。
前回でも言いましたが、OLED SSD1306 はグラフィック描画には不向きです。
Pixel 単位指定ではなく、Segment / Common 方式だからです。
でも、ちょっと工夫すると、何とか描けるもんです。
予め、画面すべての Segment 用配列を用意するというものです。
メモリを食うことになるのですがが、私の頭ではこれしかないように思います。
結局、Adafruit さんのライブラリでも、PROGMEM を使ったり、FLASH に保存したりしているようです。
外付け SRAM に置くのもイイかも知れません。
今回は分かりやすく、グローバル変数領域に置いて、グラフィック描画をしてみたいと思います。
使うもの
複数紹介していますが、どの組み合わせでも動作確認しております。
中国の販売店によっては仕様が変わっている場合がありますのでご注意ください。
I2C通信用 OLED SSD1396 ディスプレイモジュール
ESP-WROOM-32 ( ESP32 )開発ボード
ESP-WROOM-02 ( ESP8266 ) 開発ボード
ブレッドボード 及び ジャンパーワイヤー等
Arduino core for ESP32 または ESP8266 の設定
Arduino IDE は 1.8.5 で動作確認しております。
ESP-WROOM-32 ( ESP32 )の場合は、Arduino core for ESP32 を予めインストールしておいてください。
Arduino core for ESP32 は必ず最新版を使用してください。
インストール方法は以下の記事を参照してください。
Arduino core for the ESP32 のインストール方法
ESP-WROOM-02 ( ESP8266 )の場合は、Arduino core for the ESP8266 をインストールしておいてください。
インストール方法は以下の記事を参照してください。
Arduino IDE に Stable ( Staging )版 ESP8266 ボードをインストールする方法
接続する
前回の記事と同じなのでそれを参照してください。
Segment/Common方式のグラフィック描画方法
では、普通のグラフィックディスプレイとは異なる、厄介な Segment/Common方式のディスプレイ描画方法を私なりの視点で紹介してみたいと思います。
恐らく、Adafruit さんのライブラリでも同じようなことをやっていると思われます。
前回の記事で紹介したように、SSD1306 の初期化で、事前に左上端を始点(0 , 0)として設定してあるものとします。
Segment が最少単位ですので、画面すべての segment を配列で割り当てるという方式をやってみました。
Segment は横に0~127 まであり、Page は 0~7 までなので、簡単に分かりやすいように、グローバル変数領域で
uint8_t segment[128][8] = { 0 };
とします。
{ 0 } は {} でも良いのですが、分かりやすいように { 0 }を使っています。
配列の全てを一気にゼロで初期化するという意味です。
そうすると、下図の様に座標に近い感じで segment ごとに割り当てることができます。
こうすると、SRAMメモリを 128×8 = 1024 byte を消費することになります。
Arduino UNO や ESP8266 にはちょっと辛いですね。
でも、ESP32 なら余裕があるかと思います。
もし、SRAMを消費したくなければ、PROGMEMなどを使って FLASH へ保存するしかありません。
ただ、その分、読み書きが遅くなります。
例えば、座標 ( 11 , 3 )から、座標( 11, 10 ) までの単独の一本の水平線を描きたいとします。
すると、Page1 の3番目の segment 00001000 というビットを書き込み、それを10番目の segment まで連続書き込みすれば良いわけです。
つまり、
segment[3][1] = 0b00001000;
segment[4][1] = 0b00001000;
・
・
というように書き込んで、それを SSD1306 へ送信すれば水平線が表示されます。
では、この直線を消さずに新たに Page1 に水平線を描きたい場合はどうするか・・・。
例えば、座標 (3, 9) ~座標(10, 9) へ水平線を描きたいとします。
その場合は、
segment[3][1] = segment[3][1] | 0b00000010;
segment[4][1] = segment[4][1] | 0b00000010;
・
・
というように前に記憶したバイトを OR演算すれば、前のバイトは消えません。
では、Page を跨ぐ、(3 , 5)~(3 , 17)という垂直線はどうかというと、たとえば下図の様な感じです。
Page1 の segment[3][1] は 0xFF( 11111111 ) で簡単ですが、その上下はとても難しいですね。
座標値をビットシフトと反転を使って計算するのですが、かなり数学的な頭を使って疲れ果ててしまいました。
アマチュアの私なりに考えたプログラムは後で紹介します。
もっと良い方法があるかも知れません。
斜線は更に難しくなります。
斜線の場合は点描画を連続させる方法しか思いつきませんでした。
ただ、それでも角度によっては、X軸とY軸方向のどちらを優先して分割するかとか、難解でした。
というわけで、これを一から説明するのはとても時間が足りないので、申し訳ないですが割愛させていただきます。
後で紹介するプログラムを解明していただければと思います。
ということで、この SSD1306 の OLED ディスプレイでグラフィック描画することはとても厄介だということがお分かりいただけましたでしょうか?
SSD1306 を使うならば、文字表示に特化した方がいいと思いますし、ESP32と合体したボードを販売するならば SSD1331 にした方が絶対に良いと思いますよ。
でも、よくこれで、ゲームとか作る人がいるなぁ・・と感心してしまいます。
でも、私なりに Adafruitさんのライブラリに頼らずに、独自でグラフィック描画に挑戦してみましたので、次をご覧ください。
コメント
このプログラムはArduino UNOでも動作可能でしょうか。
山田さん
記事をご覧いただき、ありがとうございます。
残念ながら、Arduino UNOでは動きません。
I2C信号のGPIO番号を揃えたらもしかしたら動くかも知れませんが、基本的にESP32専用です。
私としては、Arduino UNOよりもESP32の方が遙かに高速で小型でメモリ容量も大きいので、ESP32を使うことをお勧めします。
Arduino IDEでプログラミングできますし…。
とてもきれいなProgramでした
苦労された事と思われます
ありがとうございました
Kara.さん
記事をご覧いただき、ありがとうございます。
しばらく、このブログ運営から離れいまして、かなり久々にこのプログラムを見ました。
この記事を書いた時にはまだプログラミングのことを良く知らない時期で、改めて見ると命名規則が無茶苦茶だなと思いました。
ただ、それでもお褒めいただいてウレシイです。
現在では、X(旧Twitter)にあるらびやんさんが、かなり前から高速描画のライブラリを公開していて、ESP32やM5Stack界隈では主流ですので、そちらを使う方がお勧めですよ~。