共立・シリコンハウスのOLEDディスプレイをSPIで動かす

#OLEDディスプレイ

大阪日本橋にある共立電子の「シリコンハウス」「デジット」は、
言ってみれば関西の秋月電子的なポジションです。
そんなお店で売っている魅力的なデバイスの一つに、
「OLEDディスプレイ」があります。
https://eleshop.jp/shop/g/gDAV411/
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/622321/58e95374-9788-b7ca-494b-029755ecdebb.jpeg" width="600">

コントローラは**WS0010**です。
いくつか種類がありますが、とてもくっきり表示されるのがポイント。
オーソドックスな制御法だと、4bit方式とか8bit方式とか、
まぁ線の多さが目立つわけです。
（べつにこのディスプレイに限った話ではありませんが）

あまりメジャーではありませんが、**SPI通信**で制御する方法があり、
そうすることで配線をかなり減らせます。
ちなみに、SPIでは**10bit**でデータを送る必要があります。
（ArduinoとかであればデフォのSPIは8bitで送受信されるらしい）

OLEDディスプレイ＋SPI化のタッグの良いところは
**1.もともとOLEDディスプレイは表示の更新が早い（複雑な図形は困難）**
**2.配線線が少ない（MOSI、CS、CLKのみ）**
なんといってもこの2つ。

オリジナルで**10bit**送信可能なSPIっぽいプログラムをしている人を見かけたので、
それに習って動作検証しました。
もともと生粋のプログラマではありませんが、
なにかのヒントになれば幸いです。

#デバイスをSPIモードに改造する
デバイス背面のチップ抵抗をいじって、SPIモードにする必要があります。
基本、**購入した際についてくる資料のとおりに改造すれば**SPIモードになります。
今回使用した２０桁ｘ４行タイプは下記のとおりに改造しました。
※SPI改造は自己責任でお願いします

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/622321/c6890b98-42e3-48b1-94e1-5585ff76de7e.jpeg" width="600">

画像はSPI改造後です。赤丸のところを修正してください。
こちらで使用したボードはArduino Micro。
また、２０桁ｘ４行タイプOLEDのピンアサインは下記のとおりにしました。

|1|2|12|14|15|
|:--|:--|:--|:--|:--|
|GND|5V|CLK|MOSI|CS|

下記ソースのとおりに配線して頂ければ動くはず。
動検用のソースなので、だいぶ荒っぽい書き方をしてしまいました笑
あくまで参考までにお願いします笑

```c:oled_display.ino
#include <SPI.h>

#define cs_pin 10
#define clk_pin 13
#define MOSI_pin 11

int spiSend(unsigned int rs, unsigned char data);

void setup()
{
  // put your setup code here, to run once
  Serial.begin(9600); // 9600bpsでポートを開く
  pinMode(cs_pin, OUTPUT);
  pinMode(MOSI_pin, OUTPUT);
  pinMode(clk_pin, OUTPUT);
  digitalWrite(cs_pin, 1);
  digitalWrite(clk_pin, 1);
  delay(100);
  displayWrite(0, 0);
  displayWrite(0, 0);
  displayWrite(0, 0);
  displayWrite(0, 0);
  displayWrite(0, 0);
  delay(10);
  displayWrite(0, 0b00111000);//function set
  delay(10);
  displayWrite(0, 0b00001100);//display set
  delay(10);
  displayWrite(0, 0b00000001);//clear disp
  delay(10);
  displayWrite(0, 0b00000010);//return home
  delay(10);
  displayWrite(0, 0b00000110);//Entry 

  delay(200);
  char chara[4] ={'S','A','K','U'};

  displayWrite(0,0b10000000);//カーソルを1行目トップへ
  displayWrite(1, chara[0]);
  displayWrite(0,0b11000000);//カーソルを2行目トップへ
  displayWrite(1, chara[1]);
  displayWrite(0,0b10010100);//カーソルを3行目トップへ
  displayWrite(1, chara[2]);
  displayWrite(0,0b11010100);//カーソルを4行目トップへ
  displayWrite(1, chara[3]);

  delay(10);

}

void loop()
{
  
}

void displayWrite(unsigned int rs, unsigned char data){
  int i = 10;//10bit send
  rs = int(rs << 9) | int(data);

  digitalWrite(cs_pin, 0);
  while (i--)
  {
    digitalWrite(MOSI_pin, (rs >> i) & 1);
    digitalWrite(clk_pin, 0);
    digitalWrite(clk_pin, 1);
  }
  digitalWrite(cs_pin, 1); 
}
```

※ソース参考　https://sakura87.net/archives/4389

#ディスプレイへのコマンド
ソースを見ていただくと、イニシャライズの際や、
実際に文字表示させている際に得体のしれないコマンドを発行しています。
これは仕様書に書かれている内容を反映したものです。
（英語が苦手な私には苦行でした・・・）

イニシャライズの際は、このコマンド発行の順番も大事なのでしょうが、
ひとまず動いてしまったので公開しています。
また、**displayWrite**関数では**8bit**のコマンドを指定すると、
頭に**2bit**の**RS、R/WB**を追加して送信しています。

##クリアディスプレイ
簡単に言えば、ディスプレイ上の表示をすべて消します。
仕様書には下記のように**10bit**送るよう、記されています。

|RS|R/WB|D7|D6|D5|D4|D3|D2|D1|D0|
|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|
|0|0|0|0|0|0|0|0|0|1|

よってサンプルコードのコマンドで下記を送信します。
**RS→0 　　R/WB→0
DB7~DB0→0b00000001**

ソコソコ時間がかかるコマンドなので、
定常的な表示ではなかなか使わないかも知れませんね。
状態遷移の際等に使うことになると思います。

##Return Home（カーソルをホームへ）
カーソルを0行目0列へ移動させます。
後で記述するSet DDRAM Addressとも関係しますが、
手っ取り早くホームへ戻るならこのコマンド。

|RS|R/WB|D7|D6|D5|D4|D3|D2|D1|D0|
|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|
|0|0|0|0|0|0|0|0|1|0|
**RS→0 　　R/WB→0
DB7~DB0→0b000000010**



##ディスプレイOnOff設定

|RS|R/WB|D7|D6|D5|D4|D3|D2|D1|D0|
|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|
|0|0|0|0|0|0|1|D|C|B|

さて出てきました、設定パラメータD,C,B。
要は設定パラメータをそれぞれ０か１か決めてコマンドを送るわけです。
**D:DISPLAY ON/OFF BIT**
これは簡単。Dが0だとディスプレイが消えます。
**C:CURSOR DISPLAY CONTROL BIT**
デバッグしているときは役に立つかもしれません。
この手のディスプレイには**カーソル**という概念があります。
Microsoft Wordのカーソルとほぼ同じもので、
文字表示コマンドを送ると、カーソルがある場所に表示されます。
言ってみればディスプレイは何処に何を表示させるかを制御しなければならないので、
いまカーソルがどこにあるかってのが見えたほうが、
コーディングしやすいように思います。
不要であれば**0**を指定しOFFにしましょう。

**B:BLINKING CONTROL BIT**
先ほどのカーソルを点滅させることができます。
そのほうが見やすければ**1**を指定してONにしましょう。

##カーソルのシフト、ディスプレイ内容のシフト
カーソルの1桁左右シフト、もしくは
ディスプレイされている内容の全体左右シフトです。

##ファンクション設定

|RS|R/WB|D7|D6|D5|D4|D3|D2|D1|D0|
|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|
|0|0|0|0|1|DL|N|F|FT1|FT0|
ディスプレイの様々な設定をセットします。
フォント、ディスプレイの行など。

###**DL:INTERFACD DATA LENGTH CONTROL BIT**
データを８ビットで送るか４ビットで送るかなんですが、
ぶっちゃけよくわからん・・・
ひとまず**8bit**に設定しています・・・

###**N:NUMBER OF DSPLAY LINE**
OLEDディスプレイが1行か2行かを選べます
2行もしくは4行の場合は**1**に設定してください。

###**F:CHARACTER FONT SET**
表示させる文字のフォントサイズを選びます。
５ｘ８か５ｘ１０の2通りですが、
これはディスプレイの仕様にのっとり５ｘ８を選ぶので、
**0**を設定します。

###**FT1、FT2:FONT TABLE SELECTION**
表示させるフォントの種類を選びます。

|FT1,FT0の設定|キャラクターテーブル|
|:--|:--|
|0,0|英語と日本語のフォントを選択|
|0,1|ウェスタンヨーロピアン１|
|1,0|英語とロシア語のフォントを選択|
|1,1|ウェスタンヨーロピアン２|

**0,0**の組み合わせ以外は使うことがないと思います。。。
と思ったけど顔文字使うならロシア語フォント必要か？！

##DDRAMアドレス設定

|RS|R/WB|D7|D6|D5|D4|D3|D2|D1|D0|
|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|
|0|0|1|ADD|ADD|ADD|ADD|ADD|ADD|ADD|

カーソルを任意の位置へ持っていくときに使えます。
要は7桁のADDが7bitの座標、つまり**128桁分**の座標に相当します。
###1行のディスプレイの場合
左から順番に**0000000、0000001、0000010・・・**となります。
16桁までのディスプレイであれば、
それ以降の座標に文字を設定しても表示はされません。
ただ、表示されずとも設定は可能なので、
例えばスクロールさせる際などに使われることになるでしょう。
###2行のディスプレイの場合
1行目左から**0000000、0000001、0000010・・・**
2行目左から**1000000、1000001、1000010・・・**となります。
アドレスの最大サイズは128桁分と変わらず、
64桁で2つに分解しているということですね。
ちなみに1行目と2行目は**独立しておらず**、
あくまでメモリ上では**128桁**として扱われています。
なのでスクロールしたりすると、行が異なっても全体がスクロールします。
###4行のディスプレイの場合
1行目左から**0000000、0000001、0000010・・・**
2行目左から**1000000、1000001、1000010・・・**
3行目左から**0010100、0010101、0010110・・・**
4行目左から**1010100、1010101、1010110・・・**
これは2行ディスプレイをさらに2分割して4行対応させています。
128桁をきれいに4分割している訳ではないので注意です！
**1行目は20桁
2行目も20桁
3行目は44桁
4行目も44桁**です。
また、メモリのアドレスとしては1行目の次が3行目と飛んでいます。
**1行目→3行目→2行目→4行目**
ですのでご注意ください。
念の為、2行タイプと4行タイプのアドレス一覧表（10進数表記）を載せておきます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/622321/dc1b58be-a08b-a2b4-7caa-1b32e8c22b29.png" width="800">

##文字表示
|RS|R/WB|D7|D6|D5|D4|D3|D2|D1|D0|
|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|
|1|0|WD|WD|WD|WD|WD|WD|WD|WD|
文字表示はRSが**1**なので注意です！
WD8bitを設定することで任意の文字を指定できます。
詳しくは仕様書のキャラクターテーブルをご覧ください。
ただ、アスキーコードと対応している様なので、
Char型でそのまま送れば英数記号は表示できると思います。

##エントリー設定
あんま使わなさそうだから解析保留。
##その他もろもろ
私は使ってないので追々追記します笑