1. 程式人生 > >SPI匯流排介紹和verilog實現

SPI匯流排介紹和verilog實現

這篇文章講SPI匯流排,SPI是serial peripheral interface 的縮寫,即序列外圍裝置介面。該介面是摩托羅拉公司提出的全雙工同步通訊的介面,該介面只有四根訊號線,在晶片的管腳上只佔用4根線,節約了晶片的管腳。

這四根訊號信如下:

1、MOSI:主器件資料輸出,從器件資料輸入。

2、MISO:主器件資料輸入,從器件資料輸出。

3、SCLK:時鐘線,有主器件控制。

4、CS:從器件的片選線,由主器件控制。

在點對點的通訊當中:無須定址工作,使用該介面實現全雙工通訊,高效簡單,一個主器件可以連線多個從裝置,每個從裝置有獨立的片選訊號。不過該介面有一個缺點,就是沒有應答機制。

該介面的工作機制:主裝置啟動,連線多個從裝置,在sdo端輸出,si端輸入資料,均在sclk的上升沿傳輸資料,則經過8/16次時鐘的改變,就能夠完成8/16 bit的資料傳輸。

一般情況下,對有該介面的時鐘會有兩方面的設定:一方面是時鐘極性,主要是用來規定空閒狀態下sclk的值,0代表空閒狀態下是低電平,1代表空閒狀態下是高電平;另一方面是時鐘相位的設定,主要是規定資料是在第一個跳變沿被取樣還是第二個跳變沿被取樣,0是代表第一個跳變沿,1是代表第二個跳變沿。下面兩幅圖給出不同相位下,不同極性的傳輸效果:

相位為0:


相位為1:


以上就是SPI介面的工作原理,下面給出其verilog實現,這裡主器件用讀命令和寫命令來控制資料的輸入和輸出,並且對於一個位元組的資料讀和寫分別用一個任務實現,如下:

module spi(clk,rd,wr,rst,data_in,si,so,sclk,cs,data_out);
parameter bit7=4'd0,bit6=4'd1,bit5=4'd2,bit4=4'd3,bit3=4'd4,bit2=4'd5,bit1=4'd6,bit0=4'd7,bit_end=4'd8;
parameter bit70=4'd0,bit60=4'd1,bit50=4'd2,bit40=4'd3,bit30=4'd4,bit20=4'd5,bit10=4'd6,bit00=4'd7,bit_end0=4'd8;

parameter size=8;
input clk,rst;
input wr,rd;//讀寫命令
input si;//spi資料輸入端
input [size-1:0]data_in;//待發送的資料

output[size-1:0]data_out;//待接收的資料
output sclk;//spi中的時鐘
output so;//spi的傳送端
output cs;//片選訊號

wire [size-1:0]data_out;
reg [size-1:0]dout_buf;
reg FF;
reg sclk;
reg so;
reg cs;


reg [3:0]send_state;//傳送狀態暫存器
reg [3:0]receive_state;//接收狀態暫存器

[email protected]
(posedge clk) begin if(!rst) begin sclk<=0; cs<=1; end else begin if(rd|wr) begin sclk<=~sclk;//當開始讀或者寫的時候,需要啟動時鐘 cs<=0; end else begin sclk=0; cs<=1; end end end [email protected](posedge sclk)//傳送資料 begin if(wr) begin send_state<=bit7; send_data; end end
[email protected]
(posedge sclk)//接收資料 begin if(rd) begin receive_state<=bit70; FF<=0; receive_data; end end assign data_out=(FF==1)?dout_buf:8'hz; task send_data;//傳送資料任務 begin case(send_state) bit7: begin so<=data_in[7]; send_state<=bit6; end bit6: begin so<=data_in[6]; send_state<=bit5; end bit5: begin so<=data_in[5]; send_state<=bit4; end bit4: begin so<=data_in[4]; send_state<=bit3; end bit3: begin so<=data_in[3]; send_state<=bit2; end bit2: begin so<=data_in[2]; send_state<=bit1; end bit1: begin so<=data_in[1]; send_state<=bit0; end bit0: begin so<=data_in[0]; send_state<=bit_end; end bit_end: begin so=1'bz; send_state<=bit7; end endcase end endtask task receive_data; begin case (receive_state) bit70: begin dout_buf[7]<=si; receive_state<=bit60; end bit60: begin dout_buf[6]<=si; receive_state<=bit50; end bit50: begin dout_buf[5]<=si; receive_state<=bit40; end bit40: begin dout_buf[4]<=si; receive_state<=bit30; end bit30: begin dout_buf[3]<=si; receive_state<=bit20; end bit20: begin dout_buf[2]<=si; receive_state<=bit10; end bit10: begin dout_buf[1]<=si; receive_state<=bit00; end bit00: begin dout_buf[0]<=si; receive_state<=bit_end; FF<=1; end bit_end0: begin dout_buf<=8'hzz; receive_state<=bit70; end endcase end endtask endmodule
測試程式碼:
`timescale 1ns/1ns
`define half_period 10
module spi_test;
parameter size=8;
wire so;
wire sclk;
wire [size-1:0]data_out;
wire cs;

reg [size-1:0]data_in;
reg wr,rd;
reg si;

reg rst;
reg [size-1:0]si_buf;
reg clk;

always#(`half_period) clk=~clk;
initial
begin
clk=0;
rst=1;
si_buf=8'b1001_1010;
data_in=8'b0101_0011;
#5
rst=0;
#10
rst=1;
wr=1;
rd=1;
#380 rd=0;
#1000 $stop;
end
[email protected](posedge clk)
begin
si_buf=si_buf<<1;
si<=si_buf[7];

end

spi m(clk,rd,wr,rst,data_in,si,so,sclk,cs,data_out);
endmodule
最後得到的模擬圖,這裡資料沒有傳輸完都設定為高阻狀態: