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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章