一,SPI說明:
1、什么是SPI?
SPI是串行外設接口(Serial Peripheral Interface)的縮寫。是 Motorola 公司推出的一 種同步串行接口技術,是一種高速的,全雙工,同步的通信總線。
2、SPI優點
支持全雙工通信、通信簡單、數據傳輸速率塊
3、缺點
沒有指定的流控制,沒有應答機制確認是否接收到數據,所以跟IIC總線協議比較在數據 可靠性上有一定的缺陷。
4、特點
1):高速、同步、全雙工、非差分、總線式
2):主從機通信模式
5、協議通信時序詳解
1):SPI的通信原理很簡單,它以主從方式工作,這種模式通常有一個主設備和一個或多 個從設備,需要至少4根線,事實上3根也可以(單向傳輸時)。也是所有基於SPI的設備共有的,它們是
SDI(數據輸入)、SDO(數據輸出)、SCLK(時鍾)、CS(片選)。
(1)SDO/MOSI ((master out slaver in))– 主設備數據輸出,從設備數據輸入;
(2)SDI/MISO – 主設備數據輸入,從設備數據輸出;
(3)SCLK – 時鍾信號,由主設備產生;
(4)CS/SS – 從設備使能信號,由主設備控制。當有多個從設備的時候,因為每個從設 備上都有一個片選引腳接入到主設備機中,當我們的主設備和某個從設備通信時將需 要將從設備對應的片選引腳電平拉低或者是拉高。
二,協議舉例說明(某通過SPI通信的芯片):
1,芯片管腳說明:
MISO:output data for spi port,output on falling edge of sclk;
MOSI:input data for spi port,latches on rising edge of sclk,MSB first;
CSB:chip select input,active low,CSB frames SPI commands and enables SPI port;
SCLK:clock for SPI;
SPI Protocol:
• SPI MOSI data is read on SCLK rising edge.
• MISO data is changed on SCLK falling edge.
• SPI communication is “MSB first”.
• SPI command is composed of a multiple of 24 SCLK cycles.
• The number of clock cycles occurring on the pin SCLK while the CSB pin is asserted low must be a multiple of 24.
• The serial output data is available on the rising edge of SCLK and transitions on the falling edge of SCLK .
2,時序圖:
3,FPGA 代碼舉例
A:作為主機發送數據:
發送模塊:
`timescale 1ns / 1ps module clk( clk50,//系統時鍾 clkout,//輸出時鍾,sclk管腳 send_flag,//發送數據指令 send_over,//數據發送完畢提示 sdo, DATA ); input clk50; output clkout; input send_flag; output send_over; input [23:0] DATA;//要發送的數據 output sdo; //寄存器型數據對象的定義 reg clkout; reg [15:0] cnt;//分頻因子 reg send_over; reg [7:0] clock_num;//clkout的數目,沒24個clk后清零一次 reg [7:0] cnt1;//發送數據位寄存器 reg sdo; //50mhz時鍾100分頻,sclk時鍾500k always @(posedge clk50) begin if((send_flag==1'b1)&&(clock_num<8'd24))begin send_over<=1'b1;//正在發送數據 if(cnt == 16'd1) begin clkout <= 1'b0;//下降沿sdo管腳數據change cnt <= cnt + 16'd1; sdo<=DATA[23-cnt1]; //MSB前 cnt1<=cnt1+8'd1; end else if(cnt == 16'd49) begin clkout <= 1'b1; cnt <= cnt + 16'd1; end else if(cnt == 16'd99) begin cnt <= 16'd0; clock_num<=clock_num+8'b1; end else cnt <= cnt + 16'd1; end else begin clock_num<=8'b0; send_over<=1'b0; clkout<=1'b0; sdo<=1'b0; cnt1<=8'd0; end end endmodule
頂層模塊:
`timescale 1ns / 1ps module SPI( clk, // 輸入時鍾: 50Mhz clkout, sdo, sdi, cs ); input clk; output clkout; output sdo; output cs; input sdi; reg [15:0] count;//cs,clk開始發出的時序控制寄存器 reg send_flag; wire send_over; wire [23:0] DATA; reg cs; reg [23:0] DATA1; reg [7:0] cycle;//發送的數據選擇位 reg [31:0] delay_count;//延時計數器 reg delay_flag;//延時結束標志位 parameter cs_num=8'd6;//發送的數據的個數 assign DATA=DATA1; always @(posedge clk) begin if(delay_flag==1'b0)begin//上電延時一段時間 delay_count<=delay_count+1'b1; if(delay_count==32'd50_000) delay_flag<=1'b1; end if((send_over==1'b0)&&(cycle<(cs_num+1))&&(delay_flag==1'b1))begin//cs,clk開始發出的時序生成代碼 count<=count+16'b1; end else if(send_over==1'b1)begin count<=16'b0; end if(count==16'd100)begin cs<=1'b1; end else if(count==16'd2800)begin case(cycle) 8'd0:begin DATA1<=24'h555555; end 8'd1:begin DATA1<=24'hAAAAAA; end 8'd2:begin DATA1<=24'h5A5A5A; end 8'd3:begin DATA1<=24'hA5A5A5; end 8'd4:begin DATA1<=24'hDDDDDD; end 8'd5:begin DATA1<=24'hb81d69; end endcase cycle<=cycle+8'b1; end else if(count==16'd2900)begin cs<=1'b0; end else if(count==16'd3000)begin send_flag<=1'b1; end if(delay_flag==1'b0)begin cs<=1'b1; send_flag<=1'b0; end else if((send_over==1'b0)&&(count<16'd10)&&(delay_flag==1'b1))begin//發送完成后send_flag為清零,為下一次發送做准備 send_flag<=1'b0; end end //例化時鍾模塊 clk clk_SPI( .clk50(clk), .clkout(clkout), .send_flag(send_flag), .send_over(send_over), .sdo(sdo), .DATA(DATA) ); endmodule
B,接收數據,並發出:
發送模塊:
`timescale 1ns / 1ps module clk( clk50,//系統時鍾 clkout,//輸出時鍾 send_flag, send_over, sdo, DATA ); input clk50; output clkout; input send_flag; output send_over; input [23:0] DATA; output sdo; //寄存器型數據對象的定義 reg clkout; reg [15:0] cnt; reg send_over; reg [7:0] clock_num; reg [7:0] cnt1; reg sdo; //initial begin //clkout<=0; //cnt<=0; //send_over<=0; //clock_num<=0; //cnt1<=0; //sdo<=0; // //end //50mhz時鍾100分頻 always @(posedge clk50) begin if((send_flag==1'b1)&&(clock_num<8'd24))begin send_over<=1'b1; if(cnt == 16'd1) begin clkout <= 1'b0; cnt <= cnt + 16'd1; sdo<=DATA[23-cnt1]; cnt1<=cnt1+8'd1; end else if(cnt == 16'd49) begin clkout <= 1'b1; cnt <= cnt + 16'd1; end else if(cnt == 16'd99) begin cnt <= 16'd0; clock_num<=clock_num+8'b1; end else cnt <= cnt + 16'd1; end else begin clock_num<=8'b0; send_over<=1'b0; clkout<=1'b0; sdo<=1'b0; cnt1<=8'd0; end end endmodule
接收模塊:
`timescale 1ns / 1ps module read( clkin,//系統時鍾 cs, sdi, DATA, flag//一個24bit讀取完畢標志位,讀取下一個字節到一半的時候清零 ); input clkin; input cs; output [23:0] DATA; input sdi; output flag; reg [7:0] cnt2; reg [23:0] DATA; reg [23:0] data_receive; reg flag; //仿真使用 //initial begin //cnt2<=0; //DATA<=0; //data_num<=0; //data_receive<=0; //flag<=0; //end // // always @(posedge clkin) begin if(cs==1'b0) begin data_receive[23-cnt2]<=sdi; if(cnt2==8'd23)begin cnt2<=1'b0; DATA<=(data_receive&24'hFFFFFE)|sdi;//最后一位這里讀不到,所以直接把sdi給他 flag<=1'b1; end else if(cnt2==8'd11) begin flag<=1'b0; cnt2<=cnt2+1'b1; end else begin cnt2<=cnt2+1'b1; end end end endmodule
頂層模塊:
`timescale 1ns / 1ps module SPI( clk, // 輸入時鍾: 50Mhz clkout, sdo, sdi, cs, clk_in, sdi_in, cs_in ); input clk; output clkout; output sdo; output cs; input sdi; input clk_in; input sdi_in; input cs_in; ///////////////////////////////////////////// wire [23:0] data_in; wire read_flag; reg [15:0] count; reg send_flag; wire send_over; reg [23:0] DATA; reg cs; reg [7:0] cycle; reg delay_flag;//這里變成了開啟發送數據的標志位 reg [7:0] cs_num; reg [7:0] read;//保證每次讀取數據的時候,頂層里面的處理代碼只執行一次,類似狀態機 reg [7:0] read_num;//讀取數據的個數 ///////////////////////////////////////////// //數據存儲寄存器 reg [23:0] DATAIN_1; reg [23:0] DATAIN_2; reg [23:0] DATAIN_3; reg [23:0] DATAIN_4; reg [23:0] DATAIN_5; reg [23:0] DATAIN_6; //仿真使用 initial begin //count<=0; //send_flag<=0; //cs<=0; //cycle<=0; delay_flag<=0; //cs_num<=0; //DATAIN_1<=0; //DATAIN_2<=0; //DATAIN_3<=0; //DATAIN_4<=0; //DATAIN_5<=0; //DATAIN_6<=0; end // always @(posedge clk) begin if((read_flag==1'b1)&&(read<=4))begin read<=read+1'b1; if(read==1)begin read_num=read_num+1'b1;end else if(read==2)begin if(data_in==24'hB81D69)begin//B81D69 if(read_num>4'd6) begin cs_num<=4'd6;end else begin cs_num<=read_num-4'd1;end end end else if(read==3)begin if(data_in==24'hB81D69)begin delay_flag<=1'b1;read_num<=0;end end else if(read==4)begin case(read_num) 4'd1:begin DATAIN_1<=data_in;end 4'd2:begin DATAIN_2<=data_in;end 4'd3:begin DATAIN_3<=data_in;end 4'd4:begin DATAIN_4<=data_in;end 4'd5:begin DATAIN_5<=data_in;end 4'd6:begin DATAIN_6<=data_in;end endcase end end else if(read_flag==0) begin read<=0;end ////////////////////////////////// if((send_over==1'b0)&&(cycle<(cs_num+1))&&(delay_flag==1'b1))begin count<=count+16'b1;end else if(send_over==1'b1)begin count<=16'b0;end if(count==16'd100)begin cs<=1'b1;end else if(count==16'd2800)begin case(cycle) 8'd0:begin DATA<=DATAIN_1;end 8'd1:begin DATA<=DATAIN_2;end 8'd2:begin DATA<=DATAIN_3;end 8'd3:begin DATA<=DATAIN_4;end 8'd4:begin DATA<=DATAIN_5;end 8'd5:begin DATA<=DATAIN_6;end endcase cycle<=cycle+8'b1; end else if(count==16'd2900)begin cs<=1'b0;end else if(count==16'd3000)begin send_flag<=1'b1; if(cycle==cs_num) begin cycle<=8'b0;delay_flag<=1'b0;end end if((send_over==1'b0)&&(count<16'd10))begin send_flag<=1'b0;end end // //例化時鍾模塊 clk clk_SPI( .clk50(clk), .clkout(clkout), .send_flag(send_flag), .send_over(send_over), .sdo(sdo), .DATA(DATA) ); // // read read_SPI( .clkin(clk_in), .cs(cs_in), .sdi(sdi_in), .DATA(data_in), .flag(read_flag) ); endmodule