SPI主機實現
一、硬件層
(1)外部引線
SPI通信的最小結構爲一主一從結構,主機向從機提供信號發送接收時鐘SCLK。主機與從機之間存有四根引線,即MOSI(主收從發)、MISO(主發從收)、SCK(通信時鐘)、CS(從機片選信號)。其中,通過在主機上增加片選信號輸出端的數目,或引入編碼,譯碼結構可以使得主機控制更多的從機,實現一主多從的SPI通信。本設計中默認爲一主一從最小結構。
(2)四種工作模式
SPI通信協議規定了4鍾工作模式,在實際應用中應當保證主機和從機工作在相同的工作模式下。SPI工作模式通過時鐘極性CPOL和時鐘相位CPHA聯合指定。其中CPOL指定SCK在空閒狀態時的電平,CPHA指定在SCK的何種邊緣進行數據採樣。其標識如下:
二、FPGA代碼實現
(1)思路
SPI模式0主機控制器在SCK上升沿時對數據進行採樣。由於在時鐘上升沿進行數據採樣,那麼,接收/發送狀態機的狀態應當早於SCK時鐘上升沿提前準備好接收/發送狀態,也就是說在低電平期間就需要準備好
對於發送狀態機,由於數據在SCK時鐘的下降沿進行狀態變換,又因爲從機在上升沿採樣,因此選擇在SCK的低電平中心將需發送的數據壓至MOSI線上。
對於接收狀態機,同樣採用SCK下降沿進行狀態跳變。接收狀態機在SCK上升沿時將數據採集至接收緩衝口,並在採集完一字節數據後生成信號標誌脈衝。
對於SPI主機控制器,另一個重要的模塊是控制模塊,它是用來生成SCK時鐘與CS片選信號的模塊。由於本文設計只有一個從機,因此只單純的控制CS的電平。SCK時鐘的生成也是依靠狀態機,其機理爲當狀態機處於IDLE空閒狀態時,檢測到讀/寫請求,在時鐘相位變爲下降沿時進入工作狀態,並輸出8個完整的SCK時鐘。此後,狀態機轉入STOP狀態,並在一個週期內檢測是否有新的請求信號到來,若無抵達空閒狀態,若有,轉入工作狀態。
(2)代碼實現
宏定義文件
//////////////////////////////////////////////////////////////////////////////////
// Company: NanJing University of Information Science & Technology
// Engineer: Yang Cheng Yu
//
// Create Date: 2020/01/13 20:01:50
// Design Name: spi_master
// Module Name: global_define
// Project Name: SPI2
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
`define SCK_PEDG (phase==9'd499)//默認使用100KHZ的頻率,50M/100k = 500
`define SCK_HIGH (phase==9'd124)
`define SCK_NEDG (phase==9'd249)
`define SCK_LOW (phase==9'd374)
控制模塊
`include "global_definition.v"
//////////////////////////////////////////////////////////////////////////////////
// Company: NanJing University of Information Science & Technology
// Engineer: Yang Cheng Yu
//
// Create Date: 2020/01/13 20:01:50
// Design Name: spi_master
// Module Name: control_module
// Project Name: SPI2
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module control_module(
input clk,
input rst,
output reg sck,
output reg cs_n,
output reg[8:0] phase,
input wr_req,
input rd_req,
output reg[3:0] cnt//位數計數器
);
reg[1:0] state;
reg[1:0] next_state;
localparam IDLE=2'b00;
localparam WORK=2'b01;
localparam STOP=2'b11;
always@(posedge clk or negedge rst)begin
if(!rst)
phase <= 9'd0;
else
if(phase==9'd499)
phase <= 9'd0;
else
phase <= phase + 1'b1;
end
//狀態機狀態轉移邏輯
always@(posedge clk or negedge rst)begin
if(!rst)
state <= IDLE;
else
if(`SCK_NEDG)
state <= next_state;
else
state <= state;
end
//狀態組合判斷
always@(wr_req or rd_req or cnt)begin
case(state)
IDLE:
if(wr_req||rd_req)
next_state <= WORK;
else
next_state <= IDLE;
WORK:
if(cnt==4'd8)
next_state <= STOP;
else
next_state <= WORK;
STOP:
if(wr_req||rd_req)
next_state <= WORK;
else
next_state <= IDLE;
default:
next_state <= IDLE;
endcase
end
//狀態機輸出
always@(posedge clk or negedge rst)begin
if(!rst)begin
sck <= 1'b0;
cs_n <= 1'b1;
cnt <= 4'd0;
end
else begin
case(state)
IDLE:begin
sck <= 1'b0;
cs_n <= 1'b1;
cnt <= 4'd0;
end
WORK:begin
cs_n <= 1'b0;
if(`SCK_PEDG)begin
sck <= 1'b1;
cnt <= cnt + 1'b1;
end
else if(`SCK_NEDG)
sck <= 1'b0;
else
sck <= sck;
end
STOP:begin
cnt <= 4'd0;
sck <= 1'b0;
cs_n <= 1'b1;
end
default:begin
cnt <= cnt;
sck <= sck;
cs_n <= cs_n;
end
endcase
end
end
endmodule
發送模塊
`include "global_definition.v"
//////////////////////////////////////////////////////////////////////////////////
// Company: NanJing University of Information Science & Technology
// Engineer: Yang Cheng Yu
//
// Create Date: 2020/01/13 20:01:50
// Design Name: spi_master
// Module Name: spi_tx
// Project Name: SPI2
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module spi_tx(
input clk,
input rst,
input wr_req,
input [7:0] data_wr,
input [8:0] phase,
output reg wr,
output reg mosi,
input [3:0] cnt
);
reg[1:0] state;
reg[1:0] next_state;
localparam IDLE=2'b00;
localparam WORK=2'b01;
localparam STOP=2'b11;
//狀態機狀態轉移
always@(posedge clk or negedge rst)begin
if(!rst)
state <= IDLE;
else
state <= next_state;
end
//狀態組合判定
always@(wr_req or cnt)begin
case(state)
IDLE:
if(wr_req)
next_state <= WORK;
else
next_state <= IDLE;
WORK:
if(cnt==4'd8)
next_state <= STOP;
else
next_state <= WORK;
STOP:
if(wr_req)
next_state <= WORK;
else
next_state <= IDLE;
default:
next_state <= next_state;
endcase
end
//狀態機輸出
always@(posedge clk or negedge rst)begin
if(!rst)begin
mosi <= 1'b0;
wr <= 1'b1;
end
else begin
case(state)
IDLE:begin
mosi <= 1'b0;
wr <= 1'b1;
end
WORK:begin
wr <= 1'b0;
//由於上升沿採樣,所以在上升沿到來前把數據給到MOSI
if(`SCK_LOW)begin
case(cnt)
4'd0:mosi <= data_wr[7];
4'd1:mosi <= data_wr[6];
4'd2:mosi <= data_wr[5];
4'd3:mosi <= data_wr[4];
4'd4:mosi <= data_wr[3];
4'd5:mosi <= data_wr[2];
4'd6:mosi <= data_wr[1];
4'd7:mosi <= data_wr[0];
default:mosi <= 1'b0;
endcase
end
end
STOP:begin
wr <= 1'b1;
mosi <= 1'b0;
end
default:begin
wr <= wr;
mosi <= mosi;
end
endcase
end
end
endmodule
接收模塊
`include "global_definition.v"
//////////////////////////////////////////////////////////////////////////////////
// Company: NanJing University of Information Science & Technology
// Engineer: Yang Cheng Yu
//
// Create Date: 2020/01/13 20:01:50
// Design Name: spi_master
// Module Name: spi_rx
// Project Name: SPI2
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module spi_rx(
input clk,
input rst,
input rd_req,
output reg[7:0] data_rd,
input[8:0] phase,
output reg rd,
input miso,
input[3:0] cnt
);
reg[1:0] state;
reg[1:0] next_state;
localparam IDLE=2'b00;
localparam WORK=2'b01;
localparam STOP=2'b11;
//狀態機狀態轉移
always@(posedge clk or negedge rst)begin
if(!rst)
state <= IDLE;
else
state <= next_state;
end
//狀態組合判定
always@(rd_req or cnt)begin
case(state)
IDLE:
if(rd_req)
next_state <= WORK;
else
next_state <= IDLE;
WORK:
if(cnt==4'd8)
next_state <= STOP;
else
next_state <= WORK;
STOP:
if(rd_req)
next_state <= WORK;
else
next_state <= IDLE;
default:
next_state <= next_state;
endcase
end
//狀態機輸出
always@(posedge clk or negedge rst)begin
if(!rst)begin
rd <= 1'b1;
data_rd <= 8'b0000_0000;
end
else begin
case(state)
IDLE:begin
rd <= 1'b1;
data_rd <= 8'b0000_0000;
end
WORK:begin
rd <= 1'b0;
if(`SCK_PEDG)begin//下降沿採樣
case(cnt)
4'd0:data_rd[7] <= miso;
4'd1:data_rd[6] <= miso;
4'd2:data_rd[5] <= miso;
4'd3:data_rd[4] <= miso;
4'd4:data_rd[3] <= miso;
4'd5:data_rd[2] <= miso;
4'd6:data_rd[1] <= miso;
4'd7:data_rd[0] <= miso;
default:data_rd <= data_rd;
endcase
end
else
data_rd <= data_rd;
end
STOP:begin
rd <= 1'b1;
data_rd <= data_rd;
end
default:begin
rd <= rd;
data_rd <= data_rd;
end
endcase
end
end
endmodule
頂層模塊及例化
//////////////////////////////////////////////////////////////////////////////////
// Company: NanJing University of Information Science & Technology
// Engineer: Yang Cheng Yu
//
// Create Date: 2020/01/13 20:01:50
// Design Name: spi_master
// Module Name: spi_master
// Project Name: SPI2
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module spi_master(
input clk,
input rst,
input miso,
output mosi,
output sck,
output cs_n,
output wr,
output rd,
input[7:0] data_wr,
output[7:0] data_rd,
input wr_req,
input rd_req
);
wire[8:0] phase;
wire[3:0] cnt;
control_module control_module(
.clk (clk),
.rst (rst),
.sck (sck),
.cs_n (cs_n),
.phase (phase),
.wr_req (wr_req),
.rd_req (rd_req),
.cnt (cnt)//位數計數器
);
spi_tx spi_tx(
.clk (clk),
.rst (rst),
.wr_req (wr_req),
.data_wr (data_wr),
.phase (phase),
.wr (wr),
.mosi (mosi),
.cnt (cnt)
);
spi_rx spi_rx(
.clk (clk),
.rst (rst),
.rd_req (rd_req),
.data_rd (data_rd),
.phase (phase),
.rd (rd),
.miso (miso),
.cnt (cnt)
);
endmodule
RTL視圖
狀態機圖