/*******************************SPI Master发送驱动器********************* --以下是米联客设计的SPI Master发送驱动器 --1.代码简洁,占用极少逻辑资源,代码结构清晰,逻辑设计严谨 --2.I_spi_tx_req,用户程序通过设置I_spi_tx_req为高,请求SPI Master发送驱动器,发送数据,SPI Master发送驱动器把I_spi_tx_data数据通过 O_spi_mosi总线发送出去 --3.通过CPOL以及CPHA自由的控制SPI Master发送驱动器的时钟极性一级时钟相位,并且在时序上做出相应的匹配。 --4.O_spi_busy,表示当前*SPI Master发送总线整忙,这时用户程序需要等待非忙的时候,请求发送数据。*********************************************************************/ `timescale 1ns / 1ps //定义仿真时间刻度/精度
module ui_mspi_tx# ( parameter CLK_DIV = 100, parameter CPOL = 1'b0, //时钟极性参数设置 parameter CPHA = 1'b1 //时钟相位参数设置 ) ( input I_clk, //系统时钟输入 input I_rstn, //系统复位输入 output O_spi_mosi, //发送SPI数据 output O_spi_sclk, //发送SPI时钟 input I_spi_tx_req, //发送数据请求 input [7:0] I_spi_tx_data, //发送数据 output O_spi_busy //发送状态忙,代表正在发送数据 );
localparam [9:0] SPI_DIV = CLK_DIV; //第二时钟边沿计数器 localparam [9:0] SPI_DIV1 = SPI_DIV/2; //第一时钟边沿计数器
reg [9:0] clk_div = 10'd0; reg spi_en = 1'b0; reg spi_clk = 1'b0; reg [3:0] tx_cnt = 4'd0; reg [7:0] spi_tx_data_r=8'd0; wire clk_end; wire clk_en1; //第一内部时钟边沿使能 wire clk_en2; //第二内部时钟边沿使能 reg spi_strobe_en; wire spi_strobe; //CPHA=0数据在第一时钟边沿上传输,CPHA=1数据在第二时钟边沿上发送
assign clk_en1 = (clk_div == SPI_DIV1);//第一内部时钟边沿使能 assign clk_en2 = (clk_div == SPI_DIV);//第二内部时钟边沿使能 assign clk_end = (clk_div == SPI_DIV1)&&(tx_cnt==4'd8); //计数器发送第一个内部时钟0到7次,当计数达到最后8时,不发送时钟//当CPHA=0时,数据的第一个SCLK转换边缘被采样,因此数据更新在第二个转换边缘上 //当CPHA=1时,数据的第二个SCLK转换边缘被采样,因此数据更新在第一个转换边缘上 assign spi_strobe = CPHA ? clk_en1&spi_strobe_en : clk_en2&spi_strobe_en ; assign O_spi_sclk = (CPOL == 1'b1) ? ~spi_clk : spi_clk;//设置SPI时钟的初始电平 assign O_spi_mosi = spi_tx_data_r[7]; assign O_spi_busy = spi_en;
always@(posedge I_clk)begin //时钟分频器 if(spi_en == 1'b0) clk_div <= 10'd0; else if(clk_div < SPI_DIV) clk_div <= clk_div + 1'b1; else clk_div <= 0; end always@(posedge I_clk)begin //生成spi内部时钟 if(spi_en == 1'b0) spi_clk <= 1'b0; else if(clk_en2) spi_clk <= 1'b0; //第二时钟边沿 else if(clk_en1&&(tx_cnt<4'd8)) //第一时钟边沿 spi_clk <= 1'b1; else spi_clk <= spi_clk; end always@(posedge I_clk)begin if(I_rstn == 1'b0) spi_strobe_en <= 1'b0; else if(tx_cnt < 4'd8)begin if(clk_en1) spi_strobe_en <= 1'b1; end else spi_strobe_en <= 1'b0; end always@(posedge I_clk)begin if((I_rstn == 1'b0)||(spi_en == 1'b0)) tx_cnt <= 4'd0; else if(clk_en1) tx_cnt <= tx_cnt + 1'b1; end always@(posedge I_clk)begin //spi发送模块 if(I_rstn == 1'b0 || clk_end)begin spi_en <= 1'b0; spi_tx_data_r <= 8'h00; end else if(I_spi_tx_req&&(spi_en == 1'b0)) begin //启用传输 spi_en <= 1'b1; spi_tx_data_r <= I_spi_tx_data; end else if(spi_en)begin spi_tx_data_r[7:0] <= (spi_strobe) ? {spi_tx_data_r[6:0],1'b1} : spi_tx_data_r; end
end endmodule |