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(將其拉低)、移動位元組,然後再次解除致能 CS(將其拉高)。
3.22.1. 讀取、寫入、交換¶
三個方法涵蓋了常見的情況:
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() 則正好相反——它會時控 N 個 SCK 脈衝,期間在 MOSI 上送出固定的虛擬位元組(預設為 0),並儲存 MISO 的位元組。write_readinto() 是全雙工形式:它從一個緩衝區送出位元組,並將同時收到的 MISO 位元組儲存到另一個緩衝區。許多周邊裝置都採用這種模式——「送出一個命令位元組,然後在下一次傳輸中讀取回應」——所以這兩個操作自然地合進一次 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 位元組是無用的(裝置在那個時間點仍在接收命令);第二個 MISO 位元組才存放暫存器的內容。確切的命令位元組格式——哪個位元是讀/寫旗標、在多位元組讀取時位址是否自動遞增——記載於裝置的資料手冊中。
3.22.3. 位元搥擊(Bit-banging)¶
上面的 SPI 實例使用的是硬體 SPI 區塊:MCU 內部一個專用的周邊裝置,擁有自己的移位暫存器與時脈產生器,能在矽晶片中產生 SCK / MOSI / MISO 波形。軟體只要把一個位元組交給它;位元就會在線上移動而不需要 CPU 進一步協助,讓 CPU 得以同時空出來做其他工作。
另一種做法是位元搥擊(bit-banging):軟體逐一迴圈處理每個位元,直接切換 GPIO 接腳來產生相同的波形。這當中沒有任何硬體周邊裝置參與——CPU 將 SCK 拉低、設定 MOSI、將 SCK 拉高、取樣 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 在大多數 Cam 上是個舒適的上限;CPU 在整個交易期間都會保持忙碌。