Arduinoで実験 (SPIデバイス)

2013/05/06


◆ SPI 制御

SPI は I2C 同様、同期型の通信方法で、I2C が 1 本の線でデータの送受信を行うのに対し、送信と受信の線を別けることにより I2C より高速に通信を行う事ができます。

Arduino IDE では標準で  SPI ライブラリがありハードウェアでの SPI 通信が行えます。

以下は SPI 通信に使用するピンの名称です。

SCK 同期信号 (D13)
MISO Master In Slave Out (D12)
マスター側が入力、スレーブ側が出力
MOSI Master Out Slave In (D11)
マスター側が出力、スレーブ側が入力
SS スレーブ選択 (D10)
I2C ではアドレスで各スレーブへ命令を与えるが、SPI では命令を与えるが、スレーブ側の SS ピンが HIGH の場合、マスターからの命令は無視される。
Arduino では、ライブラリがスレーブモードに移行するため出力状態にしておく必要がある。

SPI と称していても全てが同じ規格で動作するわけではなく、各デバイスで異なる設定があるため、SPI ライブラリを使用するためには通信方法の設定を行う必要があります。 以下が設定内容。

setBitOrder LSBFIRST/MSBFIRST
データを 0bit から 7bit の順で送信するか、(LSBFIRST)
7bit から 0bit の順で送信するかを指定します。(MSBFIRST)
setClockDivider SPI_CLOCK_DIV2 (DIV4...DIV128)
クロックの分周値を設定します。システムクロックの 1/2~1/128 を設定でき、2,4,8,16,32,64,128 の値が設定できます。
設定値は接続するデバイスによります。
setDataMode SPI_MODE0 ~ SPI_MODE3 の設定があり、信号の位相や立上り/立下りで有効かを決めます。

I2C 通信では送信と受信が完全に別個の働きをしましたが、SPI 通信は送信と受信が同じ動作となります。

マスター側のSPI用レジスタの内容を送信するとスレーブ側のレジスタの内容が書き換えられるのですがこのときスレーブ側のレジスタの内容がマスター側へ送信され、マスター側のレジスタも書き換えられます。 イメージとしてはお互いのレジスタの内容を交換する感じです。

そのため Arduino の SPI 送信関数 SPI.transfer() では戻り値があり、データ送信=データ受信となっています。

SPI.setDataMode() で設定するモードは以下の通りである。 通信するデバイスによって異なるのでデータシートを参照し通信モードを設定する。

SPI_MODE0 on/off = high/low、立ち上がりで送受信
SPI_MODE1 on/off = high/low、立ち下がりで送受信
SPI_MODE2 on/off = low/high、立ち上がりで送受信
SPI_MODE3 on/off = low/high、立ち下がりで送受信

リファレンスにもあるがSSピンは使用しない場合でも出力状態のままにしておく必要がある。基本 High で待機状態とし、コマンドの送受信時は low にする。

SPI.h の SPI.transfer() コマンドでは、SS ピンのhigh/low も行っていると思われる。


◆ SPI.h を使用しない場合

複数の SPI デバイスを制御する時は、SSピンで制御先を選択するのだが、別途シリアルデータの送受信コマンドがある。 ただしソフトウェアでの制御なのでより早い速度で通信したい場合はハードウェアSPI を使用する。

shiftOut 1バイト分のデータを1ビットずつ送信する。
shiftOut(dataPin, clockPin, bitOrder, value)
  dataPin   データを出力するピン
  clockPin  クロックを出力するピン
  bitOrder  MSBFIRST 又は LSBFIRST
  value     送信する1バイトのデータ
shiftIn 1バイト分のデータを1ビットずつ受信する。
shiftIn(dataPin, clockPin, bitOrder)
  dataPin   データ入力ピン
  clockPin  クロックを出力するピン
  bitOrder  MSBFIRST 又は LSBFIRST

SPI 通信を行う場合 SPI ライブラリを使用すれば簡単で高速なのですが、ハードウェアで SPI がサポートされていない場合はソフトウェアで行う必要があります。 Nokia 5110 SPI の項では shiftOut 命令のみの使用なので SPI ライブラリは使用していません。

shiftOut 命令は Arduino IDE 固有の命令なので他の開発環境では使用できないので同等の命令をプログラムしてみます。 とはいっても Arduino でしか動かないコードですが。

//
// シリアルデータ送信命令
//

void data_Output(unsigned char clk_pin, unsigned char data_pin, unsigned char data) {
    unsigned char count;


    for (count = 0; count < 8; count++) {
//MSBFIRST
        if ((data << count) & 0x80) {
//LSBFIRST
//      if ((data >> count) & 0x01) {
            digitalWrite(data_pin, HIGH);
        } else {
            digitalWrite(data_pin, LOW);
        }
//clock out
        digitalWrite(clk_pin, HIGH);
//      delayMicroseconds(1)
        digitalWrite(clk_pin, LOW);
//      delayMicroseconds(1)
    }

    digitalWrite(clk_pin, LOW);
    digitalWrite(data_pin, LOW);
}
 
//
// シリアルデータ受信命令
//

unsigned char data_Input(unsigned char clk_pin, unsigned char data_pin) {
    unsigned char count;
    unsigned char data = 0;

    for (count = 0; count < 8; count++) {
        data <<= 1;
        digitalWrite(clk_pin, HIGH);
        data += digitalRead(data_pin);
        digitalWrite(clk_pin, LOW);
    }

    digitalWrite(clk_pin, LOW);
    digitalWrite(data_pin, LOW);
}

 

//
// SPI書込み命令
//

#define SPI_OUT 11
#define SPI_CLK 13
#define SPI_CS  10

void SPI_write(unsigned char address, unsigned char data) {

    digitalWrite(SPI_CS, LOW);

    data_Output(SPI_CLK, SPI_OUT, address);
    data_Output(SPI_CLK, SPI_OUT, data);

    digitalWrite(SPI_CS, HIGH);

}

 

//
// SPI読込み命令
//

#define SPI_OUT 11
#define SPI_IN  12
#define SPI_CLK 13
#define SPI_CS  10

unsigned char SPI_read(unsigned char address) {
    unsigned char data;
    unsigned char count;

    digitalWrite(SPI_CS, LOW);
    data_Output(SPI_CLK, SPI_OUT, address);
    data_Input(SPI_CLK, SPI_IN);
    digitalWrite(SPI_CS, HIGH);
}

 


◆ SPI 制御の具体例

手持ちの SPI 通信を使用する機器にディスプレイと IOエキスパンダ MCP23S17 があるが、どちらも取り扱いの共通項が少ないので個別ページにて解説します。 SPI 制御の EEPROM でも入手したら以降に追記します。