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

 

posted @ 2020-05-29 13:18  菜芽caiya  阅读(2915)  评论(0编辑  收藏  举报