用Verilog代码实现一个简易的SPI从机接口

SPI可以作为FPGA与其它芯片之间一种简单有效的通信方式。

 

SPI 1 - 什么是 SPI?

 

SPI是一个简单的接口,允许一个芯片与一个或多个其他芯片通信。

看上去怎么样?

让我们从一个简单的例子开始,其中只有两个芯片必须通信在一起。

SPI需要在两片芯片之间使用4根线。

用Verilog代码实现一个简易的SPI从机接口_第1张图片

正如您所看到的,这些线被称为SCK、MOSI、MISO和SSEL,其中一个芯片被称为SPI  MASTER主芯片,而另一个芯片称为SPI  SLAVE。

SPI基础

基本上:

  1. 它是同步的。
  2. 是全双工系列。
  3. 这不是即插即用。
  4. 有一个(而且只有一个)master和一个(或更多)slave。

详情如下:

  1. 同步:时钟由主控制器产生。
  2. 全双工串行:数据被串行化,在每个时钟周期内每个方向传输一位数据,因此使用两条数据线(MOSI和MISO)。
  3. 不是即插即用:主从双方事先知道通信的细节(位顺序、交换数据的长度等)。
  4. 一个主从:从不能发起通信,只有主可以。从接收并回应。

简单传输

让我们假设主从期望传输8位数据,首先传输MSB。

MOSI行是“主输出”,MISO是“从输出”。由于SPI是全双工,两行同时切换,不同的数

据从主从和从到主。

详情如下:

 

  1. 主从将SSEL拉低,以指示通信正在启动(SSEL处于低活动状态)。
  2. 主切换时钟8次,在其MOSI线路上发送8位数据。同时,它从MISO线路上的从站接收8个数据位。
  3. 主拉起SSEL表示转移已经结束。

如果主机有多个8位数据要发送/接收,则只有在一次传输完成后才能继续发送/接收。

多个从设备

SPI主机可以通过两种方式与多路从设备进行通信:通过并联连接添加更多SSEL线,或者通过将从设备链接起来的方式。

用Verilog代码实现一个简易的SPI从机接口_第2张图片

使用多条SSEL的方式,一次只能激活一条SSEL,而未被选择的从设备不能驱动MISO线。

有多快?

SPI可以轻松地实现几Mbps(兆位每秒)。这意味着它可以用于未压缩音频或压缩视频。

 

SPI 2 - 一个简单的实现

 

SPI从HDL FPGA码

现在用于FPGA中的SPI从站。

由于SPI总线通常比FPGA工作时钟慢得多,所以我们选择使用FPGA时钟对SPI总线进行过采样。这使得从代码稍微复杂一些,但它的优点是在FPGA时钟域中运行SPI逻辑,这

将使以后的工作变得更容易。

首先是模块声明。

module SPI_slave(clk, SCK, MOSI, MISO, SSEL, LED);
input clk;

input SCK, SSEL, MOSI;
output MISO;

output LED;

请注意,我们有“CLK”(FPGA时钟)和一个LED输出。一个不错的调试工具。“CLK”需要

比SPI总线更快。

用Verilog代码实现一个简易的SPI从机接口_第3张图片

我们使用FPGA时钟和移位寄存器对SPI信号(SCK、SSEL和MOSI)进行采样/同步。

// sync SCK to the FPGA clock using a 3-bits shift register
reg [2:0] SCKr;  always @(posedge clk) SCKr <= {SCKr[1:0], SCK};
wire SCK_risingedge = (SCKr[2:1]==2'b01);  // now we can detect SCK rising edges
wire SCK_fallingedge = (SCKr[2:1]==2'b10);  // and falling edges

// same thing for SSEL
reg [2:0] SSELr;  always @(posedge clk) SSELr <= {SSELr[1:0], SSEL};
wire SSEL_active = ~SSELr[1];  // SSEL is active low
wire SSEL_startmessage = (SSELr[2:1]==2'b10);  // message starts at falling edge
wire SSEL_endmessage = (SSELr[2:1]==2'b01);  // message stops at rising edge

// and for MOSI
reg [1:0] MOSIr;  always @(posedge clk) MOSIr <= {MOSIr[0], MOSI};
wire MOSI_data = MOSIr[1];

现在从SPI总线接收数据很容易。

// we handle SPI in 8-bits format, so we need a 3 bits counter to count the bits as they come in
reg [2:0] bitcnt;

reg byte_received;  // high when a byte has been received
reg [7:0] byte_data_received;

always @(posedge clk)
begin
  if(~SSEL_active)
    bitcnt <= 3'b000;
  else
  if(SCK_risingedge)
  begin
    bitcnt <= bitcnt + 3'b001;

// implement a shift-left register (since we receive the data MSB first)
    byte_data_received <= {byte_data_received[6:0], MOSI_data};
  end
end

always @(posedge clk) byte_received <= SSEL_active && SCK_risingedge && (bitcnt==3'b111);

// we use the LSB of the data received to control an LED
reg LED;
always @(posedge clk) if(byte_received) LED <= byte_data_received[0];

最后是传输部分。

reg [7:0] byte_data_sent;

reg [7:0] cnt;
always @(posedge clk) if(SSEL_startmessage) cnt<=cnt+8'h1;  // count the messages

always @(posedge clk)
if(SSEL_active)
begin
  if(SSEL_startmessage)
    byte_data_sent <= cnt;  // first byte sent in a message is the message count
  else
  if(SCK_fallingedge)
  begin
    if(bitcnt==3'b000)
      byte_data_sent <= 8'h00;  // after that, we send 0s
    else
      byte_data_sent <= {byte_data_sent[6:0], 1'b0};
  end
end

assign MISO = byte_data_sent[7];  // send MSB first
// we assume that there is only one slave on the SPI bus
// so we don't bother with a tri-state buffer for MISO
// otherwise we would need to tri-state MISO when SSEL is inactive

endmodule

 

当我们通过调试时,我们可以看到LED的变化状态,以及FPGA返回的数据。

现在让我们看看我们是否可以用SPI做一些有用的事情。

 

SPI 3 - 应用

 

        代码

 

 

你可能感兴趣的:(通信接口)