2018年1月4日木曜日

[SSD1331]SPI初期化処理

OLEDを使用するには、専用ライブラリを用いるのが一般的ですが、どのような働きをしているのか知らないまま使っている事に疑問を感じたので、調べてみる事にしました。

使用するのは、
の3つです。
Pmod OLEDrgbは、名前のままでPmodポート用のものです。
機能は下記
  • 96×64 pixel RGB OLED screen
  • 0.8“ x 0.5” グラフィックディスプレイ
  • 16ビットカラー解像度
  • 2つの低電力ディスプレイシャットダウンモード
  • SPIインタフェース付き12ピンPmodコネクタ
  • ロジックレベル 3.3v
ロジックレベル3.3v動作なので、Arduino UNO R3の場合ロジックレベルコンバータなどを使用する必要があるかもです。私が使っているUNOは5v、3.3vのレベル変換スイッチが付いてるので、3.3vに切り替えて使用しています。

 Pmod OLEDrgbのピン配置
1
CS
2
MOSI
3
NC
4
SCK
5
GND
6
VCC
7
D/C
8
RES
9
VCCEN
10
PMODEN
11
GND
12
VCC

Arduino UNO R3との配線
Arduino UNO R3 Pin Pmod OLEDrgb Pin
SCK-13
- 4:SCK
MISO-12

MOSI-11
- 2:MOSI
SS-10 - 1:CS
9 - 8:RES
8
- 7:D/C
6:VCC or 12:VCC
9:VCCEN and 10:PMODEN

3.3V -

5V


GND - 5:GND or 11:GND

※ 9: VCCENを3.3V、10: PMODENをGNDに接続すると消費電力を約200nAに減らすことができるそうです。ただし、描画が停止する度に省電力で画面が薄暗くなります。

以上で物理的な接続は完了です。
準備が整ったら、いよいよライブラリを調べてみましょう!

ライブラリは下図の手順で開いてください。
他にもArduino IDEにインストールしておけば、ローカル環境で呼び出せてサンプルの実行も可能となるので上記の環境が整っていればそちらをお勧めします。


”examples”を開く

"test"を開く

"test.pde"をエディタで開く


まずは、冒頭部分から見て行きます。

// 任意の(4または)5ピンを使用できます
#define sclk13
#define mosi11
#define cs10
#define rst9
#define dc8

#include <Adafruit_GFX.h>
#include <Adafruit_SSD1331.h>
#include <SPI.h>

// Option 1: 任意のピンを使用すると若干遅くなります
Adafruit_SSD1331 display = Adafruit_SSD1331(cs, dc, mosi, sclk, rst);

// Option 2: ハードウェアSPIピンを使用する必要があります
// (UNOの場合はsclk = 13、sid = 11)、ピン10は出力でなければなりません。
//これははるかに高速です - microSDカードを使用する場合にも必要です(画像の描画例を参照)
//Adafruit_SSD1331 display = Adafruit_SSD1331(cs, dc, rst);

ArduinoのSPI対応PINの宣言、ライブラリの読み込み、
"display"を"Adafruit_SSD1331"クラスを"display"の名称でオブジェクト化しています。
この際、任意のPINを使用してSPI通信を行うソフトウェアSPIと、マイコン内蔵のSPIモジュールを使用したハドウェアSPIの選択が行えます。
コードは、そのままだとソフトウェアSPIが有効になっていますが、UNO R3ではハードウェアSPIに対応しているので、

// Option 1: 任意のピンを使用すると若干遅くなります
// Adafruit_SSD1331 display = Adafruit_SSD1331(cs, dc, mosi, sclk, rst);

// Option 2: ハードウェアSPIピンを使用する必要があります
// (UNOの場合はsclk = 13、sid = 11)、ピン10は出力でなければなりません。
//これははるかに高速です - microSDカードを使用する場合にも必要です(画像の描画例を参照)
Adafruit_SSD1331 display = Adafruit_SSD1331(cs, dc, rst);

ソフトウェアSPIをコメントアウト、ハードウェアSPIをアンコメントした状態で以降のコードを追います。

"Adafruit_SSD1331(cs, dc, rst)"は、"Adafruit_SSD1331.cpp"の中に記載されているので見てみましょう。

/********************************* 低レベル・ピンの初期化 */
Adafruit_SSD1331::Adafruit_SSD1331(uint8_t cs, uint8_t rs, uint8_t rst):Adafruit_GFX(TFTWIDTH, TFTHEIGHT) {
    _cs = cs;
    _rs = rs;
    _sid = 0;
    _sclk = 0;
    _rst = rst;
}

uint8_t cs 1 バイトの符号なし整数 cd 10(cs)
uint8_t rs 1 バイトの符号なし整数 rs 8 (dc)
uint8_t rst 1 バイトの符号なし整数 rst 9(rst)

ちなみに、ハードウェアSPIはソフトウェアSPIに比べて目視できるレベルで圧倒的に早いですし、SDカードを接続するなどスレーブが複数になった場合にも有効です。

ここで引数として代入されている値、"TFTWIDTH"、"TFTHEIGHT"の二つは、"Adafruit_SSD1331.h"で以下のように宣言されています。

  static const int16_t TFTWIDTH = 96;
  static const int16_t TFTHEIGHT = 64;

続いて"Adafruit_GFX(TFTWIDTH, TFTHEIGHT)"を確認してみます。
こちらは、"Adafruit_GFX.cpp"に見つけることが出来ました。
各変数の初期値を設定しているようですね。

Adafruit_GFX::Adafruit_GFX(int16_t w, int16_t h):
WIDTH(w), HEIGHT(h)
{
    _width    = WIDTH;
    _height   = HEIGHT;
    rotation  = 0;
    cursor_y  = cursor_x    = 0;
    textsize  = 1;
    textcolor = textbgcolor = 0xFFFF;
    wrap      = true;
    _cp437    = false;
    gfxFont   = NULL;
}

uint16_t w 2 バイトの符号なし整数 x TFTWIDTH(96)
uint16_t y 2 バイトの符号なし整数 y TFTHEIGHT(64)

WIDTH、HEIGHTは、"Adafruit_GFX.h"で宣言されています。

const int16_t
    WIDTH, HEIGHT;   // これは'raw'ディスプレイのw / hです - 決して変化しません

ただし、こちらに直接値を入れている箇所や、"WIDTH()"、"HEIGHT()"について設定している場所が見当たらないので未解決です。

さて、ここまでで、以下の設定が行われています。

uint8_t _cs 10(cs)
uint8_t _rs 8 (dc)
uint8_t _sid 0
uint8_t _sclk 0
uint8_t _rst 9(rst)
int16_t _width 96(WIDTH)
int16_t _height 64(HEIGHT)
uint8_t rotation 0
int16_t cursor_y 0
int16_t cursor_x 0
uint8_t textsize 1
uint16_t textcolor 0xFFFF
uint16_t textbgcolor 0xFFFF
boolean wrap true
boolean _cp437 false
gfxFont NULL

全てが必要であるかは不明ですが、コードを調べて行く上では必要となる事もあるかと思うので、ここに記載しておきました。

本来のコードからは大幅に端折って、ハードウェアSPI設定のみに絞ってありますが、ここからSSD1331の初期化から画面を黒で塗りつぶす処理までのコードを追って見ます。

// 色の定義
#define BLACK0x0000

void setup(void) {
    display.begin();
    display.fillScreen(BLACK);
}

void loop() {
}

"BLACK"の色定義、display(Adafruit_SSD1331)のbegin()の実行、fillScreen()までの流れとなっていますが、まずはbegin()を調べて見ます。

この関数は、"Adafruit_SSD1331.cpp"にあります。

void Adafruit_SSD1331::begin(void) {
    // ピンの方向を設定する
    pinMode(_rs, OUTPUT);

    // ハードウェアSPIを使用します
    SPI.begin();
    SPI.setDataMode(SPI_MODE3);

    // リセットするにはRSTをローに切り替えます; CSがlowであれば指示の受け入れが可能です
    pinMode(_cs, OUTPUT);
    digitalWrite(_cs, LOW);
    cspin = digitalPinToBitMask(_cs);
    csportreg = portOutputRegister(digitalPinToPort(_cs));

    rspin = digitalPinToBitMask(_rs);
    rsportreg = portOutputRegister(digitalPinToPort(_rs));

    // I初期化シーケンス
    writeCommand(SSD1331_CMD_DISPLAYOFF); // 0xAE
    writeCommand(SSD1331_CMD_SETREMAP); // 0xA0

    writeCommand(0x72); // RGB Color

    writeCommand(SSD1331_CMD_STARTLINE); // 0xA1
    writeCommand(0x0);
    writeCommand(SSD1331_CMD_DISPLAYOFFSET); // 0xA2
    writeCommand(0x0);
    writeCommand(SSD1331_CMD_NORMALDISPLAY); // 0xA4
    writeCommand(SSD1331_CMD_SETMULTIPLEX); // 0xA8
    writeCommand(0x3F); // 0x3F 1/64 duty
    writeCommand(SSD1331_CMD_SETMASTER); // 0xAD
    writeCommand(0x8E);
    writeCommand(SSD1331_CMD_POWERMODE); // 0xB0
    writeCommand(0x0B);
    writeCommand(SSD1331_CMD_PRECHARGE); // 0xB1
    writeCommand(0x31);
    writeCommand(SSD1331_CMD_CLOCKDIV); // 0xB3
    writeCommand(0xF0); // 7:4 = Oscillator Frequency, 3:0 = CLK Div Ratio (A[3:0]+1 = 1..16)
    writeCommand(SSD1331_CMD_PRECHARGEA); // 0x8A
    writeCommand(0x64);
    writeCommand(SSD1331_CMD_PRECHARGEB); // 0x8B
    writeCommand(0x78);
    writeCommand(SSD1331_CMD_PRECHARGEA); // 0x8C
    writeCommand(0x64);
    writeCommand(SSD1331_CMD_PRECHARGELEVEL); // 0xBB
    writeCommand(0x3A);
    writeCommand(SSD1331_CMD_VCOMH); // 0xBE
    writeCommand(0x3E);
    writeCommand(SSD1331_CMD_MASTERCURRENT); // 0x87
    writeCommand(0x06);
    writeCommand(SSD1331_CMD_CONTRASTA); // 0x81
    writeCommand(0x91);
    writeCommand(SSD1331_CMD_CONTRASTB); // 0x82
    writeCommand(0x50);
    writeCommand(SSD1331_CMD_CONTRASTC); // 0x83
    writeCommand(0x7D);
    writeCommand(SSD1331_CMD_DISPLAYON); //--turn on oled panel
}

とりあえず簡単なところから、

// ピンの方向を設定する
pinMode(_rs, OUTPUT);

// リセットするにはRSTをローに切り替えます; CSがlowであれば指示の受け入れが可能です
pinMode(_cs, OUTPUT);
digitalWrite(_cs, LOW);

if (_rst) {
    pinMode(_rst, OUTPUT);
    digitalWrite(_rst, HIGH);
    delay(500);
    digitalWrite(_rst, LOW);
    delay(500);
    digitalWrite(_rst, HIGH);
    delay(500);
}

"pinMode"、"digitalWrite"の詳細は下記の解説を見てもらえば良いかと思いますが、8(_rs)、10(_cs)ピンを出力に、10(_cs)ピンをLOW(0V)に設定しています。
"_rst"での条件分岐以降の部分は、9(_rst)を出力に設定した後に500ms毎にHIGH(3.3V)、LOW(0V)、HIGH(3.3V)と切り替えています。
コメントにもあるように"_rst"の設定の前に、"_cs"の設定が必要となります。

pinMode()

[Digital I/O]

説明
指定されたピンが入力または出力として動作するように設定します。 ピンの機能の詳細については(デジタルピン)の説明を参照してください。 Arduino 1.0.1以降、INPUT_PULLUPモードで内部プルアップ抵抗をイネーブルすることが可能です。 さらに、INPUTモードは、内部プルアップを明示的にdisablesにします。

構文
pinMode(pin, mode)

パラメータ
pin: the number of the pin whose mode you wish to set
mode: INPUT、OUTPUT、またはINPUT_PULLUP (機能の詳細な説明については (デジタルピン) のページを参照してください)

戻り値
Nothing

digitalWrite()

[Digital I/O]

説明
HIGHまたはLOW値をデジタルピンに書き込みます。

ピンがpinMode()でOUTPUTとして設定されている場合、その電圧は対応する値に設定されます。HIGHの場合は5V(または3.3Vのボードの場合は3.3V)、LOWの場合は0V(グランド)。

ピンがINPUTとして設定されている場合、digitalWrite()は入力ピンの内部プルアップをEnable(HIGH)またはDisable(LOW)にします。内部プルアップ抵抗をイネーブルにするには、pinMode()をINPUT_PULLUPに設定することを推奨します。詳細については、デジタルピンのチュートリアルを参照してください。

pinMode()をOUTPUTに設定せずにLEDをピンに接続すると、digitalWrite(HIGH)を呼び出すときにLEDが暗く見えることがあります。pinMode()を明示的に設定しなければ、digitalWrite()は内部プルアップ抵抗を有効にし、大電流制限抵抗のように動作します。

構文
digitalWrite(pin, value)

パラメータ
pin: ピン番号
mode: IHIGHまたはLOW

戻り値
Nothing

ハードウェアSPIの設定

// ハードウェアSPIを使用します
SPI.begin();
SPI.setDataMode(SPI_MODE3);

SPI対応のマイコンと"#include <SPI.h>"は必須。

SPI.begin()


説明
SCK、MOSI、およびSSを出力に設定し、SCKおよびMOSIをローに引き上げ、SSをハイにすることで、SPIバスを初期化します。

構文
SPI.begin()

パラメータ
None

戻り値
None

setDataMode()


説明
PIパラメータを設定するには、SPI.beginTransaction()SPISettingsを使用します。

SPIデータモードを設定します。つまり、クロックの極性と位相です。詳細については、SPIに関するWikipediaの記事を参照してください

構文
SPI.setDataMode(mode)

パラメータ
mode:-   SPI_MODE0 : アイドル時のクロックがLow、立ち上がりでサンプリング
-   SPI_MODE1 : アイドル時のクロックがLow、立ち下がりでサンプリング
-   SPI_MODE2 : アイドル時のクロックがHigh、立ち上がりでサンプリング
-   SPI_MODE3 : アイドル時のクロックがHigh、立ち下がりでサンプリング
slaveSelectPinslave device SS pin(Arduino Due only)

戻り値
Nothing


と、ここまではArduinoを触っていれば周知の範疇。

ここから先は、私も含め知らない人の方が多い気がします。

cspin = digitalPinToBitMask(_cs);
csportreg = portOutputRegister(digitalPinToPort(_cs));

rspin = digitalPinToBitMask(_rs);
rsportreg = portOutputRegister(digitalPinToPort(_rs));

uint8_t cspin1 バイトの符号なし整数 cspindigitalPinToBitMask()
uint8_t *csportreg1 バイトの符号なし整数 csportregportOutputRegister()
uint8_t rspin1 バイトの符号なし整数 rspindigitalPinToBitMask()
uint8_t *rsportreg1 バイトの符号なし整数 rsportregportOutputRegister()

digitalPinToBitMask()


説明
指定したピンが対応するピンのビットマスクを返すマクロです。
hardware/arduino/cores/arduino/Arduino.h に定義されています。
#define digitalPinToBitMask(P) ( pgm_read_byte( digital_pin_to_bit_mask_PGM+(P) ) )

構文
digitalPinToBitMask(P)

パラメータ
P: ピン番号

戻り値
uint8_t ビットマスク

portOutputRegister()


説明
指定したポートに対応するレジスタを返すマクロです。
hardware/arduino/cores/arduino/Arduino.h に定義されています。
#define portOutputRegister(P) ( ( volatile uint8_t * )( pgm_read_word( port_to_output_PGM + (P) ) ) )

構文
portOutputRegister(P)

パラメータ
P: ポート番号

戻り値
uint8_t レジスタ

digitalPinToPort()


説明
指定したピンに対応するポートを返すマクロです。
hardware/arduino/cores/arduino/Arduino.h に定義されています。
#define digitalPinToPort(P) ( pgm_read_byte( digital_pin_to_port_PGM + (P) ) )

構文
digitalPinToPort(P)

パラメータ
P: ピン番号

戻り値
uint8_t ポート番号

以上3つのマクロの中には、
  • pgm_read_byte(address)
  • digital_pin_to_bit_mask_PGM - 配列
  • pgm_read_word(address)
  • port_to_output_PGM - 配列
  • digital_pin_to_port_PGM - 配列
など、更に見慣れないワードが出てきます。深堀していけばフラッシュメモリ周りの知識や技術が向上しますが、ここではそれを求めている訳ではないのでビットマスクとレジスタを取得している程度の認識で留めておきます。

ここまでライブラリを眺めてきましたが、今の所SSD1331には全くアクセスしていません。基本的にはSPI通信の準備を進めてきただけで、以降でようやくSSD1331へのアクセスを行うのですが、その前に関数"writeCommand()"がどの様な働きをしているのか確認しておきましょう。

// I初期化シーケンス
writeCommand(SSD1331_CMD_DISPLAYOFF); // 0xAE

"Adafruit_SSD1331.cpp"に記載されているこの関数を見て行きます。

void Adafruit_SSD1331::writeCommand(uint8_t c) {
    *rsportreg &= ~ rspin;
    //digitalWrite(_rs, LOW);

    *csportreg &= ~ cspin;
    //digitalWrite(_cs, LOW);

    //Serial.print("C ");
    spiwrite(c);

    *csportreg |= cspin;
    //digitalWrite(_cs, HIGH);
}

uint8_t c1 バイトの符号なし整数 cSSD1331_CMD_DISPLAYOFF(0xAE)

扱われている関数は、先に出てきたものですが、"Adafruit_SSD1331.h"で、

PortReg *csportreg, *rsportreg, *sidportreg, *sclkportreg;
PortMask cspin, rspin, sidpin, sclkpin;

と宣言され、新たに出てきた"PortReg"、"PortMask"も同じヘッダファイルの中で下記のように宣言されています。

typedef volatile uint8_t PortReg;
typedef uint8_t PortMask;

まとめるとこの様になります。

volatile uint8_t *csportreg 1 バイトの符号なし整数 csportreg
volatile uint8_t *rsportreg 1 バイトの符号なし整数 rsportreg
volatile uint8_t *sidportreg1 バイトの符号なし整数 sidportreg
volatile uint8_t *sclkportreg1 バイトの符号なし整数 sclkportreg
uint8_t cspin 1 バイトの符号なし整数 cspin
uint8_t rspin 1 バイトの符号なし整数 rspin
uint8_t sidpin1 バイトの符号なし整数 sidpin
uint8_t sclkpin1 バイトの符号なし整数 sclkpin

使用されているポインタアクセス演算子&複合演算子&ビット演算子

*

[Pointer Access Operators]

説明
Dereferencingは、特にポインタで使用するための機能の1つです。アスタリスク演算子*がこの目的に使用されます。pがポインタの場合、* pはpが指すアドレスに含まれる値を表します。

&=

[Compound Operators]

説明
複合AND演算子&=は、変数と定数とともに使用され、変数の特定のビットをLOW状態(0)にします。これは、プログラミングガイドで「クリア」または「リセット」ビットと呼ばれることがよくあります。

ビット単位のAND&演算子のレビュー
0011operand1
0101operand2
----------
0001(operand1 & operand2) - 返された結果

構文
x &= y; // x = x & y;と同じ動作です。

パラメータ
x:変数。 使用できるデータ型:char、int、long
y:変数または定数。 使用できるデータ型:char、int、long

~

[Bitwise Operators]

説明
C++のビット単位のNOT演算子はチルダ文字~です。&や|と異なり、ビット単位のNOT演算子は、その右側の単一のオペランドに適用されます。ビット単位のNOTは各ビットをその反対の値に変更します: 0は1になり、1は0になります。

言い換えると:
0 1operand1
-----
1 0~operand1

|=

[Compound Operators]

説明
複合のビット単位OR演算子| =は、変数内の特定のビットを「設定」(1に設定)するために変数と定数とともに使用されることがよくあります。

ビット単位のOR | 演算子のレビュー
0011operand1
0101operand2
----------
0111(operand1 | operand2) - 返された結果

構文
x |= y; // x = x | y;と同じ動作です。

パラメータ
x:変数。 使用できるデータ型:char、int、long
y:変数または定数。 使用できるデータ型:char、int、long

"spiwrite()"は、"Adafruit_SSD1331.cpp"に記載されている関数です。下記では、ハードウェアSPIに関する箇所以外は削除してあります。(_sid = 0なので)

/********************************** 低レベルピンインタフェース */
inline void Adafruit_SSD1331::spiwrite(uint8_t c) {
    if (!_sid) {
        SPI.transfer(c);
        return;
    }
}

uint8_t c1 バイトの符号なし整数 c0xAE
uint8_t _sid1 バイトの符号なし整数 _sid0

SPI.transfer() / transfer16()


説明
SPI転送は、同時送受信が基本となっています: 受信したデータはreceivedVal(またはreceivedVal16)に返されます。バッファ転送の場合、受信したデータはバッファ内の現在の位置に格納されます(古いデータは受信したデータに置き換えられます)。

構文
receivedVal = SPI.transfer(val)
receivedVal16 = SPI.transfer16(val16)
SPI.transfer(buffer, size)

パラメータ
val: バスを介して送信するバイト
val16: バスを介して送出するための2バイトの変数
buffer: 転送されるデータの配列

戻り値
受信したデータ

これらの情報から、"spiwrite()"は、引数"c"を経由して"transfer()"でSPI通信により引き継いだ値を送信していることがわかります。

では、SPIを行う前の処理を調べて見ましょう。
"&= ~"は説明にもある通りで、ポインタ"*rsportreg"や"*csportreg"に0を代入しています。これはコードのコメントにある様に、各端子を"LOW"に指定している状態です。
SPI送信が終わった後に、"|="で1(HIGH)に戻しています。
一旦0(LOW)にしているので、代入した変数の値にしからならいので"|="を用いている意味は良くわからないです。

この辺りに関しては、SSD1331 Datasheetに、

Table 8 - シリアルインタフェースの制御端子
Function E R/W# CS# D/C#
Write command Tie low Tie low L L
Write data Tie low Tie low L H

7.2 コマンドデコーダ
このモジュールは、入力をD / C#ピンの入力に基づいてデータまたはコマンドとして解釈する必要があるかどうかを決定します。D/C#ピンがHIGHの場合、データはグラフィック表示データRAM(GDDRAM)に書き込まれます。D/C#がLOWの場合、D0〜D15の入力はコマンドとして解釈され、デコードされて対応するコマンドレジスタに書き込まれます。

と、記載されています。
これらを参考に先ほどの処理内容と照らし合わせると、

CS#(*csportreg) : LOW / D/C#(*rsportreg) : LOW

なので、"Write command"に設定して、"spiwrite(c);"でコマンドの書き込みを行い、CS#(*csportreg) : HIGH に戻す事で"Write command"を解除しています。この辺りは、コマンド、データの書き込みに追いては必須となります。


なんとなく何が行われているのか理解出来てきたので、再び初期化にあたってのコード一覧を見てみます。

    // I初期化シーケンス
    writeCommand(SSD1331_CMD_DISPLAYOFF); // 0xAE
    writeCommand(SSD1331_CMD_SETREMAP); // 0xA0

    writeCommand(0x72); // RGB Color

    writeCommand(SSD1331_CMD_STARTLINE); // 0xA1
    writeCommand(0x0);
    writeCommand(SSD1331_CMD_DISPLAYOFFSET); // 0xA2
    writeCommand(0x0);
    writeCommand(SSD1331_CMD_NORMALDISPLAY); // 0xA4
    writeCommand(SSD1331_CMD_SETMULTIPLEX); // 0xA8
    writeCommand(0x3F); // 0x3F 1/64 duty
    writeCommand(SSD1331_CMD_SETMASTER); // 0xAD
    writeCommand(0x8E);
    writeCommand(SSD1331_CMD_POWERMODE); // 0xB0
    writeCommand(0x0B);
    writeCommand(SSD1331_CMD_PRECHARGE); // 0xB1
    writeCommand(0x31);
    writeCommand(SSD1331_CMD_CLOCKDIV); // 0xB3
    writeCommand(0xF0); // 7:4 = Oscillator Frequency, 3:0 = CLK Div Ratio (A[3:0]+1 = 1..16)
    writeCommand(SSD1331_CMD_PRECHARGEA); // 0x8A
    writeCommand(0x64);
    writeCommand(SSD1331_CMD_PRECHARGEB); // 0x8B
    writeCommand(0x78);
    writeCommand(SSD1331_CMD_PRECHARGEA); // 0x8C
    writeCommand(0x64);
    writeCommand(SSD1331_CMD_PRECHARGELEVEL); // 0xBB
    writeCommand(0x3A);
    writeCommand(SSD1331_CMD_VCOMH); // 0xBE
    writeCommand(0x3E);
    writeCommand(SSD1331_CMD_MASTERCURRENT); // 0x87
    writeCommand(0x06);
    writeCommand(SSD1331_CMD_CONTRASTA); // 0x81
    writeCommand(0x91);
    writeCommand(SSD1331_CMD_CONTRASTB); // 0x82
    writeCommand(0x50);
    writeCommand(SSD1331_CMD_CONTRASTC); // 0x83
    writeCommand(0x7D);
    writeCommand(SSD1331_CMD_DISPLAYON); //--turn on oled panel

このコマンドの大半は、"Adafruit_SSD1331.h"で定義されています。
定義内容とそれぞれの機能をSSD1331のDatasheetを確認しながら調べて見ます。

0xAC
#define SSD1331_CMD_DISPLAYOFF 0xAE
#define SSD1331_CMD_DISPLAYON 0xAF
D/C# Hex D7 D6 D5 D4 D3 D2 D1 D0
0
0
0
AC
AE
AF
1


0


1


0


1


1


A1


A0



ACh = 調光モードで表示ON
AEh = 表示OFF (sleep mode) Default
AFh = ノーマルモードで表示ON

9.1.13 ディスプレイのON / OFFを設定する (ACh / AEh / AFh)
これらの1バイトコマンドは、OLEDパネルの表示をONまたはOFFするために使用されます。表示がONのときは、マスタ構成設定コマンドで選択した回路がONになります。ディスプレイがオフのとき、これらの回路はオフになり、セグメントとコモン出力はハイインピーダンス状態になります。

これらのコマンドは、次の3つの状態のいずれかに表示を設定します:
o ACh:暗いモードの表示ON
o AEh:表示OFF(スリープモード)
o AFh:通常の明るさ表示オン
ここで、暗いモード設定はコマンドABhによって制御される。

#define SSD1331_CMD_SETREMAP 0xA0
// RGB Color 0x72
D/C# Hex D7 D6 D5 D4 D3 D2 D1 D0
0
0

A0
A[7:0]
72
1
A7
0
0
A6
1
1
A5
1
0
A4
1
0
A3
0
0
A2
0
0
A1
1
0
A0
0
※A [7 : 0] = 01000000 Default

ドライバのリマップと色深度を設定する
A [0] = 0、水平アドレスインクリメント
A [0] = 1、垂直アドレスインクリメント

A [1] = 0、RAMカラム0〜95はピンセグ(SA、SB、SC)にマップ0〜95
A [1] = 1、RAMカラム0〜95はピンセグ(SA、SB、SC)にマップ95〜0

A [2] = 0、順序 SA、SB、SC(例えば、RGB)
A [2] = 1、逆順 SC、SB、SA(例えば、BGR)

A [3] = 0、COM上での左右スワッピングを無効にする
A [3] = 1、COMに左右スワップを設定する

A [4] = 0、COM0からCOM [N-1]へスキャン
A [4] = 1、COM [N-1]からCOM0にスキャンする。
ここで、Nは多重比です。

A [5] = 0、COMスプリット奇数を無効にする(RESET)
A [5] = 1、COMスプリット奇数を有効にする

A [7:6] = 00; 256色フォーマット
A [7:6] = 01; 65kカラーフォーマット
A [7:6] = 10; 65kカラーフォーマット2

9/18ビットモードを選択した場合、設定に関係なく色深度は65kに固定されます。

9.1.6 リマップとデータフォーマットを設定 (A0h)
このコマンドには複数の構成があり、各ビットの設定は次のように記述されています。

•アドレスインクリメントモード(A [0])
0に設定すると、ドライバは水平アドレスインクリメントモードに設定されます。ディスプレイRAMが読み書きされた後、列アドレスポインタは自動的に1だけ増加する。列アドレスポインタが列終了アドレスに到達すると、列アドレスポインタは列開始アドレスにリセットされ、行アドレスポインタは1だけ増加される。水平アドレスインクリメントモードの行および列アドレスポイントの移動シーケンスを図23に示します。

Figure 23 - 水平アドレスインクリメントモードのアドレスポインタ移動
COL 0 COL 1 ..... COL 94 COL 95
Row 0




Row 1




: : : : : :
Row 62




Row 63





A [0]を1に設定すると、ドライバは垂直アドレスインクリメントモードに設定されます。表示RAMが読み書きされた後、行アドレスポインタは自動的に1だけ増加する。行アドレスポインタが行終了アドレスに達すると、行アドレスポインタは行開始アドレスにリセットされ、列アドレスポインタは1だけ増加される。垂直アドレスインクリメントモードの行および列アドレスポイントの移動シーケンスを図24に示します。

Figure 24 - 垂直アドレスインクリメントモードのアドレスポインタの動き
COL 0 COL 1 ..... COL 94 COL 95
Row 0
:
Row 63
Row 0
:
Row 63
: :
Row 0
:
Row 63
Row 0
:
Row 63

•列アドレスマッピング(A [1])
このコマンドビットは、左から右へまたはその逆に配置されたセグメントを有するOLEDモジュールにおけるセグメント信号の柔軟なレイアウトのために作られる。表示方向は、表示データRAM列0〜SEG0端子(A [1] = 0)、または表示データRAM列95をSEG0端子(A [1] = 1)にマッピングする方法です。両方の効果を図25に示します。

Figure 25 - 列アドレスマッピングの例
詳細は省略。SSD1331 Datasheetを確認して下さい。

•RGBマッピング(A [2])
このコマンドビットは、OLEDモジュールのセグメント信号をフレキシブルにレイアウトするために作成されたもので、フィルタ設計にマッチします。

•COM左/右リマップ(A [3])
このコマンドビットは、COM0が左側または右側のいずれかに配置されたOLEDモジュールの共通信号の柔軟なレイアウト用に作成されています。ピン配置の詳細は表12と図26にあります。

•COMスキャン方向の​​リマップ(A [4])
このビットは、OLEDモジュールにおける共通信号の柔軟なレイアウトのための共通の走査方向を、上から下へ、またはその逆に決定する。ピン配置の詳細は表12と図26にあります。

•COMピン(A [5])の奇数偶数分割
このビットは、COMピンの奇数偶数配列を設定できます。
A [5] = 0:COMスプリット奇数を無効、コモンのピン割り当てはCOM63 COM62 .... COM 33 COM32..SC95..SA0..COM0 COM1.... COM30 COM31
A [5] = 1:COMスプリット奇数を有効、コモンのピン割り当てをCOM63 COM61.... COM3 COM1..SC95..SA0..COM0 COM2.... COM60 COM62
ピン配置の詳細は表12と図26にあります。

•カラーモードを表示する(A [7:6])
65kまたは256色モードを選択します。異なるモードのディスプレイRAMデータフォーマットについては、セクション7.5で説明しています。

Table 12 - 異なるCOM出力設定の図
Figure 26 - COMピンのハードウェア構成(MUX比:64)
これらも長いので省略しています。詳細を確認したい場合はSSD1331 Datasheetを確認して下さい。

これは"0xA0"のリマップとデータフォーマットを設定のためのパラメーター。
"0x72"をビットで見てみると、
A [7 : 0] = 01110010
A [0] = 0、水平アドレスインクリメント
A [1] = 1、RAMカラム0〜95はピンセグ(SA、SB、SC)にマップ95〜0
A [2] = 0、順序 SA、SB、SC(例えば、RGB)
A [3] = 0、COM上での左右スワッピングを無効にする
A [4] = 1、COM [N-1]からCOM0にスキャンする
A [5] = 1、COMスプリット奇数を有効にする
A [7:6] = 01; 65kカラーフォーマット
以上が、ここで設定されている内容。


#define SSD1331_CMD_STARTLINE 0xA1

0x0
D/C# Hex D7 D6 D5 D4 D3 D2 D1 D0
0
0

A1
A[5:0]
0
1
0
0
0
0
0
1
A5
0
0
A4
0
0
A3
0
0
A2
0
0
A1
0
1
A0
0

行単位で表示開始行レジスタを設定する
A[5:0]: 00dから63dまで

9.1.7 表示開始行を設定する (A1h)
このコマンドは、Display Start Lineレジスタを設定して、0〜63の値を選択して表示RAMの開始アドレスを決定します。表13および表14に、このコマンドの例を示します。ここでの"Row(行)"は、グラフィック表示データRAM行を意味する。

Table 13 - 表示オフセットを設定し、再設定なしの開始行を表示する例
Table 14 - ディスプレイのオフセットを設定し、リマップでスタートラインを表示する例 
これらも長いので省略しています。詳細を確認したい場合はSSD1331 Datasheetを確認して下さい。

#define SSD1331_CMD_DISPLAYOFFSET 0xA2

0x0
D/C# Hex D7 D6 D5 D4 D3 D2 D1 D0
0
0

A1
A[5:0]
0
1
0
0
0
0
0
1
A5
0
0
A4
0
0
A3
0
0
A2
0
1
A1
0
0
A0
0

Comによる垂直オフセットの設定
A[5:0]: 00dから63dまで

9.1.8 ディスプレイのオフセットを設定する (A2h)
このコマンドは、表示開始ライン(COM0が表示開始ライン、表示開始ラインレジスタが0)がCOM0-63のいずれかにマッピングされるように指定します。たとえば、COM16をCOM0方向に16行移動するには、2番目のコマンドの6ビットデータを0010000bで指定する必要があります。表13および表14に、このコマンドの例を示します。ここでの"Row(行)"は、グラフィック表示データRAM行を意味する。

Table 13 - 表示オフセットを設定し、再設定なしの開始行を表示する例
Table 14 - ディスプレイのオフセットを設定し、リマップでスタートラインを表示する例 
これらも長いので省略しています。詳細を確認したい場合はSSD1331 Datasheetを確認して下さい。

#define SSD1331_CMD_NORMALDISPLAY 0xA4
D/C# Hex D7 D6 D5 D4 D3 D2 D1 D0
0
0
0
0
A4
A5
A6
A7
1



0



1



0



0



1



X1



X0




A4h=通常表示
A5h=全体表示オン、GS63ですべてのピクセルがオンになります
A6h=全体表示OFF、全画素OFF
A7h=逆表示

9.1.9 表示モードを設定する (A4h ~ A7h)
これらはシングルバイトコマンドであり、通常表示、全体表示ON、全体表示OFF、反転表示の設定に使用されます。
•通常表示(A4h)
上記の効果をリセットし、対応するグレーレベルでデータをONにします。
•全体表示をオンに設定する(A5h)
表示データRAMの内容にかかわらず、ディスプレイ全体を強制的に "GS63"にします。
•全体表示をオフにする(A6h)
表示データRAMの内容にかかわらず、ディスプレイ全体を強制的にグレーレベル "GS0"にします。
•逆表示(A7h)
表示データの階調は、 "GS0"→ "GS63"、 "GS1"→ "GS62"、...と変化する。

#define SSD1331_CMD_SETMULTIPLEX 0xA8
// 0x3F 1/64 duty 0x3F
D/C# Hex D7 D6 D5 D4 D3 D2 D1 D0
0
0

A8
A[5:0]
3F
1
0
0
0
0
0
1
A5
1
0
A4
1
1
A3
1
0
A2
1
0
A1
1
0
A0
1

MUX比をN + 1 Muxに設定する
N = A[5:0] 15dから63dまで
A[5:0] 00dから14dまでは無効な入力です

9.1.10 マルチプレックス比率の設定 (A8h)
このコマンドは、デフォルトの1:64のマルチプレクスモードを、16から64のマルチプレックスモードに切り替えます。例えば、多重比が16に設定されている場合、16本の共通ピンのみがイネーブルされます。有効化された共通ピンの開始および終了は、コマンドA2hによってプログラムされた“Display Offset”レジスタの設定に依存する。

#define SSD1331_CMD_SETMASTER 0xAD

0x8E
D/C# Hex D7 D6 D5 D4 D3 D2 D1 D0
0
0

AD
A[0]
8E
1
1
1
0
0
0
1
0
0
0
0
0
1
1
1
1
1
1
0
1
1
1
A0
0

A[0]=0b, 外部VCC電源を選択
A[0]=1b, 予約済み(RESET)

Note
(1) RESET後にビットA [0]を0bに設定する必要があります。
(2) TSet Display ONコマンド(AFh)を発行した後で設定が有効になります。

9.1.12 マスター構成の設定 (ADh)
このコマンドは、外部VCC電源を選択します。 外部VCC電源はVCCピンに接続する必要があります。RESET後、A [0]ビットを0bに設定する必要があります。

このコマンドは、Set Display ONコマンド(AFh)を発行した後にアクティブになります。

#define SSD1331_CMD_POWERMODE 0xB0

0x0B
D/C# Hex D7 D6 D5 D4 D3 D2 D1 D0
0
0

B0
A[7:0]
0B
1
A7
0
0
A6
0
1
A5
0
1
A4
0
0
A3
1
0
A2
0
0
A1
1
0
A0
1

A[7:0]=1Ah, 省電力モードを有効にする(RESET)
A[7:0]=0Bh, 省電力モードを無効にする

9.1.14 省電力モード (B0h)
このコマンドは、省電力モードを有効または無効にするときに使用します。

#define SSD1331_CMD_PRECHARGE 0xB1

0x31
D/C# Hex D7 D6 D5 D4 D3 D2 D1 D0
0
0

B1
A[7:0]
31
1
A7
0
0
A6
0
1
A5
1
1
A4
1
0
A3
0
0
A2
0
0
A1
0
1
A0
1

A[3:0] N DCLKでのフェーズ1周期。1〜15 DCLKが許容。
A[7:4] N DCLKでのフェーズ2周期。1〜15 DCLKが許容。

Note
(1) フェーズ1およびフェーズ2で0 DCLKは無効となります。

9.1.15 フェーズ1と2の期間調整 (B1h)
このコマンドは、ドライバのセグメント波形の位相1と2の長さを設定します。
• Phase 1 (A[3:0]): DCLK単位で1〜15の期間を設定します。OLEDピクセルのより大きなキャパシタンスは、以前のデータ電荷を完全に放電するためにより長い期間を必要とすることがある。
• Phase 2 (A[7:4]): DCLK単位で1〜15の期間を設定します。OLED画素のより大きな容量をA、BおよびCの色の目標電圧VPに充電するためにより長い期間が必要である。

#define SSD1331_CMD_CLOCKDIV 0xB3
A[7:4] = オシレータ周波数, A[3:0] = CLK Div比 (A[3:0]+1 = 1..16) 0xF0
D/C# Hex D7 D6 D5 D4 D3 D2 D1 D0
0
0

B3
A[7:0]
F0
1
A7
1
0
A6
1
1
A5
1
1
A4
1
0
A3
0
0
A2
0
1
A1
0
1
A0
0

A[3:0] D0h表示クロック(DCLK)の分周比(D)を定義します:
分周比 (D) = A[3:0] + 1 (i.e., 1 to 16)
A[7:4] Fosc周波数。
設定値が増加すると周波数が上昇する。

9.1.16 ディスプレイクロック分周比/発振器周波数の設定 (B3h)
このコマンドは2つの機能で構成されています:
• 表示クロック分周比 (A[3:0])
CLKからDCLK(表示クロック)を発生するように分周比を設定します。分周比は1〜16で、リセット値は1です。DCLKとCLKの関係については、7.3.1項を参照してください。

• オシレータ周波数 (A[7:4])
CLSピンをHighにすると、CLKのソースであるオシレータ周波数Foscをプログラムします。4ビットの値は、以下に示すように16種類の周波数設定を使用可能にします。デフォルト設定は1101bです。

#define SSD1331_CMD_PRECHARGEA 0x8A

0x64
#define SSD1331_CMD_PRECHARGEB 0x8B

0x78
#define SSD1331_CMD_PRECHARGEC 0x8C

0x64
D/C# Hex D7 D6 D5 D4 D3 D2 D1 D0
0
0

0
0

0
0

8A
A[7:0]
64
8B
A[7:0]
78
8C
A[7:0]
64
1
A7
0
1
A7
0
1
A7
0
0
A6
1
0
A6
1
0
A6
1
0
A5
1
0
A5
1
0
A5
1
0
A4
0
0
A4
1
0
A4
0
1
A3
0
1
A3
1
1
A3
0
0
A2
1
0
A2
0
1
A2
1
1
A1
0
1
A1
0
0
A1
0
0
A0
0
1
A0
0
0
A0
0

A[7:0]: 2番目のプリチャージ速度を設定する。
Ranges: 0000000b〜1111111bでは、A [7:0]の値が高いほど、第2プリチャージ速度が速くなります。

Note
(1) 8AhのA [7:0]、8ChのA [7:0]、8ChのA [7:0]のデフォルト値は、それぞれ色A、B、C(コマンド:81h、82h、83h参照)のコントラスト値に等しい。
(2) 6バイト (8Ah A[7:0], 8Bh A[7:0]はすべて一緒に入力する必要があります。たとえば、元の値は次のようになります。
Original value
8Ah A[7:0]: 80h
8Bh A[7:0]: 80h
8Ch A[7:0]: 80h
8Bh A [7:0]の値を75hに変更する場合は、次の6バイトをすべて入力する必要があります:
8Ah,80h,
8Bh,75h,
8Ch,80h.

9.1.5 カラー A, B, C の2番目のプリチャージ速度を設定 (8Ah)
設定された値は、色A、B、Cのコントラストと一致する必要があります。最初の試行は、コントラストA、B、Cと同じ値でなければなりません。より速い速度が必要な場合は、より高い値を設定することができ、その逆も可能です。図22は、コマンド8Ah、8Bhおよび8Chを使用して、異なる速度で第2のプリチャージを設定する効果を示しています。

 Figure 22 - 異なる速度で第2のプリチャージを設定する効果
これらも長いので省略しています。詳細を確認したい場合はSSD1331 Datasheetを確認して下さい。

#define SSD1331_CMD_PRECHARGELEVEL 0xBB

0x3A
D/C# Hex D7 D6 D5 D4 D3 D2 D1 D0
0
0

BB
A[5:1]
3A
1
0
0
0
0
0
1
A5
1
1
A4
1
1
A3
1
0
A2
0
1
A1
1
1
0
0

プリチャージ電圧レベルを設定します。3色とも同じプリチャージ電圧を共有します。
A[5:1] Hex code pre-charge voltage
00000 00h 0.10 x VCC
: : :
11111 3Eh 0.50 x VCC
A [5:1]の詳細設定については、図30を参照してください。

9.1.19 プリチャージ電圧の設定 (BBh) 
このコマンドは、セグメント・ピンのプリチャージ電圧レベルを設定します。VPレベルはVCCを基準にしてプログラムされます。図30は、コマンドBBh A [5:1]によるプリチャージ電圧レベルの設定の詳細を示しています

 Figure 30 – コマンドBBhによる典型的なプリチャージ電圧レベルの設定
これらも長いので省略しています。詳細を確認したい場合はSSD1331 Datasheetを確認して下さい。

#define SSD1331_CMD_VCOMH 0xBE

0x3E
D/C# Hex D7 D6 D5 D4 D3 D2 D1 D0
0
0

BE
A[5:1]
3E
1
0
0
0
0
0
1
A5
1
1
A4
1
1
A3
1
1
A2
1
1
A1
1
0
0
0

COMの電圧レベル(V COMH)を選択解除する
A[5:1] Hex code pre-charge voltage
00000 00h 0.44 x VCC
01000 10h 0.52 x VCC
10000 20h 0.61 x VCC
11000 30h 0.71 x VCC
11111 3Eh 0.83 x VCC

9.1.20 VCOMH電圧を設定 (BEh)
このコマンドは、共通ピンの高電圧レベルを設定します。VCOMHのレベルはVCCを基準にしてプログラムされています。

#define SSD1331_CMD_MASTERCURRENT 0x87

0x06
D/C# Hex D7 D6 D5 D4 D3 D2 D1 D0
0
0

87
A[3:0]
06
1
0
0
0
0
0
0
0
0
0
0
0
0
A3
0
1
A2
1
1
A1
1
1
A0
0

1/16、2/16〜16/16の減衰に対応するマスタ電流減衰係数A [3:0]を00d〜15dに設定します。

9.1.4 マスター電流制御 (87h)
このコマンドはセグメント出力電流をスケーリング係数で制御します。この因子は、色A、BおよびCに共通である。チップには16のマスタ制御ステップがあります。 因子は1 [0000b]〜16 [1111b]の範囲である。RESETは16 [1111b]です。マスターの電流値が小さいほど、OLEDパネルの表示が暗くなります。たとえば、元のセグメント出力電流が倍率= 16で160uAの場合、倍率を8に設定して電流を80uAに減らします。

#define SSD1331_CMD_VCOMH 0xBE

0x3E
D/C# Hex D7 D6 D5 D4 D3 D2 D1 D0
0
0

BE
A[5:1]
3E
1
0
0
0
0
0
1
A5
1
1
A4
1
1
A3
1
1
A2
1
1
A1
1
0
0
0

COMの電圧レベル(V COMH)を選択解除する
A[5:1] Hex code pre-charge voltage
00000 00h 0.44 x VCC
01000 10h 0.52 x VCC
10000 20h 0.61 x VCC
11000 30h 0.71 x VCC
11111 3Eh 0.83 x VCC

9.1.20 VCOMH電圧を設定 (BEh)
このコマンドは、共通ピンの高電圧レベルを設定します。VCOMHのレベルはVCCを基準にしてプログラムされています。

#define SSD1331_CMD_CONTRASTA 0x81

0x91
#define SSD1331_CMD_CONTRASTB 0x82

0x50
#define SSD1331_CMD_CONTRASTC 0x83

0x7D
D/C# Hex D7 D6 D5 D4 D3 D2 D1 D0
0
0

0
0

0
0

81
A[7:0]
91
82
A[7:0]
50
83
A[7:0]
7D
1
A7
1
1
A7
0
1
A7
0
0
A6
0
0
A6
1
0
A6
1
0
A5
0
0
A5
0
0
A5
1
0
A4
1
0
A4
1
0
A4
1
0
A3
0
0
A3
0
0
A3
1
0
A2
0
0
A2
0
0
A2
1
0
A1
0
1
A1
0
1
A1
0
1
A0
1
0
A0
0
1
A0
1

すべてのカラー "A"セグメントのコントラストを設定する。
(Pins:SA0 – SA95)
A[7:0] 有効範囲:00d〜255d

すべてのカラー "B"セグメントのコントラストを設定する。
(Pins:SB0 – SB95)
A[7:0] 有効範囲:00d〜255d

すべてのカラー "C"セグメントのコントラストを設定する。
(Pins:SC0 – SC95)
A[7:0] 有効範囲:00d〜255d

9.1.3 色のコントラストを設定 A, B, C (81h, 82h, 83h)
このコマンドは、各色A、B、Cのコントラスト設定を設定するコマンドです。このチップは、色A、BおよびCの3つのコントラスト制御回路を有する。各コントラスト回路は、00hからFFhまでの256のコントラストステップを有する。コントラストステップでセグメント出力電流ISEGが増加するため、カラーが明るくなります。

以上が初期化処理で用いられている定数となります。

想定していたより初期化処理の調査結果が広がったので、画面描画(画面の塗潰し)は別途調べたいと思います。

0 件のコメント:

コメントを投稿