|TNKS1407 解説
← 解説一覧へ

SPI と I²C ── 時計の線を、引く

2026.06.29

UART の次の一歩は、線を一本足すこと ── クロック(時計)の線だ。時計を共有すると、速さの約束も“真ん中で読む”も要らなくなる。基板の中でチップ同士を結ぶ二大定番、SPI(4本・全二重・線で選ぶ)と I²C(2本・アドレスで選ぶ)を、波形とコードで開けてみる。UART で握った勘が、そのまま効いてくる。

▶ SPI の波形ラボを全画面で開く

UART で、いちばん素朴な「一本の線にバイトを乗せる」を見た。その最後に、UART の性格は「クロックの線を共有しない(非同期)」ことだ、と置いた。今日は、そこに線を一本、足してみる。足すのは、時計(クロック)の線だ。

たった一本だけど、これで景色が変わる。速さを前もって約束する必要も、ビットの“真ん中”をねらって読む必要も、なくなる。 時計の線が「いま、このビットを読め」と、一発ごとに教えてくれるからだ。この「時計を共有する」やり方を 同期式(synchronous)と呼ぶ。基板の中で、マイコンとセンサ、マイコンと ADC、みたいにチップ同士を近くで結ぶときの定番が、ここで出てくる二つ ── SPII²C だ。

1. 時計の線を、引く ── SPI

SPI は線が 4本。順に、CS(相手を選ぶ)、SCLK(クロック=master が刻む時計)、MOSI(master→slave のデータ)、MISO(slave→master のデータ)。

この4本、実は 呼び名がいろいろあって、データシートを読むたび混乱する所だ。先に一枚、地図にしておく。

古典的な名前新しい(包括的)名前機器視点の別名意味
SCLK / SCKSCKCLKクロック(master が刻む時計)
MOSICOPImaster の SDO / slave の SDImaster → slave のデータ
MISOCIPOmaster の SDI / slave の SDOslave → master のデータ
SS / nSSCS / CSnCE / CSB相手を選ぶ(Low で選択)

ここに、配線でいつも引っかかる罠が一つ隠れている。MOSI / MISO は「向き」で名付けられている(MOSI=Master Out Slave In)。だから一本の線の両端が同じ「MOSI」で、配線は 同じ名前どうしをつなぐ(MOSI–MOSI、MISO–MISO)── たすき掛けは要らない。ところが **SDI / SDO は「その機器から見た入出力」**なので、こっちは たすき掛けになる(master の SDO → slave の SDI)。UART の TX/RX と同じ罠が、名前の流儀の違いとして、ここにも顔を出すわけだ。

(ちなみに「master / slave」という言い方は、近年 controller / peripheral へ置き換えが進んでいて、その流れで MOSI/MISO も COPI/CIPO になった。中身は何も変わらない ── 同じ線の、呼び名違いだ。この記事では、データシートや widget で今もよく見る master/slave のまま進める。)

UART との違いは、SCLK があること、これに尽きる。master が SCLK をカチカチ刻むと、そのエッジ(立ち上がり)に合わせてデータが読まれる。何ヘルツで叩こうが構わない ── 速さの約束が要らない。時計がぜんぶ決めてくれるからだ。しかも MOSI と MISO が別々にあるので、送りながら同時に受ける(全二重)。master と slave のシフトレジスタが輪のように繋がって、1ビットずつ入れ替わっていく。

下の widget で、4本の線 → クロック → エッジで読む → 同時に交換、と一歩ずつ組み上げてみてほしい。UART が「真ん中で読む」だったのに対し、SPI は「エッジで読む」── 時計があるから迷いがない、という所が見どころだ。

相手の選び方も UART と違う。SPI はアドレスを持たない。CS の線を Low に落とした相手とだけ話す。slave が複数なら、CS 線をそれぞれに引く ── 2台なら CS が2本、3台なら3本。速くて単純だけど、台数ぶん線が増えていくのが弱点だ。

2. 2本で、多数を ── I²C

その「線が増える」を嫌って、たった2本で多数を相手にするのが I²C だ。線は SDA(データ)と SCL(クロック)だけ。CS 線が無いぶん、相手は アドレスで選ぶ ── 同じ2本のバスに何台ぶら下げても、7ビットのアドレスで呼び分けられる。

「そのアドレスって、どうやって指定するの?」── 仕組みはシンプルだ。START の直後、master が最初に流す 1バイトめが、まるごと「宛先」になっている:上位 7ビットがアドレス、最後の 1ビットが R/W(読みたいのか・書きたいのか)。バスにぶら下がった全員がこの1バイトを聞いて、自分のアドレスと一致した1台だけが「はい(ACK)」と返事をし、以降のやり取りに応じる。残りは黙る。CS の線を引く代わりに、線の上を流れる最初の1バイトで相手を呼ぶ、というわけだ。

そのアドレスの値は、チップごとにデータシートで決まっている(たとえば、よくある温度センサなら 0x48)。同じ型のチップを2台3台と同じバスに付けたいとき用に、下位の数ビットを基板のピンの結線で選べるようになっていることが多い ── だから「0x48〜0x4B の最大4台まで同居OK」みたいな書き方を、データシートでよく見る。コードでは、この1バイトめを自分で組み立てる必要はなく、ライブラリにアドレスを数値で渡すだけでいい(後で出てくる bus.read_byte(addr)addr が、まさにこれ)。

2本しかないから、やり取りには作法が要る。SCL が High のあいだに SDA を動かすのは「合図」専用、と決めておく:SCL が High のまま SDA が下がったら START(始めるよ)、上がったら STOP(終わり)。その合図にはさまれた中で、SCL のリズムに乗せてビットを送る。

SDA SCL START アドレス7bit + R/W ACK データ STOP
I²C のやり取りの骨格(簡略)。SCL が High のまま SDA が下がる=START、上がる=STOP。その間に、アドレス(誰に)+データを SCL のリズムで送る。受け取った側は1ビット分 SDA を Low に引いて「届いたよ(ACK)」を返す。図の途中で SDA が何度も上下しているのは“データのビット”で、これは合図ではない(SCL が Low の間に動かしているから)。2本で、選んで・送って・確かめる、を全部こなしている。

図をよく見ると、「SDA、途中で何回も上がってるのに、どうして STOP にならないの?」と思うかもしれない(いい目だ)。からくりは、さっきの決まりにある ── SDA の上下が“合図”になるのは、SCL が High のあいだに動いたときだけ。データを送っている最中、SDA を動かすのは必ず SCL が Low の間で、そのときの上下は「ただのビットの 0/1」であって、合図じゃない。だから途中の上がり下がりはぜんぶ受け流され、最後に SCL が High のまま SDA がスッと上がった、その一回だけが STOP として効く。「いつ動かすかで、意味を切り替える」── これが、線が2本しかない I²C の、いちばんの知恵だ。

もう一つ I²C 特有の作法に、ACK がある。1バイト送るごとに、受け取った側が SDA を1ビット分 Low に引いて「ちゃんと届いた」と返事をする。返事が無ければ(NACK)、その相手は居ないか、手一杯ということ。2本しかない線で、選んで・送って・確かめてを全部やるための、よくできた約束だ。

ちなみに、SDA と SCL は両端から「引っぱり下げる」ことしかできない作り(オープンドレイン)で、High に戻すのは外付けの抵抗(プルアップ)の仕事 ── だから I²C の基板にはたいてい、線を電源に吊る小さな抵抗が2本いる。…と、配線の細かい話に逸れかけたので、戻ろう。要は「2本・アドレス・ACK 付き」が I²C の顔だ、と握っておけばいい。

3. コードでは ── 線で選ぶか、アドレスで選ぶか

性格の違いは、コードにそのまま出る。SPI は CS の線で相手を選んで「送る=受ける」、I²C はアドレスで相手を選んで「そのレジスタを読む」。下の widget で1行ずつ(言語は Python ── SPI は spidev、I²C は smbus というライブラリを使う。C にも同じ役の API があるけれど、やる三手=開く・相手を選ぶ・読み書き、はどの言語でも変わらない)。

I²C の「アドレスで呼ぶ」は、実物だと気持ちがいい。バスにぶら下がっている相手を、総当たりで呼んでみて、返事(ACK)があったアドレスだけ拾えば、つながっている全員の名簿ができる ── i2cdetect というツールがやっているのは、これだ。

# I²C バスに居る相手を、アドレス総当たりで探す
import smbus
bus = smbus.SMBus(1)
for addr in range(0x03, 0x78):
    try:
        bus.read_byte(addr)      # ACK が返れば…居る
        print(f'見つけた: {addr:#04x}')
    except OSError:
        pass                  # 返事なし=そのアドレスには誰も居ない
アドレスで呼べる=総当たりで名簿が作れる。SPI(CS 線で選ぶ)には無い芸当だ。

4. どこに居るのか ── 近くて速い、その先へ

SPI と I²C は、どちらも 同期式(時計を引く)で、得意は 基板の中・近距離。その上で、二つは性格が分かれる。

UART(非同期・1対1・線が少ない)と並べると、三者の住み分けが見えてくる。「時計を共有する/しない」「線で選ぶ/アドレスで選ぶ」── この二つの軸で、だいたい整理できる。

ただし SPI も I²C も、想定しているのは 基板の上の、数センチ〜十数センチだ。クロックを共有する以上、線が長くなるとエッジが鈍り、ノイズに弱くなる。基板の外 ── 機械の上のセンサまで、この「点対点」を持ち出したくなると、別の作りが要る。

そこで次は、基板を出る。いつものセンサの3本線をそのまま使いながら、その同じ線でセンサにしゃべらせる ── IO-Link へ進む。面白いことに、その中身は第一歩の UART そのものだ。点対点のまま、ただのスイッチを“賢いセンサ”にする一段。そこまで「1対1」を見届けたら、いよいよ一本のバスに大勢を・長い距離でぶら下げる世界(RS-485)へ向かう。土台に、もう一段を積もう。