//主程序
//功能:完成32位以内SPI接口的数据双向通信
module lcd_spi_m
#(
parameter [5:0]spi_in_width =6'd9,//spi 输入位数
parameter [5:0]spi_out_width =6'd9,//SPI 输出位数
parameter [0:0]spi_cpol=1'b0,//空闲状态SCL电平 0:SCL=0 1:SCL=1
parameter [0:0]spi_cpha=1'b1 //SPI数据在哪个SCL边沿有效 0:数据在SCL第一个边沿有效,1:数据在SCL第二个边沿有效,
)
(
input wire [0:0] rst_n_i, //复位输入,低电平复位
input wire [0:0] spi_x2clk_i,//SPI系统时钟 为SCL输出时钟的两倍
input wire [31:0] spi_data_i,//输入32位要从MOSI发送出去的数据
input wire [0:0] spi_start, //单次发送开始,把数据送到spi_data_i 并把spi_start维技一个周期的高电平
input wire [0:0] spi_miso_i,//主机接收从机输出引脚
output reg [0:0] spi_done,//SPI完成一次传输并从spi_data_o输出读到的数据
output reg [0:0] spi_busy,//SPI忙信号输出,在忙状态时不接收外部数据,高表示忙
output reg [0:0] spi_cs_o,//SPI片选信号输出低有效
output reg [0:0] spi_scl_o,//SPI 时钟信号输出,请结合CPOL CPHA分析有效性
output reg [0:0] spi_mosi_o,//SPI主机输出从机输入接口
output reg [31:0] spi_data_o//从从机读到的数据在SPI_DONE为高时为有效数据
);
localparam [7:0] CS_F_DELAY=8'D1; //CS前面延时
localparam [7:0] CS_B_DELAY=8'D1; //CS后面延时
localparam [7:0] CS_F_CNT=CS_F_DELAY+CS_F_DELAY; //CS前面延时
localparam [7:0] CS_B_CNT=CS_B_DELAY+CS_B_DELAY; //CS后面延时
localparam [7:0] WIDTH_MAX=(spi_in_width>spi_in_width)? (spi_in_width*2'D2+CS_F_CNT+CS_B_CNT) :(spi_out_width*2'D2+CS_F_CNT+CS_B_CNT);
//产生spi_x2clk_i时钟计数
reg [6:0] clk_cnt;
always @(posedge spi_x2clk_i or negedge rst_n_i)
begin
if(rst_n_i==1'b0)
clk_cnt<=6'd0;
else if(spi_busy==1'b1 )
if(clk_cnt<WIDTH_MAX)
clk_cnt<=clk_cnt+1'd1;
else
clk_cnt<=7'd0;
else
clk_cnt<=7'd0;
end
//输出SPI_CS信号
always @(posedge spi_x2clk_i or negedge rst_n_i)
begin
if(rst_n_i==1'b0)
spi_cs_o<=1'b1;
else if(spi_start==1'b1 && spi_busy==1'b0)
spi_cs_o<=1'b0;
else if(clk_cnt<(WIDTH_MAX-1'd1))
spi_cs_o<=spi_cs_o;
else
spi_cs_o<=1'b1;
end
//输出spi_scl信号
always @(posedge spi_x2clk_i or negedge rst_n_i)
begin
if(rst_n_i==1'b0)
begin
if(spi_cpol==1'b0)
spi_scl_o<=1'b0;
else
spi_scl_o<=1'b1;
spi_done<=1'b0;
end
else if(clk_cnt>CS_F_CNT-1'd1 && clk_cnt<(WIDTH_MAX-CS_B_CNT-1'd1))
begin
spi_scl_o<=~spi_scl_o;
end
else if(clk_cnt>=WIDTH_MAX-1'd1)
begin
spi_done<=1'b1;
end
else
begin
if(spi_cpol==1'b0)
spi_scl_o<=1'b0;
else
spi_scl_o<=1'b1;
spi_done<=1'b0;
end
end
//在spi_start为1时捕获数据
reg [31:0] temp_data_i;
//输出SPI_MOSI信号
always @(posedge spi_x2clk_i or negedge rst_n_i)
begin
if(rst_n_i==1'b0)
begin
spi_mosi_o<=1'b0;
spi_busy<=1'b0;
temp_data_i<=32'b0;
end
else if(spi_start==1'b1 && spi_busy==1'b0) //在spi_start为1时捕获数据
begin
temp_data_i<=spi_data_i;
spi_busy<=1'b1;
end
else if(spi_done==1'b1)
spi_busy<=1'b0;
else if(spi_cpha==1'b0)
begin
if((clk_cnt>=(CS_F_CNT-8'd1)) && (clk_cnt<(WIDTH_MAX-CS_B_CNT-CS_F_CNT)) && (clk_cnt%2==1) )
begin
spi_mosi_o<=temp_data_i[spi_out_width-1'd1];
temp_data_i<={temp_data_i[(spi_out_width-2'd2):0],temp_data_i[spi_out_width-1'd1]};
end
else
begin
spi_mosi_o<=spi_mosi_o;
end
end
else if(spi_cpha==1'b1)
begin
if((clk_cnt>=CS_F_CNT) && clk_cnt<(WIDTH_MAX-CS_B_CNT-CS_F_CNT) && (clk_cnt%2==0) )
begin
spi_mosi_o<=temp_data_i[spi_out_width-1'd1];
temp_data_i<={temp_data_i[spi_out_width-2'd2:0],temp_data_i[spi_out_width-1'd0]};
end
else
begin
spi_mosi_o<=spi_mosi_o;
end
end
else
begin
spi_mosi_o<=1'b1;
end
end
//接收SPI_MISO信号
always @(posedge spi_x2clk_i or negedge rst_n_i)
begin
if(rst_n_i==1'b0)
spi_data_o<=32'b0;
else if(spi_cpha==1'b0)
begin
if((clk_cnt>=CS_F_CNT-8'D1) && (clk_cnt<(WIDTH_MAX-CS_B_CNT)) && (clk_cnt%2==1) )
begin
spi_data_o<={spi_data_o[30:0],spi_miso_i};
end
else
begin
spi_data_o<=spi_data_o;
end
end
else if(spi_cpha==1'b1)
begin
if((clk_cnt>=CS_F_CNT) && (clk_cnt<(WIDTH_MAX-CS_B_CNT)) && (clk_cnt%2==1) )
begin
spi_data_o<={spi_data_o[30:0],spi_miso_i};
end
else
begin
spi_data_o<=spi_data_o;
end
end
else
begin
spi_data_o<=spi_data_o;
end
end
endmodule
//testbench
`timescale 1ns/1ns
module lcd_spi_m_tb();
reg rst_n_i;
reg spi_x2clk_i;
reg [31:0] spi_data_i;
reg spi_start;
reg spi_miso_i;
wire [0:0] spi_done;
wire [0:0] spi_busy;
wire [0:0] spi_cs_o;
wire [0:0] spi_scl_o;
wire [0:0] spi_mosi_o;
wire [31:0] spi_data_o;
always #50 spi_x2clk_i<=~spi_x2clk_i;
initial begin
rst_n_i=0;
spi_x2clk_i=0;
#200;
spi_start=0;
rst_n_i=1;
@(posedge spi_x2clk_i)
spi_data_i=9'h1B0;
spi_start=1;
@(posedge spi_x2clk_i)
spi_start=0;
@(posedge spi_done)
#200;
@(posedge spi_x2clk_i)
spi_data_i=9'h1AA;
spi_start=1;
@(posedge spi_x2clk_i)
spi_start=0;
@(posedge spi_done)
$stop;
end
always @(negedge spi_scl_o or negedge rst_n_i )//要根据CPOL CPHA配置 spi_scl_o采样极性
begin
if(rst_n_i==1'b0)
spi_miso_i<=1'b0;
else if (spi_cs_o==1'b0 )
spi_miso_i<=~spi_miso_i;
else
spi_miso_i<=1'b0;
end
lcd_spi_m
#(
.spi_in_width(6'd9),//spi 输入位数
.spi_out_width (6'd9),//SPI 输出位数
.spi_cpol(1'b0),
.spi_cpha(1'b0)
)
lcd_spi_m_inst
(
.rst_n_i (rst_n_i ) ,
.spi_x2clk_i (spi_x2clk_i ) ,
.spi_data_i (spi_data_i ) ,
.spi_start (spi_start ) ,
.spi_miso_i (spi_miso_i ) ,
.spi_done (spi_done ) ,
.spi_busy (spi_busy ) ,
.spi_cs_o (spi_cs_o ) ,
.spi_scl_o (spi_scl_o ) ,
.spi_mosi_o (spi_mosi_o ) ,
.spi_data_o (spi_data_o )
);
endmodule
![]()
![]()
![]()
![]()