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>讀數階段軟件仿真

 

 

 

波形也和手冊的時序相對應


免責聲明!

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



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