声明:本篇文章面向在已对SPI的四种时序有所了解的人
我们采用SPI3模式以及将FPGA作从机,STM32作主机的方式讲解,在STM32控制部分采用的是半双工模式,但其实半双工与全双工区别不大,
稍加修改即可
本文章属于SPI的升级版,将原本的片选线CS_N再多加一根,变成spi_cs_cmd和spi_cs_data,当spi_cs_cmd拉低的时候表示传送的是命令(命令只由单片机发送),当cs_data拉低的时候表示传送的是数据(数据可以是FPGA传给单片机的,也可以是单片机传给FPGA的)
module spi #(
parameter cmd_width = 8,
parameter data_width = 32 //这个位宽是可以自己定义的,由于STM32F407的硬件SPI不支持32位,所 以在STM32部分我选用了模拟SPI,具体STM32部分代码可以在我的后续文章中查看
)
(
input clk,
input rstn,
input spi_scl, //SCK
input spi_sdi, //MOSI
input spi_cs_cmd, //CS_CMD
input spi_cs_data, //CS_DATA
input [data_width - 1:0] din, //送入SPI模块的数据,将来是要通过SPI模块的MISO口传到单片机的
output reg spi_sdo, //MISO
output reg cmd_done, //通过判断CS_CMD的边沿变化判断CMD命令是不是传输完成
output reg data_done, //通过判断CS_DATA的边沿变化判断DATA数据是不是传输完成
output reg [cmd_width - 1:0] dcmd, //送出SPI模块的数据,即单片机传输给FPGA的bit数据,dcmd为命令
output reg [data_width - 1:0] dout //送出SPI模块的数据,即单片机传输给FPGA的bit数据,dout为数据
);
reg [1:0] r_spi_scl;
reg [1:0] r_cs_cmd;
reg [1:0] r_cs_data;
wire pos_spi_scl;
wire neg_spi_scl;
wire pos_cs_cmd;
wire neg_cs_cmd;
wire en_cs_cmd;
wire pos_cs_data;
wire neg_cs_data;
wire en_cs_data;
reg [data_width - 1:0] r_din;
// 边沿检测
always @(posedge clk , negedge rstn) begin
if(!rstn)
r_spi_scl <= 2'd0;
else
r_spi_scl <= {r_spi_scl[0], spi_scl};
end
assign pos_spi_scl = (!r_spi_scl[1]) &(r_spi_scl[0]);
assign neg_spi_scl = (r_spi_scl[1]) &(!r_spi_scl[0]);
always @(posedge clk , negedge rstn) begin
if(!rstn)
r_cs_cmd <= 2'd0;
else
r_cs_cmd <= {r_cs_cmd[0], spi_cs_cmd};
end
assign pos_cs_cmd = (!r_cs_cmd[1]) &(r_cs_cmd[0]);
assign neg_cs_cmd = (r_cs_cmd[1]) &(!r_cs_cmd[0]);
assign en_cs_cmd = (!r_cs_cmd[1]) &(!r_cs_cmd[0]);
always @(posedge clk , negedge rstn) begin
if(!rstn)
r_cs_data <= 2'd0;
else
r_cs_data <= {r_cs_data[0], spi_cs_data};
end
assign pos_cs_data = (!r_cs_data[1]) &(r_cs_data[0]);
assign neg_cs_data = (r_cs_data[1]) &(!r_cs_data[0]);
assign en_cs_data = (!r_cs_data[1]) &(!r_cs_data[0]);
// 接收模块
always @(posedge clk , negedge rstn) begin
if(!rstn)
dcmd <= 0;
else if(neg_cs_cmd)
dcmd <= 0;
else if(en_cs_cmd)
if(pos_spi_scl) //由于采用的是SPI3模式,所以上升沿采样数据
dcmd[cmd_width - 1:0] <= {dcmd[cmd_width - 2:0], spi_sdi};
else
dcmd[cmd_width - 1:0] <= dcmd[cmd_width - 1:0];
else
dcmd <= dcmd;
end
always @(posedge clk , negedge rstn) begin
if(!rstn)
dout <= 0;
else if(neg_cs_data)
dout <= 0;
else if(en_cs_data)
if(pos_spi_scl) //由于采用的是SPI3模式,所以上升沿采样数据
dout[data_width - 1:0] <= {dout[data_width - 2:0], spi_sdi};
else
dout[data_width - 1:0] <= dout[data_width - 1:0];
else
dout <= dout;
end
always @(posedge clk , negedge rstn) begin
if(!rstn)
cmd_done <= 1'b0;
else if(pos_cs_cmd)
cmd_done <= 1'b1;
else
cmd_done <= 1'b0;
end
always @(posedge clk , negedge rstn) begin
if(!rstn)
data_done <= 1'b0;
else if(pos_cs_data)
data_done <= 1'b1;
else
data_done <= 1'b0;
end
// 发送模块
always @(posedge clk , negedge rstn) begin
if(!rstn) begin
spi_sdo <= 1'b0;
r_din <= 0;
end
else if(en_cs_data)
if(neg_spi_scl) begin //由于采用的是SPI3模式,所以下降沿发送数据
spi_sdo <= r_din[data_width - 1];
r_din[data_width - 1:0] <= {r_din[data_width - 2:0], 1'b0};
end
else begin
spi_sdo <= spi_sdo;
r_din[data_width - 1:0] <= r_din[data_width - 1:0];
end
else if(!en_cs_data) begin
spi_sdo <= spi_sdo;
r_din[data_width - 1:0] <= din[data_width - 1:0];
end
else begin
spi_sdo <= spi_sdo;
r_din[data_width - 1:0] <= r_din[data_width - 1:0];
end
end
endmodule