SPI總線協議¶
概要¶
本片教程,我們詳細的為大家講解SPI總線協議。
什么是SPI¶
SPI是串行外設接口(Serial Peripheral Interface)的縮寫。是 Motorola 公司推出的一 種同步串行接口技術,是一種高速的,全雙工,同步的通信總線。SPI協議主要用于短距離的通信系統中,特別是嵌入式系統,很多芯片的外圍設備,比如LED顯示驅動器、I/O接口芯片、UART收發器等都廣泛的采用SPI總線協議。
通信原理¶
SPI的通信原理很簡單,它以主從方式工作,這種模式通常有一個主設備和一個或多
個從設備。在英文中,通常把主設備稱作為 Master, 從設備稱作為 Slave.
物理接線¶
SPI理論上需要4根線才能進行雙向數據傳輸,3根線可以進行單向傳輸:
SPI理論上的4根接線分別是以下四種:
功能編號 | 縮寫含義 | 中文含義 |
---|---|---|
SDO 或者叫 MOSI | Master Output Slave Input | 主設備數據輸出,從設備數據輸入 |
SDI 或者叫 MISO | Master Input Slave Output | 主設備數據輸入,從設備數據輸出 |
SCLK | serial clock | 時鐘信號, 由主設備產生 |
CS 或者叫SS | chip select | 片選信號, 從設備使能信號,由主設備控制。 |
為了讓大家深刻理解這種主從的模式,我們1Z實驗室做出了下圖方便大家理解。
PS: 把上面這兩張圖 重新做一下吧
起始、停止信號¶
如上圖,紅色編號1和6即為起始和停止信號的發生區域。
CS片選信號(圖中的NSS)電平由高變低,則產生起始信號;
CS片選信號電平由低變高,則產生停止信號。
從機檢測到自己的CS片選信號線電平被置低,則開始與主機進行通訊;
反之,檢測到NSS電平被拉高,則停止通訊。
數據有效性¶
MOSI和MISO線在SCK的每個時鐘周期傳輸一位數據,開發者可以自行設置MSB或LSB先行,不過需要保證兩個通訊設備都使用同樣的協定。
從以下的時序圖可以看出,在SCK時鐘周期的上升沿和下降沿時進行觸發和采樣。
這里的觸發和采樣其實是兩個特殊的時間節點,分別對應了SCK時鐘周期的上升沿和下降沿。
SPI有四種通訊模式,在SCK上升沿觸發,下降沿采樣只是其中一種模式。四種模式的主要區別便是總線空閑時SCK的狀態及數據采樣時刻。這涉及到“時鐘極性CPOL”和“時鐘相位CPHA”,由CPOL和CPHA的組合而產生了四種的通訊模式。
-
CPOL:即在沒有數據傳輸時,時鐘的空閑狀態的電平。上面的兩幅圖示中,無數據傳輸時的時鐘空閑狀態為低電平。
-
CPHA:即數據的采樣時刻,可以是SCK的上升沿,也可以是SCK的下降沿。
如果我們將CPOL和CPHA的兩種狀態分別用0,1表示,因此由這兩種方式排列組合,便可以產生四種模式的SPI:
SPI模式 | CPOL | 空閑時SCK時鐘 | CPHA | 采樣時刻 |
---|---|---|---|---|
0 | 0 | 低電平 | 0 | SCK下降沿 |
1 | 0 | 低電平 | 1 | SCK上升沿 |
2 | 1 | 高電平 | 0 | SCK下降沿 |
3 | 1 | 高電平 | 1 | SCK上升沿 |
配合下圖,你可以仔細揣摩一番:
很重要的一點是,主機和從機需要工作在相同的模式下才能正常通訊
SPI優點¶
-
支持全雙工通信,發送數據和接收數據可以同時進行。
-
通信簡單
-
數據傳輸速率快
SPI缺點¶
-
接線繁雜,需要至少四根接線
-
在多個從機的情況下,每個從機都需要接入一根CS片選信號線,這是十分的浪費芯片的IO資源
硬件資源¶
NodeMCU-32S擁有兩組硬件SPI總線資源, 分別是HSPI和VSPI,所對應的引腳如下圖:
ValueError: SPI ID must be either HSPI(1) or VSPI(2)
為方便大家接線,貼出下表:
SPI組號 | MOSI | MISO | CS | LCK |
---|---|---|---|---|
VSPI | GPIO 23 | GPIO 19 | GPIO 5 | GPIO 18 |
HSPI | GPIO 13 | GPIO 12 | GPIO 15 | GPIO14 |
除此兩組SPI硬件資源外,其余的GPIO理論上也可以配置成SPI總線的輸入輸出管腳,只要滿足該管腳既能夠作為輸入也能夠作為輸出。因此,在MicroPython中,我們擁有兩種模式的SPI總線,即:
-
硬件SPI
-
軟件SPI (GPIO模擬)
SPI API文檔¶
硬件SPI構造¶
上文我們為大家羅列出了兩組硬件SPI,構造他們很簡單:
以下是HSPI的構造:
>>> from machine import SPI >>> hspi = SPI(1) >>> hspi SPI(id=1, baudrate=500000, polarity=0, phase=0, bits=8, firstbit=0, sck=-1, mosi=-1, miso=-1) >>>
以下是VSPI的構造:
>>> from machine import SPI >>> vspi = SPI(2) >>> vspi SPI(id=1, baudrate=500000, polarity=0, phase=0, bits=8, firstbit=0, sck=-1, mosi=-1, miso=-1) >>>
注意¶
你可能會發現無法同時構造兩個硬件SPI,即使你認為這么做并不該出錯。
然而MicroPython并不允許你這樣做,hspi和vspi只能存在一個,不能一起使用。
軟件SPI構造¶
軟件SPI構造較為硬件要傳入的參數比較繁多,有兩種構造方法:使用類構造或使用init
函數構造
類構造¶
SPI(baudrate, polarity, phase, bits, firtbit, sck, mosi, miso)
-
baudrate
:SCK時鐘頻率 范圍0 < baudrate ≤ 0x0FFFFFFF (十進制:0 < baudrate ≤ 2147483647)
-
polarity
:極性 -
0
時鐘空閑時候的電平是低電平,所以當SCLK有效的時候,就是高電平 -
1
時鐘空閑時候的電平是高電平,所以當SCLK有效的時候,就是低電平 -
phase
:相位 -
0
在第一時鐘沿采樣數據 -
1
在第二時鐘沿采樣數據 -
bits
:傳輸數據位數 -
firtbit
:數據傳輸的第一位(高位或低位) -
sck
時鐘信號引腳 -
mosi
主設備輸出,從設備輸入引腳 -
miso
主設備輸入,從設備輸出引腳
? Pin(0)、Pin(2)、Pin(4)、Pin(5)、Pin(9)、Pin(16~19)、Pin(21~23)、Pin(25~27)
示例:
from machine import SPI, Pin spi = SPI(baudrate=100000, polarity=1, phase=0, sck=Pin(17), mosi=Pin(27), miso=Pin(18))
使用init函數構造¶
SPI.init(baudrate, polarity, phase, sck, mosi, miso)
函數說明:初始化SPI總線
參數含義同上文類構造一致
示例:
spi = SPI.init(baudrate=100000, polarity=1, phase=0, sck=Pin(17), mosi=Pin(27), miso=Pin(18))
寫數據¶
SPI.write(buf)¶
函數說明:將 buf 中的所有數據寫入到總線。
示例:
buf = bytearray([1,2,3,4,5,6,7,8]) spi.write(buf)
讀數據¶
SPI.read(len, data=0x00)¶
函數說明:讀取len個數據的同時寫入len個data數據,以數組的形式返回讀取到的數據。
len
: 需要讀取的字節長度
data
: 寫入的單字節數據
示例:
buf=bytearray(2) #申請長度為2的緩沖區 buf = spi.read(2, 0x00)
SPI.readinto(buf, data=0x00)¶
函數說明:讀取buf.len個數據并存入buf中,同時寫入buf.len個data數據,函數返回None。
buf
: 數據緩沖區
data
: 寫入的單字節數據
SPI.write_readinto(write_buf, read_buf)¶
函數說明:寫入write_buf并讀取到 read_buf,寫入并讀取的長度為buf長度,要求兩個緩沖區長度相同。
write_buf
: 寫數據緩沖區
read_buf
: 讀數據緩沖區
示例:
write_buf = bytearray([1, 2, 3, 4, 5, 6, 7, 8]) read_buf = bytearray(8) spi.write_readinto (write_buf, read_buf) print(read_buf)
釋放資源¶
SPI.deinit()¶
函數說明:關閉SPI。
示例:
spi.deinit()
宏¶
-
SPI.MSB = 0 從一個字節中的最高位依次到最低位開始發送該字節數據
-
SPI.LSB = 1 從一個字節中的最低位依次到最高位開始發送該字節數據
綜合示例¶
todo