SPI(Serial Peripheral Interface),是一種同步的傳輸協定,主要應用於單晶片系統中。類似 I2C(之後會提到),它的應用有快閃記憶體、EEPROM、SD 卡與顯示器等等....
SPI協定規定了4個邏訊號介面(也有三線的,但那就是單工了):
開始傳輸資料時,CS 要拉低,而我的模式是:mosi 在 SCLK 的負緣時值會改變,而 miso 則是在 SCLK 正緣時值會改變,與 Uart 不同的是:SPI 並沒有起始位元,slave 單純靠 SCLK 的正負緣讀取資料(正或負要看如何設計的),在這邊,資料位寬的話我是做 16bit;傳輸頻率 1MHZ,因為想說可以試著與 max7219 做通訊,有興趣的可以去查一下 max7219 的 datasheet。
跟 UART 一樣,先定義四個狀態:
/*---------parameter---------*/
localparam IDLE = 2'd0;
localparam START = 2'd1;
localparam SHIFT = 2'd2;
localparam STOP = 2'd3;
再來定義輸入輸出
輸入:
輸出:
module SPI(
clk_sys,
SCLK_temp,
tick_SPI,
rst_n,
SCLK,
CS,
count,
countEN,
rstcount,
ready,
finish,
SHEN,
LDEN
);
/*-----------ports declaration-----------*/
input clk_sys;
input SCLK_temp;
input rst_n;
input ready;
input tick_SPI;
input [3:0] count;
output CS;
output countEN;
output rstcount;
output SHEN;
output LDEN;
output finish;
output SCLK;
reg CS;
reg countEN;
reg rstcount;
reg SHEN;
reg LDEN;
reg finish;
wire SCLK;
/*-----------finish-----------*/
always@(posedge clk_sys or negedge rst_n)begin
if(!rst_n)begin
finish <= 1'b0;
end
else begin
if(fstate==STOP&&tick_SPI)finish <= 1'b1;
else finish <= 1'b0;
end
end
宣告跑狀態的變數:
/*---------variables---------*/
reg [1:0] fstate;
狀態邏輯:
/*-----------state-----------*/
always@(posedge clk_sys or negedge rst_n)begin
if(!rst_n)fstate <= IDLE;
else begin
case(fstate)
IDLE:begin
if(ready)fstate <= START;
else fstate <= IDLE;
end
START:begin
if(tick_SPI)fstate <= SHITF;
else fstate <= START;
end
SHITF:begin
if(count==4'd14&&tick_SPI)fstate <= STOP;
else fstate <= SHITF;
end
STOP:begin
if(tick_SPI)fstate <= IDLE;
else fstate <= STOP;
end
default:fstate <= IDLE;
endcase
end
end
輸出邏輯:
/*-----------output-----------*/
always@(posedge clk_sys or negedge rst_n)begin
if(!rst_n)begin
CS <= 1'b1;
countEN <= 1'b0;
rstcount <= 1'b0;
SHEN <= 1'b0;
LDEN <= 1'b0;
end
else begin
if(tick_SPI)begin
case(fstate)
IDLE:begin
CS <= 1'b1;
countEN <= 1'b0;
rstcount <= 1'b0;
SHEN <= 1'b0;
LDEN <= 1'b0;
end
START:begin
CS <= 1'b0;
countEN <= 1'b0;
rstcount <= 1'b0;
SHEN <= 1'b0;
LDEN <= 1'b1;
end
SHITF:begin
CS <= 1'b0;
countEN <= 1'b1;
if(count==4'd14) rstcount <= 1'b1;
else if(count<4'd14)rstcount <= 1'b0;
else rstcount <= 1'b0;//prevent latch
SHEN <= 1'b1;
LDEN <= 1'b0;
end
STOP:begin
CS <= 1'b1;
countEN <= 1'b0;
rstcount <= 1'b0;
SHEN <= 1'b0;
LDEN <= 1'b0;
end
default:begin
CS <= 1'bx;
countEN <= 1'bx;
rstcount <= 1'bx;
SHEN <= 1'bx;
LDEN <= 1'bx;
end
endcase
end
else begin
CS <= CS;
countEN <= countEN;
rstcount <= rstcount;
SHEN <= SHEN;
LDEN <= LDEN;
end
end
end
endmodule
這裡也一樣,要數到 15 卻 14 就結束是因為外部模組會延後一個 clk,如果 15 才停,那外部模組它數到 16 才停下,shift 的次數也會多一次,這種情況是我們不樂見的~!
那麼今天的的教學就到這邊,下一篇我們會完成整個 SPI 模組~~~~