• FPGA构造spi时序——AD7176为例(转)


    reference:https://blog.csdn.net/fzhykx/article/details/79490330

    项目中用到了一种常见的低速接口(spi),于是整理了一下关于spi相关的知识,与AD采样的芯片7176通信的协议为spi

    一.对spi协议的理解

    spi扫盲

        除了供电、接地两个模拟连接以外,SPI总线定义四组数字信号:

        - 接口时钟SCLK(Serial Clock,也叫SCK、CLK),master输出至slave的通讯时钟。

        - MOSI( Master Output Slave Input,也叫SIMO、MTSR、DI、DIN、SI)自master输出至slave的数据线。

        - MISO (Master Input Slave Output,也叫SOMI、MRST、DO、DOUT、SO)自slave输出至master的数据线。

        - SS(Slave select,也叫nSS、CS、CSB、CSN、EN、nSS、STE、SYNC)master对slave的片选信号,自master输出至slave,低有效。

     SPI接口是一种典型的全双工接口,通过同步时钟SCLK的脉冲将数据一位位地传送。所以在开始通讯前,master首先要配置接口时钟(确定其通讯频率是SLAVE可以支持的,通常为数兆赫兹)。

     当MASTER片选一个SLAVE时,每向SLAVE发送一个周期的SCLK信号,都会有1bit的数据从MOSI发送至slave,与此同时,slave每收到一个周期的SCLK信号,都会从MISO向master发送1bit的数据。这种全双工通讯,是由硬件保证的(MASTER与HOST中各有一个移位寄存器作为收发数据的缓存)

    二.AD7176使用简单说明

    1>写入与读入方法

        对于FPGA为master而言,要注意SCLK下降沿发数给AD7176,上升沿读AD7176的数。在写入的时候,先用SPI时序,写入一个8bits的CMD,CMD就是通信寄存器,负责控制写入还是读出和要通信的寄存器名称,随后写写入8bit/16bit/24bit的数据。

        在读数据的时候也需要通过通信寄存器也就是CMD读取,要规定好读数据和读哪个寄存器的值。

    2>AD7176配置流程


    具体寄存器还需要查看手册具体配置。

    三.程序源代码讲解

    1>程序源码

    `timescale 1ns / 1ps
    //////////////////////////////////////////////////////////////////////////////////
    // Company: 
    // Engineer: 
    // 
    // Create Date: 2017/03/10 11:08:45
    // Design Name: 
    // Module Name: spi_core
    // Project Name: 
    // Target Devices: 
    // Tool Versions: 
    // Description: 
    // 
    // Dependencies: 
    // 
    // Revision:
    // Revision 0.01 - File Created
    // Additional Comments:
    // 
    //////////////////////////////////////////////////////////////////////////////////
    
    module spi_core(
        input clk,//50M
        input rst,
        
        input [7:0] din,  //要输出
        input s_valid,      //要输出
        input[1:0] readnum,//指示指令读取数据的长度 2’b00:0,2'b01:8,2'b10:256,2'b11:1664480(320*257*2)
        output spi_ready,
        
        output [23:0] dout,
        output m_valid,
        
        input cfg_finsih,
        
        input spi_miso,
        output spi_mosi,
        output spi_cs,
        output spi_clk,
        output empty
        
        );
    /*
    本模块只完成SPI协议所规定的时序,不涉及FLASH指令的编写
    */
    parameter IDLE = 3'd0;
    parameter S1   = 3'd1;
    parameter S2   = 3'd2;
    parameter S3   = 3'd3;
    parameter S4   = 3'd4;
    parameter S5   = 3'd5;
    parameter S6   = 3'd6;
    parameter Smid =3'd7;
    reg [2:0] sta_spi;
    reg spi_ready_r;
    reg spi_clk_r;
    reg spi_cs_r;
    reg spi_mosi_r;
    
    
    wire wr_en;
    reg rd_en;
    wire dout_fifo;
    //wire empty;
    wire full;
    wire[14:0] rd_count;
    
    
    assign spi_clk  = spi_clk_r;
    assign spi_cs   = spi_cs_r;
    assign spi_mosi = dout_fifo;//spi_mosi_r;
    assign spi_ready = spi_ready_r;
    assign wr_en = s_valid;
    //fifo_generator_0 fifo_generator_0 (
    //.rst(~rst),        // input wire rst
    //.wr_clk(clk),  // input wire wr_clk
    //.rd_clk(clk),  // input wire rd_clk
    //.din(din),        // input wire [7 : 0] din
    //.wr_en(wr_en),    // input wire wr_en
    //.rd_en(rd_en),    // input wire rd_en
    //.dout(dout_fifo),      // output wire [0 : 0] dout
    //.full(full),      // output wire full
    //.empty(empty),    // output wire empty
    //.rd_data_count(rd_count) 
    //);
    fifo_generator_0 fifo_generator_0 (
      .clk(clk),                      // input wire clk
      .srst(~rst),                    // input wire srst
      .din(din),                      // input wire [7 : 0] din
      .wr_en(wr_en),                  // input wire wr_en
      .rd_en(rd_en),                  // input wire rd_en
      .dout(dout_fifo),                    // output wire [0 : 0] dout
      .full(full),                    // output wire full
      .empty(empty)                 // output wire empty
    //  .rd_data_count(rd_count)  // output wire [15 : 0] rd_data_count
    );
    reg recv_reg;
    reg recv_ok;
    reg [20:0] recv_num;
    reg [20:0] NUM;
    always @(posedge clk)
    begin
        if(~rst)begin
            sta_spi <= IDLE;
            spi_clk_r <= 1'b1;
            spi_cs_r <= 1'b1;
            spi_mosi_r <=1'b0;
            rd_en <= 1'b0;
            recv_reg <= 1'b0;
            recv_ok <= 1'b0;
            spi_ready_r <= 1'b0;
            recv_num <= 21'd0;
        end
        else begin
            case(sta_spi)
                IDLE:begin 
                    recv_ok <= 1'b0;
                    recv_num <= 21'd0;
                    spi_clk_r <= 1'b1;                
                    if(~empty)begin
                        rd_en <= 1'b1;
                        sta_spi <= S1;
                        spi_ready_r <= 1'b0;
                    end
                    else
                        spi_ready_r <= 1'b1;
                end
                S1:begin
                    rd_en <= 1'b0;
                    spi_clk_r <= 1'b0;
                    spi_cs_r <= 1'b0;
                    spi_mosi_r <= dout_fifo;
                    sta_spi <= S2;
                end
                S2:begin
                    spi_clk_r <= 1'b1;
    //                sta_spi <= S3;
                    if(~empty)begin
                        rd_en <= 1'b1;
                        sta_spi <= S1;                    
                    end
    //                else if(empty)    
    //                spi_cs_r<=1'b1;   
                    else if(readnum!=2'b00)  //就是要读数的意思
                        sta_spi <= S4;
                    else
                        sta_spi <= S3;         
                end
                S3:begin
                    sta_spi <= IDLE;
                    spi_clk_r <= 1'b0;
                    spi_cs_r <= 1'b1;
                end
                S4:begin
                    spi_clk_r <= 1'b0;
                    recv_ok <= 1'b0;
                    sta_spi <= S5;
                end
                S5:begin
                    spi_clk_r <= 1'b1;
                    recv_num <= recv_num +1'b1;
                    recv_reg <= spi_miso;
                    recv_ok <= 1'b1;            
                    if(recv_num!=NUM)
                        sta_spi <= S4;
                    else begin
                        sta_spi <= IDLE;
                        spi_cs_r <= 1'b1;
                    end
                end
            endcase
        end
    end
    
    
    always @(posedge clk)
    begin
        if(~rst)
            NUM <= 21'd0;
        else begin
            case(readnum)
                2'b00:NUM <= 21'd0;
                2'b01:NUM <= 21'd7;
                2'b11:NUM <= 21'd23;
            endcase
        end    
    end
    reg [23:0] dout_r;
    reg m_valid_r;
    reg [3:0] cnt_bit;
    always @(posedge clk)
    begin
        if(~rst)begin
            dout_r <= 16'd0;
            m_valid_r <= 1'b0;
            cnt_bit <= 4'd0;
        end
        else if(recv_ok)begin
            //m_valid_r <= 1'b0;
            dout_r <= {dout_r[22:0],recv_reg};
            cnt_bit <= cnt_bit + 1'b1;
            if(cnt_bit==4'd15)
                 m_valid_r <= 1'b1;
            else
                 m_valid_r <= 1'b0;
        end
        else
            m_valid_r <= 1'b0;
    end
    assign dout = dout_r;
    assign m_valid = m_valid_r;
    
    /*********************************/
    endmodule

    2>软件仿真时序

    配置过程也就是FPGA用SPI输出过程,每次片选信号CS拉低,下降沿发数

    3>读数阶段软件仿真

    波形也和手册的时序相对应

  • 相关阅读:
    A4纸网页打印中对应像素的设定和换算
    网页打印2-打印界面实现
    为何 .NET 总是BUG不断?
    WEB程序员也要学习学习安全防护(一)
    用Delphi编写ASP的ActiveX
    兼容性 无提示关闭窗口
    在ASP.NET 2.0中实现本地化
    取消XP的视频预览功能
    axman 的专栏,专业,真专业
    Delphi TXMLDocument 慎用 doNodeAutoIndent
  • 原文地址:https://www.cnblogs.com/limanjihe/p/9814697.html
Copyright © 2020-2023  润新知