基礎 · 読了まで14分

SPIとは?ArduinoのMOSI、MISO、SCK、CSピンをわかりやすく解説

SPIとは何か、どのように動くのかを解説します。MOSI、MISO、SCK、CSピン、SPIモードと速度、複数デバイスの接続、Uno、Mega、ESP32、Picoのピン、SDカードとRFIDの競合まで扱います。

SPI(Serial Peripheral Interface)は、マイコンがSDカード、RFIDリーダー、カラーTFTディスプレイ、無線モジュールなどのデバイスと4本の線で非常に高速にデータをやり取りするための通信方式です。クロック線(SCK)、方向ごとに分かれた2本のデータ線(MOSIとMISO)、そして通信相手のデバイスを選ぶCS線があります。I2Cより数十倍速くできるため、大量のデータを扱うモジュールでよく使われます。

この記事では、SPIのしくみ、ピン名と新しい呼び方、4つのSPIモード、速度の上限、Arduino Uno、Mega、ESP32(VSPIとHSPI)、Raspberry Pi Picoのピン、同じバスに複数のデバイスをつなぐ方法、そしてSDカードとRC522 RFIDリーダーを一緒に使うとトラブルが起きる理由を説明します。最後のセクションでは、SPIをI2CやUARTと比較します。

SPIとは?どのように動くのか

SPIには必ず1つのコントローラー(以前はマスターと呼ばれていました)と、1つ以上のペリフェラル(以前はスレーブと呼ばれていました)があります。クロックを作るのはコントローラーだけです。つまり、Arduinoが通信を始め、速度を決め、通信を終わらせます。クロックパルスが1回出るたびに、コントローラーはMOSIで1ビットを送り、同時にペリフェラルはMISOで1ビットを返します。両側のシフトレジスタは一種の輪のようになっていて、8回のクロックパルスのあとには、コントローラーのバイトがペリフェラルへ、ペリフェラルのバイトがコントローラーへ移っています。そのためSPIは全二重(full duplex)で、読み取りと書き込みが同時に行われます。

信号線旧名称新名称方向役割
SCK / SCLKSerial Clock(シリアルクロック)SCKコントローラー → ペリフェラルクロック信号。パルスごとに1ビットが移動する
MOSIMaster Out Slave In(マスター出力・スレーブ入力)COPI / SDOコントローラー → ペリフェラルArduinoからモジュールへ送るデータ
MISOMaster In Slave Out(マスター入力・スレーブ出力)CIPO / SDIペリフェラル → コントローラーモジュールからArduinoへ戻るデータ
CS / SSSlave Select(スレーブ選択)CS(Chip Select、チップセレクト)コントローラー → ペリフェラルLOWの間そのモジュールが受信する。デバイスごとに専用のCSを使う

モジュールに印刷されている表記は統一されておらず、これがいちばん混乱しやすいポイントです。RC522 RFIDモジュールではCS線がSDAと表記され(I2CのSDAとは関係ありません)、nRF24L01ではCSN、MCP3008ではCS/SHDN、ディスプレイによってはDIN(MOSI)、CLK(SCK)、DOUT(MISO)といった名前も見かけます。ルールはシンプルで、データ入力(DIN、SDI、MOSI)はArduinoのMOSIへ、データ出力(DOUT、SDO、MISO)はArduinoのMISOへつなぎます。

  1. コントローラーは、通信したいデバイスのCS線をLOWにします。ほかのデバイスはすべてCSがHIGHのままで、MISO線を手放します。
  2. コントローラーがSCKでクロックパルスを出し始めます。
  3. パルスごとにMOSIから1ビットが出て、MISOから1ビットが入ります。ビットの順番は、たいてい最上位ビットから(MSB first)です。
  4. 必要なバイト数(コマンド、アドレス、データ)を転送し終えると、コントローラーはCSをHIGHに戻し、デバイスが処理を完了します。

SPIモード:CPOLとCPHA

SPIには単一の規格がなく、アイドル時にクロックがどのレベルで止まっているか(CPOL、クロック極性)と、クロックのどちらのエッジでデータを読むか(CPHA、クロック位相)がデバイスによって異なります。この2つの設定の4通りの組み合わせが、4つのSPIモードです。コントローラーとデバイスのモードが合っていないと、データが1ビットずれて意味のない値が読めてしまいます。幸い、ほとんどのモジュールはモード0を使います。

モードCPOLCPHAアイドル時のクロックデータを読むエッジデバイスの例
SPI_MODE000LOW立ち上がりSDカード、RC522、nRF24L01、MAX7219、MCP3008、74HC595
SPI_MODE101LOW立ち下がり一部のADCやドライバIC
SPI_MODE210HIGH立ち下がりほとんど使われない
SPI_MODE311HIGH立ち上がりADXL345加速度センサー、CSピンのない一部のST7789ディスプレイ
モジュールから毎回0xFFや0x00、あるいは読むたびに違うでたらめな値しか返ってこない場合は、まずデータシートでSPIモードと最大クロック速度を確認してください。速度を1 MHzまで下げて試してみると、原因が配線なのか設定なのかがすぐにわかります。

SPIの速度とケーブルの長さ

SPIでは速度を決めるのはコントローラーで、上限はたいていデバイス側にあります。Arduino Unoは16 MHzのクロックの半分にあたる8 MHzまで出せます。SPIライブラリのデフォルトは4 MHzです。ESP32は専用のSPIピン(IO_MUX)で最大80 MHzのクロックを出せますが、GPIOマトリクス経由でほかのピンに信号を回すと上限が下がります。Raspberry Pi Picoは125 MHzのシステムクロックで約62.5 MHzまで出せます。高速なSPI信号には短い配線が必要です。ブレッドボードと10〜20 cmのジャンパーワイヤーなら数MHzは問題ありませんが、20〜30 MHz以上では配線を短くし、できればはんだ付けで接続してください。数メートルの長さはSPIには向いていません。

デバイス役割最大SPIクロック(データシート)SPIモード
microSDカードデータ記録起動時は400 kHz、その後25 MHzまでモード0
RC522 (MFRC522)RFIDリーダー10 MHzモード0
nRF24L012.4 GHz無線10 MHzモード0
MAX7219LEDマトリクス / 7セグメント10 MHzモード0
MCP30088チャンネル10ビットADC5 Vで3.6 MHz、2.7 Vで1.35 MHzモード0または3
ILI93412.4〜3.2インチのカラーTFT書き込み時10 MHz(実際にはもっと高速で駆動される)モード0

SPIピン:Arduino Uno、Mega、ESP32、Raspberry Pi Pico

信号線Arduino Uno / NanoArduino MegaESP32 VSPI(デフォルト)ESP32 HSPIRaspberry Pi Pico SPI0
SCKD13D52GPIO 18GPIO 14GP18
MOSID11D51GPIO 23GPIO 13GP19 (TX)
MISOD12D50GPIO 19GPIO 12GP16 (RX)
CS(デフォルト)D10D53GPIO 5GPIO 15GP17
ロジックレベル5 V5 V3.3 V3.3 V3.3 V

Unoでは、同じSPI信号がボードの端にある6ピンのICSPヘッダーにも出ています。LeonardoやMicroのようなボードでは、SPIはICSPからしか取れません。CSにD10を使う必要はなく、好きなデジタルピンを使えます。ただし、UnoではD10を入力のままにしないでください。D10が入力の状態でSPIハードウェアがLOWを読むと、コントローラーモードから外れてしまいます。SPI.begin()はこのピンを自動的に出力にするので、ほかの用途のために入力に変えないようにしましょう。ESP32では、ArduinoのSPIオブジェクトはデフォルトでVSPIを使います。GPIOマトリクスのおかげで、SPIをほぼ好きなピンに移すこともできます。Picoでは、SPI0とSPI1をいくつかの異なるピングループに割り当てられます(例:SPI1ならGP10、GP11、GP12)。詳しくは「ピン配置」ツールを見てください。

RC522、nRF24L01、そしてSDカード自体は3.3 Vのデバイスです。Unoの5 V信号に直接つなぐと、時間とともにデバイスが壊れることがあります。市販のSDカードモジュールの多くにはレベル変換回路が載っていますが、RC522とnRF24L01は3V3ピンから給電し、信号線にはレベル変換モジュールを使うことを検討してください。ESP32とPicoはもともと3.3 Vで動くので、これらのデバイスと直接接続できます。

ArduinoのSPIライブラリ:基本の関数

関数何をするか
SPI.begin()SPIハードウェアを開始し、SCK、MOSI、SSピンを出力にします。
SPISettings(速度, MSBFIRST, SPI_MODE0)デバイスの速度、ビット順、モードを1つのオブジェクトにまとめます。
SPI.beginTransaction(設定)設定を適用し、バスをこのデバイス用に確保します。設定の異なるデバイス間の切り替えを安全にします。
SPI.transfer(バイト)1バイトを送信し、同時に受信したバイトを返します。
SPI.transfer16(値)16ビットの値を一度に送受信します。
SPI.transfer(バッファ, 長さ)配列を送信します。受信したバイトは同じ配列に上書きされます。
SPI.endTransaction()バスを解放します。CSをHIGHにしたあとに呼び出します。

最初の例:SPIで74HC595にデータを送る

SPIを理解するいちばん簡単な方法は、74HC595シフトレジスタを使うことです。595は実はMISOのない、もっともシンプルなSPIペリフェラルだからです。ArduinoのMOSIピンを595のDS入力に、SCKをSH_CPに、D10をラッチ(latch)の役割をするST_CPにつなぎます。shiftOut()関数と違い、ハードウェアSPIはバイトを数マイクロ秒で送ります。

C++ (Arduino IDE)
1// Donanım SPI ile 74HC595: 8 LED'i iki kabloyla sürmek / 74HC595 over hardware SPI: 8 LEDs from two data wires
2// D11 (MOSI) -> DS (14), D13 (SCK) -> SH_CP (11), D10 -> ST_CP / latch (12)
3#include <SPI.h>
4
5const int LATCH_PIN = 10;     // 595'te CS gibi davranır / acts like CS on the 595
6
7void write595(byte value) {
8  SPI.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0));  // 4 MHz, mod 0 / 4 MHz, mode 0
9  digitalWrite(LATCH_PIN, LOW);
10  SPI.transfer(value);                 // 8 biti gönder / shift out 8 bits
11  digitalWrite(LATCH_PIN, HIGH);       // yükselen kenarda çıkışlara aktar / latch on the rising edge
12  SPI.endTransaction();
13}
14
15void setup() {
16  pinMode(LATCH_PIN, OUTPUT);
17  digitalWrite(LATCH_PIN, HIGH);
18  SPI.begin();
19}
20
21void loop() {
22  for (int i = 0; i < 8; i++) {        // yürüyen ışık / running light
23    write595(1 << i);
24    delay(100);
25  }
26}

SPIでデータを読む:MCP3008 ADC

全二重通信を確かめるために、MCP3008を使ってみましょう。このICはArduinoに10ビットのアナログ入力を8つ追加します。ESP32の非線形なADCや、3つしかないPicoのアナログ入力の代わりとしても優れています。アナログ読み取りの基本はADCとは?の記事で説明しました。配線:CLK → D13、DOUT → D12、DIN → D11、CS → D10、VDDとVREF → 5V、AGNDとDGND → GND。

C++ (Arduino IDE)
1// MCP3008 (8 kanal, 10 bit ADC) okuma / Reading an MCP3008 (8-channel 10-bit ADC)
2// D13 -> CLK, D12 <- DOUT, D11 -> DIN, D10 -> CS/SHDN
3#include <SPI.h>
4
5const int CS_PIN = 10;
6SPISettings mcpSettings(1000000, MSBFIRST, SPI_MODE0);   // 1 MHz, mod 0 / 1 MHz, mode 0
7
8int readMCP3008(byte channel) {
9  SPI.beginTransaction(mcpSettings);
10  digitalWrite(CS_PIN, LOW);                   // cihazı seç / select the chip
11  SPI.transfer(0x01);                          // başlangıç biti / start bit
12  byte high = SPI.transfer(0x80 | (channel << 4));   // tek uçlu + kanal / single-ended + channel
13  byte low = SPI.transfer(0x00);               // kalan 8 bit / remaining 8 bits
14  digitalWrite(CS_PIN, HIGH);                  // seçimi bırak / release the chip
15  SPI.endTransaction();
16  return ((high & 0x03) << 8) | low;           // 10 bitlik sonuç / 10-bit result
17}
18
19void setup() {
20  Serial.begin(9600);
21  pinMode(CS_PIN, OUTPUT);
22  digitalWrite(CS_PIN, HIGH);                  // başta seçili değil / not selected at start
23  SPI.begin();
24}
25
26void loop() {
27  int raw = readMCP3008(0);
28  float volts = raw * 5.0 / 1023.0;
29  Serial.print("CH0: ");
30  Serial.print(raw);
31  Serial.print("  ");
32  Serial.print(volts, 2);
33  Serial.println(" V");
34  delay(500);
35}

ここでは3バイトを送り、同時に3バイトを受け取っています。1バイト目(0x01)はスタートビットだけです。2バイト目ではシングルエンド測定とチャンネル番号を送り、その間にMCP3008が結果の上位2ビットを返します。3バイト目は0x00で、中身に意味はありません。クロックを出して結果の残り8ビットを受け取るためだけに送ります。SPIでは「読む」ことは、つねに何かを送ることを意味します。結果は0〜1023の範囲で、VREF(ここでは5 V)を基準に電圧に換算します。

複数のSPIデバイス:CS線を分ける

SPIとI2Cのいちばん大きな違いは、SPIがアドレスを使わないことです。デバイスはSCK、MOSI、MISOの線を共有し、それぞれにArduinoから専用のCS線を1本ずつ引きます。CSがLOWになっているデバイスだけが受信し、そのデバイスだけがMISOにデータを書き込みます。デバイス3台なら3 + 3 = 6本、5台なら3 + 5 = 8本の線が必要で、デバイスの数に応じてピン数が増えます。一方、74HC595やMAX7219のような一部のICは縦続接続(デイジーチェーン)ができます。1つの出力を次の入力につなぎ、すべてを1本のCSで駆動します。

  • 同時にLOWにするCSは1本だけにしてください。2つのデバイスを同時に選ぶとMISO線で衝突し、両方から壊れたデータを読むことになります。
  • setup()では、まずすべてのCSピンを出力にしてHIGHにし、それからライブラリを開始します。浮いた状態のCSピンは偶然LOWに見えて、バスを占有してしまうことがあります。
  • デバイスごとにbeginTransactionを使いましょう。SDカードは25 MHz、MCP3008は1 MHzを必要とすることがありますが、トランザクションを使えば切り替えのたびに正しい速度とモードが自動で適用されます。
  • 割り込みの中でSPIを使う場合は、SPI.usingInterrupt()でライブラリに知らせるか、割り込みの中でSPIの処理をしないようにしてください。
  • 各デバイスのCS線に10 kΩのプルアップ抵抗を追加すると、Arduinoの起動中やリセット中にデバイスが勝手に選択されるのを防げます。

SDカードとRFIDを同じバスに:なぜ動かないのか

Arduinoのフォーラムでよく見る質問があります。RC522 RFIDは単体で動き、SDカードモジュールも単体で動くのに、両方を同じSPIバスにつなぐと片方、または両方が応答しなくなるというものです。ソフトウェアでCSピンが正しく設定されていても、原因はたいていハードウェアにあります。安価なSDカードモジュールの多くには、5 V信号を3.3 Vに変換する74LVC125バッファICが載っていて、このICのMISO線を駆動するチャンネルは常に有効になっています。そのため、SDカードのCSがHIGHでもモジュールはMISO線を手放さず、RC522が応答しようとすると、2つの出力が線を取り合うことになります。

  • 適切なモジュールを選ぶ:CSがHIGHのときにMISOをハイインピーダンスにするモジュールなら、問題なくバスを共有できます。モジュールが線を手放すかどうかは、CSをHIGHにした状態でMISOを10 kΩで最初はGNDへ、次に5 Vへ引き、テスターで測ると確認できます。
  • バッファのイネーブル端子をCSにつなぐ:はんだ付けに慣れているなら、74LVC125のMISOチャンネルのOE端子をGNDから外し、モジュールのCS線につなぐことで根本的に解決できます。
  • MISOに直列抵抗を入れる:SDモジュールのMISO出力に1 kΩ程度の直列抵抗を入れると、RC522が線の電位を押し切れるようになります。簡単ですが、速度が制限される応急処置です。
  • 別のSPIバスを使う:ESP32なら、SDカードをVSPIに、RC522をHSPIにつなげば2つのデバイスが干渉することはありません。Unoには2つ目のハードウェアSPIがないので、この方法は使えません。
  • 初期化の順番に注意する:まずすべてのCSをHIGHにし、次にSPI.begin()、そのあとSD.begin()とPCD_Init()を呼び出します。下の例はこの順番に従っています。
C++ (Arduino IDE)
1// Aynı SPI hattında SD kart ve RC522 RFID / SD card and RC522 RFID on the same SPI bus
2// Ortak / shared: D11 MOSI, D12 MISO, D13 SCK. CS: SD -> D4, RC522 SDA(SS) -> D10, RC522 RST -> D9
3#include <SPI.h>
4#include <SD.h>
5#include <MFRC522.h>
6
7const int SD_CS = 4;
8const int RFID_CS = 10;
9const int RFID_RST = 9;
10
11MFRC522 rfid(RFID_CS, RFID_RST);
12
13void setup() {
14  Serial.begin(9600);
15  // Önce TÜM CS pinlerini HIGH yap: hiçbir cihaz hattı meşgul etmesin
16  // First drive ALL CS pins HIGH so no device grabs the bus
17  pinMode(SD_CS, OUTPUT);
18  digitalWrite(SD_CS, HIGH);
19  pinMode(RFID_CS, OUTPUT);
20  digitalWrite(RFID_CS, HIGH);
21
22  SPI.begin();
23  if (!SD.begin(SD_CS)) {
24    Serial.println(F("SD bulunamadi / SD not found"));
25  }
26  rfid.PCD_Init();
27  Serial.println(F("Karti okut / Scan a card"));
28}
29
30void loop() {
31  if (!rfid.PICC_IsNewCardPresent() || !rfid.PICC_ReadCardSerial()) return;
32
33  String uid = "";
34  for (byte i = 0; i < rfid.uid.size; i++) {
35    if (rfid.uid.uidByte[i] < 0x10) uid += "0";
36    uid += String(rfid.uid.uidByte[i], HEX);
37  }
38  rfid.PICC_HaltA();
39
40  File f = SD.open("kartlar.txt", FILE_WRITE);   // kütüphane CS'yi kendisi yönetir / the library handles CS itself
41  if (f) {
42    f.print(millis());
43    f.print(";");
44    f.println(uid);
45    f.close();
46    Serial.println("Kaydedildi / Saved: " + uid);
47  } else {
48    Serial.println(F("Dosya acilamadi / File open failed"));
49  }
50}

このコードでは、SDライブラリとMFRC522ライブラリがそれぞれ自分のCSピンをLOWやHIGHにします。必要なのは、最初に両方をHIGHにしておくことと、ハードウェアがMISOを正しく共有できるようにしておくことだけです。コードは読み取ったカードのUIDを、時刻の情報と一緒にkartlar.txtファイル(kartlarはトルコ語で「カード」)に書き込みます。Unoではこの2つのライブラリを合わせるとRAMの約半分を使います。F()マクロは定数の文字列をフラッシュに置くことで空きを作ります。

ESP32のVSPIとHSPI:2つの独立したSPIバス

ESP32の内部には4つのSPIユニットがあります。SPI0とSPI1は内蔵フラッシュメモリ用に予約されているため、使えるのはVSPI(SPI3)とHSPI(SPI2)です。両方を使うのは、速度やモードが大きく異なるデバイスを分けたり、上で説明したMISOの競合を避けたりするいちばんすっきりした方法です。Arduinoコアでは、2つ目のバス用にSPIClassオブジェクトを作り、begin()でピンを指定します。下のコードは、SDカードをデフォルトのVSPIで、MCP3008をHSPIで動かし、毎秒の測定値をCSVファイルに追記します。

C++ (Arduino IDE)
1// ESP32: iki ayrı SPI hattı / two separate SPI buses
2// VSPI (varsayılan SPI / default SPI): SD kart / SD card -> SCK 18, MISO 19, MOSI 23, CS 5
3// HSPI: MCP3008 -> SCK 14, MISO 12, MOSI 13, CS 15
4#include <SPI.h>
5#include <SD.h>
6
7const int SD_CS = 5;
8const int ADC_CS = 15;
9
10SPIClass hspi(HSPI);                              // ikinci donanım SPI / second hardware SPI
11SPISettings mcpSettings(1000000, MSBFIRST, SPI_MODE0);
12
13int readMCP3008(byte channel) {
14  hspi.beginTransaction(mcpSettings);
15  digitalWrite(ADC_CS, LOW);
16  hspi.transfer(0x01);
17  byte high = hspi.transfer(0x80 | (channel << 4));
18  byte low = hspi.transfer(0x00);
19  digitalWrite(ADC_CS, HIGH);
20  hspi.endTransaction();
21  return ((high & 0x03) << 8) | low;
22}
23
24void setup() {
25  Serial.begin(115200);
26  pinMode(ADC_CS, OUTPUT);
27  digitalWrite(ADC_CS, HIGH);
28  hspi.begin(14, 12, 13, ADC_CS);                 // SCK, MISO, MOSI, SS
29
30  if (!SD.begin(SD_CS)) {                         // SD varsayılan VSPI'yi kullanır / SD uses the default VSPI
31    Serial.println("SD bulunamadi / SD not found");
32  }
33}
34
35void loop() {
36  int raw = readMCP3008(0);
37  File f = SD.open("/olcum.csv", FILE_APPEND);    // ekle kipinde aç / open in append mode
38  if (f) {
39    f.printf("%lu,%d\n", millis(), raw);
40    f.close();
41  }
42  Serial.printf("CH0: %d\n", raw);
43  delay(1000);
44}
HSPIのMISOピンであるGPIO 12は、ESP32の起動用(ストラッピング)ピンの1つです。起動時にHIGHだと、ボードはフラッシュが1.8 Vで動くと判断して起動しません。MISOにプルアップ抵抗が付いたモジュール(多くのSDカードモジュールなど)はHSPIにつながないか、MISOを別のピンに移してください。この例でSDカードをVSPIに置いているのはそのためです。

Raspberry Pi PicoでSPIを使う(MicroPython)

Picoには2つのハードウェアSPIユニット(SPI0とSPI1)があり、MicroPythonではmachine.SPIクラスで使います。polarityとphaseのパラメーターはCPOLとCPHAに対応し、両方が0ならモード0です。CSは普通の出力ピンとして自分で管理します。write_readinto()関数は、SPIの全二重のしくみをいちばんよく表す関数で、1つの配列を送りながら、受信したバイトを別の配列に書き込みます。同じMCP3008を、今度は3.3 Vで給電して読んでみましょう。

MicroPython
1# Raspberry Pi Pico: SPI0 ile MCP3008 okuma / reading an MCP3008 on SPI0
2# GP18 -> CLK, GP19 -> DIN (MOSI), GP16 <- DOUT (MISO), GP17 -> CS. MCP3008 VDD ve VREF 3V3 / VDD and VREF on 3V3
3from machine import Pin, SPI
4import time
5
6spi = SPI(0, baudrate=1_000_000, polarity=0, phase=0,   # mod 0 / mode 0
7          sck=Pin(18), mosi=Pin(19), miso=Pin(16))
8cs = Pin(17, Pin.OUT, value=1)                          # başta seçili değil / not selected at start
9
10def read_mcp3008(channel):
11    tx = bytearray([0x01, 0x80 | (channel << 4), 0x00])
12    rx = bytearray(3)
13    cs.value(0)                                          # cihazı seç / select the chip
14    spi.write_readinto(tx, rx)                           # aynı anda gönder ve al / send and receive at once
15    cs.value(1)
16    return ((rx[1] & 0x03) << 8) | rx[2]
17
18while True:
19    raw = read_mcp3008(0)
20    print("CH0:", raw, "{:.2f} V".format(raw * 3.3 / 1023))
21    time.sleep(0.5)

SPI、I2C、UARTの比較

特徴SPII2CUART
線の数4本(デバイスを1台追加するごとにCSが1本増える)2本(SDA、SCL)2本(TX、RX)
一般的な速度1〜80 MHz100 kHz・400 kHz・1 MHz9600〜115200ボー(数Mbit/sまで)
クロックあり(コントローラーが生成)あり(コントローラーが生成)なし。両側が同じボーレートに合わせる
方向全二重半二重全二重
デバイスの選択専用のCS線7ビットのアドレス2台のデバイスのみ(1対1)
追加の部品不要プルアップ抵抗不要
距離短い(基板上、数十cm程度)短い(1 m程度)数メートル。RS-485ならはるかに長い
代表的なモジュールSDカード、RC522、TFT、nRF24L01、MAX7219OLED、I2C LCDバックパック、RTC、BME280、MPU6050GPS、HC-05、SIM800L、ESP-01、パソコン

どれを使うかはたいていモジュールで決まりますが、自分で設計するときは次のルールが役立ちます。速度とデータ量が重要なら(ディスプレイの画像、ファイル、音声)SPI、少ない線で遅いセンサーをたくさんつなぐならI2C、2台のデバイスをテキストベースのシンプルな接続でつなぎたいならUARTです。UARTの詳細はUARTのシリアル通信の記事にあります。1つのプロジェクトで3つすべてを使うこともよくあります。たとえば、I2Cでセンサーを読み、SPIでSDカードに書き込み、UARTでGPSから位置を取得するデータロガーです。

SPIを使うモジュールとプロジェクトのアイデア

  • カード式のドアロックと入室記録:RC522でカードを読み、日付とUIDをSDカードに書き込みます。
  • 温度ロガー:センサーのデータをCSVとしてmicroSDカードに記録し、Excelでグラフにします。
  • スクロール表示の電光掲示板:縦続接続したMAX7219 LEDマトリクスモジュールに時刻やメッセージを表示します。
  • カラーのインターフェース:ILI9341 TFTディスプレイにグラフィックやメニューを描き、タッチパネルも同じSPIバスにつなぎます。
  • ワイヤレスリモコン:2つのnRF24L01モジュールで、ロボットカーにコマンドを送ります。
  • 多チャンネルの測定:MCP3008を使って、ポテンショメーター8個のMIDIコントローラーや、土壌水分センサー8個の水やりシステムを作ります。

よくある間違いと対処法

症状考えられる原因対処法
いつも0xFFか0x00が読めるMISOが未接続、MOSIとMISOが逆、またはデバイスに電源が来ていないDINをMOSIに、DOUTをMISOにつなぎ、デバイスのVCCを測る
値がでたらめ、または1ビットずれているSPIモードが違う、またはデバイスにとってクロックが速すぎるデータシートのモードを使い、速度を1 MHzに下げて再度試す
単体では動く2つのデバイスが一緒だと動かない片方のモジュールがMISOを手放さない、または起動時にCSピンがHIGHになっていないまずすべてのCSをHIGHにする。MISOを手放さないモジュールを交換するか、別のSPIバスを使う
UnoでSPIが突然止まるD10を入力として使い、LOWに引いていたD10は出力のままにする。CSとして使うこともできる
HSPIをつなぐとESP32が起動しない起動時にGPIO 12がHIGHになっているプルアップ付きのMISOをVSPIに移すか、MISOに別のピンを選ぶ
長い配線でランダムにエラーが出る高速な信号での反射とノイズ配線を短くし、速度を下げ、GND線を信号線に沿わせて配線する
SPIで何が起きているかを見るには、安価な8チャンネルのロジックアナライザーがとても役立ちます。SCK、MOSI、MISO、CSを同時に記録し、どのバイトが送られ、どのバイトが返ってきたかをデコードしてくれます。「コードは正しいのに動かない」と思ったとき、原因が配線なのか設定なのかを数秒で示してくれます。

SPIは、SDカードからカラーディスプレイまで、Arduinoの世界の高速モジュールに共通する言葉です。4本の線、CSの考え方、4つのモードを覚えれば、新しいSPIモジュールの接続は数分で終わります。BlueGraysアプリでは、RFIDカードの読み取りとドアロック、microSD温度ロガー、MAX7219ドットマトリクス時計のプロジェクトを、回路図とコード付きでステップごとに紹介しています。

よくある質問

SPIは何の略ですか?

SPIはSerial Peripheral Interface(シリアル・ペリフェラル・インターフェース)の略です。コントローラーがSCK、MOSI、MISO、CSの4本の線で、ペリフェラルと高速かつ同期的にデータをやり取りするための方式です。

MOSIとMISOとは何ですか?

MOSI(Master Out Slave In)はコントローラーからペリフェラルへ向かうデータ線で、MISO(Master In Slave Out)はペリフェラルからコントローラーへ戻るデータ線です。新しい呼び方ではCOPIとCIPOとも呼ばれます。モジュールでは、たいていDINをMOSIに、DOUTをMISOにつなぎます。

Arduino UnoのSPIピンはどれですか?

Arduino UnoとNanoでは、SCKがD13、MISOがD12、MOSIがD11、デフォルトのCSがD10です。同じ信号はボード上の6ピンのICSPヘッダーにも出ています。Arduino Megaでは、SCKがD52、MISOがD50、MOSIがD51、CSがD53です。

SPIとI2Cの違いは何ですか?

SPIは4本の線を使う全二重の方式で、数十MHzまで速くできますが、デバイスごとに専用のCS線が必要です。I2Cは2本の線だけを使い、アドレスでデバイスを選びますが、100〜400 kHzのようにずっと低い速度で動作し、プルアップ抵抗が必要です。

1つのSPIバスに何台のデバイスをつなげますか?

理論上の上限はありません。デバイスごとにコントローラー側の空きCSピンが1本あれば十分です。ただし実際には、バスの容量と配線の長さが増えるにつれて、高速時に問題が出てきます。ピンが足りなくなったら、74HC138デコーダーでCS線を増やすか、縦続接続できるデバイスを使ってください。

SDカードとRFIDリーダーが同じSPIバスで動かないのはなぜですか?

安価なSDカードモジュールの多くに載っているレベル変換回路は、CSがHIGHでもMISO線を手放さず、RC522の応答を壊してしまいます。MISOを正しく手放すモジュールを使う、バッファICのイネーブル端子をCSにつなぐ、またはESP32で2つのデバイスを別々のSPIバスに分けることで解決できます。

全プロジェクトを見る
RFIDの値の読み取り
#53初級
プレミアム

RFIDの値の読み取り

建物のキーフォブや入館カードには、見えない識別番号が保存されています。このプロジェクトでは、その番号を読み取ります。カードをRFIDリーダーに近づけると、Arduinoがカードの番号をコンピューターの画面に表示します。カード式のドアや錠のプロジェクトで、どのカードに解錠を許可するかを決めるための第一歩です。

4.7(47)
61
RFID認証
#54中級
プレミアム

RFID認証

マンションのカード式入館システムと同じ仕組みです。カードをRFIDリーダーに近づけると、Arduinoがカードの番号を登録済みの番号と比較します。登録済みのカードなら緑色、未登録のカードなら赤色のLEDが3秒間点灯し、結果はコンピューターの画面にも表示されます。カード式のドアや錠のプロジェクトの基礎をここで学べます。

4.8(64)
83
ESP32 RFID RC522カードリーダー(LCD付き)
#123中級

ESP32 RFID RC522カードリーダー(LCD付き)

このプロジェクトでは、MFRC522を搭載したRC522 RFIDモジュールをSPIでESP32に接続し、カードやタグのUIDを読み取ります。ドアロック、会員システム、入退室管理などの基礎となる仕組みです。読み取ったUIDはLCDディスプレイに表示します。このバージョンでは、許可されたカードのリストとの照合は行いません。注意:RC522は3.3V専用です。5Vを供給するとモジュールが焼損します!

4.8(21)
39
ESP32のRFIDドアロック(RC522 + サーボモーター + OLED + ブザー + LED)
#137上級
プレミアム

ESP32のRFIDドアロック(RC522 + サーボモーター + OLED + ブザー + LED)

ESP32、RC522 RFIDモジュール、SG90サーボモーター、SSD1306 OLEDディスプレイ、アクティブブザー、LED2個を使ってスマートドアロックを作ります。カードを読み取ると、UIDをコード内に登録した許可済みカードの一覧と照合します。許可されたカードなら、サーボモーターを回して解錠し、緑色のLEDが点灯して短い音が2回鳴ります。5秒後に自動で施錠します。許可されていないカードでは赤色のLEDが点灯し、短い間隔で3回音を鳴らして警告し、読み取ったUIDをOLEDに表示します。実際のIoTセキュリティの仕組みを実践的に学べます。

4.8(9)
15
ESP32温度データロガー(DHT22 + DS1307 RTC + microSD + OLED)
#144上級
プレミアム

ESP32温度データロガー(DHT22 + DS1307 RTC + microSD + OLED)

ESP32で日時付きの温度・湿度記録システムを作ります!DHT22で1分ごとに温度と湿度を読み取り、DS1307 RTCから取得した日時とともにmicroSDカードへCSV形式で記録します。OLEDには最新の温度を大きな文字で表示し、湿度のパーセント値、日時、総記録数も表示します。SDカードの/temp_log.csvファイルはExcel、Google Sheets、Pythonのpandasで開いて分析できます。温室の監視、家や部屋の温度管理、実験室のデータ収集、食品の保管環境や養蜂箱の監視に適しています。

4.7(7)
12
ドットマトリクス時計とカレンダー(MAX7219、DS1307)
#84中級
プレミアム

ドットマトリクス時計とカレンダー(MAX7219、DS1307)

机や壁に置き、時刻と日付をスクロール表示する光るパネルです。8x32のLEDディスプレイには大きな数字の時計が表示され、コロンの2つの点が毎秒点滅します。毎分の初めに曜日と日付がスクロールし、その後、時計表示に戻ります。時計モジュールには専用の電池があるため、電源を抜いても時刻を保持します。

5.0(37)
59
32x8 LEDマトリクス4枚でアニメーションと文字表示
#90中級
プレミアム

32x8 LEDマトリクス4枚でアニメーションと文字表示

横長のLEDマトリクスモジュールを4枚上下に並べると、1,000個以上の光の点からなる正方形のディスプレイになります。まず映画のように文字が上から雨のように降り、続いてカーテンが開いて2行の文字がスクロールし、その間を波やひし形が流れていきます。一連の演出はちらつかず滑らかに動き、終わると自動で最初から繰り返します。

4.6(40)
66
ピクセルスネーク:8x8マトリクス
#88上級
プレミアム

ピクセルスネーク:8x8マトリクス

定番のスネークゲームを自分の手で作って遊びましょう。カラフルな8x8のLEDマトリクスで、ジョイスティックを使って緑のヘビを動かし、赤いリンゴを食べます。リンゴを食べるたびにヘビが長くなり、少しずつ速くなって、小さな数字ディスプレイのスコアも増えます。壁や自分のしっぽにぶつかると画面が赤く3回点滅し、ボタンを押すと再び始められます。

4.7(39)
57