ArduinoでのSPI通信についてです。SPI通信(Serail Peripheral Interface:SPI)はマイコン系と周辺デバイス間の短距離間で利用されている同期型のシリアル通信の一種です。同様の通信方法としては、I2cがあります。ArduinoやRaspberry Piの周辺デバイスでは、LCDやADコンバータ等で採用されていると思います。I2Cと同様に使いこなしたい通信方法の一つだと思います。
ここでは、2台のArduino Unoを以下のように接続してマスタとスレーブにしてごく簡単な通信を確認しました。Arduino UnoはそれぞれPCに接続しています。

主に以下を参考にしました。ありがとうございます。
Arduinoリファレンス
Arduinoリファレンス日本語訳
ArduinoでSPIの双方向通信をしてみた
ここから具体的な内容です。
Arduino Uno間の配線について
SPIでは以下の配線を使います。
MISO (Master In Slave Out):スレーブからマスタへのライン
MOSI (Master Out Slave In):マスタからスレーブへのライン
SCK (Serial Clock) :データ転送同期用のクロック信号
SS (Slave Select):スレーブデバイスを選択、つまりマスタへのデータ転送を可能にするためのピン。
SSのピンがSPIの特徴の一つでこのピンを利用して複数のスレーブを制御することになります。このピンがLowの場合にマスタとの通信が有効になり、Highの場合はマスタとの通信を無視することになります。
マスタ、スレーブそれぞれ以下のように接続しました。
SS – Pin10
MOSI – Pin11
MISO – Pin12
SCK – Pin13
スケッチの内容
Arduinoでは、SPI.hをインクルードして、SPIライブライが利用出来ます。以下のようなスケッチを書きました。処理としては、マスタ側のIDEのシリアルモニタから入力した文字をそのままスレーブ側から送信します。このようなスケッチで単純な通信の確認が出来たという内容です。
マスタ側
/*
* SPIマスタ
* SS - Pin10
* MOSI - Pin11
* MISO - Pin12
* SCK - Pin13
*/
#include <SPI.h>
#define SSPin 10
void setup() {
Serial.begin (9600);
Serial.println("Master");
pinMode(SS,OUTPUT); //SSピンを出力設定
SPI.setBitOrder(MSBFIRST); //最上位ビット(MSB)から送信
SPI.setClockDivider(SPI_CLOCK_DIV4); //通信速度をデフォルト
SPI.setDataMode(SPI_MODE2); //アイドル5Vで0V→5Vの変化で送信する
SPI.begin(); //開始
}
void loop() {
char snd;
char rcv;
//UARTから読み込み
snd = Serial.read();
//データがあれば送信
if(snd != -1){
//このSlaveをセレクトする
//(マスタ通信は有効です)
SetSSPin(LOW);
//送信
SPI.transfer(snd);
delay(100);
//受信
rcv = SPI.transfer(0);
//データ確認
Serial.println(rcv);
}
}
/*
* SSピンの設定
* Lowでマスタからの通信が有効(セレクト)
* (Highではマスタから通信出来ない(無視される)セレクト解除の状態)
*/
void SetSSPin(int val)
{
digitalWrite(SSPin, val);
}
SPIライブラリを利用していますので、詳細が必要な場合は上記の参考にしたサイトを参照して頂ければと思いますが、要点は以下です。
初期設定で以下を設定しています。
SSピンを出力
ビット送信の順序を最上位ビット(MSBFIRST)
通信速度をデフォルト(SPI_CLOCK_DIV4)
送信のタイミングをアイドル5Vで0V→5Vの変化で送信(SPI_MODE2)
loop() では、UARTで文字入力があれば、スレーブ側へ送信しています。ここでの要点は、やはりSSピンの設定です。SSピンをLOWに設定してこのマスタをスレーブの通信を有効にしています。実際には必要なタイミングに応じてHIGHにも設定
してまたLOWに設定する処理になると思います。ここでは単純にLOWに設定しているだけです。これだけでここでは通信が出来ました。
スレーブ側
/*
* SPIスレーブ
* SS - Pin10
* MOSI - Pin11
* MISO - Pin12
* SCK - Pin13
*/
#include <SPI.h>
void setup() {
pinMode(MISO,OUTPUT); //MISOを出力
SPI.setBitOrder(MSBFIRST); //最上位ビット(MSB)から送信
SPI.setClockDivider(SPI_CLOCK_DIV4); //通信速度をデフォルト
SPI.setDataMode(SPI_MODE2); //アイドル5Vで0V→5Vの変化で送信する
SPCR |= _BV(SPE);
SPI.attachInterrupt();
}
// SPI割り込み処理
ISR(SPI_STC_vect)
{
byte ch = SPDR; //SPIの受信バッファから取得
SPDR = ch; //送信バッファに書き込む
}
void loop() {
// put your main code here, to run repeatedly:
}
MISOを出力に設定して、マスタ側と同様に初期設定をして割り込みを有効にしています。割り込み処理で受信データをそのまま送信しています。部分的にレジスタも使っています。(※必要な場合は以下等を参照して下さい。)
SPI関連レジスタ
動作確認
以下のようにマスタ側のシリアルモニタで確認出来ました。Hello!と送信すると1文字ずつ返ってきました。この時にシリアルモニタの下の部分で「改行なし」に設定すると分かりやすいです。

これで単純な通信がとりあえずは確認出来たということで、次回は以下で実際のデバイスの温度センサADT7310を使ってみます。
Arduino UnoでSPI通信(その2)温度センサADT7310