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