SPI 簡介
SPI全稱為Seriel Peripheral Interface (串行外設接口),是 MCU 中常用的外設接口。SPI 通信原理很簡單,它是以主從方式進行工作,通常有一個主設備和一個或多個從設備,至少需要4根線(支持全雙工)工作,分別為 MISO(主入從出),MOSI(主出從入),SCLK(時鍾),SS(片選)。
Standard-SPI
基本的 SPI 協議也被稱為 Standard-SPI,Standard-SPI 是串行通信協議,數據是逐位進行傳輸,在 SCLK 的邊沿進行 MOSI 和 MISO 的傳輸。數據輸出通過 MOSI 線進行傳輸,數據在時鍾上升沿或者下降沿改變,在緊接着的下降沿或者上升沿被采樣完成一位數據傳輸,輸入同理。
Dual-SPI
由於在實際應用中較少使用全雙工模式,因此為了能夠充分利用數據線,引入了 Dual-SPI 和 Quad-SPI ,在 Dual-SPI 協議中,MOSI、MISO 數據線被重命名為 SD0、SD1 ,變為既可以輸入也可以輸出的 inout 信號。
Dual-SPI 由於同時使用兩根數據線進行傳輸,一個時鍾周期可以傳輸2 bit數據,因此可以將 Standard-SPI的吞吐率提高一倍。
Quad-SPI
Quad-SPI 是在 Dual-SPI 的基礎上再添加了兩根數據線,所以數據線變為了SD0、SD1、SD2、SD3。
Quad-SPI 由於同時使用四根數據線進行傳輸,一個時鍾周期可以傳輸4 bit數據,因此可以將 Standard-SPI的吞吐率提高3倍。
SPI 總線四種工作方式
SPI 在數據傳輸的時候,需要確定兩件事情
-
數據是在時鍾的上升沿采集還是下降沿采集
-
時鍾的初始(空閑)狀態是為高電平還是低電平
CPOL:時鍾極性, 表示 SPI 在空閑時, 時鍾信號是高電平還是低電平。
CPHA:時鍾相位, 表示 SPI 設備是在 SCK 管腳上的時鍾信號變為上升沿時觸發數據采樣, 還是在時鍾信號變為下降沿時觸發數據采樣。
那么CPOL 有兩種可能,CPHA 有兩種可能,則 SPI 傳輸就有四種模式。
傳輸方式 | 描述 |
---|---|
方式1 | CPOL= 0,CPHA=0。SCK串行時鍾線空閑是為低電平,數據在SCK時鍾的上升沿被采樣,數據在SCK時鍾的下降沿切換 |
方式2 | CPOL= 0,CPHA=1。SCK串行時鍾線空閑是為低電平,數據在SCK時鍾的下降沿被采樣,數據在SCK時鍾的上升沿切換 |
方式3 | CPOL= 1,CPHA=0。SCK串行時鍾線空閑是為高電平,數據在SCK時鍾的下降沿被采樣,數據在SCK時鍾的上升沿切換 |
方式4 | CPOL= 1,CPHA=1。SCK串行時鍾線空閑是為高電平,數據在SCK時鍾的上升沿被采樣,數據在SCK時鍾的下降沿切換 |
SPI通信協議
通訊的起始信號:SS 信號線由高變低,是 SPI 通訊的起始信號。SS 是每個從機各自獨占的信號線,當從機在自己的 SS 線檢測到起始信號后,就知道自己被主機選中了,開始准備與主機通訊。
通訊的停止信號:SS 信號由低變高,是 SPI 通訊的停止信號,表示本次通訊結束,從機的選中狀態被取消。
數據有效性:SPI 使用 MOSI 及 MISO 信號線來傳輸數據,使用 SCLK 信號線進行數據同步。 MOSI 及 MISO 數據線在 SCLK 的每個時鍾周期傳輸一位數據,且數據輸入輸出是同時進行的。數據傳輸時,MSB 先行或 LSB 先行並沒有作硬性規定,但要保證兩個 SPI 通訊設備之間使用同樣的協定,一般采用 MSB 先行的方式。
_ _ _ _ _ _ _ _ _
inClk _/ \_/ \_/ \_/ \_/ \_/ \.....\_/ \_/ \_/ \_
___ _____
inReset_EnableB \_________________________________/
_____ ___
outSpiCsB \_________________________________/
_______ ___
inData8Send[7:0] _______XD8SXXX...
___ ___ ___ ___ _________
inSpiMosi XD7_XD6_XD5_X.....XD0_X_________
___________ _ _ _ _ ___________
outSpiClk \_/ \_/ \_/ \.....\_/
___________
outData8Receive[7:0] XD8SX XXXD8R*D8SX___
FPGA 程序實現
一個用了好多年的 SPI 程序,很健壯,小編拋出來。
library ieee;
--Library UNISIM;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
--use UNISIM.vcomponents.all;
entity SpiSerDes is
port
(
-- SerDes clock and control signals
inClk : in std_logic; -- System clock. Fmax <= SPI peripheral Fmax.
inReset_EnableB : in std_logic; -- Active-high, synchronous reset.
inStartTransfer : in std_logic; -- Active-high, initiate transfer of data
outTransferDone : out std_logic; -- DONE='1' when transfer is done
-- Parallel data ports
inData8Send : in std_logic_vector(7 downto 0); -- Sent to SPI device
outData8Receive : out std_logic_vector(7 downto 0); -- Received from SPI device
-- SPI ports and tristate control - Connect these to the SPI bus
outSpiCsB : out std_logic; -- SPI chip-select to SPI device
-- or all SPI outputs control enable
outSpiClk : out std_logic; -- SPI clock to SPI device
outSpiMosi : out std_logic; -- SPI master-out, slave-in to SPI device
inSpiMiso : in std_logic -- SPI master-in, slave-out from SPI device
);
end SpiSerDes;
architecture behavioral of SpiSerDes is
-- Constants
constant cShiftCountInit : std_logic_vector(8 downto 0) := B"000000001";
-- Registers
signal regShiftCount : std_logic_vector(8 downto 0) := cShiftCountInit;
signal regShiftData : std_logic_vector(7 downto 0) := B"00000000";
signal regSpiCsB : std_logic := '1';
signal regSpiMosi : std_logic := '1';
signal regTransferDoneDelayed : std_logic := '1';
-- Signals
signal intTransferDone : std_logic;
-- Attributes
attribute clock_signal : string;
attribute clock_signal of inClk : signal is "yes";
begin
-- Internal signals
intTransferDone <= regShiftCount(0);
-- TransferDone delayed by half clock cycle
processTransferDone : process (inClk)
begin
if (falling_edge(inClk)) then
regTransferDoneDelayed <= intTransferDone;
end if;
end process processTransferDone;
-- SPI chip-select (active-Low) is always inverse of inReset_EnableB.
processSpiCsB : process (inClk)
begin
if (rising_edge(inClk)) then
regSpiCsB <= inReset_EnableB;
end if;
end process processSpiCsB;
-- Track transfer of serial data with barrel shifter.
processShiftCount : process (inClk)
begin
if (rising_edge(inClk)) then
if (inReset_EnableB='1') then
regShiftCount <= cShiftCountInit;
elsif ((intTransferDone='0') or (inStartTransfer='1')) then
-- Barrel shift (rotate right)
regShiftCount <= regShiftCount(0) & regShiftCount(8 downto 1);
end if;
end if;
end process processShiftCount;
-- Simultaneous serialize outgoing data & deserialize incoming data. MSB first
processShiftData : process (inClk)
begin
if (rising_edge(inClk)) then
if (intTransferDone='0') then
-- SHIFT-left while not outTransferDone
regShiftData <= regShiftData(6 downto 0) & inSpiMiso;
elsif (inStartTransfer='1') then
-- Load data to start a new transfer sequence from a done state
regShiftData <= inData8Send;
end if;
end if;
end process processShiftData;
-- SPI MOSI register outputs on falling edge of inClk. MSB first.
processSpiMosi : process (inClk)
begin
if (falling_edge(inClk)) then
if (inReset_EnableB='1') then
regSpiMosi <= '1';
elsif (intTransferDone='0') then
regSpiMosi <= regShiftData(7);
end if;
end if;
end process processSpiMosi;
-- Assign outputs
outSpiClk <= (inClk or intTransferDone or regTransferDoneDelayed);
outSpiCsB <= regSpiCsB;
outSpiMosi <= regSpiMosi;
outTransferDone <= intTransferDone;
outData8Receive <= regShiftData;
end behavioral;
假設上升沿發送、下降沿接收、高位先發送。
假設主機8位寄存器裝的是待發送的數據10101010
參考鏈接
https://blog.csdn.net/weiqifa0/article/details/82765892
https://mp.weixin.qq.com/s/h1xco58oRDbIq8z3zaP_pA
工程源碼獲取
獲取資料方法一:集贊
關注小編公眾號后,將本文轉發至朋友圈,集齊3個贊,截圖發送到后台,小編會在24小時之內回復。備注:【領取SPI源碼】即可領取資料
獲取資料方法二:轉發群
關注小編公眾號后,將本文轉發至不低於100人的群(FPGA相關行業),截圖發送到后台,小編會在24小時之內回復。備注:【領取SPI源碼】即可領取資料