FPGA 29 ADC(数据采集)模数字转换驱动设计

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t93Mpr2Y-1627401356069)(FPGA 09 阻塞和非阻塞状态的理解.assets/test.png)]

FPGA 29 ADC(数据采集)模数字转换驱动设计

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wfZPS8Im-1629962218562)(img/blog_img/fpga/image-20210826112214548.png)]

模块名称 : adc_128s022 模数驱动模块设计

主要功能 :本实验设计了adc_128s022 数模驱动芯片,通过在ADC_DIN输出端发送配置数据来完成芯片通道的配置和数字电压信号的传输,ADC_DOUT 来获取信号的输入,最终内部转换得到输出采集的数字信号。

设计流程:我们在这次实验中,在内部通过CLK的时钟分频,来输出时钟ADC_SCLK信号,根据ADC_SCLK 的时钟,通过ADC_DIN 来发送通道的配置引脚,随后ADC_DOUT 来接收模拟信号的采样数据点。最终完成在内部进行转换得到电压。通过该时钟,我们进而来设计线性序列机(可以理解一种比较特殊的状态机)来编写ADC模数芯片的SPI的时序.

AD128S022芯片的线性序列逻辑:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xZcSzq4o-1629962218565)(img/blog_img/fpga/image-20210818230658359.png)]

12位分辨率

CS_N : 片选信号,低电平有效

SCLK : 时钟

DIN(FPGA输出) : 信号输入引脚,SCLK 上升沿采样输入,【对于FPGA,需要下降沿改变输出的数据】

DOUT(对应FPGA输入): 转换结构输出脚,SCLK下降沿输出,【对于FPGA,还是上升沿读取】。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bxx8H4DV-1629962218566)(FPGA 29 ADC 模数转换驱动模块.assets/image-20210323184039234.png)]

模块实现:

数据手册的基本信息:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7OBLtMDe-1629962218568)(img/blog_img/fpga/image-20210826145701649.png)]

ksps (kilo samples per second) : 每秒多少K次采样。

500ksps -1Msps : 表示的是采样速率是,500k采样/1s - 1000k采样/1s.

功能描述:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OLHu0Jqo-1629962218571)(img/blog_img/fpga/image-20210826142533908.png)]

注:这里主要关注的是一些关于dac的位数,工作电压,支持的采样速率,低功耗模式等较为重要的信息。

这个是内部电路给出的时序操作图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C7gFHsug-1629962218571)(img/blog_img/fpga/image-20210826144917803.png)]

实现过程可以看到,在CS片选信号拉低以后,我们将以SCLK的下降来进行计数,DIN在第四个下降沿到来时,开始连续的发送3个采样的信道地址ADDR[2:0]。同时我们也统计上升沿的个数,我们在SCLK的第5个上升沿开始接收采样的数据DATA[11:0],统计16个上升沿数据。随后完成一次数据的采集。

在上述图片中,描述的是一个连续的信号采集过程,一次完整的信号采集一共有16个上升沿和16个下降沿,我们以下降作为一次的开始(实际的情况也是下降沿作为信号采集的起始信号)计数,一共有16个时钟周期。随后又是16个时钟周期开始执行相同的操作。

数据典型电路:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VwR2goVH-1629962218572)(img/blog_img/fpga/image-20210826145126072.png)]

布局布线设计电路:
在这里插入图片描述

ad128s022.v 文件

module ad128s022
(
			Clk,
			Rst_n,
			
			Channel,
			Data,
			
			En_Conv,
			Conv_Done,
			ADC_State,
			DIV_PARAM,
			
			ADC_SCLK,
			ADC_DOUT,
			ADC_DIN,
			ADC_CS_N	
);

			input Clk;
			input Rst_n;
			
			input  [2:0]Channel;
			output reg [11:0]Data;
			
			input En_Conv;
			output reg Conv_Done;
			output ADC_State;
			input [7:0]DIV_PARAM;
			
			output reg ADC_SCLK;
			input  ADC_DOUT;
			output reg ADC_DIN;
			output reg ADC_CS_N;	

			
			reg en ;
			reg [2:0]r_Channel;
			reg [7:0]DIV_CNT ;
			reg  SCLK2X ;
			reg [5:0]SCLK_GEN_CNT;
			reg [11:0]r_Data;
			

			
			
			//  en handle 
			always@(posedge Clk or negedge Rst_n)
			if(!Rst_n)
				en <= 1'b0;
			else if(En_Conv)
				en <= 1'b1;
			else if(Conv_Done)
				en <= 1'b0 ;
			else
				en <= en ;
			
			

			// r_Channel  we set En_Conv is a plus signal 
			always@(posedge Clk or negedge Rst_n)
			if(!Rst_n)
				r_Channel <= 3'd0;
			else if(En_Conv)
				r_Channel <= Channel;
			else
				r_Channel <= r_Channel;
			
			// 时钟分频脉冲计数
			// reg [7:0]DIV_CNT ;
			always@(posedge Clk or negedge Rst_n)
			if(!Rst_n)
				DIV_CNT <= 8'd0 ;
			else if(en)
				begin
				if(DIV_CNT == (DIV_PARAM -1) )
					DIV_CNT <= 8'd0 ;
				else	
					DIV_CNT <= DIV_CNT + 1'd1 ;
				end
			else	
				DIV_CNT <= 8'd0 ;
			
			// SCLK2X plus
			// reg  SCLK2X
			always@(posedge Clk or negedge Rst_n)
			if(!Rst_n)
				SCLK2X <= 1'b0;
			else if(DIV_CNT == (DIV_PARAM -1) )
				SCLK2X <= 1'b1 ;
			else
				SCLK2X <= 1'b0;
				
			// reg [5:0]SCLK_GEN_CNT 
			always@(posedge Clk or negedge Rst_n)
			if(!Rst_n)
				SCLK_GEN_CNT  <= 6'd0 ;
			else if(en && SCLK2X)
				begin
				if(SCLK_GEN_CNT == 6'd33)
					SCLK_GEN_CNT <=6'd0;
				else
					SCLK_GEN_CNT <= SCLK_GEN_CNT + 1'b1 ;
				end
			else
				SCLK_GEN_CNT <= SCLK_GEN_CNT ;
			//
			
			/*
			output ADC_SCLK
			input  ADC_DOUT;
			output ADC_DIN;
			output reg ADC_CS_N;	
			reg [11:0]r_Data;
			*/
			
			always@(posedge Clk or negedge Rst_n)
			if(!Rst_n)
				begin
				ADC_SCLK <= 1'b1;
				ADC_DIN  <= 1'b0;
				ADC_CS_N  <= 1'b1;
				end
			else if(en && SCLK2X)
				begin
				case(SCLK_GEN_CNT)
				0: begin ADC_CS_N <= 1'b0; ADC_SCLK <= 1'b1; ADC_DIN <= 1'b0; end
				1,3: begin ADC_SCLK <= 1'b0; end
				2,4,6,8:  begin ADC_SCLK <= 1'b1 ; end
				5:  begin ADC_SCLK <= 1'b0; ADC_DIN <= r_Channel[2]; end
				7:  begin ADC_SCLK <= 1'b0; ADC_DIN <= r_Channel[1]; end
				9:  begin ADC_SCLK <= 1'b0; ADC_DIN <= r_Channel[0]; end
				10: begin ADC_SCLK <= 1'b1; r_Data[11] <= ADC_DOUT ; end
				12: begin ADC_SCLK <= 1'b1; r_Data[10] <= ADC_DOUT ; end
				14: begin ADC_SCLK <= 1'b1; r_Data[9] <= ADC_DOUT ; end
				16: begin ADC_SCLK <= 1'b1; r_Data[8] <= ADC_DOUT ; end
				18: begin ADC_SCLK <= 1'b1; r_Data[7] <= ADC_DOUT ; end
				20: begin ADC_SCLK <= 1'b1; r_Data[6] <= ADC_DOUT ; end
				22: begin ADC_SCLK <= 1'b1; r_Data[5] <= ADC_DOUT ; end
				24: begin ADC_SCLK <= 1'b1; r_Data[4] <= ADC_DOUT ; end
				26: begin ADC_SCLK <= 1'b1; r_Data[3] <= ADC_DOUT ; end
				28: begin ADC_SCLK <= 1'b1; r_Data[2] <= ADC_DOUT ; end
				30: begin ADC_SCLK <= 1'b1; r_Data[1] <= ADC_DOUT ; end
				32: begin ADC_SCLK <= 1'b1; r_Data[0] <= ADC_DOUT ; end
				11,13,15,17,19,21,23,25,27,29,31: begin ADC_SCLK <= 1'b0;  end
				33: begin ADC_CS_N <= 1'b1; end
				default: ;
				endcase
				end	
			
		// output [11:0]Data 
		always@(posedge Clk or negedge Rst_n)
		if(!Rst_n)
			Data  <= 12'd0 ;
		else if(en && SCLK2X && (SCLK_GEN_CNT == 6'd33))
			Data <= r_Data ;
		else
			Data <= Data ;
	
		// conv_Done signal
		always@(posedge Clk or negedge Rst_n)
		if(!Rst_n)
			Conv_Done  <= 1'd0 ;
		else if(en && SCLK2X && (SCLK_GEN_CNT == 6'd33))
			Conv_Done <= 1'd1 ;
		else
			Conv_Done <= 1'd0 ;	
		
		//产生ADC工作状态指示信号,保持和CS片选信号同步即可
		assign ADC_State = ADC_CS_N ; 
			
endmodule





注(这个是用于分析时序的时钟问题): SCLK2X 和 DIV_PARAM 这两个信号,DIV_PARAM 是一个输入分频信号,可以看到:

情况1:

假设DIV_PARAM =2时,

SCLK2X = Clk/DIV_PARAM = 50Mhz/2=25Mhz 的【周期脉冲信号,非标准方波信号】。

这样的话,ADC_SCLK输出的时钟是 = SCLK2X/2 =25Mhz/2 =12.5Mhz 的时钟。

情况2:

假设DIV_PARAM =4时,

SCLK2X = Clk/DIV_PARAM = 50Mhz/4=12.5Mhz 的【周期脉冲信号,非标准方波信号】

这样的话,ADC_SCLK输出的时钟是 = SCLK2X/2 =25Mhz/2 =6.25Mhz 的时钟。

这里非常需要了解输出SCLK 的最终的时钟输出。

SCL2X:这个是来统计上升沿和下降沿的计数。

  • 3
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
FPGA与高速ADC接口是数字信号处理系统中非常重要的一部分,它们可以帮助我们实现高速数据采集数字信号处理、通信等功能。其中,ADC数据采集的核心,采样率的高低决定了系统的性能,因此如何有效地与高速ADC接口是一个非常关键的问题。 本文将介绍一种基于Xilinx FPGA与高速ADC接口的实现方案,以250MSPS采样率的ADC9481为例。具体实现过程如下: 1.硬件连接 首先需要将ADC9481与FPGA进行连接。ADC9481有两组LVDS输出,每组包含14位数据和1位时钟。因此需要使用一对LVDS差分信号来传输一组数据和时钟信号,共需要8对LVDS差分信号。 2.时钟配置 ADC的采样率由时钟信号控制,因此需要配置FPGA的时钟使其与ADC时钟同步。ADC9481的时钟频率最高可达500MHz,一般使用LVPECL时钟驱动器来提供时钟信号。 在FPGA端,需要将时钟信号通过BUFG(全局缓存)引脚输入到FPGA的时钟管理单元(MMCM)中,使用MMCM生成与ADC时钟同步的本地时钟信号。 3.数据接收 ADC的数据输出是14位的差分数据,需要通过FPGA的差分输入接口进行接收。在FPGA端,可以使用选择器和寄存器来对数据进行处理和存储。选择器可以选择要写入哪个寄存器,而寄存器则用于存储ADC的采样数据。在这个过程中,需要注意选择器和寄存器的延迟时间,确保数据正常存储。 4.数据处理 ADC采样数据的处理包括去偏置、解码、滤波等操作。其中,去偏置是为了消除ADC的直流偏置,解码是将ADC的输出数据转换成相应的数字量,滤波则是为了去除高频噪声。 在FPGA端,可以使用DSP48E1块进行数据处理。DSP48E1块是Xilinx FPGA中的专用数字信号处理块,它可以进行加、减、乘、除、滤波等操作。在这个过程中,需要注意DSP48E1块的使用方法和相关参数的设置。 5.数据存储 最后,需要将处理后的数据存储到内存中。在FPGA端,可以使用Block RAM(BRAM)或FIFO等存储器来存储数据。其中,BRAM是单端口存储器,适用于小型数据存储;而FIFO是双端口存储器,适用于大型数据存储。 在存储数据时,需要注意存储器的读写时序和容量,确保数据能够正常存储。 通过以上实现步骤,就可以与高速ADC接口进行有效的数据采集和处理。当然,具体实现过程可能因硬件设备和应用场景而有所不同,需要根据实际情况进行调整和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值