2020年7月11日土曜日

複数のOLED(SH1106 128x64 I2C & SPI)をNANOで制御

OLEDシリーズ、第5弾だと思うのですが(-_-;)
やっぱ表示モノは面白いです。こんなに嵌るとは思いませんでした^^;

ちょっと思いつきで、1個のArduinoで複数のOLEDを制御してみました。

まずは、[スケッチの例]のリストの下の方のスケッチ例の
[sh1106_128x64_i2c]を開いて

シリアルを複数のやる時は、
mySerial.begin(9600);
とかで名前を変えられてたので
たぶん、同じ様にいけるんじゃないかな?
一番怪しいのは、この32行目
*********
display(OLED_RESET);

display_01(OLED_RESET);
*********
に変えてみます。

で、スケッチ全体の
「display.」⇒「display_01.」に

こんな風に置換して

いざ、コンパイル!

お~っ!通りました!

動くかな~
書き込んでみると~
動きますね~\(^o^)/

上手くいったので

[スケッチの例]のリストの下の方の
[sh1106_128x64_spi]を開きます。

SPIは、ピン数が多い分、引数も多いです。

実は、最近、SPI I/FのOLEDが届いたのです。
ドライバは、SH1106の128x64です。

スケッチと順番が異なってて配線を間違えそうなので
シルク表示と同じ順番になるように書き換えて、PINもPD8~PD12にします。
SCK:OLED_SCK
SDA:OLED_MOSI
RES:OLED_RESET
DC :OLED_DC
CS :OLED_CS

スケッチのここを書き換えるだけです。

配線して書き込むと、無地動作しました。
BLUEのOLEDで、目視では、真っ青ですが、写真ではかなり白っぽいです。

そのSPI用のExample[[sh1106_128x64_spi]の
フラッシュメモリ、グローバル変数(RAM)の使用量です。

I2C用のExample[[sh1106_128x64_i2c]も
フラッシュメモリ、グローバル変数(RAM)の使用量は全く同じです。
このまま、SPI用とI2C用を合体すると
使用量オーバーは確実のようです。

では、I2CのExampleスケッチから
ロゴ、円、TEXTなどを取り去って、LINEアートだけにして
コンパイル!
ん~ん、フラッシュメモリの使用量はかなり減ったけど
グローバル変数使用量が思ったより減ってないですね~(T_T)

まあ、やってみることにします。
LINEアートだけにしたI2C用のスケッチに
SPI用を追加していきます。

SPI.h:SPI用、Wire.h:I2C用 です。

1つ目のOLEDは、I2Cタイプで、「display_01」にします。

2つ目のOLEDは、SPIタイプで、「display_02」にします。

void setup() は、これです。

最初からsetup()までのスケッチは、これです。
*********
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH1106.h>

#define OLED_01_RESET 4
Adafruit_SH1106 display_01(OLED_01_RESET);

#define OLED_02_CLK    8
#define OLED_02_MOSI   9
#define OLED_02_RESET 10
#define OLED_02_DC    11
#define OLED_02_CS    12
Adafruit_SH1106 display_02(OLED_02_MOSI, OLED_02_CLK, OLED_02_DC, OLED_02_RESET, OLED_02_CS);

void setup()   {
  // by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
  display_01.begin(SH1106_SWITCHCAPVCC, 0x3C);  // initialize for OLED_01 I2C
  display_02.begin(SH1106_SWITCHCAPVCC);  // initialize for OLED_02 SPI
}
*********

void loop() 内は、exampleのスケッチの一部を単純に
display_01 と display_02 に同じものを交互に描画するだけです。

まずは、コンパイルだけ
おっ!グローバル変数は、まだ余裕があります\(^o^)/

書き込んでみると~
お~~っ!意外やあっけなく動きました\(^o^)/
今回はArduino NANOにしております。
コンパクトにOLEDメーターができそうです。

ちなみに、その後、わかったのですが
SPI.h:SPI用、Wire.h:I2C用 がなくても動きました。
OLED制御分までAdafruitライブラリに含まれてるんですね。
まあ、メモリ使用量は変わらないので、お守りに入れておいてもいいでしょう(-_-;)

所が~~~
display_01には、ラインアートだけ
display_02には、TEXTだけを表示させようとしたら、
後で思うに、何でこんな行に追加したんだろう(-_-;)
display_01にライン1本描画する毎に
display_02にTEXTを出したかった気もするが・・・(-_-;)
*********
void loop() {

//* for文の後に置いても、No.1にも表示される
  display_02.setTextSize(1); 
  display_02.setTextColor(WHITE);
  display_02.setCursor(80, 1);
  
  display_01.clearDisplay();
  display_02.clearDisplay(); 

  for (int16_t i=0; i<display_01.width(); i+=4) { //・・・A
    display_01.drawLine(0, 0, i, display_01.height()-1, WHITE);
    display_02.print("No.2 SPI"); 
    display_01.display();
    display_02.display(); 
  }

  for (int16_t i=0; i<display_01.height(); i+=4) {//・・・B
    display_01.drawLine(0, 0, display_01.width()-1, i, WHITE);
    display_01.display();
  }
  delay(250);
}
*********

display_01:左、、display_02:右 ですが
左にもTEXT「No.2 SPI」が描画されて、
右には、上のスケッチ「・・・A」のforルーチンのラインが描画されるのです(T_T)
「・・・B」のforルーチンのラインは左だけに描画されます。

そこでTEXT表示ルーチンを「・・・A」のforルーチンの外に出し後に置くと
*********
void loop() {

  display_01.clearDisplay();

  for (int16_t i=0; i<display_01.width(); i+=4) { //・・・A
    display_01.drawLine(0, 0, i, display_01.height()-1, WHITE);
    display_01.display();
  }

  display_02.setTextSize(1); //★ FONT Size x1
  display_02.setTextColor(WHITE); //★
  display_02.setCursor(80, 1); //★

  display_02.clearDisplay();
  display_02.print("No.2 SPI"); //★・・・どの場所においてもNo.1にもTEXTが表示される
  display_02.display(); //★

  for (int16_t i=0; i<display_01.height(); i+=4) { //・・・B
    display_01.drawLine(0, 0, display_01.width()-1, i, WHITE);
    display_01.display();
  }
  delay(250);
}
*********

ラインアートのスケッチ「・・・A」部は、
右:display_02には描画されなくなりました。

が、右:display_02 だけのTEXT「No.2 SPI」描画のはずが
左:display_01にも「No.2 SPI」が描画されるのです(T_T)
ラインアートのスケッチ「・・・B」部は、右:display_02には描画されません。
もしかして「print()」命令は、複数対応できないのかな~(T_T)

ここまで来ると何とかしたいですね~
気分転換してもうひと頑張りします^^;
・・・そうだ!左のdisplay_01にもTEXTを描画してみよう
*********
void loop() {

  display_01.clearDisplay();
  display_01.setTextSize(1);
  display_01.setTextColor(WHITE);
  display_01.setCursor(80, 30);
  display_01.print("No.1 I2C");
  display_01.display();

  for (int16_t i=0; i<display_01.width(); i+=4) {
    display_01.drawLine(0, 0, i, display_01.height()-1, WHITE);
    display_01.display();
  }

  display_02.clearDisplay();
  display_02.setTextSize(1);
  display_02.setTextColor(WHITE);
  display_02.setCursor(80, 1);
  display_02.print("No.2 SPI"); 
  display_02.display();

  for (int16_t i=30; i<64; i+=2) {
    display_02.drawLine(0, i, 127, i, WHITE);
    display_02.display();
  }
*********

上手くいきました\(^o^)/
最初からdisplay_01とdisplay_2を分ければ良かったんですね~
このスケッチ全体は、Google Driveのここ
「MultiOLED_I2C_SPI_D.ino」です。

最初に入れたTEXTの位置が悪くて、ちょっと手こずりましたが(-_-;)
本命の、1つのArduinoで2つのメーターをやりたいと思います。
まずは、この時やったVolt MeterをI2C-typeでは、
メモリ使用量は、これです。

次は、その後やったタコメーターをSPI-typeでやると、
メモリ使用量は、これです。
単純に足し算されると完全にメモリオーバーです。

上の2つを夫々、display_01、display_02にして
変数名が被らないように修正して、単純に合体します。
ぎりぎりでもいいからオーバーしないことを祈って、コンパイル!
おっ!オーバーしてないです。これはいけますね~

いざ、書き込み!・・・・・・動きました!
NANOの入力pinを指で触るとメーターが振れるので
正常に動作してます\(^o^)/

1個のArduino NANOで、2個のOLED(I2CとSPI)を制御できました。
NANOだとボードも小さいのでコンパクトに納められそうです。
上記のようなメーターであれば、3個までメモリはもちそうですね~
スケッチは、Google Driveのここ
「MultiOLED_I2C_SPI_Volt_Tacho_Meter_A.ino」です。

USBは書き込み時しか使わないので
同じATmega329P328Pチップが乗ってるProMiniの方が小さくできそうですね~
NANOの価格とほぼ同じですが、この辺りをポチっておきます^^;
US$ 1.25(21% OFF、送料 $ 0.62)



書き込み用のUSB~Serial変換器は、使い慣れたCH340Gのをポチッ!
US$ 0.82(10% OFF)

こうなると、3つやってみたくなりました^^;
I2Cは、チップ抵抗を移動してアドレス変えないといけないので
SPI 2個とI2C 1個でやろうと思います。
上の2個の時のVolt MeterとTachometerに加え
3個目にもVolt Meterを追加しました。
コンパイル!
お~っ!2個の時からフラッシュメモリ 73 ⇒ 81%と
グローバル変数 69 ⇒ 71%!思ったより増加が少ないですね~^^;

で、2個のSPI typeの配線は、
CSだけOLED毎に独立させればいいかとやったのですが、
 display_0x.begin(SH1106_SWITCHCAPVCC);
を最後にやったSPI OLEDだけ描画され、他方のSPI typeは真黒なのです。
DCも分離してみますが、ダメです(T_T)

ん~ん SPIができないはずはない、Google先生に聞いてみると
ここにSPI OLED 2個の結線図がありました。
眺めると、まさかのRESETが独立配線になってます(・o・)

で、結局、CSとRESETの2本を独立に配線にしたら
3個で動きました\(^o^)/
左 WHITE:I2C type(display_01)
右 BLUE:SPI type(display_02)
中 BLUE:SPI type(display_03)
中央のは表示だけ「A」で中身は電圧計です^^;
スケッチは、Google Driveのここ
「MultiOLED_I2C_SPIx2_Volt_Tacho_Meter_A.ino」です。

配線はこれです。
SPIのOLEDは、Fritzingパーツがないですね~
仕方ないのでGIMPでパーツ作って、パワポで作図です(T_T)

ここまできたら、調子に乗って限界まで!
4個いけそうです^^; 同じく電圧計スケッチを追加して
2 ⇒ 3 ⇒ 4個で
フラッシュメモリ 73 ⇒ 81 ⇒ 88%
グローバル変数 69 ⇒ 71 ⇒ 74%
この調子だと、5個でも96%と77%辺りと推測され、
NANOの端子もD6、D13、A0、A7空いてるので5個までいけるでしょう。
まあ、メモリ的にもI/O的にも、限界は、5個ってことですね。

反応は若干遅れ気味です。0.2~0.3秒辺りのようです。
一応、スケッチはGoogle Driveのここ
「MultiOLED_I2C_SPIx3_Volt_Tacho_Meter_A.ino」です。

えいっ!ここまできたら5個だ!
また電圧計スケッチを追加してコンパイル!
さすがに「メモリが少なく・・・」の警告がでましたが、
予想に近い使用量です。
2 ⇒ 3 ⇒ 4 ⇒ 5個で
フラッシュメモリ 73 ⇒ 81 ⇒ 88 ⇒ 95%
グローバル変数 69 ⇒ 71 ⇒ 74 ⇒ 80%
6個は無理ですね~^^;

5個並べるのも大変でしたが、圧巻ですね~
」「」「」はフェイクで、中身は全部電圧計「」と同じです^^;
さすがに応答速度が0.5secに迫る遅さです(-_-;)
もはやオブジェ化してきましたが、スケッチは同Google Drive
「MultiOLED_I2C_SPIx4_Volt_Tacho_Meter_A.ino」です。

ちょっと、loop()1周の最後にPIND命令で
オンボードのRXとTX LEDをトグルしたら
5個の全部の更新にほぼ0.43secですね~

配線図は、パスです(-_-;)
最後に、5個での反応速度を動画で!

その後、Multi OLEDを調べていると、GitHubのここ
I2Cの高速Read/Writeライブラリかな?を見つけました。

更にGitHubのこっちには、それを使って
I2Cの同じアドレスでも8台まで同時に接続できるライブラリがありました。
0x3Cと0x3Dのアドレスであれば、夫々8台で全16台のOLEDを制御できるようです。
しかも9wire・・・・8台で9本かな?


ちょっと忘備録、
実は、Exampleのラインアートの「display.width()」の意味がわからず
探してるとこの「SH1106 Class Reference」ってのが見つかりました。
adafruit_sh1106ライブラリにかなり近いようです。
更に同サイトのここに詳細なリファレンスがあります。

2 件のコメント:

昔青年 さんのコメント...

OLEDの百花繚乱!これでどうするの!?て感じですね。
早いとこ、CNCのアナログコックピット風回転計に仕上げてください(笑)

マーティーの工房日誌 さんのコメント...

そうなんですよね~
「これでどうするの~」本人も重々感じておりま~す(-_-;)
雨が続いて気力が出ないこの頃であります(T_T)