XADC功能很强大后续将通过多篇文章来介绍XADC的功能,本文将介绍在PL端调用片内XADC资源来采集 ADC对应管脚的模拟电压的功能
- 此章节内容适用于Lemon ZYNQ主板,如是其他板子请看对应板子目录
- 本文在 vivado2018.3版本上演示
电路介绍:
PL端有部分IO口是可以作XADC 引脚使用的,这些引脚的输入模拟电压范围是(0-1V),我们使用外部的电阻分压电路,让采集范围从(0-1V)拓展成(0-3.3V)。具体的分压电路如下图所示。(备注 一组XADC输入是由ADx_P 和ADx_N两个IO组成的,两个IO共同构成了XADC的差分输入功能。我们用电路将差分模拟输入转换成单端模拟输入功能,这样我们就可以直接采集外部单端模拟信号了(0-3.3V))
这些XADC引脚在电路上的位置如下图:其中A0-A5部分是如上图电阻分压后的单端模拟采集IO,也是本章实验的目标IO。
ANALOG排针位置同样也还有一组差分信号(VP和VN脚)这两个脚是直接从芯片独立的XADC脚上引出的,这VP和VN脚可以不经任何配置直接被PS采集(备注VP VN范围是0-1V),这部分我们在PS相关实验中会介绍, 这里我们主要介绍A0-A5的IO输入。(备注 如果要用VP,VN的差分模拟输入去采集单端模拟信号,我们只需要将VN端接GND,这样VP端采集到的电压值就是我么要采集的单端模拟信号结果了)
以下接正题
XADC除了可以读取芯片内部的 温度和电压等功能外(PS部分实验有介绍), 其实XADC跟单片机的ADC一样 还可以对外部的模拟量进行采集。 下面就演示PL端读取芯片引脚电压来进行演示
芯片内部 XADC资源框图如下
其中红圈框选出来的地方就是对应芯片外部管脚,不作为XADC 引脚的时候,可以作为普通IO口使用
板子排针接口引出来的XADC接口如下图所示 例 AD0P 和AD0N 是一组差分模拟脚,当然也可以直接将N端在硬件上用杜邦线接GND,作为单端信号检测。
XILINX 官方有一份 7系列的 XADC手册, 这里为了方便大家查找也上传到本网站
本文将以板子上的A0来演示XADC的功能,其他的adc口大家自行尝试。
一、Vivado工程创建
工程创建的过程可以参考实验一中的内容,这里不详细描述了。基于Lemon ZYNQ的FPGA实验一 用ZYNQ的PL资源点亮一个LED(完整图文)(芯片型号选XC7Z020CLG00-1)
二、添加XADC IP,并对IP进行设置
1.在IP Catalog中 添加 XADC 模块
2.在XADC 中进行下列设置 ,选择DRP接口(fpga调用的时候选择此接口),Channel Sequencer(该模式可以添加多路adc采样,方便以后拓展), Continuous Mode (连续采样模式,让XADC 一直工作在数据采样的模式下) ,DCLK设置默认的100Mhz
3.第二页的设置 保持默认就好
4.Alarms 警告页, 实验中并不需要报警信号 所以这里报警信号的勾全部都取消
5. 实验中 我们选A0来作为测试的引脚,根据原理图可以看到A0对应芯片的XADC资源是XADC_AD1
所以在下列菜单中我们选中我们选中vauxp1/vauxn1 ,并去掉VP/VN ,否则采样频率会降低一半
6. 之后 按下OK 结束 xadc 的IP配置
7. 在弹出的窗口 选择Generate
8. 等待IP 编译完后,就能在工程中看到 我们添加的XADC模块了
三、添加一个ILA测试模块
1)这里我们计划添加一个ILA来观察XADC采集的结果,
2)选择 Native 接口, 探针数量为2 深度可以根据自己的需求选择 ,这里用8192(因为采样频率比较低,所以通过增加采样深度,来让采样更直观)
3)因为XADC 是12bit的,所以这里我们 位宽选择12,另外第二通道设置位宽为1,用来观察DRDY信号 之后选择OK进行保存
四、增加PLL模块来生成100Mhz 时钟
1) 将输入时钟设置成板子上的125M
2)输出时钟设置成100m
3)为了方便操作这里 去掉了 reset,和locked信号,之后点选OK 保存配置
五、增加顶层模块
六 、编辑代码
下图是XADC 的DRP接口,
XADC 转换模块在运行的过程中会自动把数据输入到DRP中,这里我们不需要写寄存器,只需要读取,因此di_in直接给0 ,读写使能信号一直设置成低电平,即读使能dwe_in
.di_in (16'd0) .dwe_in(1'b0)
do_out 就是最终输出的数据,这个数据是16bit的,其中的高12bit就是我们需要的adc数据
drdy_out 可以理解是输出指示信号,等于1的时候代表采集完成
den_in 与 daddr_in ,以及XADC 的 channel_out 和 eoc_out 首先介绍 channel_out 和eoc_out,当每个通道的ADC转换完成后eoc_out都会被拉高,与此同时channel_out指示对应转换完成的通道位。 而den_in代表输入信号使能,daddr_in代表drp的寄存器地址。所以这里将XADC 的eoc_out 代表转化完成标志 和DRP的den_in端口连接在一起,这样多路采样的某一路采样完成后,则DRP的寄存器地址就被指向daddr_in(因为daddr_in和channel_out补两位连接在一起,所以DRP的寄存器地址就指向对应刚转换完成的 ADC通道地址了), 用这种方式每当adc某个通道转换完了,do_out的输出都是刚转换完的这个通道的结果。(当然理论上也可以通过daddr_in永远固定,或者手动轮询采集某个通道的值,这个大家自行尝试)
.den_in (eoc_out) .eoc_out (eoc_out),//EOC_OUT,就是转化完成标志 .daddr_in ({2'd0,channel_out}), .channel_out (channel_out),//输出地址
因为XADC 会以轮询的方式在多个ADC端口中来回采样,所以我们需要增加一段逻辑来判断当前采集的数据是否是我们需要的通道的数据
reg [11:0]xadc_value_vauxp1;
always @(posedge clk_100m)begin
if(drdy_out== 1'b1 && channel_out==5'h11)begin//11是 vauxp1通道的地址
xadc_vauxp1_value <= do_out[15:4];
end
else begin
xadc_vauxp1_value<= xadc_vauxp1_value;
end
end
如何确定 具体的通道对应哪个地址呢,可以参考下图,比方说 Temp 就是 地址00h ,如果是Vccint 就是01h ,我们是Vauxp[1]按照图片推算,通道1应该是 11h
以上就是 DRP接口的所有信号介绍
剩下的XADC信号介绍
vp、vn,是ZYNQ 物理存在的XADC脚,如果不测量外部数据可以直接给0(我们的板子是没有引出的,所以直接给0);
dclk_in 时钟信号连接DRP所使用的时钟即可,注意频率需要和XADC IP中的设置一样,这里需要给100Mhz
vauxp1、vauxn1 这两个信号就是通道1差分输入的P和N信号,这里我们把两个信号都引出(备注即使只采集单端信号,也把n端引出,否则直接给n赋0,实际测量误差挺大)
.vauxp1(vauxp1),
.vauxn1(vauxn1),
备注 :理论上如果要使用单端测量模拟信号,可以直接把vauxn1直接接板子的GND 就可以 ,我也看到网上有一种说法是直接在模块例化的时候将N端 直接赋0,这个方法我也做了测试实际测试得到用这种方式测量到的电压和真实的电压会有很大的出入(我这边测量得到的结果是,fpga内部 直接给n端赋0,采集到的电压值会比正常电压低0.4-0.5V, 怀疑压差可能是FPGA芯片内部对地的逻辑门导致的)所以不论是测量单端还是差分电压,都请将 XADC 的P和N都引出(Lemon Zynq A0-A5的N端都已经通过电阻接GND了)。
另外因为我们Lemon ZYNQ主板的A0-A5 的N端都是通过电阻直接连接GND的,所以我们直接拿P端去测量单端模拟信号即可。而不需要外部再去连接N端信号线了。但是如果是电路上A0-A5以外的XADC IO脚或者其他的ZYNQ主板,则作为单端测量可以通过杜邦线直接将XADC差分输入脚的N端接GND将P端接测量信号,作为差分方式,则P和N都用来接信号两端。
(备注 因为输入阻抗比较大,所以如果直接用板子去测量50欧阻抗的信号发生器输出的波形,实际测量到的结果可能是两倍信号的大小,这个是正常的(高端的示波器也会有50欧和1M欧两档,如输入阻抗和信号源不完全匹配,可能会造成波形幅度偏大或者偏小的情况))
添加下列完整代码
//www.hellofpga.com
`timescale 1ns / 1ps
module XADC_TEST_MODULE(
input vauxp1,
input vauxn1,
input clk
);
wire [4:0] channel_out;
wire eoc_out;
wire clk_100m;
wire drdy_out;
wire [15:0]do_out;
reg [11:0]xadc_vauxp1_value;
always @(posedge clk_100m)begin
if(drdy_out== 1'b1 && channel_out==5'h11)begin//11是 vauxp1通道的地址
xadc_vauxp1_value <= do_out[15:4];
end
else begin
xadc_vauxp1_value<= xadc_vauxp1_value;
end
end
clk_wiz_0 clk_wiz_inst(
.clk_out1(clk_100m),
.clk_in1(clk)
);
xadc_wiz_0 xadc_wiz_inst (
.daddr_in({2'd0,channel_out}),
.dclk_in(clk_100m),
.den_in(eoc_out),
.di_in(16'd0),
.dwe_in(1'b0),
.drdy_out(drdy_out),
.do_out(do_out[15:0]),
.reset_in(1'b0),
.vauxp1(vauxp1),
.vauxn1(vauxn1),
.channel_out(channel_out),
.eoc_out(eoc_out),
.eos_out(),
.alarm_out(),
.busy_out(),
.vp_in(),
.vn_in()
);
ila_0 u_ila(
.clk(clk_100m),
.probe0(xadc_vauxp1_value),
.probe1(drdy_out)
);
endmodule
六、编译 综合 添加管脚约束
1) 编译 RUN
2)之后按下图所示添加约束(XADC 第1通道 vauxp1 接在排针的E17脚, vauxn1接在排针的D18脚 )
3)综合并生成 bit文件
七、下载并在线测试结果
重要的事情再说一遍因为我们的电路上有做电阻分压电路,所以XADC采集电压范围是(0-3V),超出范围有可能损坏器件
1)点选 Program Device 然后选择芯片xc7z020_1
2)点选 program
3)之后系统会对FPGA进行编程配置,并且vivado 会弹出ila调试界面
硬件连接 XADC 第1通道 vauxp1 接在排针的E17脚, vauxn1接在排针的D18脚 ,如果是单端测量则可以通过杜邦线直接将XADC差分输入脚的N端接GND将P端接测量信号,如果是差分方式,则P和N直接接在被测信号的两端(信号和信号的参考地)
注意 XADC 采集的范围是 0-1V,又因为我们的Xadc IO是接在分压电路上的,所以输入端对应的范围是(0-3.3V) 所以对应的电压换算公式是
Voltage = (ADC /4096) x 3.3V
观测一 观测波形发生器的数值,这里我们将波形发生器设置成(VPP:1.65V, OFFSET 0.825V,频率为20khz的正弦波)考虑到波形发生器是低阻输出(50欧)而我们的xadc 又是高阻抗输入,所以这里实际采集到的波形理论上应该接近于两倍关系 即(VPP:3.3V, OFFSET 1.5V) 。
为什么会出现采集关系是波形发生器的两倍,这个问题上文也解答过,即使在示波器上也会出现这个现象,大家感兴趣的可以百度搜索下 “为什么示波器采集到的波形是信号发生器的两倍”
对FPGA进行program,随后系统会弹出ILA的观察界面,按下箭头进行数据的采集
接下来对采集的数据进行一些简单的设置以方便观察结果,首先将采集的结果定义为无符号整数
再将数据定义为 模拟信号,以方便观察波形
接下来我们观察ila的结果,下列两张图是测试结果,可以看到,输入的信号是一个正弦波(每个drdy_out高电平代表一个新的数据采集完成)
通过换算可以得到 正弦波的 最高电压接近 3978 (换算得到 3978/4096×3.3V=3.20V)
通过换算可以得到 正弦波的 最低电压接近 50(换算得到 50/4096×3.3V=0.04V)
采集到的波形和信号发生器生成的波形满足两倍关系,即数据正确(为什么是两倍关系 上文中有提到)
事实上,如果我们将波形发生器的频率再降低,可以测量到更为准确的最高和最低电压。(因为采样率的原因,当设置在25khz 甚至50khz情况下,我们无法有效的采集到正弦波最高和最低点的电压)
观察二 用XADC 采集数字稳压源的电压
实验分别测试了3V 1.5V和0.1V 电压,通过ila界面 换算 结果都是正确的
另外 XADC功能非常强大 可以求平均后输出,也可以多路进行采集,更多的功能大家请自行研究。 XADC资料暂时整理到这里,后续有时间再出一个PS采集管脚电压的demo, (XADC 拿来测试管脚全网的资料太少了自己也是摸索了好久才调通,大家其他的功能也自行尝试)
重要的事情再说一遍 XADC采集电压范围是(0-1V),因为我们的A0-A5在电路上有分压电路,所以端口输入范围可以到(0-3.3V),超出范围有可能损坏器件。
- 本文的完整工程下载:10_PL_XADC_PIN_TEST
- VIVADO的版本:2018.3
- 工程创建目录:E:\Lemon_ZYNQ\FPGA\10_PL_XADC_PIN_TEST