高速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 模块的实物图如下图所示。
本实验中,各端口信号的管脚分配如下表所示。
信号名 | 方向 | 管脚 | 端口说明 | 电平标准 |
---|---|---|---|---|
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流文件后下载到板子,得到结果正确,如下所示:文章来源:https://www.toymoban.com/news/detail-463878.html
文章来源地址https://www.toymoban.com/news/detail-463878.html
到了这里,关于ZYNQ学习笔记——高速ADDA实验的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!