FPGA AD7606串行驱动与并行驱动

发布于:2025-09-04 ⋅ 阅读:(22) ⋅ 点赞:(0)

AD7606是一个八通道16分辨率的adc,有两种测量范围5v和10v,每个通道采样率最高200ksps,支持多种驱动方案,最常用的有串行方案与并行方案,其中串行方案采用spi协议进行数据传输,可以在io引脚不够用的情况下采用,而并行方案采用16个io在一个采样边沿同时接收一次采样数据。

首先介绍ad7606的内部结构

内部主要部分有四个模块,模块1是在每个通道处添加了2阶巴特沃斯模拟低通滤波器,用来抗混叠,其截止频率受电压测量范围影响,当范围为5v时截止频率15khz,10v时23khz

因此在使用ad7606测量截止频率以上的信号时,需要在前方加入仪表放大器来放大信号,否则信号会被ad7606滤除

模块2用来控制复位、测量范围、通道转换,range为0时测量范围0~5v,1时测量范围0~10v,通道转换是指八个通道可分为两组,A组包含0~3通道,B组包含4~7通道,转换的意思就是在adc内部进行模拟量向数字量的转换,转换需要消耗一定的时间,而要指定那组通道转换则受convst信号影响,convst A信号拉高会让A组转换,convst B拉高会让B组转换,一般convst AB同时拉高。还需要注意的是busy信号为高表示adc正在转换中,convst信号可以看作是一个起始信号,用来通知adc进行下一次转换。frstdata表示这是采样的第一个数据,一般不需要管,悬空就行。

模块3是一个均值滤波器,受os0~os2三个信号的影响,os全程oversampling,即过采样,他的意思是比如os=3‘b001,那么就会进行连续2次模数转换,然后求均值作为输出,当os=3’b010则意味着连续4次模数转换后求均值,当然由于本质是一个低通fir滤波器,会进一步压缩模拟截止频率,也会降低采样率,在官方手册中如下,可以看到当os=110时过采样达到最大,就是连续进行64次模数转换后求均值输出,此时采样率只有3.125khz,5v范围下截止频率1.5khz,10v范围下截止频率1.5khz,但信噪比提升到了96.9db。

转换时间(busy拉高期间)随os升高不断升高

模块4是输出数据以及输出方式选择,PAR引脚为低时表示采用并行方案,为高表示串行方案。当采用串行方案时,两组通道A和B会同时通过spi协议输出,可以认为一个cs信号和一个sclk信号控制了两个miso通道。

时序图:

首先在上电后拉高复位,然后同时拉低convst ab,然后busy信号会拉高,等busy信号拉低后开始读取数据,串行读取如下,就是一个正常的spi流程,frstdata不用管,悬空即可

并行读取如下,cs拉低后,rd信号每拉高一次,data并行通道就会输出下一个采样通道的数据,等8个通道全读取后拉高cs,如果只想读取n个通道,那么可以提前拉高cs

串行代码:

需要注意的是,两组通道在串行中是同时输出的,即先输出a组的通道0和b组的通道4,然后输出a组通道1和b组通道5...而在并行中是按照通道0、通道1、通道2...这种顺序输出的

module ad7606_serial(
    input               i_clk   ,
    input               i_rst   ,
    input               i_en    ,
    input               i_din_a ,
    input               i_din_b ,
    input               i_busy  ,
    output  reg [15:0]  o_dout_0,
    output  reg [15:0]  o_dout_1,
    output  reg [15:0]  o_dout_2,
    output  reg [15:0]  o_dout_3,
    output  reg [15:0]  o_dout_4,
    output  reg [15:0]  o_dout_5,
    output  reg [15:0]  o_dout_6,
    output  reg [15:0]  o_dout_7,
    output  reg         o_dvld  ,
    output  reg         o_cs    ,
    output  reg         o_sclk  ,
    output  reg [1:0]   o_convst,
    output      [2:0]   o_os    ,
    output              o_rst   ,
    output              o_range
    );
    
    assign o_os     = 3'b000    ;
    assign o_rst    = 0         ;
    assign o_range  = 1         ;
    
    localparam  S_IDLE  = 6'b000001 ,
                S_CVST  = 6'b000010 ,
                S_BUSY  = 6'b000100 ,
                S_CSEL  = 6'b001000 ,
                S_RECV  = 6'b010000 ,
                S_DONE  = 6'b100000 ;
                
    reg     [5:0]   r_cstate;
    reg     [5:0]   r_nstate;
    
    reg             r_cvst_flag;
    
    always@(posedge i_clk or posedge i_rst) begin
        if(i_rst)
            r_cvst_flag <= 'b0;
        else if(r_cstate == S_CVST)
            r_cvst_flag <= 'b1;
        else
            r_cvst_flag <= 'b0;
    end
    
    reg     r_busy_d0;
    reg     r_busy_d1;
    
    always@(posedge i_clk or posedge i_rst) begin
        if(i_rst) begin
            r_busy_d0 <= 'b0;
            r_busy_d1 <= 'b0;
        end
        else begin
            r_busy_d0 <= i_busy     ;
            r_busy_d1 <= r_busy_d0  ;
        end
    end
    
    reg     [1:0]   r_clk_cnt;
    
    always@(posedge i_clk or posedge i_rst) begin
        if(i_rst)
            r_clk_cnt <= 'd0;
        else if(r_cstate == S_RECV)
            r_clk_cnt <= r_clk_cnt + 1;
        else
            r_clk_cnt <= 'd0;
    end
    
    always@(posedge i_clk or posedge i_rst) begin
        if(i_rst)
            o_convst <= 2'b11;
        else if(r_cstate == S_CVST)
            o_convst <= 2'b00;
        else
            o_convst <= 2'b11;
    end
    
    always@(posedge i_clk or posedge i_rst) begin
        if(i_rst)
            o_cs <= 'b1;
        else if(r_cstate == S_DONE)
            o_cs <= 'b1;
        else if(r_cstate == S_CSEL)
            o_cs <= 'b0;
        else
            o_cs <= o_cs;
    end
    
    always@(posedge i_clk or posedge i_rst) begin
        if(i_rst)
            o_sclk <= 'b1;
        else if(r_cstate == S_RECV)
            o_sclk <= r_clk_cnt[0] ? ~o_sclk : o_sclk;
        else
            o_sclk <= 'b1;
    end
    
    reg     [15:0]  r_rxdata_a;
    reg     [15:0]  r_rxdata_b;
    
    always@(posedge i_clk or posedge i_rst) begin
        if(i_rst) begin
            r_rxdata_a <= 'd0;
            r_rxdata_b <= 'd0;
        end
        else if(r_cstate == S_RECV) begin
            if(r_clk_cnt == 2'b11) begin
                r_rxdata_a <= {r_rxdata_a[14:0], i_din_a};
                r_rxdata_b <= {r_rxdata_b[14:0], i_din_b};
            end
            else begin
                r_rxdata_a <= r_rxdata_a;
                r_rxdata_b <= r_rxdata_b;
            end
        end
        else begin
            r_rxdata_a <= 'd0;
            r_rxdata_b <= 'd0;
        end
    end
    
    reg     [3:0]   r_bit_cnt   ;
    reg     [1:0]   r_ch_cnt    ;
    
    always@(posedge i_clk or posedge i_rst) begin
        if(i_rst)
            r_bit_cnt <= 'd0;
        else if(r_cstate == S_RECV)
            r_bit_cnt <= r_clk_cnt == 2'b11 ? r_bit_cnt + 1 : r_bit_cnt;
        else
            r_bit_cnt <= 'd0;
    end
    
    wire    w_rxdone    ;
    wire    w_cycdone   ;
    
    assign  w_rxdone = r_clk_cnt == 2'b11 & r_bit_cnt == 4'b1111;
    assign  w_cycdone= r_ch_cnt == 2'b11 & w_rxdone             ;
    
    always@(posedge i_clk or posedge i_rst) begin
        if(i_rst)
            r_ch_cnt <= 'd0;
        else if(r_cstate == S_RECV)
            if(w_rxdone)
                r_ch_cnt <= r_ch_cnt + 1;
            else
                r_ch_cnt <= r_ch_cnt;
        else
            r_ch_cnt <= 'd0;
    end
    
    reg     [1:0]   r_ch_cnt_d  ;
    reg             r_rxdone    ;
    
    always@(posedge i_clk or posedge i_rst) begin
        if(i_rst) begin
            r_ch_cnt_d  <= 'd0;
            r_rxdone    <= 'd0;
        end
        else begin
            r_ch_cnt_d  <= r_ch_cnt;
            r_rxdone    <= w_rxdone;
        end
    end
    
    always@(posedge i_clk or posedge i_rst) begin
        if(i_rst) begin
            o_dvld   <= 'b0;
            o_dout_0 <= 'd0;
            o_dout_1 <= 'd0;
            o_dout_2 <= 'd0;
            o_dout_3 <= 'd0;
            o_dout_4 <= 'd0;
            o_dout_5 <= 'd0;
            o_dout_6 <= 'd0;
            o_dout_7 <= 'd0;
        end
        else if(r_rxdone) begin
            o_dvld <= 'b1;
            case(r_ch_cnt_d)
                0:  begin
                    o_dout_0 <= r_rxdata_a  ;
                    o_dout_1 <= o_dout_1    ;
                    o_dout_2 <= o_dout_2    ;
                    o_dout_3 <= o_dout_3    ;
                    o_dout_4 <= r_rxdata_b  ;
                    o_dout_5 <= o_dout_5    ;
                    o_dout_6 <= o_dout_6    ;
                    o_dout_7 <= o_dout_7    ;
                end
                1:  begin
                    o_dout_0 <= o_dout_0    ;
                    o_dout_1 <= r_rxdata_a  ;
                    o_dout_2 <= o_dout_2    ;
                    o_dout_3 <= o_dout_3    ;
                    o_dout_4 <= o_dout_4    ;
                    o_dout_5 <= r_rxdata_b  ;
                    o_dout_6 <= o_dout_6    ;
                    o_dout_7 <= o_dout_7    ;                              
                end
                2:  begin
                    o_dout_0 <= o_dout_0    ;
                    o_dout_1 <= o_dout_1    ;
                    o_dout_2 <= r_rxdata_a  ;
                    o_dout_3 <= o_dout_3    ;
                    o_dout_4 <= o_dout_4    ;
                    o_dout_5 <= o_dout_5    ;
                    o_dout_6 <= r_rxdata_b  ;
                    o_dout_7 <= o_dout_7    ;   
                end
                3:  begin
                    o_dout_0 <= o_dout_0    ;
                    o_dout_1 <= o_dout_1    ;
                    o_dout_2 <= o_dout_2    ;
                    o_dout_3 <= r_rxdata_a  ;
                    o_dout_4 <= o_dout_4    ;
                    o_dout_5 <= o_dout_5    ;
                    o_dout_6 <= o_dout_6    ;
                    o_dout_7 <= r_rxdata_b  ;   
                end
                default: begin
                    o_dout_0 <= o_dout_0;
                    o_dout_1 <= o_dout_1;
                    o_dout_2 <= o_dout_2;
                    o_dout_3 <= o_dout_3;
                    o_dout_4 <= o_dout_4;
                    o_dout_5 <= o_dout_5;
                    o_dout_6 <= o_dout_6;
                    o_dout_7 <= o_dout_7;
                end
            endcase
        end
        else begin
            o_dvld   <= 'b0;
            o_dout_0 <= o_dout_0;
            o_dout_1 <= o_dout_1;
            o_dout_2 <= o_dout_2;
            o_dout_3 <= o_dout_3;
            o_dout_4 <= o_dout_4;
            o_dout_5 <= o_dout_5;
            o_dout_6 <= o_dout_6;
            o_dout_7 <= o_dout_7;
        end
    end
    
    always@(posedge i_clk or posedge i_rst) begin
        if(i_rst)
            r_cstate <= S_IDLE;
        else
            r_cstate <= r_nstate;
    end
    
    always@(*) begin
        case(r_cstate)
            S_IDLE: r_nstate = i_en ? S_CVST : S_IDLE;
            S_CVST: r_nstate = r_cvst_flag ? S_BUSY : S_CVST;
            S_BUSY: r_nstate = {r_busy_d0,r_busy_d1} == 2'b01 ? S_CSEL : S_BUSY;
            S_CSEL: r_nstate = S_RECV;
            S_RECV: r_nstate = w_cycdone ? S_DONE : S_RECV;
            S_DONE: r_nstate = S_IDLE;
            default:r_nstate = S_IDLE;
        endcase
    end
     
endmodule

并行代码:

多了俩参数,第一个参数表示你要采集几个通道的数据,因为如果只要采集一个通道,那么输入1就行,这时候代码会放弃接受后7个通道的数据,节约时间。

`timescale 1ns / 1ps

module ad7606_parallel#(
	parameter	CH_UESD  	= 8 ,
	parameter	CLK_PERIOD	= 20
)(
    input               i_clk   ,
    input               i_rst   ,
    input               i_en    ,
    input       [15:0]  i_din 	,
    input               i_busy  ,
    output  reg [15:0]  o_dout_0,
    output  reg [15:0]  o_dout_1,
    output  reg [15:0]  o_dout_2,
    output  reg [15:0]  o_dout_3,
    output  reg [15:0]  o_dout_4,
    output  reg [15:0]  o_dout_5,
    output  reg [15:0]  o_dout_6,
    output  reg [15:0]  o_dout_7,
    output  reg         o_dvld  ,
    output  reg         o_cs    ,
    output  reg         o_rd  	,
    output  reg [1:0]   o_convst,
    output      [2:0]   o_os    ,
    output  reg         o_rst   ,
    output              o_range
    );
	
	wire	[3:0]	w_ch_used;
	
	generate
		if(CH_UESD > 0 && CH_UESD < 9)
			assign w_ch_used = CH_UESD;
		else 
			assign w_ch_used = 8;
	endgenerate
	
	assign	o_os = 3'b000	;
	assign	o_range = 1'b0	;//1:+-10v;0:+-5v
	
	localparam	RST_TMIN 	= 50	+ 20,
				CVST_TMIN	= 25	+ 20,
				RDP_TMIN	= 15	+ 20,// 高电平
				RDN_TMIN	= 21	+ 20;// 低电平,一般3.3v以上供电21ns,5V供电16ns
				
	wire	[7:0]	w_rstTmin	;
	wire	[7:0]	w_cvstTmin	;
	wire	[7:0]	w_rdpTmin	;
	wire	[7:0]	w_rdnTmin	;
	
	generate
		assign w_rstTmin = RST_TMIN > CLK_PERIOD ? RST_TMIN - CLK_PERIOD : 0;
		assign w_cvstTmin= CVST_TMIN > CLK_PERIOD ? CVST_TMIN - CLK_PERIOD : 0;
		assign w_rdpTmin = RDP_TMIN > CLK_PERIOD ? RDP_TMIN - CLK_PERIOD : 0;
		assign w_rdnTmin = RDN_TMIN > CLK_PERIOD ? RDN_TMIN - CLK_PERIOD : 0;
	endgenerate
	
	reg		[7:0]	r_rstTime	;
	reg		[7:0]	r_cvstTime	;
	reg		[7:0]	r_rdpTime	;
	reg		[7:0]	r_rdnTime	;
				
	localparam	S_IDLE	= 8'b00000001,
				S_RST	= 8'b00000010,
				S_CVST 	= 8'b00000100,
				S_BUSY	= 8'b00001000,
				S_CSEL	= 8'b00010000,
				S_RDN	= 8'b00100000,
				S_RDP	= 8'b01000000,
				S_DONE	= 8'b10000000;
				
	reg		[7:0]	r_cstate	;
	reg		[7:0]	r_nstate	;
	reg				r_rst_done	;
	// rst start
	always@(posedge i_clk or posedge i_rst) begin
		if(i_rst) 
			r_rst_done <= 1'b0;
		else if(!i_en)
			r_rst_done <= 1'b0;
		else if(r_cstate == S_RST)
			r_rst_done <= 1'b1;
		else
			r_rst_done <= r_rst_done;
	end
				
	always@(posedge i_clk or posedge i_rst) begin
		if(i_rst)
			r_rstTime <= 0;
		else if(r_cstate == S_RST)
			r_rstTime <= r_rstTime + CLK_PERIOD;
		else
			r_rstTime <= 0;
	end
	
	always@(posedge i_clk or posedge i_rst) begin
		if(i_rst)
			o_rst <= 1'b0;
		else if(r_cstate == S_RST) 
			o_rst <= 1'b1;
		else
			o_rst <= 1'b0;
	end
	// rst end
	// cvst start
	
	always@(posedge i_clk or posedge i_rst) begin
		if(i_rst)
			r_cvstTime <= 0;
		else if(r_cstate == S_CVST)
			r_cvstTime <= r_cvstTime + CLK_PERIOD;
		else
			r_cvstTime <= 0;
	end
	
	always@(posedge i_clk or posedge i_rst) begin
		if(i_rst)
			o_convst <= 2'b11;
		else if(r_cstate == S_CVST)
			o_convst <= 2'b00;
		else
			o_convst <= 2'b11;
	end
	// cvst end
	// busy start
	reg		[1:0]	r_busyEdge	;
	
	always@(posedge i_clk) begin
		r_busyEdge[0] <= i_busy;
		r_busyEdge[1] <= r_busyEdge[0];
	end
	
	wire	w_nedge_busy;
	
	assign w_nedge_busy = r_busyEdge == 2'b10;
	// busy end
	// cs start
	always@(posedge i_clk or posedge i_rst) begin
		if(i_rst)
			o_cs <= 1'b1;
		else if(r_cstate == S_DONE)
			o_cs <= 1'b1;
		else if(r_cstate == S_CSEL)
			o_cs <= 1'b0;
		else	
			o_cs <= o_cs;
	end
	// cs end
	// read start
	
	always@(posedge i_clk or posedge i_rst) begin
		if(i_rst)
			r_rdpTime <= 0;
		else if(r_cstate == S_RDP)
			r_rdpTime <= r_rdpTime + CLK_PERIOD;
		else
			r_rdpTime <= 0;
	end
	
	always@(posedge i_clk or posedge i_rst) begin
		if(i_rst)
			r_rdnTime <= 0;
		else if(r_cstate == S_RDN)
			r_rdnTime <= r_rdnTime + CLK_PERIOD;
		else
			r_rdnTime <= 0;
	end
	
	task DOUT;
	input	[15:0]	ch0;
	input	[15:0]	ch1;
	input	[15:0]	ch2;
	input	[15:0]	ch3;
	input	[15:0]	ch4;
	input	[15:0]	ch5;
	input	[15:0]	ch6;
	input	[15:0]	ch7;
	begin
		o_dout_0 <= ch0;
		o_dout_1 <= ch1;
		o_dout_2 <= ch2;
		o_dout_3 <= ch3;
		o_dout_4 <= ch4;
		o_dout_5 <= ch5;
		o_dout_6 <= ch6;
		o_dout_7 <= ch7;
	end
	endtask
	
	reg		[3:0]	r_ch_cnt	;
	always@(posedge i_clk or posedge i_rst) begin
		if(i_rst) 
			DOUT(0,0,0,0,0,0,0,0);
		else if(r_cstate == S_RDP)
			case(r_ch_cnt)
				0:	DOUT(i_din,o_dout_1,o_dout_2,o_dout_3,o_dout_4,o_dout_5,o_dout_6,o_dout_7);
				1: 	DOUT(o_dout_0,i_din,o_dout_2,o_dout_3,o_dout_4,o_dout_5,o_dout_6,o_dout_7);
				2:	DOUT(o_dout_0,o_dout_1,i_din,o_dout_3,o_dout_4,o_dout_5,o_dout_6,o_dout_7);
				3:	DOUT(o_dout_0,o_dout_1,o_dout_2,i_din,o_dout_4,o_dout_5,o_dout_6,o_dout_7);
				4:	DOUT(o_dout_0,o_dout_1,o_dout_2,o_dout_3,i_din,o_dout_5,o_dout_6,o_dout_7);
				5:	DOUT(o_dout_0,o_dout_1,o_dout_2,o_dout_3,o_dout_4,i_din,o_dout_6,o_dout_7);
				6:	DOUT(o_dout_0,o_dout_1,o_dout_2,o_dout_3,o_dout_4,o_dout_5,i_din,o_dout_7);
				7:	DOUT(o_dout_0,o_dout_1,o_dout_2,o_dout_3,o_dout_4,o_dout_5,o_dout_6,i_din);
				default:DOUT(o_dout_0,o_dout_1,o_dout_2,o_dout_3,
							o_dout_4,o_dout_5,o_dout_6,o_dout_7);
			endcase
		else 
			DOUT(o_dout_0,o_dout_1,o_dout_2,o_dout_3,
				o_dout_4,o_dout_5,o_dout_6,o_dout_7);
	end
	
	always@(posedge i_clk or posedge i_rst) begin
		if(i_rst)
			r_ch_cnt <= 'd0;
		else if(r_cstate == S_IDLE)
			r_ch_cnt <= 'd0;
		else if(r_cstate == S_RDP & r_nstate == S_RDN)
			r_ch_cnt <= r_ch_cnt + 1;
		else
			r_ch_cnt <= r_ch_cnt;
	end
	
	always@(posedge i_clk or posedge i_rst) begin
		if(i_rst)
			o_rd <= 1'b1;
		else if(r_cstate == S_RDP)
			o_rd <= 1'b1;
		else if(r_cstate == S_RDN)
			o_rd <= 1'b0;
		else
			o_rd <= o_rd;
	end
	// read end
	
	// done start
	always@(posedge i_clk or posedge i_rst) begin
		if(i_rst)
			o_dvld <= 1'b0;
		else if(r_cstate == S_DONE)
			o_dvld <= 1'b1;
		else 
			o_dvld <= 1'b0;
	end
	// done end
	
	always@(posedge i_clk or posedge i_rst) begin
		if(i_rst)
			r_cstate <= S_IDLE;
		else
			r_cstate <= r_nstate;
	end
	
	always@(*) begin
		if(i_rst)
			r_nstate = S_IDLE;
		else 
			case(r_cstate)
				S_IDLE:	r_nstate = i_en ? (r_rst_done ? S_CVST : S_RST) : S_IDLE;
				S_RST:	r_nstate = (r_rstTime >= w_rstTmin) ? S_CVST : S_RST;
				S_CVST:	r_nstate = (r_cvstTime >= w_cvstTmin) ? S_BUSY : S_CVST;
				S_BUSY:	r_nstate = w_nedge_busy ? S_CSEL : S_BUSY;
				S_CSEL:	r_nstate = S_RDN;
				S_RDN:	r_nstate = (r_rdnTime >= w_rdnTmin) ? S_RDP : S_RDN;
				S_RDP:	r_nstate = (r_rdpTime >= w_rdpTmin) ? (r_ch_cnt >= w_ch_used ? S_DONE : S_RDN) : S_RDP;
				S_DONE:	r_nstate = S_IDLE;
				default:r_nstate = S_IDLE;
			endcase
	end
	
ila_ad 
ila (
	.clk	(i_clk			), // input wire clk

	.probe0	(i_en			), // input wire [0:0]  probe0  
	.probe1	(r_rstTime		), // input wire [7:0]  probe1 
	.probe2	(r_cvstTime		), // input wire [7:0]  probe2 
	.probe3	(r_rdnTime		), // input wire [7:0]  probe3 
	.probe4	(r_rdpTime		), // input wire [7:0]  probe4 
	.probe5	(r_cstate		), // input wire [7:0]  probe5 
	.probe6	(o_convst		),	// input wire [1:0]  probe6 
	.probe7	(w_nedge_busy	), // input wire [0:0]  probe7 
	.probe8	(o_cs			), // input wire [0:0]  probe8 
	.probe9	(i_busy			), // input wire [0:0]  probe9 
	.probe10(o_rd			), // input wire [0:0]  probe10 
	.probe11(o_rst			), // input wire [0:0]  probe11
	.probe12(r_ch_cnt		), // input wire [7:0]  probe12 
	.probe13(i_din			), // input wire [15:0]  probe13 
	.probe14(o_dout_0		) // input wire [15:0]  probe14
);

endmodule

 

 


网站公告

今日签到

点亮在社区的每一天
去签到