AD7961介绍
AD7961是一款16位模数转换芯片,最高采样率可达5MSPS,串行LVDS接口,真差分输入。
所谓真差分输入,指电压输入端口 I N + IN+ IN+、 I N − IN- IN− 均可参与信号传输,其中 I N + IN+ IN+ 与信号相同, I N − IN- IN− 反相,注意,反相不是指电压为负,实际上 I N + IN+ IN+、 I N − IN- IN− 的电压范围均在 0 ∼ 5 V 0\sim 5V 0∼5V ,ADC采样值为 I N + IN+ IN+、 I N − IN- IN− 的差值。真差分输入可以有效抑制共模噪声。
与之相对应的伪差分输入, I N − IN- IN− 仅作为直流参考端,并不参与信号传输。
LVDS(Low-Voltage Differential Signaling),低电压差分信号,是一种差分数字信号传输技术,最高传输速率可达几百Mbps,由于采用差分信号,其具有低噪声的特点。
如上图所示,IN±即模拟电压信号的差分输入端,CNV±、D±、DCO±、CLK±为四个LVDS差分信号对,用于控制ADC的数据采集。EN0~EN3用于配置ADC的工作模式。
CNV±为转换控制信号,ADC在CNV±的上升沿转换IN±的电压差;D±为数据端口;CLK±为时钟输入;DCO±为时钟输出,在回声时钟模式下输出回声时钟用于数据同步。
AD7961有两种采样工作模式:自时钟模式(Self Clocked Mode)、回声时钟模式(Echoed Clock Interface Mode)。
当DCO+被接地时,选中自时钟模式;当DCO+不接地时,DCO±被用作回声时钟,选中回声时钟模式。
回声时钟模式
回声时钟模式下,CNV±上升沿启动转换,转换时间t_MSB=200ns(因此对应5MSPS采样率)。CNV±必须在 t C N V H = 0.6 × t C Y C t_{CNVH}=0.6\times t_{CYC} tCNVH=0.6×tCYC内拉低, t C Y C t_{CYC} tCYC为采样周期(两个CNV上升沿间的时间)。
该模式使用全部LVDS对,DCO±为CLK±的缓冲,与D±同步,DCO+下降沿对应D±的更新。DCO最大比CLK延迟5ns,经典值为2ns延迟。host端(FPGA、单片机等)可通过DCO+的上升沿抓取D±的数据,由于DCO±上升沿对应数据的稳定状态,因此建议在此时进行数据读取,尽管在DCO±下降沿也是可以读取的。
注意,第16个CLK必须出现在转换阶段完成之前,即下一个CNV不能早于 C L K L a s t − t C L K L CLK_{Last}-t_{CLKL} CLKLast−tCLKL,其中 t C L K L = 160 n s t_{CLKL}=160ns tCLKL=160ns,,否则会造成数据丢失。
在Last Data发送完成后(第16个CLK下降沿),到下一次转换结束之前,D±与DCO±均被拉到0。
在回声时钟模式下,CLK空闲位为0。
该模式下,host端仅需要维护一个16位移位寄存器,因此实现起来比较方便。
自时钟模式
自时钟模式不使用DCO±进行数据同步,而是使用数据头010进行同步。自时钟模式下,CLK空闲位为1。
在转换结束后,D±自动输出0,在CLK的前两个下降沿,1、0被输出锁存,其中第一个下降沿出现在CLK由空闲进入工作状态时。
与回声时钟模式类似,第18个CLK必须出现在转换阶段完成之前。
FPGA驱动与仿真
FPGA驱动:
/******************************FILE HEAD**********************************
* file_name : AD7961.v
* function : AD7961驱动
* author : 今朝无言
* date : 2022-04-15
*************************************************************************/
module AD7961(
input clk_100M,
input rst_n,
output reg [15:0] data,
output reg out_available, //上升沿指示数据可用
input D_p, //LVDS Data Output
input D_n,
input DCO_p, //LVDS Buffered Clock Output
input DCO_n,
output CLK_p, //LVDS Clock input,type val: 4ns(250MHz)
output CLK_n,
output CNV_p, //LVDS Convert Input
output CNV_n,
output EN0, //Enable
output EN1,
output EN2,
output EN3
);
parameter N = 20; //t_CYC=N*T_100M,应大于200ns,所以N至少20
//------------------------ Single --------------------------
wire D;
wire DCO;
wire CLK;
reg CNV;
//----------------------- 配置EN0~3 -------------------------
assign {EN3,EN2,EN1,EN0} = 4'b0001;
// X001,输入采样网络带宽28MHz X101,带宽9MHz XX11,休眠,对CNV无响应
// 更具体配置详见官方文档
//------------------------- 采样 ----------------------------
reg [15:0] data_tmp = 16'd0;
reg [7:0] state = 8'd0;
reg CS = 0; //CLK的使能信号
always @(negedge clk_100M or negedge rst_n) begin
if(!rst_n)begin
CNV <= 1'b0;
data <= 8'd0;
out_available <= 1'b0;
CS <= 1'b0;
state <= 8'd0;
end
else begin
if(state<=15) begin
CS <= 1'b1; //产生16个CLK
state <= state + 1'b1;
if(state==0) begin
CNV <= 1'b1; //启动转换
end
else if(state>=5) begin
out_available <= 1'b0; //无效数据读取标志
CNV <= 1'b0; //CNV拉低
end
end
else if(state==16) begin
CS <= 1'b0;
state <= state + 1'b1;
end
else if(state==17) begin
data <= data_tmp; //寄存数据
state <= state + 1'b1;
end
else if(state==18) begin
out_available <= 1'b1; //有效数据读取标志
state <= state + 1'b1;
end
else if(state<N-1) begin //等待
state <= state + 1'b1;
end
else begin
state <= 0;
end
end
end
assign CLK = CS & clk_100M;
always @(posedge DCO or negedge rst_n) begin
if(!rst_n) begin
data_tmp <= 16'd0;
end
else begin
data_tmp[15:0] <= {data_tmp[14:0], D}; //移位寄存
end
end
//-------------------- LVDS <-> Single -----------------------
// Data In LVDS -> Single
IBUFDS #(
.DIFF_TERM ("TRUE"), // Differential Termination
.IBUF_LOW_PWR ("FALSE"), // Low power="TRUE", Highest performance="FALSE"
.IOSTANDARD ("LVDS") // Specify the input I/O standard
)
Data_In_IBUFDS(
.O (D), // Buffer output
.I (D_p), // Diff_p buffer input
.IB (D_n) // Diff_n buffer input
);
// DCO In LVDS -> Single
IBUFDS #(
.DIFF_TERM ("TRUE"),
.IBUF_LOW_PWR ("FALSE"),
.IOSTANDARD ("LVDS")
)
DCO_In_IBUFDS(
.O (DCO),
.I (DCO_p),
.IB (DCO_n)
);
// CLK Out Single -> LVDS
OBUFDS #(
.IOSTANDARD ("LVDS"), // Specify the output I/O standard
.SLEW ("FAST") // Specify the output slew rate
)
CLK_Out_OBUFDS(
.O (CLK_p), // Diff_p output
.OB (CLK_n), // Diff_n output
.I (CLK) // Buffer input
);
// CNV Out Single -> LVDS
OBUFDS #(
.IOSTANDARD ("LVDS"),
.SLEW ("FAST")
)
CNV_Out_OBUFDS(
.O (CNV_p),
.OB (CNV_n),
.I (CNV)
);
endmodule
//END OF AD7961.v FILE***************************************************
testbench:
`timescale 1ns/1ps
module AD7961_tb();
//----------------------- 100MHz -------------------------
reg clk_100M = 1;
always #5 begin
clk_100M <= ~clk_100M;
end
//------------------------ AD7961 ------------------------
reg rst_n;
wire [15:0] data;
wire out_available;
reg D_p,D_n;
reg DCO_p,DCO_n;
wire CLK_p,CLK_n;
wire CNV_p,CNV_n;
wire EN0,EN1,EN2,EN3;
AD7961 AD7961_inst(
.clk_100M (clk_100M),
.rst_n (rst_n),
.data (data),
.out_available (out_available),
.D_p (D_p),
.D_n (D_n),
.DCO_p (DCO_p),
.DCO_n (DCO_n),
.CLK_p (CLK_p),
.CLK_n (CLK_n),
.CNV_p (CNV_p),
.CNV_n (CNV_n),
.EN0 (EN0),
.EN1 (EN1),
.EN2 (EN2),
.EN3 (EN3)
);
//-------------------------- tb --------------------------
initial begin
rst_n <= 0;
#(100);
rst_n <= 1;
AD_sim(16'd123);
AD_sim(16'd54321);
AD_sim(16'd2468);
#(1000);
$stop;
end
//---------------------- 模拟一次AD采样 ---------------------
task AD_sim;
input [15:0] data;
integer i;
begin
wait(CNV_p); //CNV+上升沿开始转换
//#(200); //转换时间200ns
D_p <= data[15];
D_n <= ~data[15];
for(i=1; i<=16; i=i+1) begin
wait(CLK_p);
#(2); //模拟DCO相对于CLK的延时,2ns
DCO_p <= 1; //回声时钟DCO+-
DCO_n <= 0;
wait(~CLK_p);
#(2);
DCO_p <= 0;
DCO_n <= 1;
if(i<16) begin
D_p <= data[15-i]; //DCO+-下降沿改变D+-
D_n <= ~data[15-i];
end
else begin
D_p <= 0; //输出完毕,D+-拉到0
D_n <= 1;
end
end
end
endtask
endmodule
综合后时序仿真如下: