1. spi通信协议
SPI是一个同步的数据总线,总共有四根线,分别为:SCLK、CS、MISO、MOSI。产生时钟的一侧为主机(master),另一侧为从机(slave),主机只有一个,从机可以有多个,分为一主一从、一主多从以及菊花式连接方式的一主多从。
整体的传输大概可以分为以下几个过程:
①:主机先将NSS信号拉低,这样保证开始接收数据;
②:当接收端检测到时钟的边沿信号时,它将立即读取数据线上的信号,这样就得到了一位数据(1bit);
③:主机发送到从机时:主机产生相应的时钟信号,然后数据一位一位地将从MOSI信号线上进行发送到从机;
④:主机接收从机数据:如果从机需要将数据发送回主机,则主机将继续生成预定数量的时钟信号,并且从机会将数据通过MISO信号线发送;
2. 四种工作模式
CPOL表示SCLK的空闲状态的有效电平;
CPOL = 0表示SCLK的空闲状态是低电平,CPOL = 1表示SCLK的空闲状态是高电平;
CPHA规定了数据的采样时间;
CPHA = 0表示在SCLK的有效电平的第一个跳变边沿进行数据采样,CPHA= 1表示在SCLK的有效电平的第二个跳边沿进行数据采样;
Mode0:CPOL=0,CPHA =0:当空闲态时,SCLK处于低电平,数据采样是在第1个边沿,也就 是SCLK由低电平到高电平的跳变,所以数据采样是在上升沿,数据发送是在下降沿。
Mode1:CPOL=0,CPHA=1:当空闲态时,SCLK处于低电平,数据发送是在第2个边沿,也就是SCLK由低电平到高电平的跳变,所以数据采样是在下降沿,数据发送是在上升沿。
Mode2:CPOL=1,CPHA=0:当空闲态时,SCLK处于高电平,数据采集是在第1个边沿,也就是SCLK由高电平到低电平的跳变,所以数据采集是在下降沿,数据发送是在上升沿。
Mode3:CPOL=1,CPHA=1:当空闲态时,SCLK处于高电平,数据发送是在第2个边沿,也就是SCLK由高电平到低电平的跳变,所以数据采集是在上升沿,数据发送是在下降沿。
3. 时序图
SPI0:(最常用)
SPI1:
SPI2:
SPI3:
代码及仿真:
spi_drive:
`timescale 1ns / 1ps
module spi_drive#(
parameter P_DATA_WIDTH = 8 ,
P_READ_DATA_WIDTH = 8 ,
P_CPOL = 0 ,
P_CPHL = 0
)(
input i_clk ,
input i_rst ,
output o_spi_clk ,
output o_spi_cs ,
output o_spi_mosi ,
input i_spi_miso ,
input [P_DATA_WIDTH - 1 :0] i_user_data ,
input i_user_valid ,
output o_user_ready ,
output [P_READ_DATA_WIDTH - 1:0] o_user_read_data ,
output o_user_read_valid
);
reg ro_spi_clk ;
reg ro_spi_cs ;
reg ro_spi_mosi ;
reg ro_user_ready ;
reg [P_DATA_WIDTH - 1:0] r_user_data ;
reg r_run ;
reg [15:0] r_cnt ;
reg r_spi_cnt ;
reg [P_READ_DATA_WIDTH - 1:0] ro_user_read_data ;
reg ro_user_read_valid ;
reg r_run_1d ;
wire w_user_active ;
wire w_run_negedge ;
assign o_spi_clk = ro_spi_clk ;
assign o_spi_cs = ro_spi_cs ;
assign o_spi_mosi = ro_spi_mosi ;
assign o_user_ready = ro_user_ready ;
assign o_user_read_data = ro_user_read_data ;
assign o_user_read_valid = ro_user_read_valid ;
assign w_run_negedge = !r_run & r_run_1d ;
assign w_user_active = i_user_valid & o_user_ready;
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
ro_user_ready <='d1;
else if(w_user_active)
ro_user_ready <= 'd0;
else if(w_run_negedge)
ro_user_ready <= 'd1;
else
ro_user_ready <= ro_user_ready;
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
r_user_data <= 'd0;
else if(w_user_active)
r_user_data <= i_user_data;
else if(r_spi_cnt)
r_user_data <= r_user_data << 1;
else
r_user_data <= r_user_data;
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
r_run <= 'd0;
else if(r_spi_cnt && r_cnt == 7)
r_run <= 'd0;
else if(w_user_active)
r_run <= 'd1;
else
r_run <= r_run;
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
r_run_1d <= 'd0;
else
r_run_1d <= r_run;
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
r_cnt <= 'd0;
else if(r_spi_cnt && r_cnt == 7)
r_cnt <= 'd0;
else if(r_spi_cnt)
r_cnt <= r_cnt + 1;
else
r_cnt <= r_cnt;
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
r_spi_cnt <= 'd0;
else if(r_run)
r_spi_cnt <= r_spi_cnt + 1;
else
r_spi_cnt <= 'd0;
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
ro_spi_clk <= P_CPOL;
else if(r_run)
ro_spi_clk <= ~ro_spi_clk;
else
ro_spi_clk <= P_CPOL;
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
ro_spi_cs <= 'd1;
else if(w_user_active)
ro_spi_cs <= 'd0;
else if(!r_run)
ro_spi_cs <= 'd1;
else
ro_spi_cs <= ro_spi_cs;
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
ro_spi_mosi <= 'd0;
else if(w_user_active)
ro_spi_mosi <= i_user_data[P_DATA_WIDTH - 1];
else if(r_spi_cnt)
ro_spi_mosi <= r_user_data[P_DATA_WIDTH - 2];
else
ro_spi_mosi <= ro_spi_mosi;
end
always@(posedge ro_spi_clk,posedge i_rst)
begin
if(i_rst)
ro_user_read_data <= 'd0;
else
ro_user_read_data <= {ro_user_read_data[P_DATA_WIDTH - 2 : 0],i_spi_miso};
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
ro_user_read_valid <= 'd0;
else if(r_spi_cnt && r_cnt == 7)
ro_user_read_valid <= 'd1;
else
ro_user_read_valid <= 'd0;
end
endmodule
spi_drive_tb:
`timescale 1ns / 1ns
module spi_drive_tb();
localparam P_CLK_CYCLE = 20;
reg clk,rst;
initial
begin
rst = 'd1;
#100;
@(posedge clk)rst = 'd0;
end
always
begin
clk = 0;
#(P_CLK_CYCLE/2);
clk = 1;
#(P_CLK_CYCLE/2);
end
wire w_spi_clk ;
wire w_spi_cs ;
wire w_spi_mosi ;
wire w_user_ready ;
wire [7:0] w_user_read_data ;
wire w_user_read_valid ;
reg [7:0] r_user_data ;
reg r_user_valid ;
spi_drive#(
.P_DATA_WIDTH ( 8 ),
.P_READ_DATA_WIDTH ( 8 ),
.P_CPOL ( 0 ),
.P_CPHL ( 0 )
)
spi_drive_u0
(
.i_clk (clk ),
.i_rst (rst ),
.o_spi_clk (w_spi_clk ),
.o_spi_cs (w_spi_cs ),
.o_spi_mosi (w_spi_mosi ),
.i_spi_miso (w_spi_mosi ),
.i_user_data (8'h55 ),
.i_user_valid (1 ),
.o_user_ready (w_user_ready ),
.o_user_read_data (w_user_read_data ),
.o_user_read_valid (w_user_read_valid )
);
endmodule