google-site-verification: google3bd66dd162ef54c7.html
FC2ブログ

1.3インチOLED をArduinoで使う(ライブラリの選定)

◆まえがき
1.3インチOLEDの話の続きです。 電源ピンの配列の問題も修正でき、デモプログラムによる動作確認も終わったので、いよいよプログラムから使ってみます。

1.3インチOLEDヲArduino NANOに接続
動作確認中の1.3インチOLEDです。下にはArduino NANO があります。

◆u8glibなんて使いたくない
0.96インチのOLEDのコントローラーはSSD1306でしたが、1.3インチOLEDのコントローラーはSH1106なのでそのままでは使えません。

デモプログラムは u8glib の Example を使ったのでそのまま u8glib を使えば良さそうなものですが、私はu8glibのあのプログラムの書き方、つまり表示ルーチンを do while ループの中にまとめ、nextPage() で判定して繰り返す書き方になじめないので、出来れば使いたくないです。

◆Adafruit_SH1106
そんなことで、これまで通り Adafluit のライブラリ(Adfuit_GFX)を使いたいのですが、SH1106用のドライバは出ていないようです?。

調べてみると、wonho-maker さんが GitHub に登録してくれている Adafruit_SH1106というライブラリ があったので、これを使ってみることにしました。ちなみにこのライブラリは Arduino IDE のライブラリマネージャからは出て来ませんでした。

◆プログラムの例
少し長いですが実際に使っている状態で説明した方が判り易いので全体が見える形で掲載します。なお、プログラムの中身はこの記事に掲載したマンデルブロ集合の表示プログラムです
/* 1.3インチOLED(SH1106)にマンデルブロ集合を描く write mandelbrot set on 1.3inch OLED(SH1106)
20200604MandelBrotSH1106.ino
2020/06/04 ラジオペンチ http://radiopench.blog96.fc2.com/
*/

#include <Wire.h>
#include <Adafruit_GFX.h>
//#include <Adafruit_SSD1306.h>
#include <Adafruit_SH1106.h> // use SH1106

#define zRatio 2.0 // ズーム比率 zoom up ratio

//Adafruit_SSD1306 oled(128, 64, &Wire, -1); // device name is oled
//Adafruit_SH1106 oled(4); // use SH1106
Adafruit_SH1106 oled(-1); // use SH1106

struct body { // 条件表の構造 structure of table
int s_pN; // プログラムNo. prog. No.
float s_x; // X座標 x position
float s_y; // Y座標 y position
float s_mag; // 初期倍率(表示サイズ)initial plane size
int s_zN; // ズーム回数 zooming times
int s_iN; // イタレーション回数 iteration times
};

struct body recipe[10] = { // 表示レシピの設定表 spec. table for display
{ 1, -0.65, 0.0, 0.0625, 4, 100}, // 全体像 over view of mandelbot set
{ 2, -0.22255579, 1.119984429, 1.0, 19, 100}, // 本体上部のエンドレスなヒゲ endless whisker
{ 3, -1.2556, 0.380974, 1.0, 7, 100}, // 隠れた集合hiddon shape
{ 4, -1.73959677, 2.562320E-4, 100.0, 9, 100}, // ワーム warm
{ 5, -1.941239, 0.0, 0.5, 10, 100}, // 左端付近に隠れた集合 flower and mandelblot set
{ 6, -1.7489405, 0.0, 2000.0, 5, 100}, // 魚の骨と花 fish bone and flower(high mag)
{ 7, -1.26233923, 0.40812936, 2.0, 9, 100}, // 左の丸の上に隠れた集合 hidden set
{ 8, 0.269194959, 0.00440711, 1000.0, 5, 100}, // アンモナイトAmmonite
{ 9, 999.9, 0.0, 1.00, 1, 100} // 終了 end definition
};

void setup() {
pinMode(12, INPUT_PULLUP); // 一時停止スイッチ pause switch (pause when connect to GND)
pinMode(13, OUTPUT); // 計算中表示LED calculation status display LED
Serial.begin(115200);
// oled.begin(SSD1306_SWITCHCAPVCC, 0x3C); // OLEDのI2Cアドレス(モジュールの設定に合わせる)set I2C address for the module
oled.begin(SH1106_SWITCHCAPVCC, 0x3C); // use SH1106

uuPinOutputLow(0b00111111111100, 0b001111); // 空き端子処理 unused pin handling
startScreen(); // 開始画面表示 display start screen
}

void loop() { // main
int px, py; // 画面の作画座標 screen address
float x0, y0; // マンデルブロ空間の中心位置 center address of screen
float mag, pitch; // サイズ display size variable
float x, y; // マンデルブロ判定座標 mandelbrot judgement position
int pN; // プログラム番号 program No.
int mResult; // マンデルブロ領域判定結果 mandelbrot judgement result
int zN; // ズーム回数 zooming Number
int col; // 表示色 dosplay color
int iN; // イタレーション回数 iteration number

for (int i = 0; i < 10; i++) { // 設定されたパターンを読み出して順に実行 execute the pattern in sequence
pN = recipe[i].s_pN; // プログラムNo. program nunber
x0 = recipe[i].s_x; // X座標 x position
if (abs(x0) >= 900) { // もし値が900以上なら if the value over 900
break; // 中止(最初に戻る) end this loop and return begginig
}
y0 = recipe[i].s_y; // Y y position
mag = recipe[i].s_mag; // 初期倍率(サイズ)initial magnification(size)
zN = recipe[i].s_zN; // ズーム回数 zooming number of times
iN = recipe[i].s_iN; // イタレーション回数 iteration numbers

oled.clearDisplay(); // 先頭で画面を全クリア clear display
writeBorder(); // 外形枠を描く write border graphic area

for (int z = 0; z <= zN; z++) { // 指定のズーム回数繰り返す execute specified timescount
pitch = 1.0 / (30.0 * mag); // y軸サイズ=±30画素(61画素)pitch value calc.
Serial.print(pN); Serial.print(", "); Serial.println(mag, 2);
paraDisp(pN, z, x0, y0, mag); // display numeric informations
oled.display();

for (py = 1; py <= 61; py++) { // Y軸画面サイズ(1-61) display position of x
for (px = 40; px <= 126; px++) { // X軸画面サイズ指定(40-126) display position of y
x = (83 - px) * pitch - x0; // calculate the position value
y = - (31 - py) * pitch - y0;
mResult = mandelbrot(x, y, iN); // マンデルブロ判定 Mandelbrot judgement
if (mResult == 0) { // 内側なら if inside of the area
col = BLACK; // 黒で塗る paint black
} else { // 外側なら if outside and,
if ((mResult % 2) == 0) { // 判定結果が偶数なら、if result is even number
col = WHITE; // 白!paint white
} else { // そうでなかったら、odd number, so
col = BLACK; // 黒!paint black
}
}
oled.drawPixel(px, py, col); // 画面にプロット plot result on OLED
} // x loop completed
oled.display(); // 実際の表示(1行ずつ更新)display execute (line by line)
}
delay(1000); // 次の面の開始までちょっと待つ wait
while (digitalRead(12) == LOW) { // ピン12がLOW(ONだったら)if halt switch is ON
} // wait
mag *= zRatio; // 拡大 zoom up next show
}
delay(2000); // プログラムの境で長めに待つ long wait between program
} // end of main
}

int mandelbrot(float a, float b, int maxN) { // マンデルブロ領域判定, Mandelbrot area judgment
float xM = 0.0, yM = 0.0; // xMemory,yMemory
float x1, y1; // temporary register for calc.
for (int n = 1; n < maxN; n++) { // 指定回数までチェック, check up to specified times
digitalWrite(13, HIGH);
x1 = xM * xM - yM * yM - a; // element of calc.
y1 = 2.0 * xM * yM - b;
digitalWrite(13, LOW);
if ( x1 * x1 + y1 * y1 > 4.0 ) { // 発散していたら, if it was diverging (over 4)
return n; // その回数を返す return the count
}
xM = x1; // 次のループ用に値を保存 Save value for next loop
yM = y1;
}
return 0; // 指定回数内で発散しなければ(収束したとみなす)Returns zero if it dooes't diverge
}

void paraDisp(int i, int z, float x0, float y0, float mag) {
oled.fillRect(0, 0, 37, 64, BLACK); // erase character display area
oled.setCursor(0, 0);
oled.setTextColor(WHITE);
oled.print("P:"); // disp prog. No.
oled.print(i); oled.print(","); oled.println(z);
oled.println("mag:"); // disp magnification
if (mag < 1) {
oled.println(mag, 2); // *.**
} else {
oled.println(mag, 0); // **
}
oled.println();
oled.println("x:"); // x poition
oled.println(x0, 3);
oled.println("y:"); // y position
oled.println(y0, 3);
}

void writeBorder() { // write graphic area border line
oled.drawFastHLine( 39, 0, 43, WHITE); // upper left hrizontal line
oled.drawFastHLine( 85, 0, 43, WHITE); // upper right
oled.drawFastHLine( 39, 62, 43, WHITE); // lower left
oled.drawFastHLine( 85, 62, 43, WHITE); // lower right
oled.drawFastHLine( 37, 31, 3, WHITE); // left center short mark (y0)
oled.drawFastHLine( 127, 31, 1, WHITE); // right center mark (y0)

oled.drawFastVLine( 39, 0, 30, WHITE); // left upper
oled.drawFastVLine( 39, 33, 30, WHITE); // left lower
oled.drawFastVLine(127, 0, 30, WHITE); // right upper
oled.drawFastVLine(127, 33, 30, WHITE); // right lower
oled.drawFastVLine( 83, 0, 1, WHITE); // upper center dot (x0)
oled.drawFastVLine( 83, 62, 2, WHITE); // lower center short line (x0)
}

void startScreen() { // 開始時表示画面
oled.clearDisplay();
oled.setTextColor(WHITE);
oled.println("Mandelbrot set");
oled.println("version 0.7"); // version No.
oled.println("by radiopench1"); //
oled.display();
delay(2000);
}

void uuPinOutputLow(unsigned int d, unsigned int a) { // 指定ピンを出力/LOWに設定(un used Pin set to Output Low)
// ビットパターンで該当ピンを指定(1で有効)。d:D13-D0, a:A5-A0
// PORTx=0, DDRx=1
unsigned int x;
uint8_t oldSREG = SREG; // ステータスレジスタ保存(割込状態保存)*1
cli(); // 割込み禁止*1
x = d & 0x00FF ; PORTD &= ~x; DDRD |= x; // D0-7
x = (d >> 8) & 0x3F; PORTB &= ~x; DDRB |= x; // D8-13
x = a & 0x003F ; PORTC &= ~x; DDRC |= x; // A0-5
SREG = oldSREG; // ステータスレジスタ復元(割込状態復元)*1
}

デバイスを SSD1306からSH1106に入れ替える時に必要なプログラムの変更箇所は行番号で、9, 14, 42の3箇所だけで済みました。これは嬉しいです。なお、変更前の行はコメントアウトした状態で残してあります。

◆動作確認中
0.96と1.3インチOLED
上はこの記事のプログラムを使って1.3インチOLED(SH1106)を動かしている状態。下は0.96インチのOLED(SSD1306)を従来のプログラムで動かしている状態です。

やはり画面が大きいと見易いです。あとこうやって並べると気付くのですが、0.96インチOLEDは表示の色が少し青っぽいです。逆に言うと1.3インチOLEDの方が少し赤っぽいです。あと、1.3インチの方は、白ベタ表示した時にたぶん8ラインをグループとしたストライプ状の明るさムラが少し目立つことがありました。まあ、表示の色などは個体差があるでしょうから参考情報です。

◆まとめ
・wonho-maker さんの Adafruit_SH1106 ライブラリを使うことで、これまで通り Adafruit のライブラリを使うことが出来て良かったです。作者の方に感謝します。

・Adafruit_SH1106ライブラリのコメントに書いてありますが、SSD1306用との大きな違いはフラームメモリの転送の部分だけ、つまり、 oled.display(); の処理だけ修正してあるそうです。他にスクロール処理も無いようです。SH1106の解説にSSD1306互換と書いてあって期待を持たせたりするのですが、どうも完全な互換性は無いようです。

・今回OLEDのライブラリを探してみて判ったのですが、u8glib には u8g2 という新しいバージョンがあり、u8glib の方は既にサポートが終わっているそうです。ここで嬉しいのは、u8g2 では画面を操作したい時に関数を呼ぶスタイルも使えるようになったことです。つまり、これまでは画面表示の処理は全て do while ループの中にまとめて書くことが要求されていて、そんな面倒くさいことやってられないよ、と言う感じだったのですが、そういう制限が無くなったようです。何で最初からそういう仕様にしなかったのか? それには納得できる理由があったので、話の整理が付いたら記事で解説を試みたいと思います。

【追記】
Adafruit_sh1106 はI2Cのクロックが100kHzに設定されるようです。400kHzにするためには追加修正が必要。
関連記事

コメントの投稿

管理者にだけ表示を許可する

カレンダー
06 | 2020/07 | 08
- - - 1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31 -
プロフィール

ラジオペンチ

Author:ラジオペンチ
電子工作を中心としたブログです。たまに近所(東京都稲城市)の話題など。60過ぎて視力や器用さの衰えを感じつつ日々挑戦!
コメントを入れる時にメールアドレスの記入は不要です。なお、非公開コメントは受け付けていません。

記事が気に入ったらクリックを!
最新記事
カテゴリ
最新コメント
リンク
FC2カウンター
検索フォーム
月別アーカイブ
RSSリンクの表示
QRコード
QRコード