Verilog實現SPI通信(包括對任務和函數用法的講解)

一、基本知識

   1、SPI

      SPI是串行外設接口(Serial Peripheral Interface)的縮寫。它是一種高速的,全雙工,同步的通信總線,並且在芯片的管腳上只佔用四根線。

SPI的通信原理很簡單,它以主從方式工作,這種模式通常有一個主設備和一個或多個從設備,需要至少4根線,事實上3根也可以(單向傳輸時)。也是所有基於SPI的設備共有的,它們是SDI(數據輸入)、SDO(數據輸出)、SCLK(時鐘)、CS(片選)。

(1)SDI – SerialData In,串行數據輸入;

(2)SDO – SerialDataOut,串行數據輸出;

(3)SCLK – Serial Clock,時鐘信號,由主設備產生;

(4)CS – Chip Select,從設備使能信號,由主設備控制。

2、SPI的工作模式

    

3、Verilog中的任務

        任務是通過調用來執行的,而且只有在調用時才執行。在定義任務時,設計者可以爲其添加輸入和輸出端口,用於在任務調用時傳遞參數。任務可以包含帶時序控制的語句,當調用帶時序控制的任務時,任務返回時的時間和調用時的時間可能不相同。任務可以彼此調用,而且任務還可以調用函數。

(1)任務的定義

task  <任務名>
    端口及數據類型聲明語句
     其他語句


endtask

(2) 任務的特點

  • 任務的定義與調用需在一個module模塊內
  • 定義任務時,沒有端口名列表,但需要緊接着進行輸入、輸出端口和數據類型的說明。
  • 當任務被調用時,任務被激活。調用時,須列出端口名列表,端口名的排序和類型必須與任務定義中的相一致。
  • 一個任務可以調用別的任務和函數,可以調用的任務和函數個數不限
  • 爲了提高任務的模塊化程度,傳遞給任務的參數名通常不使用與任務內部I/O聲明的參數名相同的參數名。
  • 任務定義結構中不能出現initial和always過程塊

(3)任務使用

module mult(clk,a,b,out,en_mult)

    input clk,en_mult;
    input [3:0] a,b;
    output [7:0] out;
    reg [7:0] out;
       task muotme;//任務定義
         input [3:0] xme,tome;
         output [7:0] result;
         wait(en_mult)  //電平敏感事件觸發
         result=xme*tome;
       endtask
       always @(posedge clk)
          muotme(a,b,out);//任務調用時傳遞給任務的參數與任務I/O聲明時的參數順序相同
endmodule

4、verilog中的函數

       函數和任務一樣,也是定義一個可重複調用的模塊,但是函數可以返回一個值,因此可以出現在等號右邊的表達式中,而任務的返回值只能通過任務的輸出端口來獲得。

    (1)  函數的定義:

function <返回值位寬或類型說明> 函數名;
    輸入端口與類型說明
    局部變量說明
    塊語句
endfunction

   (2)  函數的特點:

  • 函數定義不能包含任何時序控制語句。
  • 函數必須包含輸入,但不能有輸出或雙向信號。
  • 函數中不能使用非阻塞賦值語句。
  • 一個函數只能返回一個值,該值的變量名與函數名同名。
  • 傳遞給函數參數的順序與函數定義時輸入參數聲明的順序相同。
  • 函數定義必須包含在模塊定義之內。
  • 函數不能調用任務,但任務可以調用函數。

(3)舉例用函數定義一個8-3編碼器

module code_83(din,dout)

   input  [7:0] din;
   output [2:0] dout;
        function [2:0] code;  //函數定義
           input [7:0] din;//函數只有輸入,輸出爲函數本身
            casex(din)
                 8'b1xxx_xxxx:cade=3'h7;
                 8'b01xx_xxxx:cade=3'h6;
                 8'b001x_xxxx:cade=3'h5;
                 8'b0001_xxxx:cade=3'h4;
                 8'b0000_1xxx:cade=3'h3;
                 8'b0000_01xx:cade=3'h2;
                 8'b0000_001x:cade=3'h1;
                 8'b0000_0001:cade=3'h0;
                 default:code=3'hx;
            endcase
         endfuntion
          assign dout=code(din);//函數調用

endmodule

二、實現過程

設計兩段式狀態機,完成SPI的模式1

一個模塊控制讀還是寫,一個完成對SPI引腳控制

1、定義模塊輸入輸出引腳

module tem_rec(
input clk_10m,//輸入10Mhz的時鐘
input rst, //高電平有效
input spidi,//spi數據輸入端 
output spiclk,//spi中的時鐘
output spido,//spi的發送端 
output spics,//片選信號
output [7:0] data,
    );
reg spics;//spi片選信號
reg spiclk;//spi時鐘
reg spido;////spi寫信號
reg [7:0] data;//讀的數據轉換成8位並行數據輸出

2、讀寫控制模塊編寫:

//總狀態控制
parameter ini=4'd0, state_1=4'd1, state_2=4'd2, state_3 =4'd3,state_4=4'd4,state_5=4'd5,
state_6=4'd6,state_7=4'd7,state_8=4'd8,state_9=4'd9,state_10=4'd10,state_11=4'd11,
state_12=4'd12,state_13=4'd13,state_14=4'd14,state_end=4'd15;//使用循環碼
reg wr;
reg rd;//讀寫命令
reg [3:0] next_state;//狀態轉移
always@(posedge clk_10m or posedge rst)
begin
   if(rst) begin
       next_state<=ini;
       wr<=0;
       rd<=0;
       data<=8'd0;//待發送
   end
   else	begin
       case(next_state)
       ini://初始化 
       begin
		  next_state<=state_1;
       end
       state_1: //寫
       begin
          if(send_rdy) begin       			 
			wr<=0;
            next_state<=state_2;
          end	
		 else//寫寄存器
			wr<=1;
       end
       state_2://讀
	   begin
			if(rec_rdy) begin
				rd<=0;
			 end
		   else begin
				rd<=1;
		   end
	  end
      endcase
   end
end

3、SPI引腳控制模塊編寫:

//*spi的數據接收和發送*//
reg send_rdy;//發送完一字節標誌位
reg rec_rdy;//接收完成一字節數據標誌位
always @(posedge clk_10m)
begin
   if(rst) begin
	  spistate<=idle;
      spics<=1'b1;
	  spiclk<=1'b1;
	  spido<=1'b1;
	  send_rdy<=1'b0;
	  rec_rdy<=1'b0;
   else 
   begin
      case(spistate)//讀寫狀態控制
	  idle:
      begin
         if((wr==1'b1)&&(rd==1'b0)) begin//發送數據轉換
			spistate<=send_data;//轉發送狀態
			dsend<=datain;//準備待發送數據
			send_rdy<=1'b0;
			rec_rdy<=1'b0;   
		end
        else if((wr==1'b0)&&(rd==1'b1) begin
            spistate<=receive_data;//轉接收轉態
            dstate<=8'd0;
			rec_rdy<=1'b0;
			send_rdy<=1'b0;
        end
      end
       send_data://發送數據狀態
	   begin
          case(dstate)
			8'd0://產生片選信號有效
			begin
				spics<=1'b1;
				spiclk<=1'b1;
				spido<=1'b1;//發送出去的數據
				dstate<=8'd1;
			end
			8'd1:
			begin
				spics<=1'b1;
				spiclk<=1'b1;
				spido<=1'b1;
				dstate<=8'd2;
			end
			8'd2:
			begin
				spics<=1'b0;
				spiclk<=1'b1;
				spido<=1'b1;
				dstate<=8'd3;
			end
			8'd3:
			begin
				spics<=1'b0;
				spiclk<=1'b0;
				spido<=datain[7];//發送數據最高位
				dstate<=8'd4;
			end
			8'd4:
			begin
				spics<=1'b0;
				spiclk<=1'b1;
				spido<=datain[7];
				dstate<=8'd5;
			end
			8'd5:
			begin
				spics<=1'b0;
				spiclk<=1'b0;
				spido<=datain[6];
				dstate<=8'd6;
			end
			8'd6:
			begin
				spics<=1'b0;
				spiclk<=1'b1;
				spido<=datain[6];
				dstate<=8'd7;
			end
			8'd7:
			begin
				spics<=1'b0;
				spiclk<=1'b0;
				spido<=datain[5];
				dstate<=8'd8;
			end
			8'd8:
			begin
				spics<=1'b0;
				spiclk<=1'b1;
				spido<=datain[5];
				dstate<=8'd9;
			end
			8'd9:
			begin
				spics<=1'b0;
				spiclk<=1'b0;
				spido<=datain[4];
				dstate<=8'd10;
			end
			8'd10:
			begin
				spics<=1'b0;
				spiclk<=1'b1;
				spido<=datain[4];
				dstate<=8'd11;
			end
			8'd11:
			begin
				spics<=1'b0;
				spiclk<=1'b0;
				spido<=datain[3];
				dstate<=8'd12;
			end
			8'd12:
			begin
				spics<=1'b0;
				spiclk<=1'b1;
				spido<=datain[3];
				dstate<=8'd13;
			end
			8'd13:
			begin
				spics<=1'b0;
				spiclk<=1'b0;
				spido<=datain[2];
				dstate<=8'd14;
			end
			8'd14:
			begin
				spics<=1'b0;
				spiclk<=1'b1;
				spido<=datain[2];
				dstate<=8'd15;
			end
			8'd15:
			begin
				spics<=1'b0;
				spiclk<=1'b0;
				spido<=datain[1];
				dstate<=8'd16;
			end
			8'd16:
			begin
				spics<=1'b0;
				spiclk<=1'b1;
				spido<=datain[1];
				dstate<=8'd17;
			end
			8'd17:
			begin
				spics<=1'b0;
				spiclk<=1'b0;
				spido<=datain[0];
				dstate<=8'd18;
			end
			8'd18:
			begin
				spics<=1'b0;
				spiclk<=1'b1;
				spido<=datain[0];
				dstate<=8'd19;
			end
			8'd19:
			begin
				spics<=1'b1;
				spiclk<=1'b1;
				spido<=1'b1;
				dstate<=8'd20;
			//  	send_rdy<=1'b1;
			end
			8'd20://一個字節數據發送完成
			begin
				spics<=1'b1;
				spiclk<=1'b1;
				spido<=1'b1;
				dstate<=8'd0;
				spistate<=idle;
				send_rdy<=1'b1;//發送完成一幀數據標誌位
			end
			default    
			begin
				spics<=1'b1;
				spiclk<=1'b1;
				spido<=1'b1;
				spistate<=idle;
			end
			endcase//對應發送狀態
       end
       receive_data://接收數據狀態
       begin
           case (dstate) //片選信號有效
			8'd0:
			begin
				spics <= 1'b1;
				spiclk <= 1'b1;
				spido <= 1'b1;
				dstate <= 8'd1;
			end
			8'd1:
			begin
				spics <= 1'b1;
				spiclk <= 1'b1;
				spido <= 1'b1;
				dstate <= 8'd2;
			end
			8'd2:
			begin
				spics <= 1'b0;
				spiclk <= 1'b1;
				spido <= 1'b1;
				dstate <= 8'd3;
			end
			8'd3:
			begin
				spics <= 1'b0;
				spiclk <= 1'b0;
            dreceive[7] <= spidi;				
				dstate <= 8'd4;
			end
			8'd4:
			begin
				spics <= 1'b0;
				spiclk <= 1'b1; //緊接着上升沿的下降沿數據被讀取       
				dreceive[7] <= spidi; //接收數據最高位
				dstate <= 8'd5;
			end
			8'd5:
			begin
				spics <= 1'b0;
				spiclk <= 1'b0;
				dstate <= 8'd6;
				dreceive[6] <= spidi;
			end
			8'd6:
			begin
				spics <= 1'b0;
				spiclk <= 1'b1;
				dreceive[6] <= spidi;
				dstate <= 8'd7;
			end
			8'd7:
			begin
				spics <= 1'b0;
				spiclk <= 1'b0;
				dstate <= 8'd8;
				dreceive[5] <= spidi;
			end
			8'd8:
			begin
				spics <= 1'b0;
				spiclk <= 1'b1;
				dreceive[5] <= spidi;
				dstate <= 8'd9;
			end
			8'd9:
			begin
				spics <= 1'b0;
				spiclk <= 1'b0;
				dstate <= 8'd10;
				dreceive[4] <= spidi;
			end
			8'd10:
			begin
				spics <= 1'b0;
				spiclk <= 1'b1;
				dreceive[4] <= spidi;
				dstate <= 8'd11;
			end
			8'd11:
			begin
				spics <= 1'b0;
				spiclk <= 1'b0;
				dstate <= 8'd12;
				dreceive[3] <= spidi;
			end
			8'd12:
			begin
				spics <= 1'b0;
				spiclk <= 1'b1;
				dreceive[3] <= spidi;
				dstate <= 8'd13;
			end
			8'd13:
			begin
				spics <= 1'b0;
				spiclk <= 1'b0;
				dstate <= 8'd14;
				dreceive[2] <= spidi;
			end
			8'd14:
			begin
				spics <= 1'b0;
				spiclk <= 1'b1;
				dreceive[2] <= spidi;
				dstate <= 8'd15;
			end
			8'd15:
			begin
				spics <= 1'b0;
				spiclk <= 1'b0;
				dstate <= 8'd16;
				dreceive[1] <= spidi;
			end
			8'd16:
			begin
				spics <= 1'b0;
				spiclk <= 1'b1;
				dreceive[1] <= spidi;
				dstate <= 8'd17;
			end
			8'd17:
			begin
				spics <= 1'b0;
				spiclk <= 1'b0;
				dstate <= 8'd18;
				dreceive[0] <= spidi; 
			end
			8'd18:
			begin
				spics <= 1'b0;
				spiclk <= 1'b1;
				dreceive[0] <= spidi; //接收數據最低位
				dstate <= 8'd19;
			 end
			8'd19:
			begin
				spics <= 1'b1;
				spiclk <= 1'b1;
				spido<= 1'b1;
				dstate <= 8'd20;
				dataout[7:0]<= dreceive[7:0];
				//rec_rdy<=1'b1;
			end
			8'd20:
			begin
				spics <= 1'b1;//片選信號無效
				spiclk <= 1'b1;     
				spido <= 1'b1;
				dstate <= 8'd0;
				spistate <= idle;
				rec_rdy<=1'b1;//接收完一個字節標誌位有效
			end
			default
			begin
				spics<=1'b1;
				spiclk<=1'b1;
				spido<=1'b1;
		
			end
			endcase//對應接收狀態
       end
      endcase
   end
end

 

            

 

        

 

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