1. 程式人生 > 其它 >SPI協議介紹

SPI協議介紹

1、 SPI簡介

SPI,是英語Serial Peripheral interface的縮寫,顧名思義就是序列外圍裝置介面。是Motorola首先在其MC68HCXX系列處理器上定義的。SPI介面主要應用在 EEPROM,FLASH,實時時鐘,AD轉換器,還有數字訊號處理器和數字訊號解碼器之間。SPI,是一種高速的,全雙工,同步的通訊匯流排,並且在晶片的管腳上只佔用四根線,節約了晶片的管腳,同時為PCB的佈局上節省空間,提供方便,正是出於這種簡單易用的特性,現在越來越多的晶片集成了這種通訊協議。

2、 SPI特點2.1採用主-從模式(Master-Slave) 的控制方式

SPI 規定了兩個 SPI 裝置之間通訊必須由主裝置 (Master) 來控制次裝置 (Slave). 一個 Master 裝置可以通過提供 Clock 以及對 Slave 裝置進行片選 (Slave Select) 來控制多個 Slave 裝置, SPI 協議還規定 Slave 裝置的 Clock 由 Master 裝置通過 SCK 管腳提供給 Slave 裝置, Slave 裝置本身不能產生或控制 Clock, 沒有 Clock 則 Slave 裝置不能正常工作

2.2採用同步方式(Synchronous)傳輸資料

Master 裝置會根據將要交換的資料來產生相應的時鐘脈衝(Clock Pulse), 時鐘脈衝組成了時鐘訊號(Clock Signal) , 時鐘訊號通過時鐘極性 (CPOL) 和 時鐘相位 (CPHA) 控制著兩個 SPI 裝置間何時資料交換以及何時對接收到的資料進行取樣, 來保證資料在兩個裝置之間是同步傳輸的.

2.3資料交換(Data Exchanges)

SPI 裝置間的資料傳輸之所以又被稱為資料交換, 是因為 SPI 協議規定一個 SPI 裝置不能在資料通訊過程中僅僅只充當一個 "傳送者(Transmitter)" 或者 "接收者(Receiver)". 在每個 Clock 週期內, SPI 裝置都會發送並接收一個 bit 大小的資料, 相當於該裝置有一個 bit 大小的資料被交換了. 一個 Slave 裝置要想能夠接收到 Master 發過來的控制訊號, 必須在此之前能夠被 Master 裝置進行訪問 (Access). 所以, Master 裝置必須首先通過 SS/CS pin 對 Slave 裝置進行片選, 把想要訪問的 Slave 裝置選上. 在資料傳輸的過程中, 每次接收到的資料必須在下一次資料傳輸之前被取樣. 如果之前接收到的資料沒有被讀取, 那麼這些已經接收完成的資料將有可能會被丟棄, 導致 SPI 物理模組最終失效. 因此, 在程式中一般都會在 SPI 傳輸完資料後, 去讀取 SPI 裝置裡的資料, 即使這些資料(Dummy Data)在我們的程式裡是無用的。

2.4 SPI有四種傳輸模式

上升沿、下降沿、前沿、後沿觸發。當然也有MSB和LSB傳輸方式.

2.5 SPI只有主模式和從模式之分。

沒有讀和寫的說法,因為實質上每次SPI是主從裝置在交換資料。也就是說,你發一個數據必然會收到一個數據;你要收一個數據必須也要先發一個數據。

3、 工作機制3.1概述

上圖只是對 SPI 裝置間通訊的一個簡單的描述, 下面就來解釋一下圖中所示的幾個元件(Module):

SSPBUF,Synchronous Serial Port Buffer, 泛指 SPI 裝置裡面的內部緩衝區, 一般在物理上是以 FIFO 的形式, 儲存傳輸過程中的臨時資料;

SSPSR, Synchronous Serial Port Register, 泛指 SPI 裝置裡面的移位暫存器(Shift Regitser), 它的作用是根據設定好的資料位寬(bit-width) 把資料移入或者移出 SSPBUF;

Controller, 泛指 SPI 裝置裡面的控制暫存器, 可以通過配置它們來設定 SPI 匯流排的傳輸模式。

通常情況下, 我們只需要對上圖所描述的四個管腳(pin) 進行程式設計即可控制整個 SPI 裝置之間的資料通訊:

SCK, Serial Clock, 主要的作用是 Master 裝置往 Slave 裝置傳輸時鐘訊號, 控制資料交換的時機以及速率;

SS/CS, Slave Select/Chip Select, 用於 Master 裝置片選 Slave 裝置, 使被選中的 Slave 裝置能夠被 Master 裝置所訪問;

SDO/MOSI, Serial Data Output/Master Out Slave In, 在 Master 上面也被稱為 Tx-Channel, 作為資料的出口, 主要用於 SPI 裝置傳送資料;

SDI/MISO, Serial Data Input/Master In Slave Out, 在 Master 上面也被稱為 Rx-Channel, 作為資料的入口, 主要用於SPI 裝置接收資料;

SPI 裝置在進行通訊的過程中, Master 裝置和 Slave 裝置之間會產生一個數據鏈路迴環(Data Loop), 就像上圖所畫的那樣, 通過 SDO 和 SDI 管腳, SSPSR 控制資料移入移出 SSPBUF, Controller 確定 SPI 匯流排的通訊模式, SCK 傳輸時鐘訊號。

3.2 Timing

上圖通過 Master 裝置與 Slave 裝置之間交換1 Byte 資料來說明 SPI 協議的工作機制.

首先, 在這裡解釋一下相位和極性的概念

3.2.1 SPI相關的縮寫或說法

SPI的極性Polarity和相位Phase,最常見的寫法是CPOL和CPHA,不過也有一些其他寫法,簡單總結如下:

(1) CKPOL (Clock Polarity) = CPOL = POL = Polarity = (時鐘)極性

(2) CKPHA (Clock Phase) = CPHA = PHA = Phase = (時鐘)相位

(3) SCK=SCLK=SPI的時鐘

(4) Edge=邊沿,即時鐘電平變化的時刻,即上升沿(rising edge)或者下降沿(falling edge)

對於一個時鐘週期內,有兩個edge,分別稱為:

Leading edge=前一個邊沿=第一個邊沿,對於開始電壓是1,那麼就是1變成0的時候,對於開始電壓是0,那麼就是0變成1的時候;

Trailing edge=後一個邊沿=第二個邊沿,對於開始電壓是1,那麼就是0變成1的時候(即在第一次1變成0之後,才可能有後面的0變成1),對於開始電壓是0,那麼就是1變成0的時候;

3.2.2 SPI的相位和極性

CPOL和CPHA,分別都可以是0或時1,對應的四種組合就是:

Mode 0 CPOL=0, CPHA=0

Mode 1 CPOL=0, CPHA=1

Mode 2 CPOL=1, CPHA=0

Mode 3 CPOL=1, CPHA=1

3.2.3 CPOL極性

先說什麼是SCLK時鐘的空閒時刻,其就是當SCLK在數傳送8個bit位元資料之前和之後的狀態,於此對應的,SCLK在傳送資料的時候,就是正常的工作的時候,有效active的時刻了。

先說英文,其精簡解釋為:Clock Polarity = IDLE state of SCK。

再用中文詳解:

SPI的CPOL,表示當SCLK空閒idle的時候,其電平的值是低電平0還是高電平1:

CPOL=0,時鐘空閒idle時候的電平是低電平,所以當SCLK有效的時候,就是高電平,就是所謂的active-high;

CPOL=1,時鐘空閒idle時候的電平是高電平,所以當SCLK有效的時候,就是低電平,就是所謂的active-low;

3.2.4 CPHA相位

首先說明一點,capture strobe = latch = read = sample,都是表示資料取樣,資料有效的時刻。相位,對應著資料取樣是在第幾個邊沿(edge),是第一個邊沿還是第二個邊沿,0對應著第一個邊沿,1對應著第二個邊沿。

對於:

CPHA=0,表示第一個邊沿:

對於CPOL=0,idle時候的是低電平,第一個邊沿就是從低變到高,所以是上升沿;

對於CPOL=1,idle時候的是高電平,第一個邊沿就是從高變到低,所以是下降沿;

CPHA=1,表示第二個邊沿:

對於CPOL=0,idle時候的是低電平,第二個邊沿就是從高變到低,所以是下降沿;

對於CPOL=1,idle時候的是高電平,第一個邊沿就是從低變到高,所以是上升沿;

還是上圖大家更容易看懂

3.2.5 軟體中如何設定SPI的極性和相位

SPI分主裝置和從裝置,兩者通過SPI協議通訊。

而設定SPI的模式,是從裝置的模式,決定了主裝置的模式。

所以要先去搞懂從裝置的SPI是何種模式,然後再將主裝置的SPI的模式,設定和從裝置相同的模式,即可正常通訊。

對於從裝置的SPI是什麼模式,有兩種:

3.2.5.1固定的,有SPI從裝置硬體決定的

SPI從裝置,具體是什麼模式,相關的datasheet中會有描述,需要自己去datasheet中找到相關的描述,即:

關於SPI從裝置,在空閒的時候,是高電平還是低電平,即決定了CPOL是0還是1;

然後再找到關於裝置是在上升沿還是下降沿去取樣資料,這樣就是,在定了CPOL的值的前提下,對應著可以推算出CPHA是0還是1了。

3.2.5.2 可配置的,由軟體自己設定

從裝置也是一個SPI控制器,4種模式都支援,此時只要自己設定為某種模式即可。

然後知道了從裝置的模式後,再去將SPI主裝置的模式,設定為和從裝置模式一樣,即可。

對於如何配置SPI的CPOL和CPHA的話,不多細說,多數都是直接去寫對應的SPI控制器中對應暫存器中的CPOL和CPHA那兩位,寫0或寫1即可。

3.3 SSPSR

SSPSR 是 SPI 裝置內部的移位暫存器(Shift Register). 它的主要作用是根據 SPI 時鐘訊號狀態, 往 SSPBUF 裡移入或者移出資料, 每次移動的資料大小由 Bus-Width 以及 Channel-Width 所決定。

Bus-Width 的作用是指定地址匯流排到 Master 裝置之間資料傳輸的單位.

例如, 我們想要往 Master 裝置裡面的 SSPBUF 寫入 16 Byte 大小的資料: 首先, 給 Master 裝置的配置暫存器設定 Bus-Width 為 Byte; 然後往 Master 裝置的 Tx-Data 移位暫存器在地址匯流排的入口寫入資料, 每次寫入 1 Byte 大小的資料(使用 writeb 函式); 寫完 1 Byte 資料之後, Master 裝置裡面的 Tx-Data 移位暫存器會自動把從地址匯流排傳來的1 Byte 資料移入 SSPBUF 裡; 上述動作一共需要重複執行 16 次.

Channel-Width 的作用是指定 Master 裝置與 Slave 裝置之間資料傳輸的單位. 與 Bus-Width 相似, Master 裝置內部的移位暫存器會依據 Channel-Width 自動地把資料從 Master-SSPBUF 裡通過 Master-SDO 管腳搬運到 Slave 裝置裡的 Slave-SDI 引腳, Slave-SSPSR 再把每次接收的資料移入 Slave-SSPBUF裡.通常情況下, Bus-Width 總是會大於或等於 Channel-Width, 這樣能保證不會出現因 Master 與 Slave 之間資料交換的頻率比地址匯流排與 Master 之間的資料交換頻率要快, 導致 SSPBUF 裡面存放的資料為無效資料這樣的情況.

3.4 SSPBUF

我們知道, 在每個時鐘週期內, Master 與 Slave 之間交換的資料其實都是 SPI 內部移位暫存器從 SSPBUF 裡面拷貝的. 我們可以通過往 SSPBUF 對應的暫存器 (Tx-Data / Rx-Data register) 裡讀寫資料, 間接地操控 SPI 裝置內部的 SSPBUF.

例如, 在傳送資料之前, 我們應該先往 Master 的 Tx-Data 暫存器寫入將要傳送出去的資料, 這些資料會被 Master-SSPSR 移位暫存器根據 Bus-Width 自動移入 Master-SSPBUF 裡, 然後這些資料又會被 Master-SSPSR 根據 Channel-Width 從 Master-SSPBUF 中移出, 通過 Master-SDO 管腳傳給 Slave-SDI 管腳, Slave-SSPSR 則把從 Slave-SDI 接收到的資料移入 Slave-SSPBUF 裡. 與此同時, Slave-SSPBUF 裡面的資料根據每次接收資料的大小(Channel-Width), 通過 Slave-SDO 發往 Master-SDI, Master-SSPSR 再把從 Master-SDI 接收的資料移入 Master-SSPBUF.在單次資料傳輸完成之後, 使用者程式可以通過從 Master 裝置的 Rx-Data 暫存器讀取 Master 裝置資料交換得到的資料.

3.5 Controller

Master 裝置裡面的 Controller 主要通過時鐘訊號(Clock Signal)以及片選訊號(Slave Select Signal)來控制 Slave 裝置. Slave 裝置會一直等待, 直到接收到 Master 裝置發過來的片選訊號, 然後根據時鐘訊號來工作.

Master 裝置的片選操作必須由程式所實現. 例如: 由程式把 SS/CS 管腳的時鐘訊號拉低電平, 完成 SPI 裝置資料通訊的前期工作; 當程式想讓 SPI 裝置結束資料通訊時, 再把 SS/CS 管腳上的時鐘訊號拉高電平.

4. SPI舉例

上面說了那麼多,在這裡我來舉一個例子幫助大家理解。

SPI是一個環形匯流排結構,由ss(cs)、sck、sdi、sdo構成,其時序其實很簡單,主要是在sck的控制下,兩個雙向移位暫存器進行資料交換。

假設下面的8位暫存器裝的是待發送的資料10101010,上升沿傳送、下降沿接收、高位先發送。

那麼第一個上升沿來的時候 資料將會是sdo=1;暫存器=0101010x。下降沿到來的時候,sdi上的電平將所存到暫存器中去,那麼這時暫存器=0101010sdi,這樣在 8個時鐘脈衝以後,兩個暫存器的內容互相交換一次。這樣就完成裡一個spi時序。

舉例:

假設主機和從機初始化就緒:並且主機的sbuff=0xaa,從機的sbuff=0x55,下面將分步對spi的8個時鐘週期的資料情況演示一遍:假設上升沿傳送資料

這樣就完成了兩個暫存器8位的交換,上面的上表示上升沿、下表示下降沿,sdi、sdo相對於主機而言的。已經很接近理解了,下一步就是把 上面的過程轉為動畫

5、 STM32驅動

STM32的spi較為簡單,因為STM32的內部整合有SPI。

5.1 SPI的初始化

[cpp]view plaincopy

  1. void SPIInit(void)

  2. {

  3. SPI_InitTypeDef SPI_InitStructure;

  4. //初始化SPI與GPIO口的連線

  5. FLASH_GPIO_Init();

  6. /*!< Deselect the FLASH: Chip Select high */

  7. CE_High();

  8. /*!< SPI configuration */

  9. SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;

  10. SPI_InitStructure.SPI_Mode = SPI_Mode_Master;

  11. SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;

  12. SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;

  13. SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;

  14. SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;

  15. SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;

  16. SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;

  17. SPI_InitStructure.SPI_CRCPolynomial = 7;

  18. SPI_Init(SPI1, &SPI_InitStructure);

  19. /*!< Enable the sFLASH_SPI */

  20. SPI_Cmd(SPI1, ENABLE);

  21. }

5.2 SPI寫1個位元組

[cpp]view plaincopy

  1. uint8 Send_Byte(uint8 data)

  2. {

  3. //傳送不為空

  4. while(!(SPI1->SR & SPI_I2S_FLAG_TXE));

  5. SPI1->DR = data;

  6. //讀取收到的資料

  7. while(!(SPI1->SR & SPI_I2S_FLAG_RXNE));

  8. return SPI1->DR;

  9. }

5.3 SPI讀一個位元組

[cpp]view plaincopy

  1. uint8 Get_Byte(void)

  2. {

  3. //傳送不為空

  4. while(!(SPI1->SR & SPI_I2S_FLAG_TXE)); // 傳送緩衝為空,跳出迴圈

  5. SPI1->DR = 0xFF; //

  6. //讀取收到的資料

  7. while(!(SPI1->SR & SPI_I2S_FLAG_RXNE)); //接受緩衝非空,跳出迴圈

  8. return SPI1->DR;

  9. }