SPI总线介绍和verilog实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/IamSarah/article/details/76269737

这篇文章讲SPI总线,SPI是serial peripheral interface 的缩写,即串行外围设备接口。该接口是摩托罗拉公司提出的全双工同步通信的接口,该接口只有四根信号线,在芯片的管脚上只占用4根线,节约了芯片的管脚。

这四根信号信如下:

1、MOSI:主器件数据输出,从器件数据输入。

2、MISO:主器件数据输入,从器件数据输出。

3、SCLK:时钟线,有主器件控制。

4、CS:从器件的片选线,由主器件控制。

在点对点的通信当中:无须寻址工作,使用该接口实现全双工通信,高效简单,一个主器件可以连接多个从设备,每个从设备有独立的片选信号。不过该接口有一个缺点,就是没有应答机制。

该接口的工作机制:主设备启动,连接多个从设备,在sdo端输出,si端输入数据,均在sclk的上升沿传输数据,则经过8/16次时钟的改变,就能够完成8/16 bit的数据传输。

一般情况下,对有该接口的时钟会有两方面的设置:一方面是时钟极性,主要是用来规定空闲状态下sclk的值,0代表空闲状态下是低电平,1代表空闲状态下是高电平;另一方面是时钟相位的设置,主要是规定数据是在第一个跳变沿被采样还是第二个跳变沿被采样,0是代表第一个跳变沿,1是代表第二个跳变沿。下面两幅图给出不同相位下,不同极性的传输效果:

相位为0:


相位为1:


以上就是SPI接口的工作原理,下面给出其verilog实现,这里主器件用读命令和写命令来控制数据的输入和输出,并且对于一个字节的数据读和写分别用一个任务实现,如下:

module spi(clk,rd,wr,rst,data_in,si,so,sclk,cs,data_out);
parameter bit7=4'd0,bit6=4'd1,bit5=4'd2,bit4=4'd3,bit3=4'd4,bit2=4'd5,bit1=4'd6,bit0=4'd7,bit_end=4'd8;
parameter bit70=4'd0,bit60=4'd1,bit50=4'd2,bit40=4'd3,bit30=4'd4,bit20=4'd5,bit10=4'd6,bit00=4'd7,bit_end0=4'd8;

parameter size=8;
input clk,rst;
input wr,rd;//读写命令
input si;//spi数据输入端
input [size-1:0]data_in;//待发送的数据

output[size-1:0]data_out;//待接收的数据
output sclk;//spi中的时钟
output so;//spi的发送端
output cs;//片选信号

wire [size-1:0]data_out;
reg [size-1:0]dout_buf;
reg FF;
reg sclk;
reg so;
reg cs;


reg [3:0]send_state;//发送状态寄存器
reg [3:0]receive_state;//接收状态寄存器

always@(posedge clk)
begin
if(!rst)
begin
 sclk<=0;
 cs<=1;
 end
else 
begin
 if(rd|wr) 
 begin
 sclk<=~sclk;//当开始读或者写的时候,需要启动时钟
 cs<=0;
 end
 else 
 begin
 sclk=0;
 cs<=1;
 end
end
end


always@(posedge sclk)//发送数据
begin
if(wr)
begin
send_state<=bit7;
send_data;
end
end

always@(posedge sclk)//接收数据
begin
if(rd)
begin
receive_state<=bit70;
FF<=0;
receive_data;
end
end

assign data_out=(FF==1)?dout_buf:8'hz;

task send_data;//发送数据任务
begin
case(send_state)
bit7:
     begin
	 so<=data_in[7];
	 send_state<=bit6;
	 end
bit6:
     begin
	 so<=data_in[6];
	 send_state<=bit5;
	 end
bit5:
     begin
	 so<=data_in[5];
	 send_state<=bit4;
	 end
bit4:
     begin
	 so<=data_in[4];
	 send_state<=bit3;
	 end
bit3:
     begin
	 so<=data_in[3];
	 send_state<=bit2;
	 end
bit2:
     begin
	 so<=data_in[2];
	 send_state<=bit1;
	 end
bit1:
     begin
	 so<=data_in[1];
	 send_state<=bit0;
	 end
bit0:
     begin
	 so<=data_in[0];
	 send_state<=bit_end;
	 end
bit_end:
     begin
	 so=1'bz;
	 send_state<=bit7;
	 end
endcase
end
endtask

task receive_data;
begin
case (receive_state)
bit70:
      begin
	  dout_buf[7]<=si;
	  receive_state<=bit60;
	  end
bit60:
      begin
	  dout_buf[6]<=si;
	  receive_state<=bit50;
	  end
bit50:
      begin
	  dout_buf[5]<=si;
	  receive_state<=bit40;
	  end
bit40:
      begin
	   dout_buf[4]<=si;
	  receive_state<=bit30;
	  end
bit30:
      begin
	  dout_buf[3]<=si;
	  receive_state<=bit20;
	  end
bit20:
      begin
	  dout_buf[2]<=si;
	  receive_state<=bit10;
	  end
bit10:
      begin
	  dout_buf[1]<=si;
	  receive_state<=bit00;
	  end
bit00:
      begin
	  dout_buf[0]<=si;
	  receive_state<=bit_end;
	  FF<=1;
	  end
bit_end0:
      begin
	  dout_buf<=8'hzz;
	  receive_state<=bit70;
	  end
endcase
end
endtask
endmodule
测试代码:

`timescale 1ns/1ns
`define half_period 10
module spi_test;
parameter size=8;
wire so;
wire sclk;
wire [size-1:0]data_out;
wire cs;

reg [size-1:0]data_in;
reg wr,rd;
reg si;

reg rst;
reg [size-1:0]si_buf;
reg clk;

always#(`half_period) clk=~clk;
initial
begin
clk=0;
rst=1;
si_buf=8'b1001_1010;
data_in=8'b0101_0011;
#5
rst=0;
#10
rst=1;
wr=1;
rd=1;
#380 rd=0;
#1000 $stop;
end
always@(posedge clk)
begin
si_buf=si_buf<<1;
si<=si_buf[7];

end

spi m(clk,rd,wr,rst,data_in,si,so,sclk,cs,data_out);
endmodule
最后得到的仿真图,这里数据没有传输完都设置为高阻状态:





猜你喜欢

转载自blog.csdn.net/IamSarah/article/details/76269737