一、SPI協(xié)議
1、SPI協(xié)議概括
SPI(Serial Peripheral Interface)——串行外圍設(shè)備接口。是Motorola首先在其MC68HCXX系列處理器上定義的。SPI接口主要應(yīng)用在EEPROM、FLASH、實(shí)時(shí)時(shí)鐘,AD轉(zhuǎn)換器以及數(shù)字信號(hào)處理器和數(shù)字信號(hào)解碼器之間。SPI是一種高速,全雙工,同步的通信總線,在芯片上只占用四根線(CS、MOSI、MISO、SCK),極大的節(jié)約了芯片的引腳。
2、SPI物理層
SPI的通信原理很簡(jiǎn)單,它以主從方式工作,這種模式通常有一個(gè)主設(shè)備和一個(gè)或者多個(gè)從設(shè)備。圖1是一個(gè)主設(shè)備一個(gè)從設(shè)備的物理連接示意圖。圖中SCK是由主設(shè)備發(fā)送給從的時(shí)鐘,該時(shí)鐘決定了主設(shè)備發(fā)送數(shù)據(jù)的速率;MOSI是主設(shè)備發(fā)送給從設(shè)備的數(shù)據(jù);MISO是從設(shè)備發(fā)送給主設(shè)備的數(shù)據(jù);CS是片選信號(hào),即只有片選信號(hào)為預(yù)先規(guī)定的使能信號(hào)時(shí)(高電平或者低電平)對(duì)此芯片的操作才有效。
圖1 點(diǎn)對(duì)點(diǎn)通信
圖2 一主多從通信
3、SPI協(xié)議層
SPI通信是四線串行通信,也就是說數(shù)據(jù)是一位一位傳輸?shù)摹_@也即是SCK存在的意義,SCK提供通信所需的時(shí)鐘脈沖,MOSI和MISO則基于此時(shí)鐘進(jìn)行數(shù)據(jù)傳輸。數(shù)據(jù)輸出通過MOSI線,數(shù)據(jù)在時(shí)鐘的上升沿或下降沿時(shí)改變,在緊接著的下降沿或者上升沿被讀取。完成一位數(shù)據(jù)傳輸,輸入也使用同樣原理。這樣,至少在8次時(shí)鐘信號(hào)的改變(上升沿和下降沿為一次),就可以實(shí)現(xiàn)8位數(shù)據(jù)的傳輸。
需要注意的是,SCK信號(hào)線只由主設(shè)備控制,從設(shè)備不能控制信號(hào)線。同樣,在一個(gè)基于SPI的設(shè)備中,至少要有一個(gè)主控設(shè)備。這樣傳輸?shù)奶攸c(diǎn):此傳輸方式有一個(gè)優(yōu)點(diǎn),與普通串行通信不同,普通的串行通信一次連續(xù)傳送至少8位數(shù)據(jù),而SPI允許數(shù)據(jù)一位一位的傳送,甚至允許暫停,因?yàn)镾CK時(shí)鐘線由主控設(shè)備控制,當(dāng)沒有時(shí)鐘跳變時(shí),從設(shè)備不采集或傳送數(shù)據(jù)。也就是說,主設(shè)備通過對(duì)SCK時(shí)鐘線的控制可以完成對(duì)通信的控制。SPI協(xié)議還可以實(shí)現(xiàn)數(shù)據(jù)的交換:因?yàn)镾PI的數(shù)據(jù)輸入和輸出線獨(dú)立所以允許同時(shí)完成數(shù)據(jù)的輸入和輸出。不同的SPI設(shè)備的實(shí)現(xiàn)方式不盡相同,主要時(shí)改變和采集數(shù)據(jù)的時(shí)間不同,在時(shí)鐘信號(hào)上升沿或下降沿采集有不同的定義。
SPI總線有四種工作方式(SPI0、SPI1、SPI2、SPI3),其中使用的最為廣泛的是SPI0和SPI3方式。
SPI模塊為了和外設(shè)進(jìn)行數(shù)據(jù)交換,根據(jù)外設(shè)工作要求,其輸出串行同步時(shí)鐘極性和相位可以進(jìn)行配置,時(shí)鐘極性(CPOL)對(duì)傳輸協(xié)議沒有重大的影響。如果CPOL=0,串行同步時(shí)鐘的空閑狀態(tài)為低電平;如果CPOL=1,串行同步時(shí)鐘的空閑狀態(tài)為高電平。時(shí)鐘相位(CPHA)能夠配置用于選擇兩種不同的傳輸協(xié)議之一進(jìn)行數(shù)據(jù)傳輸。如果CPHA=0,在串行同步時(shí)鐘的第一個(gè)跳變沿(上升沿或下降沿)數(shù)據(jù)被采集;如果CPHA=1,在串行同步時(shí)鐘的第二個(gè)跳變沿(上升沿或下降沿)數(shù)據(jù)被采集。SPI主模塊和與之通信的外設(shè)時(shí)鐘相位和極性應(yīng)該一致。
SPI時(shí)序圖詳解:SPI接口有四種不同的數(shù)據(jù)傳輸時(shí)序,取決于CPOL和CPHA的組合。圖3中給出了這四種時(shí)序,時(shí)序與CPOL和CPHA的關(guān)系也可以從圖中看出。
圖3 SPI四種時(shí)序
圖3中可以看出,CPOL是用來決定SCK時(shí)鐘信號(hào)空閑時(shí)的電平。CPOL=0,SCK空閑時(shí)為低電平;CPOL=1,SCK空閑時(shí)為高電平。CPHA是用來決定采樣輸入數(shù)據(jù)MISO時(shí)刻,CPHA = 0,在第一個(gè)SCK時(shí)鐘沿進(jìn)行數(shù)據(jù)采樣;CPHA=1,在第二個(gè)SCK時(shí)鐘沿進(jìn)行數(shù)據(jù)采集。(工作模式的確定:由SLAVE的工作模式確定MASTER的工作模式)。
二、SPI協(xié)議使用舉例
這里通過使用SPI3來實(shí)現(xiàn)主機(jī)發(fā)送數(shù)據(jù)。
圖4 SPI3 工作模式的主機(jī)發(fā)送數(shù)據(jù)
在SPI3模式下,CPOL = 1,CPHA = 1。SCK在空閑時(shí)為高電平,在SCK的第二個(gè)時(shí)鐘沿從機(jī)進(jìn)行數(shù)據(jù)的采集(只考慮主機(jī)發(fā)送情況),在SCK的第一個(gè)時(shí)鐘沿發(fā)送數(shù)據(jù)MOSI。
三、使用verilog實(shí)現(xiàn)SPI3工作模式的時(shí)序
1、SPI3模式下工作過程如下圖所示,
圖5 SPI發(fā)送數(shù)據(jù)過程
接下來分析圖5所示SPI發(fā)送數(shù)據(jù)的過程,首先在復(fù)位信號(hào)到來時(shí),進(jìn)入s0狀態(tài),在s0狀態(tài)計(jì)數(shù)器和分頻器模塊加載初始值,如果發(fā)送數(shù)據(jù)開始信號(hào)spi_start有效進(jìn)入s1狀態(tài),s1狀態(tài)加載待發(fā)送的數(shù)據(jù),同時(shí)計(jì)數(shù)器計(jì)數(shù)計(jì)數(shù),分頻器開始工作,如果i=1,進(jìn)入s2狀態(tài),s2狀態(tài)主要用來發(fā)送數(shù)據(jù),如果i為偶數(shù),進(jìn)入s3狀態(tài),該狀態(tài)是用來采集數(shù)據(jù),由于只考慮發(fā)送,因此此模塊不進(jìn)行數(shù)據(jù)采集工作,如果i=15,進(jìn)入s4狀態(tài),否則如果i為奇數(shù),則進(jìn)入s2狀態(tài)。;在s4狀態(tài),發(fā)送最后一位數(shù)據(jù),如果i=16,進(jìn)入s5狀態(tài),此時(shí)整個(gè)SPI時(shí)序模擬完成。
2、數(shù)據(jù)路徑
由圖5可知,構(gòu)成SPI發(fā)送時(shí)序的基本電路塊包括計(jì)數(shù)器,移位寄存器和觸發(fā)器模塊。
圖6 數(shù)據(jù)路徑
圖6中,左移寄存器將8位的待發(fā)送的數(shù)據(jù)spi_data轉(zhuǎn)換為串行的數(shù)據(jù)mosi一位一位的發(fā)送出去,計(jì)數(shù)器用來計(jì)數(shù)發(fā)送數(shù)據(jù)的個(gè)數(shù),觸發(fā)器用來產(chǎn)生分頻后的sck時(shí)鐘信號(hào)。
3、控制信號(hào)
圖7 控制信號(hào)
圖7中給出了各個(gè)狀態(tài)哪些控制信號(hào)應(yīng)該有效,參照?qǐng)D5圖6圖7可以理清spi整個(gè)發(fā)送數(shù)據(jù)的過程。
四、 verilog描述
接下來使用verilog來描述圖6所示的電路,控制信號(hào)可根據(jù)圖7進(jìn)行描述。
spi發(fā)送模塊(該模塊主要描述控制信號(hào)):
module SPI_SEND(input clk_50m,
input rst_n,
input spi_start,
input[7:0] spi_data,
output reg spi_done,
output sck,
output reg cs,
output mosi
);
reg load_c;
reg en_c;
reg load_a;
reg en_a;
reg load_b;
reg en_b;
wire [4:0]i;
parameter [4:0] s0 = 'b000001;
parameter [4:0] s1 = 'b000010;
parameter [4:0] s2 = 'b000100;
parameter [4:0] s3 = 'b001000;
parameter [4:0] s4 = 'b010000;
parameter [4:0] s5 = 'b100000;
reg [5:0]current_state = 'd0;
reg [5:0]next_state = 'd0;
always @(posedge clk_50m or negedge rst_n)
if(!rst_n)
current_state <= s0;
else
current_state <= next_state;
always @(*)
case(current_state)
s0:begin
if(spi_start)
next_state = s1;
else
next_state = s0;
end
s1:begin/該狀態(tài)加載待發(fā)送的數(shù)據(jù)
if(i == 'd1)
next_state = s2;
else
next_state = s1;
end
s2:begin1,3,5,7,9,11,13,15
if(i[0] == 1'b0)//
next_state = s3;
else
next_state = s2;
end
s3:begin2,4,6,8,10,12,14,16
if(i == 'd15)
next_state = s4;
else if(i[0] == 'd1)
next_state = s2;
else
next_state = s3;
end
s4:begin
if(i == 'd16)
next_state = s5;
else
next_state = s4;
end
s5:begin
if(i == 'd0)
next_state = s0;
else
next_state = s5;
end
default:next_state = s0;
endcase
always @(*)
case(current_state)
s0:begin///空閑狀態(tài)
load_c = 'd1;
en_c = 'd0;
load_a = 'd0;
en_a = 'd0;
load_b = 'd1;
en_b = 'd0;
spi_done = 'd0;
cs = 'd1;
end
s1:begin加載待發(fā)送數(shù)據(jù)狀態(tài)
load_c = 'd0;
en_c = 'd1;
load_a = 'd1;
en_a = 'd0;
load_b = 'd0;
en_b = 'd1;
spi_done = 'd0;
cs = 'd0;
end
s2:begin第一個(gè)時(shí)鐘沿發(fā)送數(shù)據(jù)
load_c = 'd0;
en_c = 'd1;
load_a = 'd0;
en_a = 'd1;
load_b = 'd0;
en_b = 'd1;
spi_done = 'd0;
cs = 'd0;
end
s3:begin第二個(gè)時(shí)鐘沿采樣數(shù)據(jù)
load_c = 'd0;
en_c = 'd1;
load_a = 'd0;
en_a = 'd0;
load_b = 'd0;
en_b = 'd1;
spi_done = 'd0;
cs = 'd0;
end
s4:begin數(shù)據(jù)發(fā)送完畢
load_c = 'd0;
en_c = 'd1;
load_a = 'd0;
en_a = 'd0;
load_b = 'd0;
en_b = 'd0;
spi_done = 'd0;
cs = 'd0;
end
s5:begin
load_c = 'd0;
en_c = 'd0;
load_a = 'd0;
en_a = 'd0;
load_b = 'd0;
en_b = 'd0;
spi_done = 'd1;
cs = 'd1;
end
default:begin
load_c = 'd1;
en_c = 'd0;
load_a = 'd0;
en_a = 'd0;
load_b = 'd1;
en_b = 'd0;
spi_done = 'd0;
cs = 'd1;
end
endcase
// Instantiate the module
count_num count_num (
.clk_50m(clk_50m),
.load_c(load_c),
.en_c(en_c),
.count(i)
);
// Instantiate the module
left_shifter left_shifter (
.clk_50m(clk_50m),
.load_a(load_a),
.en_a(en_a),
.spi_data_in(spi_data),
.mosi(mosi)
);
// Instantiate the module
sck_generate sck_generate (
.clk_50m(clk_50m),
.load_b(load_b),
.en_b(en_b),
.sck(sck)
);
endmodule
計(jì)數(shù)器電路描述:
module count_num(input clk_50m,
input load_c,
input en_c,
output reg[4:0]count
);
always @(posedge clk_50m)
if(load_c)
count <= 'd0;?
else if(en_c)begin
if(count == 'd16)
count <= 'd0;
else
count <= count + 'd1;
end
else
count <= count;
endmodule
移位寄存器電路描述:
module left_shifter(input clk_50m,
input load_a,
input en_a,
input [7:0]spi_data_in,
output mosi
);
reg [7:0]data_reg;
always @(posedge clk_50m)
if(load_a)
data_reg <= spi_data_in;
else if(en_a)
data_reg <= {data_reg[6:0],1'b0};
else
data_reg <= data_reg;
assign mosi = data_reg[7];
endmodule
觸發(fā)器電路描述:
//SPI3模式下工作,SCK空閑時(shí)為高電平
//
module sck_generate(input clk_50m,
input load_b,
input en_b,
output reg sck
);
always @(posedge clk_50m)
if(load_b)
sck <= 'd1;
else if(en_b)
sck <= ~sck;
else
sck <= 'd1;
endmodule
仿真激勵(lì)文件:
module test;
// Inputs
reg clk_50m;
reg rst_n;
reg spi_start;
reg [7:0]spi_data;
// Outputs
wire spi_done;
wire sck;
wire cs;
wire mosi;
// Instantiate the Unit Under Test (UUT)
SPI_SEND uut (
.clk_50m(clk_50m),
.rst_n(rst_n),
.spi_start(spi_start),
.spi_done(spi_done),
.sck(sck),
.cs(cs),
.spi_data(spi_data),
.mosi(mosi)
);
initial begin
// Initialize Inputs
clk_50m = 0;
rst_n = 0;
spi_start = 0;
spi_data = 'd0;
// Wait 100 ns for global reset to finish
#100;
// Add stimulus here
end
always #5 clk_50m = ~clk_50m;
reg [4:0] count = 'd0;
always @(posedge clk_50m)
if(count == 'd20)
count <= 'd20;
else
count <= count + 'd1;
always @(posedge clk_50m)
if(count <= 'd10)
rst_n <= 'd0;
else
rst_n <= 'd1;
reg [9:0]cnt = 'd0;
always @(posedge clk_50m)
if(spi_done)
cnt <= 'd0;
else if(cnt == 'd500)
cnt <= 'd500;
else
cnt <= cnt + 'd1;
always @(posedge clk_50m)
if(cnt=='d499)begin
spi_start <= 'd1;
spi_data <= 'b10101010;
end
elsebegin
spi_start <= 'd0;
spi_data <= spi_data;
end
endmodule
使用ISIM仿真結(jié)果:
圖8 仿真結(jié)果
圖8中待發(fā)送的數(shù)據(jù)spi_data[7:0]=10101010,由于使用的是SPI3模式(CPOL=1,CPHA=1),此模式下SCK空閑時(shí)為1,在SCK第一個(gè)時(shí)鐘沿進(jìn)行數(shù)據(jù)發(fā)送(即圖中SCK下降沿進(jìn)行數(shù)據(jù)發(fā)送),從圖中波形可以看出 ,在cs為低時(shí),mosi被一位一位的送出(高位先輸出)。
審核編輯 :李倩
-
FPGA
+關(guān)注
關(guān)注
1625文章
21624瀏覽量
601245 -
SPI
+關(guān)注
關(guān)注
17文章
1688瀏覽量
91215
原文標(biāo)題:FPGA學(xué)習(xí)-基于FPGA的SPI協(xié)議實(shí)現(xiàn)
文章出處:【微信號(hào):gh_9d70b445f494,微信公眾號(hào):FPGA設(shè)計(jì)論壇】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論