Co bit について、もしかしたらまだ勘違いしているみたいです。 コメント欄で「にゃ」さんから指摘を受け、修正しました。 失礼しました。 2018/03/11
こんばんは。
今回は、しばらく放置していた、I2C通信 OLED ( 有機EL ) SSD1306 を再検証してみました。
当ブログにちょくちょく質問やコメント、ご指摘をいただいたので、それもまとめてみたいと思います。
因みに、Adafruit さんや、その他の外部ライブラリは一切使いません。
Arduino core for ESP32 および ESP8266標準の Wire ライブラリのみを使います。
それだけで OLED SSD1306 を制御すると、ディスプレイドライブの仕組みが良く分かり、更にプログラミングを工夫すれば、 Adafruit さんのライブラリよりも軽い動作でサクサク動くと思われますので、皆さんも是非試してみてはいかがでしょうか。
I2C通信 OLED SSD1306 モジュール は、私の個人的意見ですが、通信速度が SPI より遙かに遅いし、グラフィック表示は極端にやりにくいし、とても使い辛いデバイスだと思っています。
個人的にはSSD1306よりも、同じくらいのサイズのOLEDでは、フルカラーで圧倒的に使いやすい SPI 通信の OLED SSD1331 が断然お勧めですね。
でも、日本の信頼できる販売店では(ちょっと割高ですが)秋月電子通商さんの Pmod OLED のみで、あとは中国販売店しかなく、到着までに10日~2週間くらいかかって、なかなか普及しないのが残念です。
それなのに、SSD1306の方はというと、いろいろなボードが発売されてきて、当方のブログでもSSD1306関連記事のアクセスが多く、質問やご指摘が多くなってきたので、私自身も無視できなくなってきました。
というわけで、今回は過去の以下の記事のプログラムを修正しつつ、更に一歩踏み込んで、I2C OLED SSD1306 を解明してみたいと思います。
I2C極小OLED(有機EL)SSD1306をArduinoでライブラリを使わずに動作させてみました
I2C極小OLED(有機EL)SSD1306をスクロールさせたり、もうちょっと深堀りしてみました
SPI通信用のものは、後日記事にしようと思っていますので、しばらくお待ちください。
使うもの
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 ボードをインストールする方法
有機EL ( OLED ) SSD1306 モジュールについて
有機EL ( OLED ) にディスプレイドライバ SSD1306 が組み込まれたモジュールです。
I2Cインターフェース用と、SPIインターフェース用が販売されています。
残念ながら、パラレル通信のものは見かけません。
ここで説明するのは、I2C通信用 128×96 pixel のものです。
I2C は通信線が2線で済み、モジュールにプルアップ抵抗が内蔵されているものが殆どなので、接続は簡単です。
ただ、通信周波数が最大 400kHz なので、SPI 通信より遙かに遅いです。
先ほども述べましたが、日本の販売店は今のところあまり見当たりません。
ただ、Amazon ではプライム対応のもの数店舗あり、種類も多く、入手しやすいです。
コマンドとデータの送信方法( I2C 通信 )
では、SSD1306 のデータシートに書かれている、コマンドとデータの送信方法を解明してみたいと思います。
実はこれ、2年くらい前に私がこのデバイスを使った時の以下の記事では、エライ勘違いをしていたんです。
I2C極小OLED(有機EL)SSD1306をArduinoでライブラリを使わずに動作させてみました
そして、以下の記事のコメント投稿欄で、MKさんから教えていただきました。
I2C極小OLED(有機EL)SSD1306をスクロールさせたり、もうちょっと深堀りしてみました
2年前はこのデバイスを使い始めたばかりで、しかもプログラミングを独学で始めたばかりだったので、何が悪いのかも良く分からず、とりあえず動いたので何となく使っていました。
I2C についてもあまり勉強せずに動かしていて、特に問題無かったのでそのまま使っていました。
その後、SPI通信のデバイスが高速なので、そちらに乗り換え、I2C を手放し放置状態にしていました。
それが、今頃になって、ようやくある程度正しい使い方が分ってきました。
やっぱり独学っていうのは穴だらけで、他の方やその道のプロの方々からご指摘を頂かないと、自分自身では間違いを発見できないですね。
今まで、当記事をご覧になって工作をしていただいた方々、大変失礼いたしました。
いずれにせよ、私は独学アマチュアなので、ご容赦いただきたいと思います。
では、まず、SSD1306 のデータシート内の下図の様な、I2C-bus data format を見てください。
私はI2C通信についてはあまり詳しくありませんが、2年前はザッと見て理解したつもりになっていました。
これ、サラッと流して見て、理解した気になってプログラミングしてしまうと、実はエライ勘違いしてしまうんです。
正直言って、非常に分かりにくいです。
(ただ、実は後の英文で詳しく説明されていました・・・)
2年前、これを見た時、スレーブアドレスを送信した後、コマンドを連続で送り、その後データバイトを送っていました。
つまり、こんな感じです。
当時はこれでも動いたんです!
ですが、何か変なノイズが出たり、思うように動作しないこともありました。
それは当時はあまり気にしていませんでした。
でも、コメント投稿欄で MKさんからご指摘を頂いて、初めて分かりました。
それに、実は、データシートのその後の英文で詳しく説明されていたんです。
Google 翻訳のお力を頂戴して、訳してみるとこんな感じです。
Co bit について、もしかしたらまだ勘違いしたみたいです。 コメント欄で「にゃ」さんから指摘を受け、修正しました。 2018/03/11
1)マスタ装置は、Start Condition によりデータ通信を開始する。 Start Condition の定義を図8-8に示します。 Start Condition は、SCLがHIGHの間、SDAをHIGHからLOWに引き下げることによって確立されます。 (これは、シリアル通信のいわゆるスタートビット) 2)スレーブアドレスは Start Condition の後に続きます。 SSD1306の場合、スレーブアドレスはSA0をLOWまたはHIGH(D / CピンがSA0として機能する)に変更することにより、 "b0111100"または "b0111101"のいずれかになります。 3)R/W# ビットを論理 "0"にすることにより、書き込みモードになります。 4)スレーブアドレスと R/W# ビットを含む1バイトのデータを受信した後、確認応答信号( ACK )が生成されます。 アクノリッジ信号( ACK )の図式表示については、図8-9を参照してください。 アクノリッジビット( ACK )は、アクノリッジ関連のクロックパルスのHIGH期間中にSDAラインがプルダウンされると定義されます。 5)スレーブアドレスの送信後、制御バイトまたはデータバイトのいずれかをSDAを介して送信することができる。 制御バイトは主に6つの "0"に続くCoビットと D/C# ビットで構成されます。 a. Coビットが論理「0」として設定されている場合、以下の情報の送信にはデータバイトのみが含まれますデータバイトを連続して送信できる。 b. D/C# ビットは、次のデータバイトがコマンドまたはデータとして作用することを決定する。 D/C# ビットが論理「0」にセットされている場合、それは次のデータバイトをコマンドとして定義する。 D/C# ビットが論理「1」にセットされている場合、それは GDDRAM に記憶されるデータとして次のデータバイトを定義する。 GDDRAM 列アドレスポインタは、各データ書き込み後に自動的に1ずつ増加します。 6)アクノリッジビット ( ACK )は、各制御バイトまたはデータバイトを受信した後に生成されます。 7)Stop Conditionが適用されると書き込みモードが終了します。 停止条件は図8-8でも定義されています。 停止条件は、 "SCL"が "H"の間に "SDA in"をLOWからHIGHに引き上げることによって設定されます。 (これは、シリアル通信のいわゆるストップビット)
これをちゃんとじっくり読むとやっぱり理解できますね。
記事を書きながら、改めて「そうだったのか・・・」と気付いたりしています。
要するに、データシートはサラッと読み飛ばすのではなく、じっくり読み込むということがとても大事だということが分かりますね。
いゃぁ~・・・、反省しました。
でも、プログラミングを初めて間もない時だったので、ご容赦くださいませ。
m(_ _)m
I2C の アクノリッジ ( ACK ) については、須津技術研究所さんの、以下の記事が大変参考になります。
要するに、データバイト等をスレーブデバイスの SSD1306 が受信したら、SSD1306自身が ACKビットを生成するということです。
それをマスターデバイス側であるESP32 が認識すれば、そのデータは有効ということになります。
Arduino IDE の場合、Wire ライブラリがあるので、あまり ACKビットとか気にしないでプログラミングしてしまいますね。
Arduino IDE は使いやすい分、正しい通信方法やプログラミング知識をスルーしてしまいがちなので、十分注意していかねばなりませんね。
以上を踏まえると、私が解釈した、I2C コマンドとデータの正しい送信手順はこんな感じです。
(※コメント投稿の「にゃ」さんから指摘を受け、修正しました)
これのポイントは、まず、コントロールバイトというものを必ず送るということです。
そして、データシートの m>=0 words というところで勘違いし易く、データシートにあるように、コントロールバイトの後のデータバイトというものは、制御用コマンドもグラフィックデータも全てひっくるめたデータということです。制御コマンドの場合は、コントロールバイトの後に1つの制御コマンドを送るということです。
制御コマンドの後に複数のコマンドを連続して送ることができません
これをつい最近まで気付かず、複数連続して送っていました。
Co bit = 1 とすると、コントロールバイトの後にコマンドだろがデータだろうが、1バイトだけしか送信できません。
別のコマンドまたはデータを送る場合は、再度コントロールバイトを送ることになります。
コントロールバイトとコマンドまたはデータバイト1バイトのペアをm回送信するという意味です。
この、m回というのは、Arduino-ESP32ならば、I2Cライブラリ中で、StartコンディションバイトからStopコンディションバイトまで合計32byteまでと定義されていますのでご注意ください。
それ以上になったら、またStartコンディションバイトを入れなければなりません。
今まで勘違いしていてプログラムしていても、動いてしまうところが勘違いの起きやすい点です。
制御コマンドを複数送る場合は、コントロールバイトと1つの制御コマンドを一組として、それを複数回送るということです。
そして、GDDRAM、いわゆるOLED に表示させる実ビットを連続して書き込む場合は、データ書き込み用コントロールバイトを送った後、連続してデータを送ることができます。
コントロールバイトの Co ビットを 0 にして、C/D# ビットが 0 の場合は制御コマンド、1 の場合はデータとなり、その後 6 ビットは全て 0 にします。
その後、実データをコントロールバイト無しに連続して送ることができます。
このコントロールバイトを適切に設定しないと、変に画面のオフセットがかかったり、変なノイズが画面に出たりします。
ということで、このコントロールバイトは極めて重要でしたね。
MKさんに教えていただいて、これに気付いたのがつい最近です。
MKさん、ホントにありがとうございました。
そして、皆さま、今まで勘違いしていてスミマセン・・・。
m(_ _)m
そもそも英語力が無いのも原因でした・・・。
Google翻訳に感謝!
スレーブアドレスについては次で説明します。
コメント
こんにちは。PIC Basic PRO環境とPIC18FシリーズでSSD1306を使おうと試行錯誤していました。こちらの記事とデータシートをにらめっこして、思い通りに表示させる事ができるようになりました。大変参考になりました。ありがとうございます。
TMさん
記事をご覧いただき、ありがとうございます。
PIC使いの方にもこの記事が役立ってくれたことは、ちょっと嬉しいです。
苦労した甲斐がありました。
SPIを使った場合の記事も楽しみにしています!よろしくお願いします!
ノラネコさん
記事をご覧いただき、ありがとうございます。
あ、そう言えば、SSD1306 の SPI もありましたね。
すっかり忘れていました。
今、取組中の課題が多くて、かなり後回しになってしまいそうです。
スミマセン
m(_ _)m
まだ勘違いされているようですね。
Coビットの意味を全然理解されていない。
にゃ さん
コメント頂き有難うございます。
そうですか・・・。
しばらく、しばらくSSD1306は使っていなかったので、今取り込み中の作業が終わったら見直してみます。
しばらく時間がかかりますが・・・。
勘違いばかりでダメダメですね・・・。
にゃ さん
もうすっかりこの使い方を忘れていて、思い出すのに時間がかかりました。
今、改めて自分の文章を読んでみると、Co bit のところが曖昧で何か変ですね。
Google 翻訳も中途半場です。
今、私が思い出したところによると、Co_bit=0 の場合は、その後に続くデータバイトを、コントロールバイト無しに連続して送信でき、Co_bit=1 ならば、その後のデータバイトは1バイトのみと解釈しました。
例として、
Brightnessdisplay off などは、制御コマンドが1バイトだけなので、その場合は Co_bit=1 にするという感じでしょうか。実際にGDDRAM へデータバイトを連続で書き込む場合は、Co_bit=0 にして、コントロールバイト1バイトの後、連続でデータバイトのみ送るという感じだと解釈しています。
実際にそれで問題無く動作しているのですが、これでも間違えていますか??
何分、英語の解釈が苦手なもので・・・。
にゃ さん
ソースコードなどを全て修正してみました。
ご指摘の通り、私の間違えと勘違いでした。
ご指摘されなかったら、問題無く動作していたので、延々と気付かなかったと思います。
修正はしたものの、まだ間違えていたらご指摘いただければと思います。
まだまだ素人でした。
この度はありがとうございました。
m(_ _)m
ライブラリ無しでの表示、大変興味深く読ませていただきました。
ところで、SPI用の記事は公開されていますでしょうか?
yoshitsune さん
記事をご覧いただき、ありがとうございます。
残念ながら、SSD1306についてはSPI用の記事はございません。
この記事は4年以上前の古い記事で、当時の記憶が大分薄れてきていますが、恐らく、グラフィックが扱いにくいSSD1306をSPIで動かすよりも、SSD1331を使った方が断然扱いやすかったので、SSD1306のSPIはスルーしたんだと思います。
はじめまして.
現在大学生で,初めてのIotデバイス制作を始めたのですが,今回のモニターの制御が難しく,難航していたのですがこの記事に完全に助けられました.
デバイス構成が完全に同じで,すべてが書いてありました.
細かな内部動作まで解説していただきとても感謝しています.
これからも拝見させていただきます!
ありがとうございました.
吾輩は猫さん
かなり古い記事ですが、ご覧いただきありがとうございます。
そう言って頂けるとめちゃめちゃうれしいで~す!!!
今やArduino core ESP32ではらびやんさん作成のLovyan GFXという超優秀なライブラリがあるので、それでESP32やESP8266でSSD1306を制御する方が遙かに高速で高機能ですよ。
ESP32界隈ではそのライブラリが標準になっているので、試してみて下さい。
(^^)
参考になりました。ありがとうございます!
Adafruitのライブラリが結構メモリを食っていたので凄く助かりました。
自分でSSD1306のライブラリを作ってみようと思います。
じゅさん
記事をご覧いただき、ありがとうございます。
久々のコメント投稿で嬉しくなっちゃいました。
ただ、この記事は2017年10月に投稿したもので、素人の手探りでいろいろと穴だらけです。
今やESP32界隈ではらびやんさん作成のLovyanGFXライブラリが洗練されていて超高速表示でおススメですよ~。