SPI总线verilog hdl实现

SPI总线传输只需要4根线就能完成,这四根线的作用分别如下:
  SCK(Serial Clock):SCK是串行时钟线,作用是Master向Slave传输时钟信号,控制数据交换的时机和速率;
  MOSI(Master Out Slave in):在SPI Master上也被称为Tx-channel,作用是SPI主机给SPI从机发送数据;
  CS/SS(Chip Select/Slave Select):作用是SPI Master选择与哪一个SPI Slave通信,低电平表示从机被选中(低电平有效);
  MISO(Master In Slave Out):在SPI Master上也被称为Rx-channel,作用是SPI主机接收SPI从机传输过来的数据;
  SPI总线传输一共有4中模式,这4种模式分别由时钟极性(CPOL,Clock Polarity)和时钟相位(CPHA,Clock Phase)来定义,其中CPOL参数规定了SCK时钟信号空闲状态的电平,CPHA规定了数据是在SCK时钟的上升沿被采样还是下降沿被采样。
  模式0:CPOL= 0,CPHA=0。SCK串行时钟线空闲是为低电平,数据在SCK时钟的上升沿被采样,数据在SCK时钟的下降沿切换
  模式1:CPOL= 0,CPHA=1。SCK串行时钟线空闲是为低电平,数据在SCK时钟的下降沿被采样,数据在SCK时钟的上升沿切换
  模式2:CPOL= 1,CPHA=0。SCK串行时钟线空闲是为高电平,数据在SCK时钟的下降沿被采样,数据在SCK时钟的上升沿切换
  模式3:CPOL= 1,CPHA=1。SCK串行时钟线空闲是为高电平,数据在SCK时钟的上升沿被采样,数据在SCK时钟的下降沿切换
  以模式0为例

发送:当FPGA通过SPI总线往从机中发送一个字节(8-bit)的数据时,首先FPGA把CS/SS片选信号设置为0,表示准备开始发送数据,整个发送数据过程其实可以分为16个状态:
    状态0:SCK为0,MOSI为要发送的数据的最高位,即I_data_in[7]
    状态1:SCK为1,MOSI保持不变
    状态2:SCK为0,MOSI为要发送的数据的次高位,即I_data_in[6]
    状态3:SCK为1,MOSI保持不变
    状态4:SCK为0,MOSI为要发送的数据的下一位,即I_data_in[5]
    状态5:SCK为1,MOSI保持不变
    状态6:SCK为0,MOSI为要发送的数据的下一位,即I_data_in[4]
    状态7:SCK为1,MOSI保持不变
    状态8:SCK为0,MOSI为要发送的数据的下一位,即I_data_in[3]
    状态9:SCK为1,MOSI保持不变
    状态10:SCK为0,MOSI为要发送的数据的下一位,即I_data_in[2]
    状态11:SCK为1,MOSI保持不变
    状态12:SCK为0,MOSI为要发送的数据的下一位,即I_data_in[1]
    状态13:SCK为1,MOSI保持不变
    状态14:SCK为0,MOSI为要发送的数据的最低位,即I_data_in[0]
    状态15:SCK为1,MOSI保持不变  
一个字节数据发送完毕以后,产生一个发送完成标志位O_tx_done并把CS/SS信号拉高完成一次发送;
 接收:当FPGA通过SPI总线从从机中接收一个字节(8-bit)的数据时,首先FPGA把CS/SS片选信号设置为0,表示准备开始接收数据,整个接收数据过程其实也可以分为16个状态,但是与发送过程不同的是,为了保证接收到的数据准确,必须在数据的正中间采样,也就是说模式0时序图中灰色实线的地方才是代码中锁存数据的地方,所以接收过程的每个状态执行的操作为:

               状态0:SCK为0,不锁存MISO上的数据
    状态1:SCK为1,锁存MISO上的数据,即把MISO上的数据赋值给O_data_out[7]
    状态2:SCK为0,不锁存MISO上的数据
    状态3:SCK为1,锁存MISO上的数据,即把MISO上的数据赋值给O_data_out[6]
    状态4:SCK为0,不锁存MISO上的数据
    状态5:SCK为1,锁存MISO上的数据,即把MISO上的数据赋值给O_data_out[5]
    状态6:SCK为0,不锁存MISO上的数据
    状态7:SCK为1,锁存MISO上的数据,即把MISO上的数据赋值给O_data_out[4]
    状态8:SCK为0,不锁存MISO上的数据
    状态9:SCK为1,锁存MISO上的数据,即把MISO上的数据赋值给O_data_out[3]
    状态10:SCK为0,不锁存MISO上的数据
    状态11:SCK为1,锁存MISO上的数据,即把MISO上的数据赋值给O_data_out[2]
    状态12:SCK为0,不锁存MISO上的数据
    状态13:SCK为1,锁存MISO上的数据,即把MISO上的数据赋值给O_data_out[1]
    状态14:SCK为0,不锁存MISO上的数据
    状态15:SCK为1,锁存MISO上的数据,即把MISO上的数据赋值给O_data_out[0]
  一个字节数据接收完毕以后,产生一个接收完成标志位O_rx_done并把CS/SS信号拉高完成一次数据的接收

       I_clk是系统时钟;
  I_rst_n是系统复位;
  I_tx_en是主机给从机发送数据的使能信号,当I_tx_en为1时主机才能给从机发送数据;
  I_rx _en是主机从从机接收数据的使能信号,当I_rx_en为1时主机才能从从机接收数据;
  I_data_in是主机要发送的并行数据;
  O_data_out是把从机接收回来的串行数据并行化以后的并行数据;
  O_tx_done是主机给从机发送数据完成的标志位,发送完成后会产生一个高脉冲;
  O_rx_done是主机从从机接收数据完成的标志位,接收完成后会产生一个高脉冲;
  I_spi_miso、O_spi_cs、O_spi_sck和O_spi_mosi是标准SPI总线协议规定的四根线;
  完整代码如下:

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;
			 send_state<=bit7;
			 receive_state<=bit70;
          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_end0;
	                FF<=1;
	             end
             bit_end0:
                 begin
	                dout_buf<=8'hzz;
	                receive_state<=bit70;
	             end
         endcase
     end
endtask
endmodule

 

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,关于Verilog写一个SPI驱动的问题,我可以回答。首先,SPI(Serial Peripheral Interface)是一种同步串行通信协议,可以用于连接微控制器、传感器、存储器和其他外设。下面是一个简单的SPI主机驱动Verilog代码示例: ```verilog module spi_master( input clk, input reset, output ss, output mosi, input miso, input [7:0] data_out, input [2:0] data_out_ctrl, output [7:0] data_in, input [2:0] data_in_ctrl, input sclk_freq, input ss_duration ); reg [7:0] data_in_reg; reg [2:0] data_in_ctrl_reg; reg [7:0] data_out_reg; reg [2:0] data_out_ctrl_reg; reg [7:0] shift_reg; reg [7:0] shift_data; reg shift_done; reg sclk_count; reg [15:0] ss_count; reg ss_active; wire sclk; assign mosi = shift_data[7]; assign sclk = sclk_count[sclk_freq - 1]; always @(posedge clk) begin if (reset) begin data_in_reg <= 0; data_in_ctrl_reg <= 0; data_out_reg <= 0; data_out_ctrl_reg <= 0; shift_reg <= 0; shift_data <= 0; shift_done <= 0; sclk_count <= 0; ss_count <= 0; ss_active <= 0; ss <= 1; end else begin if (data_out_ctrl_reg != 3'b000) begin if (!shift_done) begin shift_reg <= {shift_reg[6:0], data_out_reg[7]}; data_out_reg <= {data_out_reg[6:0], 1'b0}; data_out_ctrl_reg <= data_out_ctrl_reg - 1; shift_done <= (data_out_ctrl_reg == 3'b000); end else begin shift_data <= shift_reg; shift_done <= 0; data_out_ctrl_reg <= 0; end end else if (data_in_ctrl_reg != 3'b000) begin if (!shift_done) begin shift_reg <= {shift_reg[6:0], miso}; data_in_ctrl_reg <= data_in_ctrl_reg - 1; shift_done <= (data_in_ctrl_reg == 3'b000); end else begin data_in_reg <= shift_reg; shift_done <= 0; data_in_ctrl_reg <= 0; end end else begin shift_data <= 0; shift_done <= 0; if (ss_count != (ss_duration * sclk_freq)) begin ss_count <= ss_count + 1; end else begin ss_count <= 0; ss_active <= 0; ss <= 1; end if (!ss_active) begin if (data_out_ctrl == 3'b001) begin ss_active <= 1; ss <= 0; data_out_ctrl_reg <= 3'b111; end else if (data_out_ctrl == 3'b010) begin ss_active <= 1; ss <= 0; shift
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值