簡介
SPI(Serial Peripheral Interface,串行外圍設(shè)備接口)通訊協(xié)議,是Motorola公司提出的一種同步串行接口技術(shù)。是一種高速、全雙工、同步通信總線。在芯片中只占用四根管腳用來控制及數(shù)據(jù)傳輸。
優(yōu)缺點:
SPI通訊協(xié)議的優(yōu)點是支持全雙工通信,通訊方式較為簡單,且相對數(shù)據(jù)傳輸速率較快;
缺點是沒有指定的流控制,沒有應(yīng)答機(jī)制確認(rèn)數(shù)據(jù)是否接收,與IIC總線通訊協(xié)議相比,在數(shù)據(jù)可靠性上有一定缺陷。
物理層
對于SPI協(xié)議的物理層,需要講解的就是SPI通訊設(shè)備的連接方式和設(shè)備引腳的功能描述。
SPI通訊設(shè)備的通訊模式是主從通訊模式,通訊雙方有主從之分,根據(jù)從機(jī)設(shè)備的個數(shù),SPI通訊設(shè)備之間的連接方式可分為一主一從和一主多從,具體見下圖1、2。
圖 1 一主一從SPI通訊設(shè)備連接圖
圖 2 一主多從SPI通訊設(shè)備連接圖
SPI通訊協(xié)議包含1條時鐘信號線、2條數(shù)據(jù)總線和1條片選信號線, 時鐘信號線為SCK,2條數(shù)據(jù)總線分別為MOSI(主輸出從輸入)、MISO(主輸入從輸出),片選信號線為,它們的作用介紹如下:
-
SCK (Serial Clock):時鐘信號線,用于同步通訊數(shù)據(jù)。由通訊主機(jī)產(chǎn)生,決定了通訊的速率,不同的設(shè)備支持的最高時鐘頻率不同,兩個設(shè)備之間通訊時,通訊速率受限于低速設(shè)備。
-
MOSI (Master Output, Slave Input):主設(shè)備輸出/從設(shè)備輸入引腳。主機(jī)的數(shù)據(jù)從這條信號線輸出,從機(jī)由這條信號線讀入主機(jī)發(fā)送的數(shù)據(jù),數(shù)據(jù)方向由主機(jī)到從機(jī)。
-
MISO (Master Input,Slave Output):主設(shè)備輸入/從設(shè)備輸出引腳。主機(jī)從這條信號線讀入數(shù)據(jù),從機(jī)的數(shù)據(jù)由這條信號線輸出到主機(jī),數(shù)據(jù)方向由從機(jī)到主機(jī)。
-
(Chip Select):片選信號線,也稱為CS_N,以下用CS_N表示。當(dāng)有多個SPI從設(shè)備與SPI主機(jī)相連時,設(shè)備的其它信號線SCK、MOSI及MISO同時并聯(lián)到相同的SPI總線上,即無論有多少個從設(shè)備,都共同使用這3條總線;而每個從設(shè)備都有獨立的這一條CS_N信號線,本信號線獨占主機(jī)的一個引腳,即有多少個從設(shè)備,就有多少條片選信號線。
I2C協(xié)議中通過設(shè)備地址來尋址、選中總線上的某個設(shè)備并與其進(jìn)行通訊;而SPI協(xié)議中沒有設(shè)備地址,它使用CS_N信號線來尋址,當(dāng)主機(jī)要選擇從設(shè)備時,把該從設(shè)備的CS_N信號線設(shè)置為低電平,該從設(shè)備即被選中,即片選有效,接著主機(jī)開始與被選中的從設(shè)備進(jìn)行 SPI通訊。所以SPI通訊以CS_N線置低電平為開始信號,以CS_N線被拉高作為結(jié)束信號。
四種通訊模式
SPI通訊協(xié)議一共有四種通訊模式,模式0、模式1、模式2以及模式3,這4種模式分別由時鐘極性(CPOL,Clock Polarity)和時鐘相位(CPHA,Clock Phase)來定義。
CPOL參數(shù)規(guī)定了空閑狀態(tài)(CS_N為高電平,設(shè)備未被選中)時SCK時鐘信號的電平狀態(tài);
CPHA規(guī)定了數(shù)據(jù)采樣是在SCK時鐘的奇數(shù)邊沿還是偶數(shù)邊沿。
極性和相位
SPI的極性Polarity和相位Phase,最常見的寫法是CPOL和CPHA,不過也有一些其他寫法,簡單總結(jié)如下:
(1) CKPOL (Clock Polarity) = CPOL = POL = Polarity = (時鐘)極性
(2) CKPHA (Clock Phase) = CPHA = PHA = Phase = (時鐘)相位
(3) SCK=SCLK=SPI的時鐘
(4) Edge=邊沿,即時鐘電平變化的時刻,即上升沿(rising edge)或者下降沿(falling edge)
模式判別
CPOL,表示當(dāng)SCLK空閑idle的時候,其電平的值是低電平0還是高電平1:
CPOL=0,時鐘空閑idle時候的電平是低電平,所以當(dāng)SCLK有效的時候,就是高電平,就是所謂的active-high;
CPOL=1,時鐘空閑idle時候的電平是高電平,所以當(dāng)SCLK有效的時候,就是低電平,就是所謂的active-low;
CPHA=0,表示第一個邊沿:
對于CPOL=0,idle時候的是低電平,第一個邊沿就是從低變到高,所以是上升沿;
對于CPOL=1,idle時候的是高電平,第一個邊沿就是從高變到低,所以是下降沿;
CPHA=1,表示第二個邊沿:
對于CPOL=0,idle時候的是低電平,第二個邊沿就是從高變到低,所以是下降沿;
對于CPOL=1,idle時候的是高電平,第一個邊沿就是從低變到高,所以是上升沿;
SPI通訊協(xié)議的4種模式如下,通訊模式時序圖,具體見下圖 :
模式0:CPOL= 0,CPHA=0??臻e狀態(tài)時SCK串行時鐘為低電平;數(shù)據(jù)采樣在SCK時鐘的上升沿;數(shù)據(jù)更新在SCK時鐘的下降沿。
模式1:CPOL= 0,CPHA=1??臻e狀態(tài)時SCK串行時鐘為低電平;數(shù)據(jù)采樣在SCK時鐘的下降沿;數(shù)據(jù)更新在SCK時鐘的上升沿。
模式2:CPOL= 1,CPHA=0??臻e狀態(tài)時SCK串行時鐘為高電平;數(shù)據(jù)采樣在SCK時鐘的下降沿;數(shù)據(jù)更新在SCK時鐘的上升沿。
模式3:CPOL= 1,CPHA=1??臻e狀態(tài)時SCK串行時鐘為高電平;數(shù)據(jù)采樣在SCK時鐘的上升沿;數(shù)據(jù)更新在SCK時鐘的下降沿。
模式的判斷:
如果起始的SCLK的電平是0,那么CPOL=0,如果是1,那么CPOL=1,
然后看數(shù)據(jù)采樣時刻,對應(yīng)到上面SCLK時鐘的位置,對應(yīng)著是第一個邊沿或是第二個邊沿,即CPHA是0或1
時序需求
tSLCH:cs_n拉低到sck高的時間
tCHSH:sck高到cs_n拉高的時間
SCK上升沿MOSI的建立時間保持時間需求
tSHSL:取消選擇的時間,兩串?dāng)?shù)據(jù)間隔時間
SPI通用模塊
實現(xiàn)功能
用于將任意寬度向量型數(shù)據(jù)轉(zhuǎn)換為SPI串行輸出,模式0:CPOL= 0,CPHA=0;
默認(rèn)串行數(shù)據(jù)mosi數(shù)據(jù)的建立時間和保持時間均為2個clk(0.5*DIV_FREQUENCY)周期,即sck上升沿的前后2個(0.5*DIV_FREQUENCY)clk數(shù)據(jù)穩(wěn)定,串行時鐘sck周期為4*clk(DIV_FREQUENCY)的周期;
默認(rèn)tSLCH(cs_n拉低到sck高的時間為6*clk(1.5*DIV_FREQUENCY)周期),tCHSH(sck高到cs_n拉高的時間為2*clk(0.5*DIV_FREQUENCY)周期);
默認(rèn)tSHSL為10個clk的周期;
若時序滿足此模塊可以不做修改。若需要修改在外部例化時修改DIV_FREQUENCY(只能偶分頻)和PERIOD_WIDTH_MAX,CNT_SHSL_MAX和CNT_SHSL_WIDTH即可。
使用方法
輸入data的位寬和計數(shù)器位寬在外部進(jìn)行例化時修改參數(shù)的值即可,不必修改SPI模塊
DATA_WIDTH_MAX修改為輸入數(shù)據(jù)的位寬,如[31:0]的數(shù)據(jù)則DATA_WIDTH_MAX=32
CNT_DATA_WIDTH_MAX修改為輸入數(shù)據(jù)的位寬計數(shù)器需要的位寬上限,即DATA_WIDTH_MAX=32對應(yīng)的二進(jìn)制位寬32=6’b10_0000,所以CNT_DATA_WIDTH_MAX=6
輸入輸出端口說明:
//input
input wire clk , //系統(tǒng)時鐘,spi串行時鐘的分頻基準(zhǔn)
input wire clr_n , //spi信號標(biāo)志信號,允許發(fā)送時一直拉高,重新發(fā)送時拉低復(fù)位再拉高
input wire [DATA_WIDTH_MAX-1:0] data , //需要data與clr_n一同進(jìn)入
//output
output reg cs_n , //片選信號
output reg sck , //串行時鐘
output reg mosi , //主輸出從輸入數(shù)據(jù)
output reg flag //spi發(fā)送完成標(biāo)志位,完成則一直拉高;clr_n置0時拉低
例化模板:文章來源:http://www.zghlxwxcb.cn/news/detail-824371.html
spi #(
.DATA_WIDTH_MAX (8 ),
.CNT_DATA_WIDTH_MAX (4 )
)
u_spi(
.clk (clk ),
.clr_n (clr_n ),
.data (data_in ),
.cs_n (cs_n ),
.sck (sck ),
.mosi (mosi ),
.flag (flag )
);
SPI模塊
//========================================================================
// module_name.v :spi.v
// Author :YprgDay
// Description :用于將任意寬度向量型數(shù)據(jù)轉(zhuǎn)換為SPI串行輸出,模式0:CPOL= 0,CPHA=0。
//========================================================================
module spi
#(
//=========================< Parameter >==============================
parameter DATA_WIDTH_MAX = 32 ,//例化時修改為輸入數(shù)據(jù)的位寬,如[31:0]的數(shù)據(jù)則DATA_WIDTH_MAX =32
parameter CNT_DATA_WIDTH_MAX = 6 ,//例化時修改為輸入數(shù)據(jù)的位寬計數(shù)器需要的位寬上限,即DATA_WIDTH_MAX=32對應(yīng)的二進(jìn)制位寬32=6'b10_0000,所以CNT_DATA_WIDTH_MAX=6
parameter DIV_FREQUENCY = 4 ,//分頻數(shù)(只允許偶分頻),串行時鐘sck周期為DIV_FREQUENCY*clk的周期
parameter PERIOD_WIDTH_MAX = 2 ,//(DIV_FREQUENCY-1)對應(yīng)的二進(jìn)制位寬即為PERIOD_WIDTH_MAX
parameter CNT_PERIOD_MAX = DIV_FREQUENCY-1 ,
parameter CNT_HALF_PERIOD_MAX = CNT_PERIOD_MAX >> 1 ,//計數(shù)分頻中值
parameter CNT_SHSL_MAX = 10 ,//tSHSL計數(shù)10個clk周期
parameter CNT_SHSL_WIDTH = 4 //CNT_SHSL_MAX的二進(jìn)制位寬
)
(
//=========================< Port Name >==============================
//input
input wire clk , //系統(tǒng)時鐘,spi串行時鐘的分頻基準(zhǔn)
input wire clr_n , //spi信號標(biāo)志信號,允許發(fā)送時一直拉高,重新發(fā)送時拉低復(fù)位再拉高
input wire [DATA_WIDTH_MAX-1:0] data , //需要data與clr_n一同進(jìn)入
//output
output reg cs_n , //片選信號
output reg sck , //串行時鐘
output reg mosi , //主輸出從輸入數(shù)據(jù)
output reg flag //spi發(fā)送完成標(biāo)志位,完成則一直拉高;clr_n置0時拉低
);
//=========================< Always block >===========================
reg [CNT_DATA_WIDTH_MAX-1:0] cnt_data_width ;//輸出到第幾位計數(shù)
reg [PERIOD_WIDTH_MAX-1:0] cnt_spi_period ;//時鐘分頻計數(shù)
reg [DATA_WIDTH_MAX-1:0] data_reg ;//存輸入數(shù)據(jù),保證一個spi發(fā)送周期數(shù)據(jù)不改變
reg [CNT_SHSL_WIDTH-1:0] cnt_shsl ;//SHSL時間計數(shù),保證兩串spi的時序需求
//輸出的cs_n片選信號
always @(posedge clk or negedge clr_n)begin
if(clr_n == 1'b0)begin
cs_n <= 1'b1;
end
else if(cnt_data_width == DATA_WIDTH_MAX && cnt_spi_period == CNT_PERIOD_MAX)begin
cs_n <= 1'b1;
end
else begin
cs_n <= 1'b0;
end
end
//輸出的sck串行時鐘信號,
always @(posedge clk or negedge clr_n)begin
if(clr_n == 1'b0)begin
sck <= 0;
end
else if(cnt_data_width > 0 && cnt_spi_period == CNT_PERIOD_MAX)begin
sck <= 0;
end
else if(cnt_data_width > 0 && cnt_spi_period == CNT_HALF_PERIOD_MAX)begin
sck <= 1;
end
else begin
sck <= sck;
end
end
//mosi的串行輸出
always @(posedge clk or negedge clr_n)begin
if(clr_n == 1'b0)begin
mosi <= 0;
end
else if(cnt_data_width == DATA_WIDTH_MAX && cnt_spi_period == CNT_PERIOD_MAX)begin
mosi <= 0;
end
else if(cnt_spi_period == CNT_PERIOD_MAX)begin
mosi <= data_reg[DATA_WIDTH_MAX-1-cnt_data_width];
end
else begin
mosi <= mosi;
end
end
//輸出的串行數(shù)據(jù)發(fā)送完成標(biāo)志信號,發(fā)送完成即拉高
always @(posedge clk or negedge clr_n)begin
if(clr_n == 1'b0)begin
flag <= 0;
end
else if(cnt_shsl == CNT_SHSL_MAX)begin
flag <= 1;
end
else begin
flag <= 0;
end
end
//輸入數(shù)據(jù)寄存,保證data在一串SPI數(shù)據(jù)發(fā)完之間不發(fā)生變化
always @(posedge clk or negedge clr_n)begin
if(clr_n == 1'b0)begin
data_reg <= 0;
end
else if(cnt_data_width == 0 && cnt_spi_period == 1)begin
data_reg <= data;
end
else begin
data_reg <= data_reg;
end
end
//時鐘四分頻計數(shù)
always @(posedge clk or negedge clr_n)begin
if(clr_n == 1'b0)begin
cnt_spi_period <= 0;
end
else if(cnt_data_width == DATA_WIDTH_MAX && cnt_spi_period == CNT_PERIOD_MAX)begin
cnt_spi_period <= cnt_spi_period;
end
else if(cnt_spi_period == CNT_PERIOD_MAX)begin
cnt_spi_period <= 0;
end
else if(cs_n == 0)begin
cnt_spi_period <= cnt_spi_period + 1'b1;
end
else;
end
//計數(shù)表示此時輸出到[DATA_WIDTH_MAX:0] data的第幾位位置
always @(posedge clk or negedge clr_n)begin
if(clr_n == 1'b0)begin
cnt_data_width <= 0;
end
else if(cnt_data_width == DATA_WIDTH_MAX && cnt_spi_period == CNT_PERIOD_MAX)begin
cnt_data_width <= cnt_data_width;
end
else if(cnt_spi_period == CNT_PERIOD_MAX)begin
cnt_data_width <= cnt_data_width + 1'b1;
end
else;
end
//SHSL時間計數(shù),保證兩串spi的時序需求,用于對flga信號拉高判斷
always @(posedge clk or negedge clr_n)begin
if(clr_n == 1'b0)begin
cnt_shsl <= 0;
end
else if(cnt_shsl == CNT_SHSL_MAX)begin
cnt_shsl <= cnt_shsl;
end
else if(cnt_data_width == DATA_WIDTH_MAX && cnt_spi_period == CNT_PERIOD_MAX)begin
cnt_shsl <= cnt_shsl + 1'b1;
end
else;
end
endmodule
仿真模塊
`timescale 1ns / 1ps
//
// Module Name: tb_spi
// Dependencies: spi模塊仿真
//
module tb_spi();
//=========================< Parameter >==============================
parameter SPI_CLK_PERIOD = 2 ;//設(shè)置spi時鐘信號周期
parameter HALF_SPI_CLK_PERIOD = SPI_CLK_PERIOD/2;//生成spi時鐘信號半周期
//=========================< Port Name >==============================
//input
reg clk ;
reg clr_n ;
reg [7:0] data_in ;
//output
wire mosi ;
wire cs_n ;
wire sck ;
wire flag ;
//==========================< Clock block >============================
always #HALF_SPI_CLK_PERIOD clk = ~clk ;
//==========================< Reset block >============================
initial begin
clk = 1'b1 ;
clr_n <= 1'b0 ;
data_in <= 0 ;
#HALF_SPI_CLK_PERIOD
clr_n <= 1'b1 ;
data_in <= 8'h83 ;
#30
data_in <= 8'h54 ;
#300
clr_n <= 1'b0 ;
data_in <= 8'hc7 ;
#10
clr_n <= 1'b1 ;
end
//==========================< Module Instance >============================
spi #(
.DATA_WIDTH_MAX (8 ),
.CNT_DATA_WIDTH_MAX (4 )
)
u_spi(
.clk (clk ),
.clr_n (clr_n ),
.data (data_in ),
.cs_n (cs_n ),
.sck (sck ),
.mosi (mosi ),
.flag (flag )
);
endmodule
仿真時序圖
以8位輸入數(shù)據(jù)[7:0]data的SPI時序圖為例文章來源地址http://www.zghlxwxcb.cn/news/detail-824371.html
到了這里,關(guān)于SPI簡介及FPGA通用MOSI模塊實現(xiàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!