OLED 0.96インチで遊ぶ (1) - SSD1306
aitendoで0.96インチのOLED(有機EL)モジュール「M096P4W(BL)」を購入しました。
128x64ドットの領域に絵を表示したりして楽しく遊べています。
Raspberry PiからPerlを通して動作させたので使い方を纏めてみました。
主にコントローラーIC「SSD1306」の制御方法についてです。
初めに、基板の端子は下の写真の向きで左から
「SDA(I2C)」「SCK(I2C)」「GND」「VCC」
の並びになっています。
これをRaspberry Piの各Pinと接続します。
Pin1(3.3V) - VCC
Pin3(SDA) - SDA
Pin5(SCL) - SCL
Pin9(GND) - GND
写真は秋月電子のブレッドボード接続キットを通して接続しています。
「SSD1306」のデータシートは、下記のサイトよりpdfをダウンロードしました。
https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf
以下、データシードから抜粋しながら記載していきます。
■ I2Cフォーマット
「SSD1306」にアクセスするI2C通信のフォーマットは下記のようになっています。
スレーブアドレス(0x3c)の後に、コントロールバイト、データバイトの順に送信します。
コントロールバイトには「Co (Continuation bit)」と「D/C# (Data / Command Selection bit)」の2ビットが含まれています。
「Co=1」で送信した時は、データバイトの次に再度コントロールバイトを送る必要がありますが「Co=0」で送信した時は、データバイトのみを連続して送ることができます。
「D/C#」は、データバイトがコマンドかデータかの区別で、コマンドは設定レジスタ、データは画像データと考えて良いと思います。
Raspberry PiからI2C通信を行うには「Raspberry Piの設定」の「I2C」を有効にして再起動しておきます。
「I2C」が有効になっているとターミナルから「i2cset」コマンドでI2C通信を行うことが可能になります。
$ i2cset -y 1 0x3c \ [コントロールバイト] \ [データバイト] \ [コントロールバイト] \ [データバイト] \ i
最後の「i」を入力するまでを「SSD1306」に送信してくれます。
例えば、下記のコマンド表は、画面の明るさを調整するコマンドですが、これを設定してみます。
「Co=1」 で送信すると、データバイトの次に再度コントロールバイトを送信する必要があります。
#!/usr/bin/perl -w $cmd = 'i2cset -y 1 0x3c '; $cmd .= '0x80 '; # コントロールバイト Co=1, D/C#=0 $cmd .= '0x81 '; # データバイト 明るさ設定 $cmd .= '0x80 '; # コントロールバイト Co=1, D/C#=0 $cmd .= '0xff '; # データバイト 明るさ最大値 $cmd .= 'i'; system($cmd);
「Co=0」で送信すると、データバイトを連続して送信することができます。
#!/usr/bin/perl -w $cmd = 'i2cset -y 1 0x3c'; $cmd .= '0x00 '; # コントロールバイト Co=0, D/C#=0 $cmd .= '0x81 '; # データバイト 明るさ設定 $cmd .= '0xff '; # データバイト 明るさ最大値 $cmd .= 'i' system($cmd);
特に理由が無ければ常に「Co=0」で送信していて問題無いと思います。
■ 画面構成
画面の構成ですが、「SSD1306」は縦8画素x横128画素を1ページとして、8ページ構成になっています。
ページ(縦位置)とカラム(横位置)を指定してから、コントロールバイトの「D/C#」へ「1」をセットして画像データを送信することで、指定した座標から画像が表示されます。
縦8画素はちょうど1Byteなので、1ByteのうちLSBが画面上側、MSBが画面下側となっています。
■ アドレッシングモード
画像データを送信して、表示する方法として3種類のモード(アドレッシングモード)があり、コマンド「0x20」の後に送る値で指定します。
「0x00」Horizontal Addressing Mode
右方向に表示が進み、指定したカラム位置まで進むと次のページへ進みます。
「0x01」Vertical Addressing Mode
縦方向に表示が進み、指定した最終ページまで進むとカラム位置が右に1画素進みます。
「0x02」Page Addressing Mode
Horizontalと同じく右方向に進みますが、次のページには進まずに同じページ内でループします。
HorizontalモードとVerticalモードでは、カラム位置の指定はコマンド「0x21」で指定し、ページ位置は「0x22」で指定します。
「0x21」の後に送る1Byte目で開始カラム位置、次の1Byteで最終カラム位置を0~127の範囲で指定します。
「0x22」の後に送る1Byte目で開始ページ位置、次の1Byteで最終ページ位置を0~7の範囲で指定します。
Horizontalモードで、0x21 0x02 0x7d 0x22 0x01 0x06 と送ることで、このような順序で表示されます。
Pageモードでは、座標指定の方法が変わります。
「0xB0」~「0xB7」の1コマンドでページ位置を指定します。
カラム座標は、開始位置のみの指定となります。
「0x00」~「0x0F」で開始位置の下位4Bitを指定し、「0x10」~「0x1F」で上位4Bitを指定します。
(カラムは0~127なので、7Bitで良いはずなのですが、データシートは8Bit指定になっています)
以上がアドレッシングモードについてですが、座標指定に関してデータシート上はPageモードとHorizontal/Verticalモードで個別の指定方法が記載されていますが、実使用としてはモードに関係無くどちらの座標指定でも可能なようです(笑)
個人的にはHorizontalモードが一番使いやすかったのでこれだけで十分かなぁ、と思っています。
■ コンフィグレーション
pdfの下の方にあるアプリケーションノートにコンフィグレーションのフローが載っていました。
パネルの設定などを行っていますが、このモジュールはデフォルトの状態で使用できるので下2つの「チャージポンプ設定」と「表示On」のみ設定すれば画面が表示されます。
チャージポンプの設定はアプリケーションノートに記載されています。
下記のコマンドで、チャージポンプと画面表示を有効にしてみます。
#!/usr/bin/perl -w $cmd = 'i2cset -y 1 0x3c '; $cmd .= '0x00 '; # コントロールバイト Co=0, D/C#=0 $cmd .= '0x8d '; # チャージポンプ設定 $cmd .= '0x14 '; # チャージポンプOn $cmd .= '0xaf '; # 画面表示On $cmd .= 'i'; system($cmd);
SSD1306内の画像データRAM(GDDRAM)が初期化されていないためノイズのような画面が表示されました。
これをHorizontalモードで初期化してみます。
#!/usr/bin/perl -w $cmd = 'i2cset -y 1 0x3c '; $cmd .= '0x00 '; # コントロールバイト Co=0, D/C#=0 $cmd .= '0x20 '; # アドレッシングモード指定 $cmd .= '0x00 '; # Horizontalモード $cmd .= '0x21 '; # カラム指定 $cmd .= '0x00 '; # 開始位置(0) $cmd .= '0x7f '; # 終了位置(127) $cmd .= '0x22 '; # ページ指定 $cmd .= '0x00 '; # 開始ページ(0) $cmd .= '0x07 '; # 終了ページ(7) $cmd .= 'i'; system($cmd); # ゼロで初期化 $cmd = 'i2cset -y 1 0x3c '; $cmd .= '0x40 '; # コントロールバイト Co=0, D/C#=1 $cmd .= '0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 '; # 8バイト $cmd .= '0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 '; # 8バイト $cmd .= '0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 '; # 8バイト $cmd .= '0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 '; # 8バイト 合計32バイト $cmd .= 'i'; system($cmd);
画像データを32バイト送信したので左上から右に32画素クリアされました。
ちなみに1度の送信でデータバイトは32バイトまでしか受け付けてくれないようで、33バイト以上だと下記のエラーが発生しました。
Error: Too many arguments! Usage: i2cset [-f] [-y] [-m MASK] [-r] I2CBUS CHIP-ADDRESS DATA-ADDRESS [VALUE] ... [MODE]
これは、i2cset コマンドの制限のようで、PICなどのマイコンから制御する際は33バイト以上でも問題なく送信できました。
32バイト送信を4回繰り返して1ページ初期化。
更に7回繰り返して1画面全体を初期化することができます。
次に、ある特定の位置に表示を行ってみます。
3ページ目の30カラムから40カラムにかけて塗りつぶしてみます。
#!/usr/bin/perl -w $cmd = 'i2cset -y 1 0x3c '; $cmd .= '0x00 '; # コントロールバイト Co=0, D/C#=0 $cmd .= '0x20 '; # アドレッシングモード指定 $cmd .= '0x00 '; # Horizontalモード $cmd .= '0x21 '; # カラム指定 $cmd .= '30 '; # 開始位置(30) $cmd .= '39 '; # 終了位置(39) $cmd .= '0x22 '; # ページ指定 $cmd .= '3 '; # 開始ページ(3) $cmd .= '3 '; # 終了ページ(3) $cmd .= 'i'; system($cmd); $cmd = 'i2cset -y 1 0x3c '; $cmd .= '0x40 '; # コントロールバイト Co=0, D/C#=1 $cmd .= '0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff '; $cmd .= '0xff 0xff '; $cmd .= 'i'; system($cmd);
一部分だけ塗りつぶすことができました。
このように座標位置の送信→画像データの送信の繰り返しで表示を行います。