自学内容网 自学内容网

AD7606 ADC的SPI驱动——FPGA学习笔记17

                                                                                                                素材来源             米联客

一、AD7606简介

功能框图:

转换控制时序:

        AD7606 支持 2 种时序转换, 由于我们采用的时串行 SPI 模式, 本身 SPI 读取数据就会耽误很多时间, 所以必须采用第二种工作时序, 才能确保 200Kbps 的采样率。

         空闲SCLK为高,CPOL为1       在SCLK第一个上升沿进行数据读取,CPHA为1

         当数据完成转换后, CS 为电平期间, 每个时钟的上升沿完成数据的采样, 表中 FRSTDATA 代表每次转换的第一个数据, 这里实际可以不用这个信号, 因为每次 CS 之后的第一个上升沿时钟我们就是开始采样数据, 每 16 次完成一路 ADC 的采集。        

        对于 AD7606 具有 8 路 ADC, DOUTA 和 DOUTB 各 4 路。 DOUTA 对于 1~4 路 ADC 通道, DOUTB 对应 5~8路 ADC 通道。而且 ADC 的 2 组 4 个通道可以一次性完成采集, 也就是说 16X4 共计 64 个时钟上升沿完成 4 路 ADC采集

引脚说明:

 

模式选择:

参数说明:

 

 

串行读取:

二、硬件设计

                                                                                                                        来源:中瑞科技

 

三、代码编写

SPI驱动部分:

`timescale 1ns / 1ps

module spi7606( 
input            I_ad_clk        ,           //系统时钟输入   
input               I_ad_rst        ,           //系统复位输入
input            I_ad_busy       ,           //ad7606 忙标志位
output      [2:0] O_ad_os         ,           //ad7606 过采样倍率选择,本驱动不使用
output            O_ad_cs         ,           //ad7606 CS信号输出,低电平SPI数据线输出AD7606寄存器数据
output reg         O_ad_sclk       ,           //ad7606 SCLK时钟输出
output           O_ad_rst        ,           //ad7606 复位输出
output            O_ad_convsta    ,           //ad7606 A组通道转换
output            O_ad_convstb    ,           //ad7606 B组通道转换
output              O_ad_range      ,           //ad7606 模拟输入范围,设置1范围:±10V,设置0范围±5V
input               I_ad_out_a      ,           //串行A组通道采集输入,V1,V2,V3,V4  
input               I_ad_out_b      ,           //串行B组通道采集输入, V5,V6,V7,V8  
output reg  [63:0]  O_ad_out_a      ,           //A组通道采集有效数据输出
output reg  [63:0]  O_ad_out_b      ,           //B组通道采集有效数据输出
output              O_ad_cap_en                 //采集完成使能
);
localparam  CLK_FREQ =  100000000                   ;
localparam  SET_5US  =  5                           ; //5us周期
localparam  T5US_DIV =  CLK_FREQ*SET_5US/1000000 -1 ; //计算5us 分频系数
localparam  SPI_DIV  =  CLK_FREQ/16666666 -1        ; //产生SPI时钟,设置最高16.666667MHZ


assign O_ad_range = 1'b1;    //±10V真直流输入范围
assign O_ad_os    = 3'b000;  //无过采样

//ad复位时间高电平,复位时间最少50ns
reg [23: 0] rst_cnt;
assign O_ad_rst   = !rst_cnt[23];
always@(posedge I_ad_clk or posedge I_ad_rst)begin        
    if(I_ad_rst) begin
        rst_cnt  <= 24'd0;
    end
    else if(!rst_cnt[23]) begin
        rst_cnt  <= rst_cnt + 1'b1;
    end
end       

//设置采样频率,AD7606为8通道可以工作于200Kbps采样率,因此采样周期为5us  1KSPS=1KHz,1MSPS=1MHz
reg [9:0] tcnt5us;
wire cycle_end = (tcnt5us == T5US_DIV);
always@ (posedge I_ad_clk)begin   
    if(O_ad_rst) begin
        tcnt5us <= 10'd0;
    end
    else if(tcnt5us < T5US_DIV) begin
        tcnt5us <= tcnt5us + 1'b1;
    end
    else begin
         tcnt5us <= 10'd0;
    end
end

localparam [9:0] SPI_DIV1    = SPI_DIV/2; //半周期分频
reg [9:0] clk_div = 10'd0;//分频计数器

//SPI 时钟分频,对于200K采样,设置20M时钟
always@(posedge I_ad_clk)begin
    if(clk_div < SPI_DIV) begin
        clk_div <= clk_div + 1'b1;
    end
    else begin
        clk_div <= 10'd0;
    end
end

//产生SPI时钟
wire clk_en1 = (clk_div == SPI_DIV1);   // 半周期使能,控制输出0
wire clk_en2 = (clk_div == SPI_DIV);    //  半周期使能,控制输出1

always@(posedge I_ad_clk)begin
    if(clk_en2) begin
        O_ad_sclk <= 1'b1; //输出高电平
    end
    else if(clk_en1) begin
        O_ad_sclk <= 1'b0; //输出高低平
    end
end

//AD转换状态机
reg [1:0] AD_S;
reg ad_convst; //ADC转换控制信号,A组通道和B组通道采用同一通道
//ADC工作于SPI模式,以及边读边转换模式,本次数据转换的同时,可以读出前一次转换的结果
assign O_ad_cs = ~((AD_S == 2'd3)&&I_ad_busy);//当AD7606工作在串行模式,cs=0代表输出通过SPI数据总线,输出之前的采样数据
assign O_ad_convsta = ad_convst; //ADC转换控制信号,A组通道和B组通道采用同一通道
assign O_ad_convstb = ad_convst; //ADC转换控制信号,A组通道和B组通道采用同一通道

always @(posedge I_ad_clk) 
begin
    if(O_ad_rst||I_ad_rst)begin
        ad_convst <= 1'b1;     
        AD_S <= 2'd0; 
    end
    else begin
        case(AD_S)  
        2'd0:if(clk_en2)begin    //clk_en2,控制SCLK输出1,因此这里相当于SCLK上升沿
            ad_convst <= 1'b0;  //设置ad_convst=0
            AD_S <= 2'd1;//下一状态
        end           
        2'd1:if(clk_en2)begin //延迟50ns,ad_convst低电平至少25ns,对于SCLK时钟是20MHZ,所以这里ad_convst低电平是50ns
            ad_convst <= 1'b1;//ad_convst 低电平50ns后拉高为高电平
            AD_S <= 2'd2;//下一状态
        end 
        2'd2:if(clk_en2&&I_ad_busy)begin//延迟50ns,并且等待busy高,只要ADC正常工作,busy必然为高,说明ADC处于采样转换下
            AD_S <= 2'd3;   
        end         
        2'd3:if(cycle_end)begin//ADC转换时间最大4.2us,5us周期结束,代表本次8通道完全采集结束
            AD_S <= 2'd0; //回到初始状态,进行下一次采样   
        end
        endcase    
     end             
end

//SPI采样
reg [7 : 0] nbits; // bits计数器,用于计数完成了从AD7606 SPI总线读出的bits数
wire ad_cap_en_r1 = (nbits==8'd64); //数据同步输出使能
reg  ad_cap_en_r2 = 1'b0; //数据同步输出使能寄存一次
assign O_ad_cap_en = ({ad_cap_en_r1,ad_cap_en_r2}==2'b10);//高电平输出系统时钟的一个周期

always@(posedge I_ad_clk) begin //寄存一次ad_cap_en_r1
    ad_cap_en_r2 <= ad_cap_en_r1;
end

//当CS信号为低电平,每个SCL的下降沿输出采样数据,每组通道采样64bits
wire cap_en  = (!O_ad_cs)&&clk_en1&&(nbits<8'd64);

//ADC 串并转换模块
always@(posedge I_ad_clk) begin
    if(O_ad_rst) begin //ADC复位期间重置相关寄存器
        O_ad_out_a  <= 64'd0; 
        O_ad_out_b  <= 64'd0; 
        nbits       <= 8'd0;
    end 
    else if(O_ad_cs)begin //高电平,重置  nbits   
        nbits   <= 8'd0;                       
    end   
    else if(cap_en)begin//当CS信号为低电平,每个SCL的下降沿采样数据,每组通道采样64bits
        nbits    <= nbits + 1'b1; //
        O_ad_out_a <= {O_ad_out_a[62:0],I_ad_out_a};//保存A组通道数据,V1~V4,每个通道16bits
        O_ad_out_b <= {O_ad_out_b[62:0],I_ad_out_b};//保存B组通道数据,V5~V8,每个通道16bits
    end                                                                                                                                    
end
                  
endmodule

顶层状态转移:

`timescale 1ns / 1ns
module ad7606_top
(
input           I_sysclk,         //系统时钟输入   
input        I_ad_busy,        //ad7606 忙标志位
output        O_ad_cs,          //ad7606 CS信号输出,低电平SPI数据线输出AD7606寄存器数据
output      O_ad_sclk,        //ad7606 SCLK时钟输出
output       O_ad_rst,         //ad7606 复位输出
output        O_ad_convsta,     //ad7606 A组通道转换
output        O_ad_convstb,     //ad7606 B组通道转换
output          O_ad_range,       //ad7606 模拟输入范围,设置1范围:±10V,设置0范围±5V
input           I_ad_out_a,       //串行A组通道采集输入,V1,V2,V3,V4  
input           I_ad_out_b,       //串行B组通道采集输入, V5,V6,V7,V8  
output          O_card_power_en     //子卡电源使能
);

assign O_card_power_en = 1'b1; //子卡上电

wire clk100M,locked;
clk_wiz_0  clk_7606_inst(.clk_out1(clk100M),.locked(locked),.clk_in1(I_sysclk)); 

//ad7606 ip相关信号
wire ad_clk_i,ad_rst_i,ad_cap_en; 
wire [63:0] ad_out_a,ad_out_b;
assign ad_clk_i      = clk100M; //ADC时钟
assign ad_rst_i      = !locked; //ADC IP内部代码复位


spi7606 spi7606_inst
(
.I_ad_clk(ad_clk_i),    //系统时钟输入               
.I_ad_rst(ad_rst_i),    //系统复位输入
.I_ad_busy(I_ad_busy),  //ad7606 忙标志位               
.O_ad_cs(O_ad_cs),      //ad7606 CS信号输出,低电平SPI数据线输出AD7606寄存器数据
.O_ad_sclk(O_ad_sclk),  //ad7606 SCLK时钟输出    
.O_ad_rst(O_ad_rst),    //ad7606 复位输出        
.O_ad_convsta(O_ad_convsta), //ad7606 A组通道转换     
.O_ad_convstb(O_ad_convstb), //ad7606 B组通道转换  
.O_ad_range(O_ad_range),     //ad7606 模拟输入范围,设置1范围:±10V,设置0范围±5V 
.I_ad_out_a(I_ad_out_a),     //串行A组通道采集输入,V1,V2,V3,V4  
.I_ad_out_b(I_ad_out_b),     //串行B组通道采集输入, V5,V6,V7,V8  
.O_ad_out_a(ad_out_a),         //A组通道采集有效数据输出
.O_ad_out_b(ad_out_b),         //B组通道采集有效数据输出
.O_ad_cap_en(ad_cap_en)        //采集完成使能
 );


wire [15:0] ad_ch1 = ad_out_a[63:48];//ADC 通道1
wire [15:0] ad_ch2 = ad_out_a[47:32];//ADC 通道2
wire [15:0] ad_ch3 = ad_out_a[31:16];//ADC 通道3
wire [15:0] ad_ch4 = ad_out_a[15: 0];//ADC 通道4
wire [15:0] ad_ch5 = ad_out_b[63:48];//ADC 通道5
wire [15:0] ad_ch6 = ad_out_b[47:32];//ADC 通道6
wire [15:0] ad_ch7 = ad_out_b[31:16];//ADC 通道7
wire [15:0] ad_ch8 = ad_out_b[15: 0];//ADC 通道8

//例化 ILA IP 用于在线逻辑分仪观察ADC采样值
ila_0 ila_debug 
(
.clk(ad_clk_i), //系统时钟
.probe0(ad_cap_en), //AD采集数据使能,ILA中使用capture功能,可以观察到更多有效数据
.probe1(ad_ch1),//ADC 通道1
.probe2(ad_ch2),//ADC 通道2
.probe3(ad_ch3),//ADC 通道3
.probe4(ad_ch4),//ADC 通道4
.probe5(ad_ch5),//ADC 通道5
.probe6(ad_ch6),//ADC 通道6
.probe7(ad_ch7),//ADC 通道7
.probe8(ad_ch8), //ADC 通道8       
.probe9(O_ad_cs),//ADC 通道7
.probe10(O_ad_sclk),//ADC 通道7
.probe11(O_ad_rst), //ADC 通道8     
.probe12(O_ad_convsta),//ADC 通道7
.probe13(I_ad_out_a), //ADC 通道8   
.probe14(O_ad_convstb),//ADC 通道7
.probe15(I_ad_out_b) //ADC 通道8                
); 

endmodule

四、仿真波形

五、上板验证

 

触发方式:

六、Bug解决

问题描述:

vivado 仿真时出现 boost::filesystem::remove: 另一个程序正在使用此文件,进程无法访问。

使用了外部编译器VScode,关闭外部编译器即可正常仿真 


原文地址:https://blog.csdn.net/m0_69082048/article/details/142662610

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!