关于SPI协议中MCU与FPGA片间通信的理解(FPGA作为从机)

来源:互联网 发布:金税盘如何备份的数据 编辑:程序博客网 时间:2024/04/30 15:43
  • Module Name : SPI_receive
  • Engineer : Bai Fengqiang
  • Target Device : EP2C8Q208C8
  • Tool versions : Quartus II 13.1
  • Create Date : 2017-7-17
  • Revision : v1.0
  • Description :

这里写图片描述
由以上时序图可知,我们采用主机模式,同时SPI作为一种外部串行传输协议,它主要由四根线控制,即SPI_CS(片选信号,有了它可以实现一个主机,多个从机的片间通信,),SPI_SCK(SPI数据串行传输时钟线),SPI_MOSI(从机输入/主机输出数据),SPI_MISO(从机输出/主机输入数据),关于传输协议一共有4种模式,由CPHA与CPOL控制,具体情况,具体分析,在此文中,我门将CPOL配置为0,CPHA配置为1,(CPOL=0,CPHA=1),即时钟上升沿采样。
由时序图可分析如:
1.SPI_CS在低电平时有效,高电平时属于无效状态。
2.SPI_MOSI在片选信号下降沿时有效,并在SPI_CLK上升沿捕获数据。
3.SPI_MISO在片选信号下降沿时有效,并在SPI_CLK上升沿捕获数据。

所以SPI_CLK的上升沿极为重要,我们可以利用脉冲边缘检测法获得,同时从MCU中送入FPGA的各种数据或者信号,属于不同时钟域的,故需要做同步,在脉冲边缘检测时,使用了两个寄存器,故在这里也使用两个,另外为了防止亚稳态的产生,我们这里采用同步释放,异步复位的处理机制。
具体代码如下:
module SPI_receiver(
clk,
rst_n,
spi_cs,
spi_data_in,
spi_clk,
reveice_data,
receiver_flag,
true_rst,
rst_n1,
rst_n2,
spi_clk1,
spi_clk2,
spi_posedge_clk,
spi_cs1,
spi_cs2,
spi_data1,
spi_data2,
spi_cs_true,
spi_true_data,s
pi_receiver_data_finish_flag,
receiver_data_cnt,
receiver_data_r
);

//global input clk and rst_n
input clk;
input rst_n;

//SPI user interface
input spi_cs;
input spi_clk;
input [7:0] spi_data_in;

//led_display interface
output reg [7:0] reveice_data;
output reg receiver_flag;

output reg rst_n1,rst_n2;
output wire true_rst;
output spi_cs_true;
output [7:0]spi_true_data;
output spi_receiver_data_finish_flag;
output reg spi_clk1,spi_clk2;
output wire spi_posedge_clk;
output reg spi_cs1,spi_cs2;
output reg [7:0]spi_data1,spi_data2;
output reg [2:0] receiver_data_cnt;
output reg [7:0] receiver_data_r;

//////////////////////防止亚稳态,这里采用同步释放,异步复位的原理。
always@(posedge clk )
if(!rst_n)
begin
rst_n1<=
rst_n2<=0;
end
else
begin
rst_n1<=1;
rst_n2<=rst_n1;
end

assign true_rst=rst_n2;
////////////////////////因为为上升沿采样,所以使用脉冲边缘检测法,得到SPI_CLK的上升沿。

always@(posedge clk or negedge true_rst)
if(!true_rst)
begin
spi_clk1<=0;
spi_clk2<=0;
end
else
begin
spi_clk1<=spi_clk;
spi_clk2<=spi_clk1;
end

assign spi_posedge_clk=(spi_clk1)&(~spi_clk2);

/////////////////////////从MCU中送入FPGA的各种数据或者信号,属于不同时钟域的,故需要做同步,在脉冲边缘检测时,使用了两个寄存器,故在这里也使用两个。

always@(posedge clk or negedge true_rst)
if(!true_rst)
begin
spi_cs1<=1;
spi_cs2<=1;
spi_data1<=0;
spi_data2<=0;
end

else

begin
spi_cs1<=spi_cs;
spi_cs2<=spi_cs1;
spi_data1<=spi_data_in;
spi_data2<=spi_data1;
end

assign spi_cs_true=spi_cs2;
assign spi_true_data=spi_data2;
assign spi_receiver_data_finish_flag=((spi_cs1)&(~spi_cs2))?1’b1:1’b0;
assign spi_posedge_clk=((spi_clk1)&(~spi_clk2))?1’b1:1’b0;

////////////////////////////////////////////接收数据

always@(posedge clk or negedge true_rst)
if(!true_rst)
begin
receiver_data_cnt<=0;
receiver_data_r<=0;
end
else
begin
if(spi_cs_true==0)
begin
if(spi_posedge_clk)
begin
receiver_data_r[7-receiver_data_cnt]<=spi_true_data;
receiver_data_cnt<=receiver_data_cnt+1;
end
else //spi_posedge_clk不为上升沿的时候,即保持数据不变
begin
receiver_data_r<=receiver_data_r;
receiver_data_cnt<=receiver_data_cnt;
end
end

    else //(spi_cs_true!=0)片选无效    begin    receiver_data_cnt<=0;    receiver_data_r<=receiver_data_r;    endend

always@(posedge clk or negedge true_rst)
if(!true_rst)
begin
receiver_flag<=0;
reveice_data<=0;
end
else
begin
if(spi_receiver_data_finish_flag)
begin
reveice_data<=receiver_data_r;
receiver_flag<=1;
end
else //接收数据没有结束
begin
reveice_data<=reveice_data;
receiver_flag<=0;
end
end

endmodule

仿真脚本:
`timescale 1 ps/ 1 ps
module SPI_receiver_vlg_tst();
// constants
// general purpose registers
reg eachvec;
// test vector input registers
reg clk;
reg rst_n;
reg spi_clk;
reg spi_cs;
reg [7:0] spi_data_in;
// wires
wire receiver_flag;
wire [7:0] reveice_data;
wire true_rst;
wire rst_n1;
wire rst_n2;
wire spi_clk1;
wire spi_clk2;
wire spi_posedge_clk;
wire spi_cs1;
wire spi_cs2;
wire [7:0]spi_data1;
wire [7:0]spi_data2;
wire spi_cs_true;
wire [7:0]spi_true_data;
wire spi_receiver_data_finish_flag;
wire [2:0]receiver_data_cnt;
wire [7:0]receiver_data_r;
// assign statements (if any)
SPI_receiver i1 (
// port map - connection between master ports and signals/registers
.clk(clk),
.receiver_flag(receiver_flag),
.reveice_data(reveice_data),
.rst_n(rst_n),
.spi_clk(spi_clk),
.spi_cs(spi_cs),
.spi_data_in(spi_data_in),

.true_rst                          (true_rst                      ),.rst_n1                            (rst_n1                        ),.rst_n2                            (rst_n2                        ),.spi_clk1                          (spi_clk1                      ),.spi_clk2                          (spi_clk2                      ),.spi_posedge_clk                   (spi_posedge_clk               ),.spi_cs1                           (spi_cs1                       ),.spi_cs2                           (spi_cs2                       ),.spi_data1                         (spi_data1                ),.spi_data2                    (spi_data2                ),.spi_cs_true                       (spi_cs_true                   ),.spi_true_data                (spi_true_data            ),.spi_receiver_data_finish_flag     (spi_receiver_data_finish_flag ),.receiver_data_cnt            (receiver_data_cnt        ),.receiver_data_r              (receiver_data_r          )

);
initial
begin
clk=0;
forever #10 clk=~clk;
end

initial begin
rst_n=0;

80;

rst_n=1;
end

task task_data_trs;
input [7:0] data_from_user;
begin
spi_cs=0;

20;

spi_clk=0;
spi_data_in=data_from_user[7];

20;

spi_clk=1;

20;

spi_clk=0;
spi_data_in=data_from_user[6];

20;

spi_clk=1;

20;

spi_clk=0;
spi_data_in=data_from_user[5];

20;

spi_clk=1;

20;

spi_clk=0;
spi_data_in=data_from_user[4];

20;

spi_clk=1;

20;

spi_clk=0;
spi_data_in=data_from_user[3];

20;

spi_clk=1;

20;

spi_clk=0;
spi_data_in=data_from_user[2];

20;

spi_clk=1;

20;

spi_clk=0;
spi_data_in=data_from_user[1];

20;

spi_clk=1;

20;

spi_clk=0;
spi_data_in=data_from_user[0];

20;

spi_clk=1;

20;

spi_clk=0;
spi_cs=1;

100;

end
endtask
initial begin
spi_cs=1;
spi_data_in=0;
spi_clk=0;
end

initial begin

100;

task_data_trs(8’h95);

100;

task_data_trs(8’hbe);
end
endmodule
仿真结果:
这里写图片描述
在测试脚本中我们由模拟SPI时序通过MCU向FPGA中发送8’h95和8’hbe,由以上可知,接收数据正确。
记住:如果我们要采样MCU传过来的SPI_CLK时钟,至少满足CLK是它的两倍,奈奎斯特。。。你懂得,测试代码中刚好2倍。

阅读全文
0 0
原创粉丝点击