SPI の解説と実装リファレンス
【概要】
SPI(Serial Peripheral Interface)は、複数のデバイス間でシリアルデータを高速に転送するための同期型通信プロトコルです。基本的には「マスタ(Master)」と「スレーブ(Slave)」の構成をとります。マスタはクロック信号(SCK)を生成し、スレーブはそれに同期してデータをやり取りします。
SPIは「フルデュプレックス(全二重)」通信をサポートしており、マスタからスレーブへのデータ送信(MOSI)と、スレーブからマスタへのデータ受信(MISO)を同時に行うことができます。また、複数のスレーブを接続する場合、各スレーブを個別に選択するCS(Chip Select)またはSS(Slave Select)ピンを用いてデバイスを識別します。
【仕様】
ArduinoのSPIライブラリは、ハードウェアSPIコントローラを抽象化して操作するためのインターフェースを提供します。
主要な関数とメソッド
SPI.beginTransaction(SPISettings settings)- 引数:
SPISettingsオブジェクト(ビットレート、ビット順序、SPIモードを指定)。 - 動作: 通信設定を適用し、バスの所有権を確保します。
SPI.transfer(uint8_t data)- 引数: 送信する8ビットデータ。
- 戻り値: 同時に受信した8ビットデータ。
- 動作: 送信と受信を同時に実行します。同期的な処理であるため、1バイトの転送が完了するまで待機します。
SPI.endTransaction()- 動作: 通信設定を解除し、バスを解放します。
通信パラメータ
- Clock Speed: 多くのハードウェアで数MHz〜数十MHzまで対応。
- Bit Order:
LSBFIRST(低位ビット優先)またはMSBFIRST(高位ビット優先)。 - SPI Mode:
SPI_MODE0: CPOL=0, CPHA=0SPI_MODE1: CPOL=0, CPHA=1SPI_MODE2: CPOL=1, CPHA=0SPI_MODE3: CPOL=1, CPHA=1
【技術的特徴】
- I2Cとの比較
配線数: I2Cは2線(SDA, SCL)ですが、SPIは最低4線(MOSI, MISO, SCK, CS)を必要とします。 - 通信速度: SPIはI2Cよりも高速な転送が可能であり、特に大容量データの転送に適しています。
- アドレス指定: I2Cはソフトウェア的なアドレス指定を行いますが、SPIはハードウェア的なCSピンの物理的な制御によってデバイスを選択します。
ハードウェアSPI vs ソフトウェアSPI(ビットバンギング)
- ハードウェアSPI: 専用のコントローラを使用するため、正確なクロックタイミングを保証し、CPU負荷を抑えて高速通信を実現します。
- ソフトウェアSPI: GPIOの操作をソフトウェアで制御します。実装は容易ですが、クロック精度の維持が難しく、転送速度もCPU速度に依存するため低速です。
【注意点】
CS(Chip Select)ピンの制御
ArduinoのSPIライブラリは、CSピンの制御を自動で行いません。エンジニアは手動でdigitalWrite()を使用して、対象のスレーブを選択(LOW)および解放(HIGH)を制御する必要があります。
転送のブロッキング性
SPI.transfer()は同期的な関数です。転送中、CPUは次の命令に進まず待機するため、極端に高い周波数での転送や、長いデータの転送はメインループの実行を阻害する可能性があります。DMA(Direct Memory Access)の利用を検討する場合、標準のSPIライブラリではなく、プラットフォーム固有のDMA対応APIを使用する必要があります。
リソースの競合
複数のデバイスが同一のハードウェアSPIバスを共有する場合、一つのデバイスへの通信中に他のデバイスへの通信を試みると、データが破損する可能性があります。必ずbeginTransaction()とendTransaction()を適切に使用し、排他制御を意識する必要があります。
【サンプルコード】
以下は、一般的なセンサー(例:ADCや外部メモリ)に対して、マスタとして1バイトを送信・受信する基本構造です。
#include <SPI.h>
const int slaveSelectPin = 10;
void setup() {
pinMode(slaveSelectPin, OUTPUT);
digitalWrite(slaveSelectPin, HIGH); // 初期状態はHigh
SPI.begin();
}
void loop() {
// 通信設定: 1MHz, MSB優先, SPI_MODE0
SPISettings settings(1000000, MSBFIRST, SPI_MODE0);
SPI.beginTransaction(settings);
digitalWrite(slaveSelectPin, LOW); // スレーブ選択
byte sent = 0x42;
byte received = SPI.transfer(sent);
digitalWrite(slaveSelectPin, HIGH); // スレーブ解放
SPI.endTransaction();
// 処理の待機
delay(1000);
}
【活用例】
- ディスプレイ制御: TFT液晶やOLEDディスプレイへの高解像度画像データの描画。
- ストレージデバイス: SDカード(SDIOプロトコル)や外部NOR/NANDフラッシュメモリの読み書き。
- 高精度センサー: 高速なサンプリングが必要な加速度センサーやインカメラ(CIS)との通信。
- データ変換: 高速なADC(アナログ-デジタル変換)やDAC(デジタル-アナログ変換)モジュールの制御。


コメント