FPGA--SPI通信


一,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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM