3.22. SPI în cod¶
machine.SPI încapsulează un controler SPI hardware; liniile CS sunt ieșiri obișnuite Pin gestionate de script. Construiește o instanță SPI cu id-ul magistralei, rata de ceas dorită și (dacă este necesar) modul:
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 selectează ce bloc SPI hardware se folosește; numerele disponibile și pinii SCK/MOSI/MISO către care se mapează depind de placă (vezi Plăci OpenMV). baudrate este frecvența SCK în herți – rata efectivă pe care o atinge hardware-ul poate fi ușor mai mică din cauza divizării ceasului, lucru pe care îl va arăta valoarea afișată a obiectului SPI.
Pinul CS este construit cu value=1 astfel încât să fie inactiv dezactivat. Fiecare tranzacție activează CS (îl aduce la nivel jos), mută octeții și dezactivează din nou CS (îl aduce la nivel înalt).
3.22.1. Citire, scriere, schimb¶
Trei metode acoperă cazurile frecvente:
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() este calea rapidă doar de scriere; controlerul împinge octeții și aruncă orice a trimis perifericul înapoi pe MISO. read() este imaginea în oglindă – generează N impulsuri SCK în timp ce trimite un octet fictiv fix (0 în mod implicit) pe MOSI și stochează octeții MISO. write_readinto() este forma full-duplex: trimite octeții dintr-un tampon (buffer) și stochează octeții MISO simultani într-altul. Multe periferice folosesc acest tipar – „trimite un octet de comandă, apoi citește răspunsul în transferul următor” – astfel încât cele două operații se potrivesc în mod natural într-un singur apel write_readinto.
Majoritatea perifericelor se așteaptă ca linia CS să rămână activată pe toată durata tranzacției (de la octeții de comandă până la octeții de răspuns), așa că păstrează parantezele cs.value(0) / cs.value(1) în jurul întregii secvențe, nu în jurul fiecărui apel de metodă.
3.22.2. O citire tipică de senzor¶
Mulți senzori SPI își organizează starea ca un set de registre interne și urmează aceeași formă de schimb: trimit adresa registrului (cu un indicator de citire/scriere în bitul cel mai semnificativ), apoi citesc sau scriu octeții registrului. O citire a registrului 0x0F pe un astfel de dispozitiv:
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]
Primul octet MISO este gunoi (dispozitivul încă primea comanda în acel moment); al doilea octet MISO conține conținutul registrului. Formatul exact al octetului de comandă – care bit este indicatorul de citire/scriere, dacă adresa se auto-incrementează la citirile pe mai mulți octeți – se găsește în fișa tehnică a dispozitivului.
3.22.3. Bit-banging¶
Instanța SPI de mai sus folosește un bloc SPI hardware: un periferic dedicat din interiorul MCU-ului, cu propriul registru de deplasare și generator de ceas, care produce formele de undă SCK / MOSI / MISO în siliciu. Software-ul îi predă pur și simplu un octet; biții se mută pe fir fără alt ajutor din partea CPU, lăsând CPU liber să facă alt lucru în paralel.
Alternativa este bit-banging-ul: software-ul parcurge în buclă fiecare bit și comută pinii GPIO direct pentru a produce aceeași formă de undă. Nu este implicat niciun periferic hardware – CPU aduce SCK la nivel jos, setează MOSI, aduce SCK la nivel înalt, eșantionează MISO și așa mai departe pentru fiecare bit al fiecărui octet. Acest lucru ocupă CPU pe toată durata tranzacției și rulează mai lent decât poate blocul hardware, dar funcționează pe orice pin și nu necesită ca un bloc hardware să fie liber.
machine.SoftSPI este implementarea prin bit-banging a aceleiași API SPI:
from machine import SoftSPI, Pin
spi = SoftSPI(baudrate=500_000, polarity=0, phase=0,
sck=Pin("P2"), mosi=Pin("P0"), miso=Pin("P1"))
Folosește-o atunci când dispozitivul trebuie să fie pe pini care nu sunt cablați la un bloc SPI hardware, sau când blocurile hardware sunt toate în uz. 500 kHz este un plafon confortabil pe majoritatea camerelor; CPU rămâne ocupat pe toată durata tranzacției.