LVDS(Low-Voltage Differential Signaling)低电压差分信号,是一种低功耗、低误码率、低串扰和低辐射的差分信号技术。
KSPS(Kilo Samples per Second),采样千次每秒,是转化速率的单位。
逐次逼近型ADC由比较器包括n位逐次比较型AD转换器,它由控制逻辑电路、时序产生器、移位寄存器、DA转换器及电压比较器组成。
逐次逼近转换过程和用天平秤重相似,从最重的开始试放与被秤物体进行比较,若物体重于砝码,则该砝码保留,否则移去。再加上第二个次重砝码,由物体的重量是否大于砝码的重量决定第二个砝码是否保留。
逐次逼近AD转换器,就是将输入模拟信号与不同的参考电压作多次比较,使转换所得的数字量在数值上逐次逼近模拟量对应值。如上图,它由脉冲启动后,在第一个时钟脉冲作用下,控制电路使时序产生器的最高位置1,其他位置0,其输出数据寄存器将100…000送到DA转换器,输入电压首先与D/A器输出电压(VREF/2)进行比较,如果V1 ≥ (VREF/2),比较器输出为1,否则为0.比较结果存于数据的寄存器的Dn-1位。然后在第二个时钟脉冲(CP)下,移位寄存器的次高位置1,其他低位置0. 如最高位已存1,则此时vO=(3/4)VREF。于是v1再与(3/4)VREF相比较,如v1≥(3/4)VREF,则次高位Dn-2存1,否则Dn-2=0;如最高位为0,则vO=VREF/4,与vO比较,如v1≥VREF/4,则Dn-2位存1,否则存0……。以此类推,逐次比较得到输出数字量。
基准电压VREF。
举例如下:电路为8位AD转换器,输入模拟量vA=6.84v,DA转换器基准电压VREF=10v。当启动脉冲低电平到来后转换开始,在第一个CP作用下,数据寄存器将D7~D0=10000000送入D/A转换器,其输出电压v0=5V,vA与v0比较,vA>v0存1;第二个CP到来时,寄存器输出D7~D0=11000000,v0为7.5V,vA再与7.5V比较,因vA<7.5V,所以D6存0;输入第三个CP时,D7~D0=10100000,v0=6.25V;vA再与v0比较,……如此重复比较下去,经8个时钟周期,转换结束。由图中v0的波形可见,在逐次比较过程中,与输出数字量对应的模拟电压v0逐渐逼近vA值,最后得到A/D转换器转换结果D7~D0为10101111。该数字量所对应的模拟电压为6.8359375V,与实际输入的模拟电压6.84V 的相对误差仅为 0.06%
本实验是打算采用AD7928这个数据采集设备,这是一个12位高速、低功耗、8通道逐次逼近型ADC。采用单电源工作,电源电压为2.7v~5.25v,最高吞吐速率可以达到1Msps。内置有低噪声、款带宽采样保持放大器,可以处理8Mhz以上的输入频率。
通过配置控制寄存器,器件的模拟输入范围可以在0v到REFin或0v到2REFin间选择,可采用标准二进制或者二进制补码输出编码。AD7928具有八个通道序列器的单端模拟输入,可以通过预先编程选择通道转换顺序。
AD7928的最小输入频率是50Khz。
AD7928的转换时间取决于SCLK频率,该频率同时用作转换控制的主时钟。
灵活的功耗/串行时钟管理。转换速率取决于串行时钟,通过提高串行时钟还可以缩短转换时间。这些器件还提供多种关断模式,可以在较低吞吐量下实现最高功效,完全关断时最大功耗是0.5ua。
时序规格
控制寄存器:
序列选择:
但是现在又有个问题,那就是,是否选择了这个SEQ=1,SHADOW=1的模式,从0到指定的地址,会循环起来呢?
模拟输入选择
8个模拟输入通道中的任何一个都可以选择用来进行转换,方法是控制寄存器中的地址位ADD2至ADD0对多路复用器进行编程。还可配置为在选定的多个通道间自动循环。通过控制寄存器中的SEQ和SHADOW位访问序列器功能。
通过阅读手册第20页,发现有三种方式可以解决上述的问题。
(1)、设置SEQ=0,SHADOW=1,这种方式然后再对SHADOW寄存器进行配置。
(2)、设置SEQ=1,SHADOW=1.也可以实现通道循环,且不需要对对SHADOW寄存器进行配置。但是这个配置必须是从通道0到指定通道依次递增,然后再从0开始。但是WRITE必须是低电平。如果是高电平,那就必须再将SEQ设为1,SHADOW设为0。
(3)、因为上一次转换输出的时候会把下一次转换的通道的地址给出,从而设计可以让8个通道循环起来,这个方法设计出来只要按照时序要求,就基本不会出现错误
目前存在的问题是,这只是8通道的AD采集,但是实验需要用到32个通道,怎么去用这一个AD采集模块去完成4次8通道AD采集???
同时使用8个通道只需要对控制寄存器的参数进行输入配置,即可让8个通道循环使用。
AD7928的数据输出包括一个前置0和3个地址位(表示转换结果对应的的通道),然后就是12个转换数据位(MSB)优先。输出编码方式可选择标准二进制或二进制补码,通过控制寄存器中的CODING位选择。
针对上述的问题,准备在使用这个AD7928之前先给那32路的信号源加上4个八选一的模拟芯片,这样AD7928这个模块就只需要四个通道就可以了,针对这四个通道进行循环。这样的话,FPGA不光要给AD7928和八选一芯片加上控制。max4581芯片(模拟开关)
我想想该怎么加控制?
把这个AD7928的通道选择为四个,这四个通道进行循环。每个通道传输来自一个八选一模块的输出,然后八路数据在这个通道里面依次传输。
这个代码是单纯的启动AD7928八个通道连续循环采集数据。
module ad7928_ctrl32(
input clk,
input rst_n,
//spi
input adc_dout,
output reg adc_cs,
output reg adc_sclk,
output reg adc_din,
//others
output reg dout_vld, //每个转换结束输出一个标志位
output [2:0] adc_sel, //控制8选1
output reg [18:0] dout,
output reg ad_change_flag //每组(即32个PD)结束之后输出一个标志位
);
parameter WRITE = 1'b1;
parameter PM = 2'b11;
parameter SEQ =1'b0;
parameter SHADOW = 1'b0;
parameter RANGE = 1'b0;
parameter CODING = 1'b1;
//先设计一个时钟频率为1Mhz的,那么周期就是1000ns
reg [5:0] cnt0;
wire add_cnt0 = 1'b1;
wire end_cnt0 = add_cnt0 && cnt0==6'd49;
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt0 <= 6'd0;
end
else if(add_cnt0) begin
if(end_cnt0) begin
cnt0 <= 6'd0;
end
else begin
cnt0 <= cnt0 + 1'b1;
end
end
end
//每次采集16位数据,并且cs的上升沿与下降沿之间最少50ns
reg [4:0] cnt1;
wire add_cnt1 = end_cnt0;
wire end_cnt1 = add_cnt1 && cnt1==5'd17;
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt1 <= 5'b0;
end
else if(add_cnt1) begin
if(end_cnt1) begin
cnt1 <= 5'b0;
end
else begin
cnt1 <= cnt1 + 1'b1;
end
end
end
//切换四个通道
reg [2:0] cnt2;
wire add_cnt2 = end_cnt1;
wire end_cnt2 = add_cnt2 && cnt2 == 3'd3;
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt2 <= 3'b0;
end
else if(add_cnt2) begin
if(end_cnt2) begin
cnt2 <= 3'b0;
end
else begin
cnt2 <= cnt2 + 1'b1;
end
end
end
//切换那4个8选1通道
reg [2:0] cnt3;
wire add_cnt3 = end_cnt2;
wire end_cnt3 = add_cnt3 && cnt3 == 3'd7;
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt3 <= 3'b0;
end
else if(add_cnt3) begin
if(end_cnt3) begin
cnt3 <= 3'b0;
end
else begin
cnt3 <= cnt3 + 1'b1;
end
end
end
//产生adc_cs
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
adc_cs <= 1'b1;
end
else if(add_cnt0 && cnt0==6'd24 && cnt1==5'd0) begin
adc_cs <= 1'b0;
end
else if(add_cnt0 && cnt0==6'd24 && cnt1==5'd17) begin
adc_cs <= 1'b1;
end
end
//产生adc_sclk
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
adc_sclk <= 1'b1;
end
else if(add_cnt0 && cnt0==6'd24 && cnt1>=5'd1 && cnt1<5'd17) begin
adc_sclk <= 1'b0;
end
else if(end_cnt0) begin
adc_sclk <= 1'b1;
end
end
//给ADC7928输入控制寄存器
wire [15:0] data;
assign data = {WRITE,SEQ,1'b0,cnt2,PM,SHADOW,1'b0,RANGE,CODING,4'b0};
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
adc_din <= 0;
end
else if(end_cnt0 && cnt1<16) begin
adc_din <= data[15-cnt1];
end
end
//输出ADC7928的16位数据
reg [15:0] tmp_dout;
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
tmp_dout <= 16'd0;
end
else if(add_cnt0 && cnt0==6'd24 && cnt1>=1 && cnt1 <17) begin
tmp_dout[16-cnt1] <= adc_dout;
end
end
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
dout <= 19'd0;
end
else if(cnt1==5'd16)begin
dout <= {cnt3,tmp_dout};
end
end
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
ad_change_flag <= 1'b0;
end
else if(cnt1==5'd16 && end_cnt3) begin
ad_change_flag <= 1'b1;
end
else begin
ad_change_flag <= 1'b0;
end
end
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
dout_vld <= 1'b0;
end
else if(add_cnt0 && cnt0==6'd25 && cnt1==5'd16)begin
dout_vld <= 1'b1;
end
else if(add_cnt0 && cnt0==6'd24 && cnt1==5'd16) begin
dout_vld <= 1'b0;
end
end
assign adc_sel = cnt3;
endmodule
这个代码仿真过了,没错,具体还得下板子
后面有机会再试试其他方法,设计这个循环通道的代码