FPGA控制AD7606进行数据采集

目录

一、AD7606数据手册

     1.ADC采样原理

     2.AD7606使用手册

二、实例

     1.状态转移图

     2.Verilog代码

     3.仿真结果

总结



一、AD7606数据手册

     1.ADC采样原理

        在实际的工程中,经前端传感器出来的信号基本都是模拟信号,而后端mcu主控芯片是基于数字信号进行处理的,因此需要用到ADC进行模数转换。ADC包括三个基本功能:抽样量化编码。抽样过程是将模拟信号在时间上离散化,使之成为抽样信号;量化是将抽样信号的幅度离散化使之成为数字信号;而编码则是将数字信号转换成数字系统所能接受的形式。如何实现这三个功能就决定了ADC的形式和性能。同时,ADC的分辨率(即采样精度)越高,需要的转换时间就越长,转换速度就越低,故ADC的分辨率和转换速率两者总是相互制约的。

       对于一个16位的ADC来说,如果输入的模拟信号幅值在0到3.3V,那么采样后输出的数字信号对应的应该在0到65535,采样精度是1/65536。因此,我们发现,采样位数越高的AD,其采样精度越高,信噪比越大,其越接近原始信号。

      采样率即是两个采样点之间的间隔时间,两个采样点之间的间隔时间越短,其采样率越高。根据奈奎斯特采样定律,信号的采样率应该大于其最大频率的2倍。而实际应用中,通常选择信号最大频率的几十倍的采样率,为了更加准确有效的恢复出原始信号。

     2.AD7606使用手册

         AD7606是16位,8通道同步采集模数数据采集系统。各器件均内置模拟输入钳位保护、二阶抗混叠滤波器、跟踪保持放大器、16位电荷再分配逐次逼近型模数转换器、灵活的数字滤波器、2.5V基准电压源、基准电压缓冲以及高速串行和并行接口。

       AD7606采用5V单电源供电,可以处理正负10V和正负5V真双极性输入信号,同时所有通道均能以高达200kSPS的吞吐速率采样。输入钳位保护电路可以耐受最高达正负16.5V的电压。无论以何种采样频率工作,  AD7606的模拟输入阻抗均为1MΩ。它采用单电源工作方式,具有片内滤波和高输入阻抗,因此无需驱动运算放大器和外部双极性电源。AD7606抗混叠滤波器的3dB截止频率22kHz;当采样率为200kSPS时,它具有40dB抗混叠抑制特性。灵活的数字滤波器采用引脚驱动,可以改善信噪比(SNR),并降低3dB带宽。

       下面给出AD7606引脚配置和功能描述,下图是AD7606的引脚图:

       下图给出AD7606的引脚功能描述:

       其中,AD7606可以选择三种工作模式,通过配置引脚PAR/SER/BYTE SEL和DB15,如下图所示:

       上述三种工作模式都需要首先给AD7606一个转换信号,如果是各通道同步采样,则需要同时给出CONVST_A和CONVST_B信号,然后等待BUSY信号,如果选择等待AD转换后读取,则需要等待BUSY信号拉低后再进行数据读取;如果选择在AD转换期间读取,则不需要等待BUSY信号拉低。下面给出两种不同时刻读取数据的时序图:

        在读取数据时,需要首先给AD7606一个读取数据的使能信号CS,接着给出读取数据的时钟信号RD/SCLK,然后AD7606给出相应的数据。

        如果工作在并行接口模式,在CS信号拉低后,RD给出8个时钟,分别读取8个通道采集到的数据DATA[15:0],在读取通道1时FRSTDATA信号拉高,时序图如下所示:

         如果工作在串行接口模式,在CS信号拉低后,SCLK给出16*4=64个时钟,分别读取DOUTA输出的采集到的通道1~4上的数据和DOUTB上输出的采集到的通道5~8上的数据,在读取通道1时,FRSTDATA信号拉高时序图如下所示:

        

         如果工作在并行字节接口模式,在CS信号拉低后,RD给出2*8=16个时钟,两个时钟为一组,分别读取某一通道采集到的数据的高八位和低八位,数据从DATA[7:0]输出,时序图如下所示:

   下表给出上述三种模式对应的时序规格:

 

 

 

 

二、实例

       要求:通过AD7606同步采集八个通道的数据,FPGA按照串行读取的操作,分别读取各通道采集到的数据。

     1.状态转移图

           共定义九个状态进行转换,通过输入caiji_flag标志信号开始每次的数据采集,当数据采集结束后,输出caiji_over结束信号。

           2.Verilog代码

module ad7606_1(
    input        clk,
	input        res,
	input        dout_a,     //ad7606通道a输入串行读取数据
	input        dout_b,    //ad7606通道b输入串行读取数据
	input        ad_busy,   //ad7606输入busy信号
	input        caiji_flag,  //外界输入开始数据采集的标志
	
	output   reg ad_cs,     //读ad7606的使能信号
	output   reg ad_rd,     //读ad7606的时钟信号
	output   reg ad_convstab  //ad7606开始转换信号,低电平有效
   

);

parameter     IDLE         = 9'b0_0000_0001;
parameter     AD_CONV      = 9'b0_0000_0010;
parameter     WAIT_1       = 9'b0_0000_0100;
parameter     WAIT_BUSY    = 9'b0_0000_1000;
parameter     READ_CH15    = 9'b0_0001_0000;
parameter     READ_CH26    = 9'b0_0010_0000;
parameter     READ_CH37    = 9'b0_0100_0000;
parameter     READ_CH48    = 9'b0_1000_0000;
parameter     READ_STOP    = 9'b1_0000_0000; 

parameter     CONV_TIME         =  5; //AD_CONV状态持续时间等价转换信号低电平持续时间
parameter     WAIT_TIME         =  5; //WAIT_1状态持续时间
parameter     FIV_CLK           =  4;
parameter     CNT_CLK           =  16;   //每个通道一次发送的时钟个数

reg           [8:0]stata;
reg           [5:0]cnt  ;   //在数据读取状态表示分频系数
reg           [4:0]cnt_bite;

reg           [15:0]databuffa;  //a通道串并转换后存储
reg           [15:0]databuffb;  //b通道串并转换后存储
//存储八个通道采集到的数据
reg           [15:0] data_ch1;
reg           [15:0] data_ch2;
reg           [15:0] data_ch3;
reg           [15:0] data_ch4;
reg           [15:0] data_ch5;
reg           [15:0] data_ch6;
reg           [15:0] data_ch7;
reg           [15:0] data_ch8;
reg           [2 :0] over;
wire               caiji_over;



always@(posedge clk or negedge res)
    if(!res)
	   cnt <= 'd0;
	else if(stata == AD_CONV)
	   if(cnt == CONV_TIME)
	      cnt <= 'd0;
	   else 
	      cnt <= cnt + 1;
	else if(stata == WAIT_1)
	   if(cnt == WAIT_TIME)
	      cnt <= 'd0;
	   else  
	      cnt <= cnt + 1;
    else if(stata == WAIT_BUSY)  //为了在下一个状态,cnt从6开始计数,为了让CS和RD信号前后有延时
	   if(ad_busy == 1'b0)
		  cnt <= 'd6;
	   else 
	      cnt <= cnt;
    else if(stata == READ_CH15 || stata == READ_CH26 || stata == READ_CH37 || stata == READ_CH48)
       if(cnt == (2*FIV_CLK-1))
	      cnt <= 'd0;
	   else
          cnt <= cnt + 1;

always@(posedge clk or negedge res)
    if(!res)
	   cnt_bite <=  'd0;
	else if(stata == READ_CH15 )
	   if(cnt_bite == (CNT_CLK) && cnt == (2*FIV_CLK-1))
	      cnt_bite <= 'd0;
	   else if(cnt == (2*FIV_CLK-1))
	      cnt_bite <= cnt_bite + 1;
	   else 
	      cnt_bite <= cnt_bite;
	else if( stata == READ_CH26 || stata == READ_CH37 || stata == READ_CH48)
	   if(cnt_bite == (CNT_CLK -1) && cnt == (2*FIV_CLK-1))
	      cnt_bite <= 'd0;
	   else if(cnt == (2*FIV_CLK-1))
	      cnt_bite <= cnt_bite + 1;
	   else 
	      cnt_bite <= cnt_bite;
    else 
	   cnt_bite <= 'd0;
	
		  
always@(posedge clk or negedge res)
    if(!res)
	   stata <= 4'd0;
	else begin
	   case(stata)
		  IDLE         :  if(caiji_flag == 1'b1)
		                      stata <= AD_CONV;
						  else 
						      stata <= IDLE;
		  AD_CONV      :  if(cnt == CONV_TIME)
		                      stata <= WAIT_1;
						  else 
						      stata <= AD_CONV;
		  WAIT_1       :  if(cnt == WAIT_1)
		                      stata <= WAIT_BUSY;
						  else 
						      stata <= WAIT_1;
		  WAIT_BUSY    :  if(ad_busy == 1'b0)
		                      stata <= READ_CH15;
						  else
                              stata <= WAIT_BUSY;						  
		  READ_CH15    :  if(cnt_bite == (CNT_CLK ) && cnt == (2*FIV_CLK-1))
		                      stata <= READ_CH26;
						  else 
						      stata <= READ_CH15;
							  
		  READ_CH26    :  if(cnt_bite == (CNT_CLK -1) && cnt == (2*FIV_CLK-1))
		                      stata <= READ_CH37;
						  else 
						      stata <= READ_CH26;
		  READ_CH37    :  if(cnt_bite == (CNT_CLK -1) && cnt == (2*FIV_CLK-1))
		                      stata <= READ_CH48;
						  else 
						      stata <= READ_CH37;
		  READ_CH48    :  if(cnt_bite == (CNT_CLK -1) && cnt == (2*FIV_CLK-1))
		                      stata <= READ_STOP;
						  else 
						      stata <= READ_CH48;
		  READ_STOP    :  stata <= IDLE;
		  default:stata <= IDLE;
	   endcase
	end 

//输出信号
always@(posedge clk or negedge res)
    if(!res)
       ad_convstab <=  1'b1; 
	else if(stata == AD_CONV)
	   ad_convstab <=  1'b0;
	else 
	   ad_convstab <= 1'b1;

always@(posedge clk or negedge res)
    if(!res)
	  ad_cs <= 1'b1;
	else if(stata == WAIT_BUSY && ad_busy == 1'b0)
	  ad_cs <= 1'b0;
	else if(stata == READ_STOP)
	  ad_cs <= 1'b1;
	else 
	  ad_cs <= ad_cs;
always@(posedge clk or negedge res)
    if(!res)
	  ad_rd <= 1'b1;
	else if(stata == READ_CH15 || stata == READ_CH26 || stata == READ_CH37 || stata == READ_CH48)
      if(cnt == (FIV_CLK-1))
	    ad_rd <= 1'b0;
	  else if(cnt == 2*FIV_CLK-1)
	    ad_rd <= 1'b1;
	  else 
	    ad_rd <= ad_rd;
	else 
	  ad_rd <= 1'b1;

//处理输入的串行数据
always@(posedge clk or negedge res)
    if(!res)begin
	   databuffa <= 16'd0;
	   databuffb <= 16'd0;
    end 
	else if(stata == READ_CH15 || stata == READ_CH26 || stata == READ_CH37 || stata == READ_CH48)begin
	   if(cnt == FIV_CLK && ad_rd == 1'b0)begin
	       databuffa <= {databuffa[14:0],dout_a};
		   databuffb <= {databuffb[14:0],dout_b};
	   end 
	   else begin
	       databuffa <= databuffa;
		   databuffb <= databuffb; 
	   end       
	end 
	else begin
	   databuffa <= 16'd0;
	   databuffb <= 16'd0;   
	end 
	
//将采集到的数据存储到data_ch1-data_ch8中      	
always@(posedge clk or negedge res)
    if(!res)begin
	    data_ch1   <=   16'd0;
	    data_ch2   <=   16'd0;
	    data_ch3   <=   16'd0;
	    data_ch4   <=   16'd0;
	    data_ch5   <=   16'd0;
	    data_ch6   <=   16'd0;
	    data_ch7   <=   16'd0;
	    data_ch8   <=   16'd0;
	end 
	else if(stata == READ_CH15 && cnt_bite == (CNT_CLK) && cnt == (2*FIV_CLK-1))begin
	    data_ch1   <= databuffa;
		data_ch5   <= databuffb;
	end 
    else if(stata == READ_CH26 && cnt_bite == (CNT_CLK -1) && cnt == (2*FIV_CLK-1))begin
	    data_ch2   <= databuffa;
		data_ch6   <= databuffb;
	end
	else if(stata == READ_CH37 && cnt_bite == (CNT_CLK -1) && cnt == (2*FIV_CLK-1))begin
	    data_ch3   <= databuffa;
		data_ch7   <= databuffb;
	end
	else if(stata == READ_CH48 && cnt_bite == (CNT_CLK -1) && cnt == (2*FIV_CLK-1))begin
	    data_ch4   <= databuffa;
		data_ch8   <= databuffb;
	end
	else begin
	    data_ch1   <=   data_ch1 ;
	    data_ch2   <=   data_ch2 ;
	    data_ch3   <=   data_ch3 ;
	    data_ch4   <=   data_ch4 ;
	    data_ch5   <=   data_ch5 ;
	    data_ch6   <=   data_ch6 ;
	    data_ch7   <=   data_ch7 ;
	    data_ch8   <=   data_ch8 ;
	end 

//八个通道采集结束后,对over信号打拍处理,输出over_flag
assign caiji_over = over[2];	
always@(posedge clk or negedge res)
    if(!res)
       over <= 3'd0;
    else if(stata == READ_STOP)begin
       over[0] <= 1'b1;	
       over[1] <= over[0];
	   over[2] <= over[1];
	end
	else begin
       over[0] <= 1'b0;	
	   over[1] <= over[0];
	   over[2] <= over[1];	   
	end 	   
	   
	   
	   
	   
	   
	   
endmodule

下面给出tb测试文件:

`timescale 1ns/1ns
module tb1();
reg     clk;
reg     res;
reg     data_b;
reg     data_a;
reg     busy;
reg     caiji_flag;
wire    ad_cs;
wire    ad_rd;
wire    ad_convstab;
reg     ad_convstab_buffer;
wire    convstab_flag;




reg    [9:0]cnt;
initial begin
   clk <= 1'b0;
   res <= 1'b0;
   busy <= 1'b0;
   data_a <= 1'b0;
   data_b <= 1'b0;
   caiji_flag <= 1'b0;
   #100 res <= 1'b1;
   
end 
always #10 clk <= ~clk;

//检测ad_convstb的下降沿
always@(posedge clk)
    ad_convstab_buffer <= ad_convstab;
assign convstab_flag = (~ad_convstab)&ad_convstab_buffer;

always@(posedge clk or negedge res)
   if(!res)
      cnt <= 0;
   else if(convstab_flag)
      cnt <= cnt + 1;
   else if(cnt == 7)
      cnt <= cnt;
   else 
      cnt <= cnt + 1;
	  
always@(posedge clk)begin
   if(cnt >5 && cnt <8)begin
       caiji_flag <= 1'b1;
	   busy       <= 1'b0;
   end 
   else if(cnt == 8)begin
       caiji_flag <= 1'b0;
	   busy       <= 1'b0;
   end  
   else if(cnt>8 && cnt<20)begin
       busy   <= 1'b1;
	   caiji_flag <= 1'b0;
   end 
   else if(cnt == 20)begin
       busy   <= 1'b0;
	   caiji_flag <= 1'b0;
   end 
   else 
       busy  <=  busy;
end 
always@(posedge ad_rd or posedge ad_cs)
   if(ad_cs)begin
      data_a <= 1'b0;
	  data_b <= 1'b0;
   end 
   else begin
      data_a <= {$random}%2;
	  data_b <= {$random}%2;   
   end 
      

ad7606_1  u_ad7606_1( 
	.                   clk          (clk)    ,
	.                   res        (res)    ,
	.                   dout_a       (data_a)    ,             //ad7606 数据
	.                   dout_b       (data_b)    ,
	.                   ad_busy      (busy)    ,             //ad7606 busy
	.                   caiji_flag   (caiji_flag)    ,
                                     
	.                   ad_cs        (ad_cs)    ,                //ad7606 AD 片选
	.                   ad_rd        (ad_rd)    ,            //ad7606 AD 串行读数据时钟
	.                   ad_convstab  (ad_convstab)     //ad7606 AD convert start
);
endmodule

        3.仿真结果

           仿真结果如下图所示:图1给出了前五个状态的时序图,图2给出了后四个状态(数据采集)的时序图,图3给出了整体的时序仿真图。

                                                                图1 

                                                               图2

                                                              图3


总结

        总体而言,该芯片是一款性能不错的AD芯片,操作时序图相对比较简单,基本能够满足一般系统的性能要求。当然如果系统对数据采集的要求过高,即要求高信噪比,那么在AD选型的过程中可以考虑18位的AD(AD7690、CBM79AD60)或者是24位AD(AD1258)芯片;这里给大家推荐一下AD7690,18位的AD芯片,单通道数据采集,差分输入,串行读取AD数据,其操作时序十分简单,封装为MSOP-10,占用PCB空间较小。

       初次创作,难免文章中存在错误,希望读者能够及时纠正并给予私信,望大家共同进步!文章来源地址https://uudwc.com/A/ABNpg

原文地址:https://blog.csdn.net/weixin_40285117/article/details/123522465

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请联系站长进行投诉反馈,一经查实,立即删除!

h
上一篇 2023年07月25日 09:37
下一篇 2023年07月25日 09:38