一. SPI總線協議
二. FPGA的SPI從機實現
1. 首先確定模塊的輸出輸入管腳
module myspi(nrst, clk, ncs, mosi, miso, sck);
input clk, nrst;
input ncs, mosi, sck;
output miso;
2. SCK跳變沿檢測
reg[2:0] sck_edge;
always @ (posedge clk or negedge nrst)
begin
if(~nrst)
begin
sck_edge <= 3'b000;
end
else
begin
sck_edge <= {sck_edge[1:0], sck};
end
end
wire sck_riseedge, sck_falledge;
assign sck_riseedge = (sck_edge[2:1] == 2'b01); //檢測到SCK由0變成1,則認爲發現上跳沿
assign sck_falledge = (sck_edge[2:1] == 2'b10); //檢測到SCK由1變成0,則認爲發現下跳沿
3. SPI接收部分
SPI接收部分使用有限狀態機:reg[7:0] byte_received;
reg[3:0] bit_received_cnt;
reg rec_flag;
reg[1:0] rec_status; //SPI接收部分狀態機
reg[7:0] rec_data;
reg[2:0] rec_flag_width; //SPI接收完成標誌位脈衝寬度寄存器
always @ (posedge clk or negedge nrst) //每次sck都會接收數據,spi的頂端模塊狀態機決定是否取用
begin
if(~nrst)
begin
byte_received <= 8'h00;
bit_received_cnt <= 4'h0;
rec_flag <= 1'b0;
rec_status <= 2'b00;
rec_flag_width <= 3'b000;
end
else
begin
if(~ncs)
begin
case (rec_status)
2'b00: begin
if(sck_riseedge)
begin
byte_received <= {byte_received[6:0], mosi};
if(bit_received_cnt == 4'h7)
begin
bit_received_cnt <= 4'b0000;
rec_status <= 2'b01;
end
else
begin
bit_received_cnt <= bit_received_cnt+1;
end
end
end
2'b01: begin
rec_data <= byte_received;
rec_flag <= 1'b1;
if(rec_flag_width==3'b100) begin
rec_flag_width <= 3'b000;
rec_status <= 2'b11;
end
else begin
rec_flag_width <= rec_flag_width+1;
end
end
2'b11: begin
rec_flag <= 1'b0;
rec_status <= 2'b00;
end
endcase
end
end
end
這裏,使用rec_flag的原因是通知另一個模塊處理接收數據(後面將會提到),rec_data若在下一次數據傳輸完成前不做處理則會丟失。
4. SPI發送部分
reg miso;
reg sending_flag; //正在發送標誌位
reg[7:0] byte_sended; //發送移位寄存器
reg[3:0] bit_sended_cnt; //SPI發送位計數器
reg[1:0] send_status; //SPI發送部分狀態機
always @ (posedge clk or negedge nrst)
begin
if(~nrst)
begin
byte_sended <= 8'h00;
bit_sended_cnt <= 4'b0000;
send_status <= 2'b00;
sending_flag <= 1'b0;
end
else
begin
if(~ncs)
begin
case (send_status)
2'b00: begin
if(send_flag)
begin //鎖存發送數據
send_status <= 2'b01; //2'b01;
byte_sended <= send_data;
sending_flag <= 1'b1;
miso <= send_data[7];
end
end
2'b01: begin //發送數據移入移位寄存器
if(sck_riseedge) begin
//miso <= byte_sended[7];
//byte_sended <= {byte_sended[6:0], 1'b0};
send_status <= 2'b11;
end
end
2'b11: begin //根據sck下降沿改變數據
miso <= byte_sended[7];
if(sck_falledge) ///---------------------------------------這裏多移了一位
begin
//miso <= byte_sended[7];
byte_sended <= {byte_sended[6:0], 1'b0};
if(bit_sended_cnt == 4'b0111)
begin
send_status <= 2'b10;
bit_sended_cnt <= 4'b0000;
sending_flag <= 1'b0;
end
else
begin
bit_sended_cnt <= bit_sended_cnt+1;
end
end
end
2'b10: begin //數據發送完畢
send_status <= 2'b00;
//sending_flag <= 1'b0;
miso <= 1'b0;
end
endcase
end
end
end
經過實測,SCK頻率低於clk頻率8倍以上,通信可靠穩定,測試芯片爲XC3S50-TQ144,平臺爲ISE,clk爲25MHz。