有機EL ( OLED ) SSD1306 を再検証してみました ( I2C 通信用 )

記事公開日:2017年10月16日
最終修正日:2018年9月26日

スポンサーリンク

Wire ライブラリのスレーブアドレスについて

Start Condition を含んだスレーブアドレスはちょっと小難しいです。
アドレスに加えて、R/W#ビットを送信することになります。

SSD1306データシートによると、I2C通信の場合はデータ書き込みモードしか対応していないようです。
要するに、GDDRAM からのデータ読み出しはできません。
これはパラレル通信の場合のみのようです。
ですから、必然と R/W#ビットは 0 となります。

下図の様に、基板に16進数の 0x7A と 0x78 と印刷されていて、0x78 の方にチップ抵抗がハンダ付けされていたら、このデバイスのアドレスは 0x78 となります。
そして、R/W#ビットを 0 とした2進数は、

01111000

となります。

ESP32_SSD1306_13

でも、Arduino core for ESP32 や ESP8266 の Wire ライブラリで、Wire.beginTransmission 関数を使って送信する場合は、それ自体に R/W# ビットを含んで送信しているようなので、R/W#ビット不要です。
というとは、0x78 という値の R/W#ビットを削除した値を入力することになります。
実際は、0x78 を右へ 1bit シフトして、このようにします。

ESP32_SSD1306_14

ということで、Wireライブラリ関数のアドレスは、16進数 0x3C として、

Wire.beginTransmission( 0x3C );

と入力します。
これは間違いやすいので気を付けてください。

また、このデバイスのチップ抵抗のハンダ付けを変えて、アドレスを変更して、2つのデバイスを使い分けるということが可能です。

I2C通信の接続について

ESP-WROOM-32 ( ESP32 )や、ESP8266開発ボードの接続はちょっと注意が必要です。
I2C通信の SCL および SDA ラインには必ずプルアップ抵抗が必要なのです。
Amazon で売られている以下の SSD1306 モジュールはプルアップ抵抗がハンダ付けされておりました。


商品によってはプルアップ抵抗がハンダ付けされていない場合がありますので、基板をよく確認しておくことが重要です。

ということで、SSD1306 モジュールの SDA , SCL ラインにプルアップ抵抗が内蔵されている場合の接続方法は下図の様になります。
過去の私の記事では、SCL がGPIO#5 , SDA が GPIO#4 にしていましたが、最近のESP32 と SSD1306 合体モジュールでは逆に接続されているものがあるので、

SCL – GPIO #4
SDA – GPIO #5

としました。

●ESPr Developer 32 の場合

ESP32_SSD1306_20

●ESP32-DevKitC の場合

ESP32_SSD1306_21

●ESPr Developer ( ESP-WROOM-02 ( ESP8266 )開発ボード )の場合

ESP32_SSD1306_22

Wire ライブラリによる SSD1306  初期化

では、Arduino core for ESP32 および ESP8266 による、OLED ( 有機EL )ディスプレイドライバの初期化を説明します。
初期化だけの抜粋スケッチはこのようになります。

  Wire.begin(5, 4);
  Wire.setClock(400000);
  delay(100);
   
  Wire.beginTransmission(ADDRES_OLED);//※このバイトも含め、以後、合計32byteまで送信できる
    Wire.write(0b10000000); //control byte, Co bit = 1, D/C# = 0 (command)
      Wire.write(0xAE); //display off
    Wire.write(0b00000000); //control byte, Co bit = 0, D/C# = 0 (command)
      Wire.write(0xA8); //Set Multiplex Ratio  0xA8, 0x3F
        Wire.write(0b00111111); //64MUX
    Wire.write(0b00000000); //control byte, Co bit = 0, D/C# = 0 (command)    
      Wire.write(0xD3); //Set Display Offset 0xD3, 0x00
        Wire.write(0x00);
    Wire.write(0b10000000); //control byte, Co bit = 1, D/C# = 0 (command)
      Wire.write(0x40); //Set Display Start Line 0x40
    Wire.write(0b10000000); //control byte, Co bit = 1, D/C# = 0 (command)
      Wire.write(0xA0); //Set Segment re-map 0xA0/0xA1
    Wire.write(0b10000000); //control byte, Co bit = 1, D/C# = 0 (command)
      Wire.write(0xC0); //Set COM Output Scan Direction 0xC0,/0xC8
    Wire.write(0b00000000); //control byte, Co bit = 0, D/C# = 0 (command)
      Wire.write(0xDA); //Set COM Pins hardware configuration 0xDA, 0x02
        Wire.write(0b00010010);
    Wire.write(0b00000000); //control byte, Co bit = 0, D/C# = 0 (command)
      Wire.write(0x81); //Set Contrast Control 0x81, default=0x7F
        Wire.write(255); //0-255
    Wire.write(0b10000000); //control byte, Co bit = 1, D/C# = 0 (command)
      Wire.write(0xA4); //Disable Entire Display On
    Wire.write(0b00000000); //control byte, Co bit = 0, D/C# = 0 (command)
      Wire.write(0xA6); //Set Normal Display 0xA6, Inverse display 0xA7
    Wire.write(0b00000000); //control byte, Co bit = 0, D/C# = 0 (command)
      Wire.write(0xD5); //Set Display Clock Divide Ratio/Oscillator Frequency 0xD5, 0x80
        Wire.write(0b10000000);
    Wire.write(0b00000000); //control byte, Co bit = 0, D/C# = 0 (command)
      Wire.write(0x20); //Set Memory Addressing Mode
        Wire.write(0x10); //Page addressing mode
  Wire.endTransmission();
  Wire.beginTransmission(ADDRES_OLED);
    Wire.write(0b00000000); //control byte, Co bit = 0, D/C# = 0 (command)
      Wire.write(0x22); //Set Page Address
        Wire.write(0); //Start page set
        Wire.write(7); //End page set
    Wire.write(0b00000000); //control byte, Co bit = 0, D/C# = 0 (command)
      Wire.write(0x21); //set Column Address
        Wire.write(0); //Column Start Address
        Wire.write(127); //Column Stop Address
    Wire.write(0b00000000); //control byte, Co bit = 0, D/C# = 0 (command)
      Wire.write(0x8D); //Set Enable charge pump regulator 0x8D, 0x14
        Wire.write(0x14);
    Wire.write(0b10000000); //control byte, Co bit = 1, D/C# = 0 (command)
      Wire.write(0xAF); //Display On 0xAF
  Wire.endTransmission();

これは、データシートにある初期化例のフローチャートにできるだけ従ってみました。

1-2行目では、ESP32 と ESP8266 のプログラムで共通化するためにこうしました。
そして、Wire.begin() だけにしてしまうと、GPIOピンを変えられないので、任意のGPIOピンにするためにこうしました。

そして、注意していただきたいのは、Wire.beginTransmission 関数は、その関数が送信するバイトも含めて合計32byteしか送信できません。
ライブラリ内で 32byte までと定義されているためです。
ですから、Wire.beginTransmission関数の後は 31byte までです。
これ、とても重要!!

ということで、途中でWire.endTransmission 関数を置いて一旦区切り、再度Wire.beginTransmission 関数でコマンド送信を再開しています。
とりあえず、今の段階ではその方法しか知らないので、他の方法がありましたら教えていただけると助かります。
これは データバイトを送信する時も同じです。

そして、内蔵のチャージポンプレギュレーターを使ったコマンドを実行しないとOLED が表示されないことも重要です。

その他のコマンドは割愛させていただきます。
スクロールコマンド等は後日アップ予定の記事で紹介したいと思いますので、しばらくお待ちください。

そして、OLED のピクセル配置の始点設定は注意が必要で、次で説明します。

スポンサーリンク

mgo-tec電子工作 関連コンテンツ ( 広告含む )

投稿者:

mgo-tec

Arduino , ESP32 ( ESP-WROOM-32 ) , ESP8266 ( ESP-WROOM-02 )等を使って、主にスマホと連携した電子工作やプログラミング記事を書いてます。ライブラリも作ったりしてます。趣味、独学でやってますので、動作保証はしません。 電子回路やプログラミングの専門家ではありません。 畑違いの仕事をしていて、介護にドップリ浸かりそうな年配者です。 少しだけ電気の知識が必要な仕事なので、電気工事士や工事担任者等の資格は持っています。

「有機EL ( OLED ) SSD1306 を再検証してみました ( I2C 通信用 )」への8件のフィードバック

  1. こんにちは。PIC Basic PRO環境とPIC18FシリーズでSSD1306を使おうと試行錯誤していました。こちらの記事とデータシートをにらめっこして、思い通りに表示させる事ができるようになりました。大変参考になりました。ありがとうございます。

    1. TMさん

      記事をご覧いただき、ありがとうございます。

      PIC使いの方にもこの記事が役立ってくれたことは、ちょっと嬉しいです。
      苦労した甲斐がありました。

    1. ノラネコさん

      記事をご覧いただき、ありがとうございます。

      あ、そう言えば、SSD1306 の SPI もありましたね。
      すっかり忘れていました。

      今、取組中の課題が多くて、かなり後回しになってしまいそうです。
      スミマセン
      m(_ _)m

  2. まだ勘違いされているようですね。
    Coビットの意味を全然理解されていない。

    1. にゃ さん

      コメント頂き有難うございます。

      そうですか・・・。
      しばらく、しばらくSSD1306は使っていなかったので、今取り込み中の作業が終わったら見直してみます。
      しばらく時間がかかりますが・・・。
      勘違いばかりでダメダメですね・・・。

    2. にゃ さん

      もうすっかりこの使い方を忘れていて、思い出すのに時間がかかりました。
      今、改めて自分の文章を読んでみると、Co bit のところが曖昧で何か変ですね。
      Google 翻訳も中途半場です。

      今、私が思い出したところによると、Co_bit=0 の場合は、その後に続くデータバイトを、コントロールバイト無しに連続して送信でき、Co_bit=1 ならば、その後のデータバイトは1バイトのみと解釈しました。

      例として、Brightness display off などは、制御コマンドが1バイトだけなので、その場合は Co_bit=1 にするという感じでしょうか。
      実際にGDDRAM へデータバイトを連続で書き込む場合は、Co_bit=0 にして、コントロールバイト1バイトの後、連続でデータバイトのみ送るという感じだと解釈しています。
      実際にそれで問題無く動作しているのですが、これでも間違えていますか??
      何分、英語の解釈が苦手なもので・・・。

    3. にゃ さん

      ソースコードなどを全て修正してみました。
      ご指摘の通り、私の間違えと勘違いでした。

      ご指摘されなかったら、問題無く動作していたので、延々と気付かなかったと思います。
      修正はしたものの、まだ間違えていたらご指摘いただければと思います。
      まだまだ素人でした。
      この度はありがとうございました。
      m(_ _)m

コメントを残す

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

*画像の文字を入力してください。(スパム防止の為)

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