OLED(有機ELディスプレ)SSD1306をArduinoからライブラリ無しで制御、処理を高速化してみました。(wire.hは使用します)
▼OLED SSD1306▼
試しにAdafruit_SSD1306.h ライブラリ使って表示しているのですがどうも表示する情報量にかかわらず(一文字だろうが)描画処理に40ms程度必要で思ったより処理時間かかります(I2C通信)。またメモリもライブラリだけで50%近く消費してしまう状況で、合わせて凝ったことしようとすると少し厳しい感じです。
▼ライブラリ使用▼
特にここら辺問題無ければライブラリは非常に便利なのでお勧めです。けど今回はライブラリ使わない場合で、その処理速度やメモリの使用状況を確認してみました。
まずはセットアップ部分からです。
Wire.beginTransmission(0x3C); //デバイスアドレス Wire.write(0x00); //Co=0, D/C#=0 Wire.write(0xA8); Wire.write(0x3F); //総ライン数 64行:0x3F(63 DEC) Wire.write(0xD3); Wire.write(0x00); //Set Display Offset 0x00 Wire.write(0x40); //Set Display Start Line 0x40 Wire.write(0xA1); //左右反転 0xA0/0xA1 Wire.write(0xC8); //上下反転 0xC0/0xC8 Wire.write(0xDA); Wire.write(0x12); //Set COM Pins hardware configuration// Wire.write(0x81); Wire.write(0xFF); //コントラスト//0-255 Wire.write(0xA4); //Disable Entire Display On A4/A5 Wire.write(0xA6); //白黒反転 A6/A7 Wire.write(0xD5); Wire.write(0x80); //Oscillator Frequency 0x80 Wire.write(0x20); Wire.write(0x00); //Addressing Mode //horizontal 0x00 Wire.write(0x8D); Wire.write(0x14); //Set Enable charge pump regulator 0x8D, 0x14 Wire.write(0xAF); //Display On 0xAF Wire.endTransmission();
初期設定です。I2C通信のそのものの作法はここでは割愛します。基本的にはこの内容は一度実行すれば良いのでsetup内に記述。ほぼデータシート内にあるセットアップフローチャートのままです。
最初にデバイスアドレス「0x3C」、続いてコントロールバイトというものを送信するのですがここでは「0x00」を送信してます。続けて各種設定を記述、詳細はデータシートを参照下さい。中身の細かい所まではいまいち理解できてません。
コントロールバイト(「0x00」//Co=0, D/C#=0 の部分)の解釈がいまいち消化不足です。他でもここら辺いろいろ情報がありましたが、どれも私の解釈と合わなくて・・・。この方法でとりあえずは問題がないのでO.Kということで・・・。Arduino I2Cのバッファサイズが32byteなので一度に送信するデータ量には注意が必要です。
で事前準備はこれだけでO.K。続いてOLEDに描画するのですが少しこのデバイスのことを理解しておく必要があります。
▼画面パターン(データシートより)▼
画面が上記のように8行、128列に分割されてます。行をページと表現しているようです。今回は初期設定のところ、「0x20」で「Horizontal」(水平)モードを選択しているため、左上から水平に進むように描画されていきます。
▼ページ▼
さらに縦8行(ページ)が行方向に8分割されていてこの単位(byte)で描画情報を送信するようです。なので 8ページ × 128列で 1024byte分の情報となります。
で具体的な描画方法なんですが、例えばデジタル風「0」を描画してみます。
▼デジタル風「0」▼
▲こんな感じの「0」を描画してみます。まず描画範囲を指定。この場合、描画範囲は横:16~27列目で縦:1~3ページ目を範囲を指定します。(0から数えてます)
▼描画範囲の指定▼
Wire.beginTransmission(0x3C); Wire.write(0x00); Wire.write(0x21); Wire.write(16); Wire.write(27); Wire.write(0x22); Wire.write(1); Wire.write(3); Wire.endTransmission();
▲「0x21」が横範囲指定のコマンドで、続けて始めと終わりを指示、縦範囲のコマンドは「0x22」で同様に続けてページ範囲を送信します。キャンバスの範囲を指定するイメージ。さらに続けて、画像の情報を送信します。
▲このように、左上から1byte単位で順に画像のドット情報を送信していきます。この場合情報量は全部で36byteとなります。
Wire.beginTransmission(0x3C); Wire.write(0x40); Wire.write(0xFC); Wire.write(0xFA); Wire.write(0xF6); Wire.write(0x0E); Wire.write(0x0E); Wire.write(0x0E); Wire.write(0x0E); Wire.write(0x0E); Wire.write(0x0E); Wire.write(0xF6); Wire.write(0xFA); Wire.write(0xFC); Wire.write(0xFF); Wire.write(0xFF); Wire.write(0xE7); Wire.write(0x00); Wire.write(0x00); Wire.write(0x00); Wire.endTransmission(); Wire.beginTransmission(0x3C); Wire.write(0x40); Wire.write(0x00); Wire.write(0x00); Wire.write(0x00); Wire.write(0xE7); Wire.write(0xFF); Wire.write(0xFF); Wire.write(0x3F); Wire.write(0x5F); Wire.write(0x6F); Wire.write(0x70); Wire.write(0x70); Wire.write(0x70); Wire.write(0x70); Wire.write(0x70); Wire.write(0x70); Wire.write(0x6F); Wire.write(0x5F); Wire.write(0x3F); Wire.endTransmission();
▲デジタル風「0」の送信部分です。「0x40」送信で以降のデータはGDDRAMへ送信します。アドレスポインタは自動でインクリメントされるため続けてデータを送信すればO.K。この場合、バッファ32byteを超えてしまうため、2回に分けて送信してます。描画方法はこれだけです。
▼例えば全画面クリアする場合は画面全部に「0x00」を書き込めばいいので、
Wire.beginTransmission(0x3C); Wire.write(0b00000000); Wire.write(0x22); Wire.write(0); Wire.write(7); Wire.write(0x21); Wire.write(0); Wire.write(127); for (int j = 0; j < 64; j++) { Wire.beginTransmission(0x3C); Wire.write(0x40); for (int i = 0; i < 16; i++) { Wire.write(0x00); } Wire.endTransmission(); }
端から端、画面いっぱい(1024byte分)に「0x00」を送信します。
この要領でOLEDに描画をします。単純に数字や文字を描画・更新するだけならば、これだけなので意外と簡素です。
測定した電圧、電流をライブラリ無しでリアルタイム表示してます。この表示量でおよそ15ms程度の処理でライブラリの半分以下となります。センサー値読み込んだり、描画以外にいろいろさせてますがそれでもメモリ使用量は35%程度なのでだいぶスリムにもなっていると思います。
コメント
はじめまして、
これぐらいなら、自前のコードで持っておけば新しく試す開発環境等にも適用できそうですね。 参考になりました、ありがとうございます。