基于FPGA的AD采集

LVDS(Low-Voltage Differential Signaling)低电压差分信号,是一种低功耗、低误码率、低串扰和低辐射的差分信号技术。
KSPS(Kilo Samples per Second),采样千次每秒,是转化速率的单位。

逐次逼近型ADC由比较器包括n位逐次比较型AD转换器,它由控制逻辑电路、时序产生器、移位寄存器、DA转换器及电压比较器组成。
基于FPGA的AD采集_第1张图片
逐次逼近转换过程和用天平秤重相似,从最重的开始试放与被秤物体进行比较,若物体重于砝码,则该砝码保留,否则移去。再加上第二个次重砝码,由物体的重量是否大于砝码的重量决定第二个砝码是否保留。
逐次逼近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%
基于FPGA的AD采集_第2张图片
基于FPGA的AD采集_第3张图片

本实验是打算采用AD7928这个数据采集设备,这是一个12位高速、低功耗、8通道逐次逼近型ADC。采用单电源工作,电源电压为2.7v~5.25v,最高吞吐速率可以达到1Msps。内置有低噪声、款带宽采样保持放大器,可以处理8Mhz以上的输入频率。

通过配置控制寄存器,器件的模拟输入范围可以在0v到REFin或0v到2REFin间选择,可采用标准二进制或者二进制补码输出编码。AD7928具有八个通道序列器的单端模拟输入,可以通过预先编程选择通道转换顺序。
在这里插入图片描述
AD7928的最小输入频率是50Khz。
在这里插入图片描述
AD7928的转换时间取决于SCLK频率,该频率同时用作转换控制的主时钟。
基于FPGA的AD采集_第4张图片
灵活的功耗/串行时钟管理。转换速率取决于串行时钟,通过提高串行时钟还可以缩短转换时间。这些器件还提供多种关断模式,可以在较低吞吐量下实现最高功效,完全关断时最大功耗是0.5ua。

在这里插入图片描述
VREF = 2.5v
Fclkmax = 20Mhz

时序规格
基于FPGA的AD采集_第5张图片
控制寄存器:
在这里插入图片描述
序列选择:
基于FPGA的AD采集_第6张图片
基于FPGA的AD采集_第7张图片
基于FPGA的AD采集_第8张图片
基于FPGA的AD采集_第9张图片
基于FPGA的AD采集_第10张图片
但是现在又有个问题,那就是,是否选择了这个SEQ=1,SHADOW=1的模式,从0到指定的地址,会循环起来呢?

基于FPGA的AD采集_第11张图片
模拟输入选择
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个通道循环使用。

基于FPGA的AD采集_第12张图片
基于FPGA的AD采集_第13张图片
AD7928的数据输出包括一个前置0和3个地址位(表示转换结果对应的的通道),然后就是12个转换数据位(MSB)优先。输出编码方式可选择标准二进制或二进制补码,通过控制寄存器中的CODING位选择。
基于FPGA的AD采集_第14张图片
基于FPGA的AD采集_第15张图片
基于FPGA的AD采集_第16张图片
基于FPGA的AD采集_第17张图片
针对上述的问题,准备在使用这个AD7928之前先给那32路的信号源加上4个八选一的模拟芯片,这样AD7928这个模块就只需要四个通道就可以了,针对这四个通道进行循环。这样的话,FPGA不光要给AD7928和八选一芯片加上控制。max4581芯片(模拟开关)
基于FPGA的AD采集_第18张图片
基于FPGA的AD采集_第19张图片

我想想该怎么加控制?
把这个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

这个代码仿真过了,没错,具体还得下板子
后面有机会再试试其他方法,设计这个循环通道的代码

你可能感兴趣的:(基于FPGA的AD采集)