3.22. コードで使う SPI

machine.SPI はハードウェア SPI コントローラをラップします。CS 線はスクリプトが管理する通常の Pin 出力です。バス id、希望するクロックレート、そして(必要であれば)モードを指定して SPI インスタンスを生成します。

from machine import SPI, Pin

spi = SPI(0, baudrate=1_000_000, polarity=0, phase=0)
cs = Pin("P3", Pin.OUT, value=1)         # CS idle high

id はどのハードウェア SPI ブロックを使うかを選択します。利用できる番号と、それらがマッピングされる SCK/MOSI/MISO ピンはボードによって異なります(OpenMV ボード を参照)。baudrate は SCK 周波数(ヘルツ単位)です。クロック分周のため、ハードウェアが実際に達成するレートはわずかに低くなることがあり、その値は SPI オブジェクトの表示で確認できます。

CS ピンは value=1 で生成されるため、デアサート状態でアイドルします。すべてのトランザクションは、CS をアサート(Low に駆動)し、バイトを移動させ、再び CS をデアサート(High に駆動)します。

3.22.1. 読み出し、書き込み、交換

一般的なケースは 3 つのメソッドでカバーできます。

cs.value(0)
spi.write(b"\x10\x20\x30")              # send 3 bytes, ignore what comes back
cs.value(1)

cs.value(0)
data = spi.read(4)                      # read 4 bytes; sends 0x00 while reading
cs.value(1)

rx = bytearray(2)
cs.value(0)
spi.write_readinto(b"\x9F\x00", rx)     # send 0x9F, 0x00; receive 2 bytes
cs.value(1)

write() は書き込み専用の高速パスです。コントローラはバイトを送り出し、ペリフェラルが MISO で返してきたものは破棄します。read() はその鏡像で、固定のダミーバイト(デフォルトは 0)を MOSI に送りながら N 個の SCK パルスをクロックし、MISO のバイトを格納します。write_readinto() は全二重の形式で、あるバッファからバイトを送信し、同時に受信した MISO のバイトを別のバッファに格納します。多くのペリフェラルはこのパターン、つまり「コマンドバイトを送り、続く転送で応答を読む」という方式を使うため、この 2 つの操作は自然に 1 回の write_readinto 呼び出しに収まります。

ほとんどのペリフェラルは、トランザクション全体(コマンドバイトから応答バイトまで)の間 CS 線がアサートされたままであることを期待します。そのため、cs.value(0) / cs.value(1) の括りは各メソッド呼び出しごとではなく、シーケンス全体 を囲むように保ってください。

3.22.2. 典型的なセンサー読み出し

多くの SPI センサーは、その状態を一連の内部レジスタとして構成しており、同じ交換の形に従います。すなわち、レジスタアドレス(最上位ビットに読み出し/書き込みフラグを付けたもの)を送り、続いてそのレジスタのバイトを読み書きします。そうしたデバイスでレジスタ 0x0F を読み出す例を示します。

rx = bytearray(2)
cs.value(0)
spi.write_readinto(b"\x8F\x00", rx)     # 0x80 = "read" flag, then reg 0x0F
cs.value(1)
register_value = rx[1]

最初の MISO バイトはゴミです(その時点ではデバイスはまだコマンドを受信中でした)。2 番目の MISO バイトにレジスタの内容が入っています。正確なコマンドバイトの形式、つまりどのビットが読み出し/書き込みフラグなのか、マルチバイト読み出しでアドレスが自動的にインクリメントされるかどうかは、デバイスのデータシートに記載されています。

3.22.3. ビットバンギング

上記の SPI インスタンスはハードウェア SPI ブロックを使います。これは MCU 内部の専用ペリフェラルで、独自のシフトレジスタとクロックジェネレータを備え、SCK/MOSI/MISO の波形をシリコン上で生成します。ソフトウェアはバイトを渡すだけで、ビットは CPU のさらなる助けなしに線上を移動するため、CPU は並行して他の作業に専念できます。

もう一つの方法がビットバンギングです。ソフトウェアが各ビットをループで処理し、GPIO ピンを直接トグルして同じ波形を生成します。ハードウェアペリフェラルは一切関与しません。CPU が SCK を Low に駆動し、MOSI をセットし、SCK を High に駆動し、MISO をサンプリングする、という処理をすべてのバイトのすべてのビットに対して行います。これはトランザクションの間ずっと CPU を占有し、ハードウェアブロックより遅く動作しますが、どのピンでも機能し、ハードウェアブロックが空いている必要もありません。

machine.SoftSPI は同じ SPI API のビットバンギング実装です。

from machine import SoftSPI, Pin

spi = SoftSPI(baudrate=500_000, polarity=0, phase=0,
              sck=Pin("P2"), mosi=Pin("P0"), miso=Pin("P1"))

デバイスをハードウェア SPI ブロックに配線されていないピン上に置く必要がある場合や、ハードウェアブロックがすべて使用中の場合に使います。500 kHz はほとんどのカメラで無理のない上限です。トランザクションの間ずっと CPU はビジー状態のままになります。