FPGA--SPI通信

Posted caiya

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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==1b1)&&(clock_num<8d24))begin
            send_over<=1b1;//正在发送数据
             if(cnt == 16d1) begin
                        clkout <= 1b0;//下降沿sdo管脚数据change
                        cnt <= cnt + 16d1;
                        sdo<=DATA[23-cnt1];        //MSB前            
                        cnt1<=cnt1+8d1;
                end        
                        
             else if(cnt == 16d49) begin
                        clkout <= 1b1;
                        cnt <= cnt + 16d1;
                  end
            else if(cnt == 16d99) begin
                        cnt <= 16d0;
                        clock_num<=clock_num+8b1;
                  end
             else 
                  cnt <= cnt + 16d1;
        end
        else begin
            clock_num<=8b0;
            send_over<=1b0;
            clkout<=1b0;
            sdo<=1b0;
            cnt1<=8d0;    
        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=8d6;//发送的数据的个数

assign DATA=DATA1;

always @(posedge clk)
begin
        if(delay_flag==1b0)begin//上电延时一段时间
            delay_count<=delay_count+1b1;
            if(delay_count==32d50_000)
                delay_flag<=1b1;
            end

        if((send_over==1b0)&&(cycle<(cs_num+1))&&(delay_flag==1b1))begin//cs,clk开始发出的时序生成代码
            count<=count+16b1;
            end
        else if(send_over==1b1)begin
            count<=16b0;
            end
            
        if(count==16d100)begin
            cs<=1b1;
            end
        else if(count==16d2800)begin
            case(cycle)
             8d0:begin
                        DATA1<=24h555555;
              end
             8d1:begin
                        DATA1<=24hAAAAAA;
              end
             8d2:begin
                        DATA1<=24h5A5A5A;
              end
             8d3:begin
                        DATA1<=24hA5A5A5;
              end
             8d4:begin
                        DATA1<=24hDDDDDD;
              end
             8d5:begin
                        DATA1<=24hb81d69;
              end
            endcase
            cycle<=cycle+8b1;
        end
        else if(count==16d2900)begin
            cs<=1b0;
            end
        else if(count==16d3000)begin
            send_flag<=1b1;
            end
            
        if(delay_flag==1b0)begin
            cs<=1b1;
            send_flag<=1b0;
            end
        else if((send_over==1b0)&&(count<16d10)&&(delay_flag==1b1))begin//发送完成后send_flag为清零,为下一次发送做准备
            send_flag<=1b0;
            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==1b1)&&(clock_num<8d24))begin
            send_over<=1b1;
             if(cnt == 16d1) begin
                        clkout <= 1b0;
                        cnt <= cnt + 16d1;
                        sdo<=DATA[23-cnt1];                    
                        cnt1<=cnt1+8d1;
                end        
                        
             else if(cnt == 16d49) begin
                        clkout <= 1b1;
                        cnt <= cnt + 16d1;
                  end
            else if(cnt == 16d99) begin
                        cnt <= 16d0;
                        clock_num<=clock_num+8b1;
                  end
             else 
                  cnt <= cnt + 16d1;
        end
        else begin
            clock_num<=8b0;
            send_over<=1b0;
            clkout<=1b0;
            sdo<=1b0;
            cnt1<=8d0;    
        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==1b0) begin
            data_receive[23-cnt2]<=sdi;
            if(cnt2==8d23)begin
                cnt2<=1b0;
                DATA<=(data_receive&24hFFFFFE)|sdi;//最后一位这里读不到,所以直接把sdi给他
                flag<=1b1;
            end
            else if(cnt2==8d11) begin
                flag<=1b0;
                cnt2<=cnt2+1b1;
                end
            else begin
                cnt2<=cnt2+1b1;
            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==1b1)&&(read<=4))begin
        read<=read+1b1;
        if(read==1)begin read_num=read_num+1b1;end
        else if(read==2)begin
            if(data_in==24hB81D69)begin//B81D69
                if(read_num>4d6) begin cs_num<=4d6;end
            else begin cs_num<=read_num-4d1;end
            end
        end
        else if(read==3)begin
            if(data_in==24hB81D69)begin     delay_flag<=1b1;read_num<=0;end
        end
        else if(read==4)begin
            case(read_num)
                4d1:begin DATAIN_1<=data_in;end
                4d2:begin DATAIN_2<=data_in;end
                4d3:begin DATAIN_3<=data_in;end
                4d4:begin DATAIN_4<=data_in;end
                4d5:begin DATAIN_5<=data_in;end
                4d6:begin DATAIN_6<=data_in;end
                endcase
            end
        end
        else if(read_flag==0) begin read<=0;end


//////////////////////////////////
        if((send_over==1b0)&&(cycle<(cs_num+1))&&(delay_flag==1b1))begin count<=count+16b1;end
        else if(send_over==1b1)begin count<=16b0;end
            
        if(count==16d100)begin cs<=1b1;end
        else if(count==16d2800)begin
            case(cycle)
             8d0:begin DATA<=DATAIN_1;end
             8d1:begin DATA<=DATAIN_2;end
             8d2:begin DATA<=DATAIN_3;end
             8d3:begin DATA<=DATAIN_4;end
             8d4:begin DATA<=DATAIN_5;end
             8d5:begin DATA<=DATAIN_6;end
            endcase
            cycle<=cycle+8b1;
        end
        else if(count==16d2900)begin cs<=1b0;end
        else if(count==16d3000)begin
                send_flag<=1b1;
                if(cycle==cs_num) begin cycle<=8b0;delay_flag<=1b0;end
            end
            
        
    if((send_over==1b0)&&(count<16d10))begin send_flag<=1b0;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

 

以上是关于FPGA--SPI通信的主要内容,如果未能解决你的问题,请参考以下文章

在tablayout片段之间进行通信[重复]

与另一个片段通信的片段接口

无法通过接口获取与片段通信的活动

如何在嵌套片段内的两个子片段之间进行通信

与 viewpager 内的片段的父片段通信

片段通信问题(尝试调用虚方法)