ZYNQ学习笔记:高速ADDA实验详解

文章目录

  • 高速AD/DA实验
  • AD/DA原理
  • 分类
  • 高速AD/DA简介
  • 硬件设计
  • 程序设计
  • 顶层模块(hs_ad_da)
  • AD 数据接收模块
  • DA数据发送模块
  • 遇到的问题
  • 高速AD/DA实验

    AD/DA原理

    D/A: 就是将_数字量转换成模拟量_
    A/D: 就是将_模拟量转换成数字量_,模拟量可以是电压电流等电信号也可以是声、光、压力、湿度等连续变化的非电物理量,但是这些非电物理量最终都要通过一定手段转换成电信号

    分类

    DAC

    网络权电阻DAC(本次实验所用类型)

    倒梯形电阻网络DAC

    权电流型DAC

    权电容型DAC

    开关树型DAC

    ADC

    直接ADC

    间接ADC

    输入/输出方式

    并行

    串行

    高速AD/DA简介

    本章我们使用的 AD-DA 模块是正点原子推出的一款高速模数-数模转换模块(ATK_HS_AD_DA) ,高速 AD 转换芯片和高速 DA 转换芯片都是由 ADI 公司生产的,分别是 AD9280/3PA9280(两款芯片兼容)和 AD9708。

    ATK_HS_AD_DA 模块的硬件结构图如下图所示。

    AD9708 芯片 AD9708 Data Sheet (waveshare.net)

    AD9280 芯片 AD9280 Data Sheet (waveshare.net)

    硬件设计

    ATK_HS_AD_DA 模块由 DA 转换芯片(AD9708) 和 AD 转换芯片(AD9280) 组成。

    AD9708 的原理图如下图所示。

    由上图可知, AD9708 输出的一对差分电流信号先经过滤波器,再经过运放电路得到一个单端的模拟电压信号。 图中右侧的 W1 为滑动变阻器, 可以调节输出的电压范围,推荐通过调节滑动变阻器,使输出的电压范围在-5V 至+5V 之间, 从而达到 AD 转换芯片的最大转换范围。

    AD9280 的原理图如下图所示

    上图中输入的模拟信号 SMA_IN(VI) 经过衰减电路后得到 AD_IN2(VO) 信号,两个模拟电压信号之间的关系是 VO=VI/5+1, 即当 VI=5V 时, VO=2V; VI=-5V 时, VO=0V。

    ATK_HS_AD_DA 模块的实物图如下图所示。

    本实验中,各端口信号的管脚分配如下表所示。

    高速AD-DA转换实验管脚分配

    信号名 方向 管脚 端口说明 电平标准
    sys_clk input U18 系统时钟, 50Mhz LVCMOS33
    sys_rst_n input N16 系统复位, 低有效 LVCMOS33
    da_clk output R18 DA(AD9708)驱动时钟 LVCMOS33
    da_data[0] output R17 输出给DA的数据 LVCMOS33
    da_data[1] output R16 输出给DA的数据 LVCMOS33
    da_data[2] output W19 输出给DA的数据 LVCMOS33
    da_data[3] output W18 输出给DA的数据 LVCMOS33
    da_data[4] output P16 输出给DA的数据 LVCMOS33
    da_data[5] output P15 输出给DA的数据 LVCMOS33
    da_data[6] output Y19 输出给DA的数据 LVCMOS33
    da_data[7] output Y18 输出给DA的数据 LVCMOS33
    ad_data[0] input V16 AD输入数据 LVCMOS33
    ad_data[1] input W16 AD输入数据 LVCMOS33
    ad_data[2] input T14 AD输入数据 LVCMOS33
    ad_data[3] input T15 AD输入数据 LVCMOS33
    ad_data[4] input Y17 AD输入数据 LVCMOS33
    ad_data[5] input Y16 AD输入数据 LVCMOS33
    ad_data[6] input T16 AD输入数据 LVCMOS33
    ad_data[7] input U17 AD输入数据 LVCMOS33
    ad_otr input V17 模拟电压超出量程标志 LVCMOS33
    ad_clk output V18 AD(AD9280)驱动时钟 LVCMOS33

    程序设计

    ​ 根据本章的实验任务, ZYNQ 需要连续输出正弦波波形的数据,才能使 AD9708 连续输出正弦波波形的模拟电压,如果通过编写代码使用三角函数公式运算的方式输出正弦波数据, 那么程序设计会变得非常复杂。在工程应用中, 一般将正弦波波形数据存储在 RAM 或者 ROM 中,由于本次实验并不需要写数据到 RAM 中,因此我们将正弦波波形数据存储在只读的 ROM 中,直接读取 ROM 中的数据发送给 DA 转换芯片即可

    ​ 图 26.4.1 是根据本章实验任务画出的系统框图。 ROM 里面事先存储好了正弦波波形的数据, DA 数据发送模块从 ROM 中读取数据,将数据和时钟送到 AD9708 的输入数据端口和输入时钟端口; AD 数据接收模块给 AD9280 输出驱动时钟信号和使能信号,并采集 AD9280 输出模数转换完成的数据。

    ​ 高速 AD/DA 实验的系统框图如图 26.4.1 所示:

    顶层模块的原理图如下图所示:

    ​ FPGA 顶层模块(hs_ad_da) 例化了以下三个模块: DA 数据发送模(da_wave_send)、 ROM 波形存储模块(rom_256x8b)和 AD 数据接收模块(ad_wave_rec) 。

    DA 数据发送模块(da_wave_send) : DA 数据发送模块输出读 ROM 地址, 将输入的 ROM 数据发送至 DA 转换芯片的数据端口。
    ROM 波形存储模块(rom_256x8b): ROM 波形存储模块由 Vivado 软件自带的 Block Memory Generator IP 核实现, 其存储的波形数据可以使用波形转存储文件的上位机来生成.coe 文件。
    AD 数据接收模块(ad_wave_rec): AD 数据接收模块输出 AD 转换芯片的驱动时钟和使能信号,随后接收 AD 转换完成的数据。

    顶层模块(hs_ad_da)

    module hs_ad_da(
        input                 sys_clk     ,  //系统时钟
        input                 sys_rst_n   ,  //系统复位,低电平有效
        //DA芯片接口
        output                da_clk      ,  //DA(AD9708)驱动时钟,最大支持125Mhz时钟
        output    [7:0]       da_data     ,  //输出给DA的数据
        //AD芯片接口
        input     [7:0]       ad_data     ,  //AD输入数据
        //模拟输入电压超出量程标志(本次试验未用到)
        input                 ad_otr      ,  //0:在量程范围 1:超出量程
        output                ad_clk         //AD(AD9280)驱动时钟,最大支持32Mhz时钟 
    );
    
    //wire define 
    wire      [7:0]    rd_addr;              //ROM读地址
    wire      [7:0]    rd_data;              //ROM读出的数据
    //*****************************************************
    //**                    main code
    //*****************************************************
    
    //DA数据发送
    da_wave_send u_da_wave_send(
        .clk         (sys_clk), 
        .rst_n       (sys_rst_n),
        .rd_data     (rd_data),
        .rd_addr     (rd_addr),
        .da_clk      (da_clk),  
        .da_data     (da_data)
        );
    
    //ROM存储波形
    rom_256x8b  u_rom_256x8b (
      .clka  (sys_clk),    // input wire clka
      .addra (rd_addr),    // input wire [7 : 0] addra
      .douta (rd_data)     // output wire [7 : 0] douta
    );
    
    //AD数据接收
    ad_wave_rec u_ad_wave_rec(
        .clk         (sys_clk),
        .rst_n       (sys_rst_n),
        .ad_data     (ad_data),
        .ad_otr      (ad_otr),
        .ad_clk      (ad_clk)
        );    
    
    //ILA采集AD数据
    ila_0  ila_0 (
        .clk         (sys_clk ), // input wire clk
        .probe0      (ad_otr ), // input wire [0:0]  probe0  
        .probe1      (ad_data)  // input wire [7:0]  probe0  
    );
    
    endmodule
    

    ​ DA 数据发送模块输出的读 ROM 地址(rd_addr) 连接至 ROM 模块的地址输入端, ROM 模块输出的数据(rd_data) 连接至 DA 数据发送模块的数据输入端, 从而完成了从 ROM 中读取数据的功能。
    ​ 在代码的第 32 至 36 行例化了 ROM 模块,由 Block Memory Generator IP 核配置生成。
    ​ 代码的第 48 行例化了一个 ILA 的 IP 核,用于捕获 ad_otr 和 ad_data 的数据。需要注意的是, ILA 的采样时钟必须使用 ad_clk(如果最后ILA调试时不显示波形,就将其改为系统时钟),否则数据可能采集错误。 ILA IP 核的配置如下图所示:

    我们把探针数量设置为 2,并且把采样深度设置为 4096。探针宽度的设置如下图所示:

    ​ 我们将两个探针的位宽设置成 1 和 8,分别对应 ad_otr 和 ad_data 的位宽,设置完成后点击“OK”按钮即可。
    ​ 我们在前面说过, ROM 中存储的波形数据可以使用上位机波形转 COE 软件生成, 在这里我们介绍一个简单易用的波形转 COE 工具的使用方法,双击“WaveToMem_V1.2.exe”运行软件。
    ​ 接下来我们对软件进行设置,如图 26.4.5 所示,这里对软件界面做个简单的介绍。
    ​ 位宽:波形数据的位宽。由于 ATK_HS_AD_DA 模块的 DA 芯片数据位宽为 8 位,因此这里保持默认,即设置成 8 位。
    ​ 深度:一个波形周期包含了多少个数据量。这里保持默认,即设置成 256。需要说明的是,在用 Block Memory Generator IP 核生成 ROM 时,配置 ROM 的宽度和深度和上位机设置的位宽和深度保持一致。
    ​ 波形频率设置:对波形倍频,倍数值越大,最终生成的波形频率越快(频率太高,可能导致波形失真),这里保持默认,即设置成 1 位。
    ​ 波形类型:软件支持将正弦波、方波、锯齿波和三角波的波形转换成存储波形格式的文件。
    ​ 生成文件:软件支持将波形转换成 COE(Vivado 软件支持的存储格式)和 MIF(Quartus 软件支持的存储格式)格式文件,这里保持默认,即选中 COE 文件格式。
    ​ 然后点击“一键生成”按钮,在弹出的界面中选择 COE 文件的存放路径并输入文件名,这里将 COE文件保存在工程的 doc 文件夹下。 WaveToMem 转换过程中的软件界面如下图所示

    使用 Notepad++代码编辑器打开生成的 COE 文件后如下图所示 :

    ​ 工程中创建了一个单端口 ROM,并命名为“rom_256x8b”,在调用 Block Memory Generator IP 核时,“Basic”选项也配置如下图所示:

    AD 数据接收模块

    module ad_wave_rec(
        input                 clk         ,  //时钟
        input                 rst_n       ,  //复位信号,低电平有效
        
        input         [7:0]   ad_data     ,  //AD输入数据
        //模拟输入电压超出量程标志(本次试验未用到)
        input                 ad_otr      ,  //0:在量程范围 1:超出量程
        output   wire        ad_clk         //AD(TLC5510)驱动时钟,最大支持20Mhz时钟
        );
    wire locked;
    //*****************************************************
    //**                    main code 
    //*****************************************************
    //parameter       DIV_END =  8'D3;
    //reg         [7:0]   div_cnt;
    //reg                 div_clk_o;
    四分频12.5Mhz
    //always @ (posedge clk or negedge rst_n)
    //    if(rst_n == 1'b0)
    //        div_cnt <= 'd0;
    //    else if(div_cnt == DIV_END)
    //        div_cnt <= 'd0;
    //    else
    //        div_cnt <= div_cnt + 1'b1;
    //always @ (posedge clk or negedge rst_n)
    //    if(rst_n == 1'b0)
    //        div_clk_o <= 1'b0;
    //    else if(div_cnt == 'd1)
    //        div_clk_o <= 1'b1;
    //    else if(div_cnt == 'd3)
    //        div_clk_o <= 1'b0;
    //assign ad_clk = div_clk_o;
    //分频(2分频,时钟频率为25Mhz),产生AD时钟
    //always @(posedge clk or negedge rst_n) begin
    //    if(rst_n == 1'b0)
    //        ad_clk <= 1'b0;
    //    else 
    //        ad_clk <= ~ad_clk; 
    //end    
    
      clk_wiz_0 u_clk_wiz_0
       (
        // Clock out ports
        .clk_out_25m_45(ad_clk),     // output clk_out_25m_45
        // Status and control signals
        .reset(~rst_n), // input reset
        .locked(locked),       // output locked
       // Clock in ports
        .clk_in1(clk));  // input clk_in1
    // INST_TAG_END ------ End INSTANTIATION Template ---------
    
    
    endmodule
    

    DA数据发送模块

    module da_wave_send(
        input                 clk    ,  //时钟
        input                 rst_n  ,  //复位信号,低电平有效
        
        input        [7:0]    rd_data,  //ROM读出的数据
        output  reg  [7:0]    rd_addr,  //读ROM地址
        //DA芯片接口
        output                da_clk ,  //DA(AD9708)驱动时钟,最大支持125Mhz时钟
        output       [7:0]    da_data   //输出给DA的数据  
        );
    
    //parameter
    //频率调节控制
    parameter  FREQ_ADJ = 8'd5;  //频率调节,FREQ_ADJ的越大,最终输出的频率越低,范围0~255
    
    //reg define
    reg    [7:0]    freq_cnt  ;  //频率调节计数器
    
    //*****************************************************
    //**                    main code
    //*****************************************************
    
    //数据rd_data是在clk的上升沿更新的,所以DA芯片在clk的下降沿锁存数据是稳定的时刻
    //而DA实际上在da_clk的上升沿锁存数据,所以时钟取反,这样clk的下降沿相当于da_clk的上升沿
    assign  da_clk = ~clk;       
    assign  da_data = rd_data;   //将读到的ROM数据赋值给DA数据端口
    
    //频率调节计数器
    always @(posedge clk or negedge rst_n) begin
        if(rst_n == 1'b0)
            freq_cnt <= 8'd0;
        else if(freq_cnt == FREQ_ADJ)    
            freq_cnt <= 8'd0;
        else         
            freq_cnt <= freq_cnt + 8'd1;
    end
    
    //读ROM地址
    always @(posedge clk or negedge rst_n) begin
        if(rst_n == 1'b0)
            rd_addr <= 8'd0;
        else begin
            if(freq_cnt == FREQ_ADJ) begin
                rd_addr <= rd_addr + 8'd1;
            end    
        end            
    end
    
    endmodule
    

    遇到的问题

    最后进行下载验证时ila调试时,不显示波形:

    解决方案是将ila的采样时钟改成系统时钟

    顶层模块中原代码如下:

    //ILA采集AD数据
    ila_0  ila_0 (
        .clk         (ad_clk ), // input wire clk
        .probe0      (ad_otr ), // input wire [0:0]  probe0  
        .probe1      (ad_data)  // input wire [7:0]  probe0  
    );
    

    将其中的采样时钟ad_clk改成sys_clk:

    ila_0  ila_0 (
        .clk         (sys_clk ), // input wire clk
        .probe0      (ad_otr ), // input wire [0:0]  probe0  
        .probe1      (ad_data)  // input wire [7:0]  probe0  
    );
    

    修改后虽然显示出来了,但是结果如图,并不是预期的正弦信号:

    原因是超过了量程,所以对AD/DA模块的滑动电阻进行调整:

    调节过后虽然显示出正弦波的形状但是有太多的毛刺。如图所示:

    分析后产生毛刺的原因为:信号在AD/DA之间传递的时候受到了干扰主要是ILA采样时钟和AD采样时钟冲突造成的。

    解决方法有:降低AD采样时钟的频率调整相位

    方法一:首先试着进行降低AD采样时钟的频率

    在ad_wave_rec模块将AD时钟从原来的25MHZ改为12.5MHZ,即将2分频改为4分频

    原来代码:

    //分频(2分频,时钟频率为25Mhz),产生AD时钟
    always @(posedge clk or negedge rst_n) begin
        if(rst_n == 1'b0)
            ad_clk <= 1'b0;
        else 
            ad_clk <= ~ad_clk; 
    end 
    

    修改后:

    parameter       DIV_END =  8'D3;        
    reg         [7:0]   div_cnt;            
    reg                 div_clk_o;          
    //四分频12.5Mhz                            
    always @ (posedge clk or negedge rst_n) 
        if(rst_n == 1'b0)                   
            div_cnt <= 'd0;                 
        else if(div_cnt == DIV_END)         
            div_cnt <= 'd0;                 
        else                                
            div_cnt <= div_cnt + 1'b1;      
    always @ (posedge clk or negedge rst_n) 
        if(rst_n == 1'b0)                   
            div_clk_o <= 1'b0;              
        else if(div_cnt == 'd1)             
            div_clk_o <= 1'b1;              
        else if(div_cnt == 'd3)             
            div_clk_o <= 1'b0;              
    assign ad_clk = div_clk_o;              
    

    结果如图,并没有得到改善:

    方法二:调整相位

    添加例化一个PLL,然后用PLL输出一个25M时钟,且相位偏移系统时钟45度,设置如下:

    生成了veo文件如下:

      clk_wiz_0 instance_name
       (
        // Clock out ports
        .clk_out_25m_45(clk_out_25m_45),     // output clk_out_25m_45
        // Status and control signals
        .reset(reset), // input reset
        .locked(locked),       // output locked
       // Clock in ports
        .clk_in1(clk_in1));      // input clk_in1
    // INST_TAG_END ------ End INSTANTIATION Template ---------
    

    将其例化到ad_wave_rec模块,并将之前的分频给注释掉,代码如下:

      clk_wiz_0 u_clk_wiz_0
       (
        // Clock out ports
        .clk_out_25m_45(ad_clk),     // output clk_out_25m_45
        // Status and control signals
        .reset(~rst_n), // input reset
        .locked(locked),       // output locked
       // Clock in ports
        .clk_in1(clk));  // input clk_in1
    // INST_TAG_END ------ End INSTANTIATION Template ---------
    

    再重新生成Bit流文件,中途如果出现如下问题,

    原因在于PLL生成时钟时在Clocking Options界面没有将系统输入时钟的Sourse改成Global buffer,具体解决方案参考来自:(vivado implementation执行时候报错:Unsupported PLLE2_ADV connectivity…_黑猫学长呀的博客-CSDN博客

    成功生成Bit流文件后下载到板子,得到结果正确,如下所示:


    物联沃分享整理
    物联沃-IOTWORD物联网 » ZYNQ学习笔记:高速ADDA实验详解

    发表评论