3.22. SPI im Code¶
machine.SPI umschließt einen Hardware-SPI-Controller; CS-Leitungen sind gewöhnliche Pin-Ausgänge, die vom Skript verwaltet werden. Erstellen Sie eine SPI-Instanz mit der Bus-ID, der gewünschten Taktrate und (falls nötig) dem Modus:
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
Die id wählt aus, welcher Hardware-SPI-Block verwendet wird; die verfügbaren Nummern und die SCK-/MOSI-/MISO-Pins, auf die sie abgebildet werden, hängen von der Platine ab (siehe die OpenMV-Boards). baudrate ist die SCK-Frequenz in Hertz – die tatsächlich von der Hardware erreichte Rate kann aufgrund der Taktteilung etwas niedriger sein, was der ausgegebene Wert des SPI-Objekts anzeigt.
Der CS-Pin wird mit value=1 erstellt, sodass er im Ruhezustand deaktiviert ist. Jede Transaktion aktiviert CS (treibt ihn auf Low), bewegt die Bytes und deaktiviert CS wieder (treibt ihn auf High).
3.22.1. Lesen, Schreiben, Austauschen¶
Drei Methoden decken die häufigen Fälle ab:
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() ist der schnelle Pfad nur zum Schreiben; der Controller schiebt die Bytes hinaus und verwirft alles, was das Peripheriegerät auf MISO zurücksendet. read() ist das Spiegelbild – es taktet N SCK-Impulse, während es ein festes Dummy-Byte (standardmäßig 0) auf MOSI sendet, und speichert die MISO-Bytes. write_readinto() ist die Vollduplex-Form: Es sendet die Bytes aus einem Puffer und speichert die gleichzeitigen MISO-Bytes in einen anderen. Viele Peripheriegeräte verwenden dieses Muster – „sende ein Befehlsbyte, lies dann die Antwort in der nächsten Übertragung“ – sodass die beiden Operationen natürlich in einen einzigen write_readinto-Aufruf passen.
Die meisten Peripheriegeräte erwarten, dass die CS-Leitung für die gesamte Transaktion (von den Befehlsbytes bis zu den Antwortbytes) aktiviert bleibt, halten Sie also die cs.value(0) / cs.value(1)-Klammern um die gesamte Sequenz, nicht um jeden einzelnen Methodenaufruf.
3.22.2. Ein typischer Sensor-Lesevorgang¶
Viele SPI-Sensoren organisieren ihren Zustand als eine Reihe interner Register und folgen demselben Austauschmuster: Sende die Registeradresse (mit einem Lese-/Schreib-Flag im obersten Bit), lies oder schreibe dann die Bytes des Registers. Ein Lesevorgang von Register 0x0F auf einem solchen Gerät:
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]
Das erste MISO-Byte ist Müll (das Gerät empfing zu diesem Zeitpunkt noch den Befehl); das zweite MISO-Byte enthält den Registerinhalt. Das genaue Befehlsbyte-Format – welches Bit das Lese-/Schreib-Flag ist, ob die Adresse bei Mehrbyte-Lesevorgängen automatisch hochzählt – steht im Datenblatt des Geräts.
3.22.3. Bit-Banging¶
Die obige SPI-Instanz verwendet einen Hardware-SPI-Block: ein dediziertes Peripheriegerät innerhalb des MCU mit eigenem Schieberegister und Taktgenerator, das die SCK-/MOSI-/MISO-Signalverläufe in Silizium erzeugt. Software übergibt ihm lediglich ein Byte; die Bits bewegen sich ohne weitere CPU-Hilfe auf der Leitung, sodass die CPU frei ist, parallel andere Arbeit zu erledigen.
Die Alternative ist Bit-Banging: Software durchläuft jedes Bit in einer Schleife und schaltet GPIO-Pins direkt um, um denselben Signalverlauf zu erzeugen. Es ist kein Hardware-Peripheriegerät beteiligt – die CPU treibt SCK auf Low, setzt MOSI, treibt SCK auf High, tastet MISO ab und so weiter für jedes Bit jedes Bytes. Das bindet die CPU für die gesamte Transaktion und läuft langsamer, als der Hardware-Block es kann, aber es funktioniert an jedem Pin und benötigt keinen freien Hardware-Block.
machine.SoftSPI ist die Bit-Bang-Implementierung derselben 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"))
Verwenden Sie es, wenn das Gerät an Pins angeschlossen sein muss, die nicht mit einem Hardware-SPI-Block verdrahtet sind, oder wenn die Hardware-Blöcke alle belegt sind. 500 kHz ist auf den meisten Cams eine angenehme Obergrenze; die CPU bleibt für die gesamte Transaktion beschäftigt.