こんばんは。
前回の記事では、ESP-WROOM-02 ( ESP8266 ) で OLED ( 有機EL ) Breakout Board – 16-bit Color 1.5″ w/microSD holder を Adafruit社標準のライブラリを使用する方法を紹介しましたが、そのままでは一切動かず、shiitakeoさんという方がESP8266用に改良されていたので、その方法を紹介しました。
今回はその方法を参考にして、ライブラリを一切使わず、さらにArduino標準のSPIライブラリも一切使わずに OLED ( SSD1351 )をドライブさせる方法をやってみます。
この方法はshiitakeoさんが方法を公開してくださったおかげで、私もこのOLEDの開発ができるわけです。大変感謝しております。m_ _m
(その方が改良された方法はこちらのページをご覧ください。このページはすぐになくなってしまう可能性があります)
そもそも、ESP-WROOM-02 ( ESP8266 )はPORTB などのレジスタ操作はできないので、そういうことを知らずにいろいろなライブラリをインストールしても、ちゃんと動かずにハマってしまいますね。
PORTB などのチップのレジスタ操作は、Arduino UNOなどのAVR系チップのレジスタ操作命令ですので、ESP8266では操作できないわけです。
ということは、Arduino IDE でレジスタ操作をしているものがあれば、それをインクルードしても動作しない可能性があるわけです。
Arduino core for ESP8266 ライブラリはその辺はよく改良されていると思います。
ということで、私の場合はフリーの日本語漢字フォントをこのディスプレイに表示させたいので、
ライブラリを一切使用せずにフォントを表示させてみたいと思います。
1.準備するもの
前回の記事と同様ですが、念のため掲載しておきます。
●Adafruit OLED Breakout Board – 16-bit Color 1.5″ w/microSD holder
スイッチサイエンスさんウェブショップにあります。
https://www.switch-science.com/catalog/1754/
128×128ピクセルの1.5インチのものを使いましたが、128×96ドットの1.27インチのものでも使えると思います。
microSDカードスロット付きですので、いろいろな用途で使えそうです。
ちょっと高価ですが、かなり高性能です。
スイッチサイエンス販売のものを購入しました。
組み立て方法は前回の記事をご参照ください。
●ESPr Developer ( ESP-WROOM-02 開発ボード ) ( ESP8266 チップ使用)
これの組み立て方法や使い方はこちらの記事をご参照ください。
その他、以下のボードもGPIOピンは同じなので使えると思います。
(※ESPr One はAmazonで販売されなくなったようです。以下のリンクはが販売元です。
ESPr® One(Arduino Uno同一形状 ESP-WROOM-02開発ボード)
以上のものはAmazonさんで在庫切れの場合はスイッチサイエンスさんのサイトで直販しております。
●その他、ブレッドボード、ジャンパーワイヤー、WindowsPC、USBケーブル等
2.接続方法
これも前回の記事と同様です。
GPIO #15 —- OC ( OLED CS )
GPIO #14 —- CL ( SCLK )
GPIO #13 —- SI ( MOSI )
GPIO #12 —- SO ( MISO )
GPIO # 4 —- DC ( Data/Command )
GPIO # 5 —- R ( Reset )
3.Arduino IDE に ESP8266ボードをインストール
Arduino IDE は Arduino.ccページの version 1.6.9 が推奨です。
Arduino core for ESP8266 WiFi Chip ボードは2016/7/4現在では2.3.0です。
インストール方法はこちらのページをご参照ください。
4.サンプルスケッチを Arduino IDE に入力
では、サンプルスケッチをArduino IDE に入力してみましょう。
【ソースコード】 (※無保証 ※PCの場合、ダブルクリックすればコード全体を選択できます)
/*This Sample Sketch of Adafruit OLED ( SSD1351 ) is for ESP-WROOM-02 ( ESP8266). * It does not work with Arduino UNO. */ #define sclk 14 #define mosi 13 #define cs 15 const int DCpin = 4; const int RSTpin = 5; //16x16ドット 平仮名「あ」 uint8_t font_a[2][16] = {{0x02,0x02,0x02,0x1f,0x02,0x02,0x03,0x04,0x0c,0x14,0x24,0x23,0x25,0x18,0x00,0x00}, {0x00,0x00,0x60,0x80,0x00,0x40,0xf0,0x48,0x44,0x82,0x82,0x02,0x04,0x18,0x60,0x00}}; //****************セットアップ************************************************* void setup() { pinMode(DCpin, OUTPUT); pinMode(sclk, OUTPUT); pinMode(mosi, OUTPUT); pinMode(RSTpin, OUTPUT); pinMode(cs, OUTPUT); digitalWrite(cs, LOW); digitalWrite(RSTpin, HIGH); delay(500); digitalWrite(RSTpin, LOW); delay(500); digitalWrite(RSTpin, HIGH); delay(500); SSD1351_Init(); delay(100); SSD1351_BlackOut(); uint8_t Red = 31, Green = 63, Blue = 31; //Max Red = 31, Max Green = 63, MaxBlue = 31 Font1x1(0, 0, Red, Green, Blue, font_a[0]); Font1x1(8, 0, Red, Green, Blue, font_a[1]); Font2x2(0, 16, Red, Green, Blue, font_a[0]); Font2x2(16, 16, Red, Green, Blue, font_a[1]); Green = 0; Blue = 0; Font1x1(32, 32, Red, Green, Blue, font_a[0]); Font1x1(40, 32, Red, Green, Blue, font_a[1]); Red = 0; Green = 63; Blue = 0; Font2x2(48, 48, Red, Green, Blue, font_a[0]); Font2x2(64, 48, Red, Green, Blue, font_a[1]); Red = 0; Green = 0; Blue = 31; Font1x1(80, 80, Red, Green, Blue, font_a[0]); Font1x1(88, 80, Red, Green, Blue, font_a[1]); Red = 31; Green = 63; Blue = 0; Font2x2(96, 96, Red, Green, Blue, font_a[0]); Font2x2(112, 96, Red, Green, Blue, font_a[1]); } //****************メインループ************************************************* void loop() { // put your main code here, to run repeatedly: } //****************SSD1351初期化************************************************* void SSD1351_Init(){ writeCommand(0xFD); //Set Command Lock writeData(0x12); //Unlock OLED driver IC MCU interface from entering command writeCommand(0xFD); //Set Command Lock writeData(0xB1); //Command A2,B1,B3,BB,BE,C1 accessible if in unlock state writeCommand(0xAE); //Sleep mode On (Display OFF) writeCommand(0xB3); //Front Clock Divider writeCommand(0xF1); // 7:4 = Oscillator Frequency, 3:0 = CLK Div Ratio (A[3:0]+1 = 1..16) writeCommand(0xCA); //Set MUX Ratio writeData(127); writeCommand(0xA0); //Set Re-map writeData(B01110100); //65k color //writeData(B10110100); //262k color //writeData(B11110100); //262k color, 16-bit format 2 writeCommand(0x15); //Set Column writeData(0); //start writeData(127); //end writeCommand(0x75); //Set Row writeData(0); //start writeData(127); //end writeCommand(0xA1); //Set Display Start Line writeData(0); writeCommand(0xA2); //Set Display Offset writeData(0); writeCommand(0xB5); //Set GPIO writeData(0); writeCommand(0xAB); //Function Selection writeData(0x01); //Enable internal Vdd /8-bit parallel //writeData(B01000001); //Enable internal Vdd /Select 16-bit parallel interface writeCommand(0xB1); //Set Reset(Phase 1) /Pre-charge(Phase 2) //writeCommand(B00110010); //5 DCLKs / 3 DCLKs writeCommand(0x74); writeCommand(0xBE); //Set VCOMH Voltage writeCommand(0x05); //0.82 x VCC [reset] writeCommand(0xA6); //Reset to normal display writeCommand(0xC1); //Set Contrast writeData(0xC8); //Red contrast (reset=0x8A) writeData(0x80); //Green contrast (reset=0x51) writeData(0xC8); //Blue contrast (reset=0x8A) writeCommand(0xC7); //Master Contrast Current Control writeData(0x0F); //0-15 writeCommand(0xB4); //Set Segment Low Voltage(VSL) writeData(0xA0); writeData(0xB5); writeData(0x55); writeCommand(0xB6); //Set Second Precharge Period writeData(0x01); //1 DCLKS writeCommand(0x9E); //Scroll Stop Moving writeCommand(0xAF); //Sleep mode On (Display ON) } //****************全画面消去************************************************* void SSD1351_BlackOut(){ writeCommand(0x15); //Set Column writeData(0x00); writeData(127); writeCommand(0x75); //Set Row writeData(0x00); writeData(127); writeCommand(0x5C); //Write RAM for(int i=0; i<128*128; i++){ writeData(0x00); writeData(0x00); //writeData(0x00); //262k colorの場合3バイト分送信 } } //****************等倍フォント表示************************************************* void Font1x1(uint8_t StartX, uint8_t StartY, uint8_t Red, uint8_t Green, uint8_t Blue, uint8_t* buf){ int16_t i,j; uint8_t RGBbit1, RGBbit2; RGBbit1 = (Red<<3) | (Green>>3); RGBbit2 = (Green<<5) | Blue; writeCommand(0x15); //Set Column writeData(StartX); writeData(StartX+7); writeCommand(0x75); //Set Row writeData(StartY); writeData(StartY+15); writeCommand(0x5C); //Write RAM for(i=0; i<16; i++){ for(j=7; j>=0; j--){ if(buf[i] & _BV(j)){ writeData(RGBbit1); writeData(RGBbit2); }else{ writeData(0); writeData(0); } } } } //****************倍角フォント表示************************************************* void Font2x2(uint8_t StartX, uint8_t StartY, uint8_t Red, uint8_t Green, uint8_t Blue, uint8_t* buf){ int16_t i,j,ii; uint8_t RGBbit1, RGBbit2; RGBbit1 = (Red<<3) | (Green>>3); RGBbit2 = (Green<<5) | Blue; writeCommand(0x15); //Set Column writeData(StartX); writeData(StartX+15); writeCommand(0x75); //Set Row writeData(StartY); writeData(StartY+31); writeCommand(0x5C); //Write RAM for(i=0; i<16; i++){ for(ii=0; ii<2; ii++){//倍角の場合2行同じものを描く for(j=7; j>=0; j--){ if(buf[i] & _BV(j)){ writeData(RGBbit1); writeData(RGBbit2); writeData(RGBbit1); writeData(RGBbit2); }else{ writeData(0); writeData(0); writeData(0); writeData(0); } } } } } //****************SPIデータ処理************************************************* void SPIwrite(uint8_t c){ digitalWrite(sclk, HIGH); int8_t i; //signed intでなければならない。負の数になると255という値になって、例外エラーになる。 for (i=7; i>=0; i--) { digitalWrite(sclk, LOW); if (c & _BV(i)) { digitalWrite(mosi, HIGH); } else { digitalWrite(mosi, LOW); } digitalWrite(sclk, HIGH); } } void writeCommand(uint8_t c) { digitalWrite(DCpin, LOW); digitalWrite(cs, LOW); SPIwrite(c); digitalWrite(cs, HIGH); } void writeData(uint8_t c) { digitalWrite(DCpin, HIGH); digitalWrite(cs, LOW); SPIwrite(c); digitalWrite(cs, HIGH); }
今回のサンプルスケッチはライブラリを一切インクルードしません!!!
SPI.hもインクルードせずに動きます。
●5-9行:
ESP-WROOM-02 ( ESP8266 ) のGPIO ピンを定義します。
#define と const int の2種類を使っている理由は、本当はconst int だけを使いたかったのですが、#defineを使わないとうまく動かなかったので、そうしてます。
●12-13行:
東雲フォントの「あ」という文字のビットを2次元配列に格納しました。BDFデータなので、文字の左側半分と右側半分と8bitで分けてます。
東雲フォントの詳細はこちらのページを参照してください。
●17-30行:
セットアップ内のここでGPIOをOUTPUTに設定し、SSD1351をリセットしています。
●32行:
ここで、68-117行の関数を呼び出し、SSD1351を初期化してます。
ここの設定はAdafruit社のSSD1351ライブラリを参考にしました。
●36行:
119-132行の関数を呼んで、ブラックアウトしてます。
●38-59行:
SSD1351のカラー設定を65kにしていますので、データシートによると、Redは5bit、Greenは6bit、Blueは5bit、となっているので、最大値はそれぞれ31, 63, 31 となります。つまり、ゼロも含めると、32×64×32=65536色となります。
262k設定もできるのですが、通信バイト量が増えて速度が遅くなるので、文字表示の場合は65kカラーで十分です。
Font1x1関数は16×16ドット等倍表示
Font2x2関数はそれを2倍角にした表示です。詳細は後述します。
●68-117行:
ここで、SSD1351を初期化してます。
これはAdafruit社のライブラリを参考にしました。
データシートを参考にしてコメントも載せましたが、未だに分からないところがあります。例えば、Clock Dividerとか・・・。
起動プロセスはSSD1306のようにフローチャートで出ていなかったので、順番はある程度変わっても大丈夫だと思います。
ここではwriteCommad関数やwriteData関数を使っていますが、それは後述します。
画面の明るさやコントラストは103-108行で設定してます。
赤、緑、青のドットそれぞれ別々にコントラストを調整できます。
それとマスターコントラスト調整ができるので、かなり微調整ができます。写真を表示するのに有り難い機能ですね。さすが高いだけあります。
その他の詳しい動作はSSD1351のデーターシートで確認してください。
データシートはスイッチサイエンスさんのページからPDFでダウンロードしてください。
●119-132行:
初期化の後、ここで全画面黒にします。
Set Column コマンドで座標xの原点を指定し、幅を127とし、Set Rowコマンドで座標yの原点を指定し、高さを127としてして、RAM書込みコマンドを指定します。
このOLED の座標は以下のようになります。
左上の隅が原点(0,0) となります。
以前の記事のOLED1306とは異なり、理解しやすい原点ですね。
その後、writeData関数を2回連続でゼロのビットを送信すると1ドットが黒く塗られるわけです。この2バイトの中にRGBの色成分のデータが入っています。この詳細は後述します。
●134-159行:
16×16ドットフォントを表示する関数です。
これはSSD1306のようにページという概念が無いので、とても簡単にピクセルを指定できるのがSSD1351の良いところです。
今回は1つのピクセルに65kカラー指定なので、それを2バイトに盛り込む設定となります。262kカラーの場合は3バイトで設定したりしますが、文字表示はスピードが命なので、最低の65kカラーとします。137-138行のRGBbit1とRGBbit2の2バイトにRGBカラーを設定します。R(赤)は最大31、G(緑)は最大63、B(青)は最大31という設定になります。これはデータシートのTable8-8を参照してください。
140-145で始点と幅、終点と高さを決めています。
146行でRAMに書き込んでいます。
148-158行でピクセル毎にカラーを指定して書き込んでいます。 buf[i] & _BV(j) ではビットを1つ1つ調べて1ならばカラーを送信。ゼロならばゼロを送信して黒にしてます。
ここはbitRead()関数を使ってもいいかもしれません。
●161-192行:
ここでは倍角に表示させるための関数です。
先の等倍表示にそれぞれ同じビットを2回送信させるだけで比較的簡単に倍角表示ができました。これはSSD1351のいいところです。
●194-220行:
実際にSSD1351にSPI通信を行う関数です。
Arduino 標準ライブラリのSPI.hが使えないので、こういう感じになります。
writeCommand関数内でDCpinをLOWにするとコマンド書き込みモードになります。 HIGHにするとデータ書き込みモードになります。
SPI通信ではcs(SS)ピンもLOWにします。
その後、SPIwrite関数でなぜかクロックのsclkピンをHIGHにせねばなりません。これがちょっと理解できないでいます。
ただ、データシートのFigure8-5ではsclkがHIGHの時に1バイト送るようなことが書いてありましたのでそういうことなのでしょう。
その後もArduino標準関数のSPI.hが使えないので、GPIOのmosiピンのHIGH、LOWを高速で切り替えて送信しなければなりません。ということで、c & _BV(i) で1バイト中のビットを1つ1つ調べて1ならばHIGHを、ゼロならばLOWにしてSPI通信を成り立たせています。
これを考えたshiitakeoさんには頭が下がります。素晴らしいです。私ならライブラリをそのまま使って、動かなかったら諦めてしまいますね。
以上です。
では、これをコンパイル書き込みしてみてください。
こんな風に表示されれば成功です。
ピクセル毎に色を指定できるので、いろいろと使えそうです。
しかも、RGBそれぞれにコントラストが設定できるので、さらに面白い事ができそうです。
では、次回はUTF-8文字列をShift_JISに変換して文字列をズラッと表示させてみたいと思います。
ではまた・・・。
Amazon.co.jp 当ブログのおすすめ
コメント
EasyWebSocketのライブラリで
Wi-FiをsoftAPにする方法を教えてください。
生田さん
ブログをご覧いただき、ありがとうございます。
以前、他の方からも同じリクエストがあったので、やってみたいと思います。
今、テスト中ですのでしばらくお待ちください。
ソフトAPに対応してみました。
現在のバージョンはbeta 1.39 です。
こちらのページをごらんください。