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>读数阶段软件仿真
波形也和手册的时序相对应