简介
AD7606是一款16位ADC芯片,可实现8通道并行采集,每通道最大速度可达1M,可实现多种模式数据采集。
介绍
本次FPGA使用的是8通道串行采样模式,设计中所用到的AD7606引脚说明如下:
|--------------|-------------|
| 名称 | 定义 |
| CONVST | 同步采集转换开始信号 |
| BUSY | ADC忙碌状态信号 |
| RD/SCLK | 采样/寄存器工作时钟 |
| CS | 片选使能 |
| DOUTA~DOUTH | ADC 8通道串行输出 |
| SDI | 寄存器数据输入 |
本次采用的寄存器读写时序如下图所示:
1、寄存器读写第一位默认为0;
2、第二位代表寄存器读写位,0代表写寄存器,1代表读寄存器;
3、6个地址位,具体寄存器定义查阅芯片手册;
4、如果是写操作,后面8bit为寄存器值,如果是读操作则通过DoutA接口读取返回的寄存器数据。
5、该芯片支持CRC模式,本次设计默认不启动CRC模式,所以后面8bitCRC在普通模式下不存在。
采样时序如下图所示:
本时序图发现如下两个问题:
1、DoutA~DoutH输出的应该是V1~V7数据,上图中标识有错误;
2、上图未标识CONVST和BUSY信号,显然这两个信号是必须存在的;
另外芯片引脚OS0~OS2都接高点平,进入Enters software mode模式。
代码
FPGA驱动代码如下:
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2024/08/05 15:39:27
// Design Name:
// Module Name: ADC_SLAVE_DRI_TOP
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module ADC_SLAVE_DRI_TOP(
input sys_clk,
input sys_rst_n,
output reg ADC_CONVST,
input ADC_BUSY,
output ADC_SCLK,
output ADC_CS,
input ADC_FRSTDATA,
input[7:0] ADC_DATA,[7]:a [6]:b [5]:c ......
output ADC_SDI,
reg set
input[15:0] adc_frame_length,
input[15:0] adc_tdata_reg,
input[15:0] adc_sample_rate_h,
input[15:0] adc_sample_rate_l,
ADC REC REG DATA
output[7:0] adc_rec_reg_data,
output adc_rec_reg_data_en,
input[3:0] adc_rw_set,//[1]:sample [0]:reg w/r
input[15:0] adc_sample_rate_reg,
input[15:0] adc_sample_channel_en_reg,
ADC SAMPLE DATA
output[15:0] rx_data_length,
output [7:0] axis_adc_data,
output axis_adc_tvalid,
output axis_adc_tlast
);
parameter ADC_CONVST_num = 'd100;
reg[7:0] spi_tdata;
reg spi_tvalid;
reg tx_reg_en_r1;
wire[31:0] ADC_SAMPLE_RATE;
reg[31:0]sample_cnt;
reg tx_reg_wr_r1;
reg tx_reg_rd_r1;
wire tx_reg_wr;
wire tx_reg_rd;
wire sample_en;
reg ADC_BUSY_r1;
reg ADC_BUSY_r2;
reg ADC_BUSY_r3;
reg ADC_BUSY_r4;
reg ADC_BUSY_r5;
wire ADC_BUSY_neg;
reg[15:0]rec_adc_a_sample_data;
reg[15:0]rec_adc_b_sample_data;
reg[15:0]rec_adc_c_sample_data;
reg[15:0]rec_adc_d_sample_data;
reg[15:0]rec_adc_e_sample_data;
reg[15:0]rec_adc_f_sample_data;
reg[15:0]rec_adc_g_sample_data;
reg[15:0]rec_adc_h_sample_data;
reg rec_adc_sample_en;
reg ADC_SCLK_i;
reg ADC_CS_i;
reg[3:0] adc_mode;
reg[31:0] sample_en_delay;
reg[31:0] adc_stop_delay;
reg[15:0] rec_data_cnt;
reg[7:0] s_axis_tdata;
reg s_axis_tvalid;
wire s_axis_tlast;
reg[15:0] s_axis_tvalid_cnt;
reg[15:0] spi_clk_cnt;
reg[15:0] clk_cnt;
reg sample_en_r1;
reg sample_en_r2;
wire sample_en_neg;
wire[13:0] axis_data_count;
reg fifo_rst_n;
reg[15:0] fifo_rst_cnt;
reg fifo_rst_en;
wire channel_a_en;
wire channel_b_en;
wire channel_c_en;
wire channel_d_en;
wire channel_e_en;
wire channel_f_en;
wire channel_g_en;
wire channel_h_en;
assign channel_a_en = adc_sample_channel_en_reg[7];
assign channel_b_en = adc_sample_channel_en_reg[6];
assign channel_c_en = adc_sample_channel_en_reg[5];
assign channel_d_en = adc_sample_channel_en_reg[4];
assign channel_e_en = adc_sample_channel_en_reg[3];
assign channel_f_en = adc_sample_channel_en_reg[2];
assign channel_g_en = adc_sample_channel_en_reg[1];
assign channel_h_en = adc_sample_channel_en_reg[0];
assign rx_data_length = adc_frame_length;
assign tx_reg_wr = adc_rw_set[0];
assign tx_reg_rd = adc_rw_set[1];
assign sample_en = adc_rw_set[2];
assign ADC_SAMPLE_RATE = {adc_sample_rate_h,adc_sample_rate_l};
assign ADC_BUSY_neg = (~ADC_BUSY_r4) && ADC_BUSY_r5;
assign ADC_SCLK = (adc_mode == 'd3)?ADC_SCLK_i:ADC_SCLK_ii;
assign ADC_CS = (adc_mode == 'd3)?ADC_CS_i:ADC_CS_ii;
assign sample_en_neg = sample_en_r2 && (~sample_en_r1);
assign s_axis_tlast = (s_axis_tvalid_cnt == adc_frame_length - 'd1)?1'b1:1'b0;
always@(posedge sys_clk or negedge sys_rst_n)begin
if(sys_rst_n == 'D0)begin
adc_mode <= 'd0;
end
else if(tx_reg_wr == 'd1)begin
adc_mode <= 'd1;
end
else if(tx_reg_rd == 'd1)begin
adc_mode <= 'd2;
end
else if(sample_en == 'd1)begin
adc_mode <= 'd3;
end
end
reg set
always@(posedge sys_clk)begin
tx_reg_wr_r1 <= tx_reg_wr;
tx_reg_rd_r1 <= tx_reg_rd;
end
always@(posedge sys_clk or negedge sys_rst_n)begin
if(sys_rst_n == 'D0)begin
spi_tdata <= 'd0;
spi_tvalid <= 'd0;
end
else if(tx_reg_wr == 'd1)begin
spi_tdata <= {2'b00,adc_tdata_reg[13:8]};
spi_tvalid <= 'd1;
end
else if(tx_reg_rd == 'd1)begin
spi_tdata <= {2'b01,adc_tdata_reg[13:8]};
spi_tvalid <= 'd1;
end
else if(tx_reg_wr_r1 == 'd1 || tx_reg_rd_r1 == 'd1)begin
spi_tdata <= adc_tdata_reg[7:0];
spi_tvalid <= 'd1;
end
else begin
spi_tdata <= 'd0;
spi_tvalid <= 'd0;
end
end
sample
always@(posedge sys_clk)begin
ADC_BUSY_r1 <= ADC_BUSY;
ADC_BUSY_r2 <= ADC_BUSY_r1;
ADC_BUSY_r3 <= ADC_BUSY_r2;
ADC_BUSY_r4 <= ADC_BUSY_r3;
ADC_BUSY_r5 <= ADC_BUSY_r4;
sample_en_r1 <= sample_en;
sample_en_r2 <= sample_en_r1;
end
always@(posedge sys_clk or negedge sys_rst_n)begin
if(sys_rst_n == 'D0)begin
sample_cnt <= 'd0;
end
else if(sample_cnt >= ADC_SAMPLE_RATE - 'd1)begin
sample_cnt <= 'd0;
end
else if(sample_en == 'd1)begin
sample_cnt <= sample_cnt + 'd1;
end
else begin
sample_cnt <= 'd0;
end
end
always@(posedge sys_clk or negedge sys_rst_n)begin
if(sys_rst_n == 'D0)begin
ADC_CONVST <= 'd0;
end
else if(sample_cnt < ADC_CONVST_num + 'd10 && sample_cnt >= 'd10 && sample_en == 'd1)begin
ADC_CONVST <= 'd1;
end
else begin
ADC_CONVST <= 'd0;
end
end
always@(posedge sys_clk or negedge sys_rst_n)begin
if(sys_rst_n == 'D0)begin
spi_clk_cnt <= 'd0;
clk_cnt <= 'd0;
end
else if(ADC_BUSY_neg == 'd1) begin
spi_clk_cnt <= 'd36;
clk_cnt <= adc_sample_rate_reg;20M;
end
else if(spi_clk_cnt > 'd0 && clk_cnt == 'd1)begin
spi_clk_cnt <= spi_clk_cnt - 'd1;
clk_cnt <= adc_sample_rate_reg;
end
else if(spi_clk_cnt > 'd0)begin
clk_cnt <= clk_cnt - 'd1;
end
end
always@(posedge sys_clk or negedge sys_rst_n)begin
if(sys_rst_n == 'D0)begin
ADC_CS_i <= 'd1;
end
else if(spi_clk_cnt > 'd0) begin
ADC_CS_i <= 'd0;
end
else begin
ADC_CS_i <= 'd1;
end
end
always@(posedge sys_clk or negedge sys_rst_n)begin
if(sys_rst_n == 'D0)begin
ADC_SCLK_i <= 'd1;
end
else if(clk_cnt == 'd1 && spi_clk_cnt >='d3 && spi_clk_cnt <= 'd34) begin
ADC_SCLK_i <= ~ADC_SCLK_i;
end
end
always@(posedge sys_clk or negedge sys_rst_n)begin
if(sys_rst_n == 'D0)begin
rec_adc_a_sample_data <= 'd0;
rec_adc_b_sample_data <= 'd0;
rec_adc_c_sample_data <= 'd0;
rec_adc_d_sample_data <= 'd0;
rec_adc_e_sample_data <= 'd0;
rec_adc_f_sample_data <= 'd0;
rec_adc_g_sample_data <= 'd0;
rec_adc_h_sample_data <= 'd0;
end
else if(clk_cnt == 'd1 && spi_clk_cnt >='d3 && spi_clk_cnt <= 'd34 && ADC_SCLK_i == 'd0) begin
rec_adc_a_sample_data <= {rec_adc_a_sample_data[14:0],ADC_DATA[7]};
rec_adc_b_sample_data <= {rec_adc_b_sample_data[14:0],ADC_DATA[6]};
rec_adc_c_sample_data <= {rec_adc_c_sample_data[14:0],ADC_DATA[5]};
rec_adc_d_sample_data <= {rec_adc_d_sample_data[14:0],ADC_DATA[4]};
rec_adc_e_sample_data <= {rec_adc_e_sample_data[14:0],ADC_DATA[3]};
rec_adc_f_sample_data <= {rec_adc_f_sample_data[14:0],ADC_DATA[2]};
rec_adc_g_sample_data <= {rec_adc_g_sample_data[14:0],ADC_DATA[1]};
rec_adc_h_sample_data <= {rec_adc_h_sample_data[14:0],ADC_DATA[0]};
end
end
always@(posedge sys_clk or negedge sys_rst_n)begin
if(sys_rst_n == 'D0)begin
rec_adc_sample_en <= 'd0;
end
else if(clk_cnt == 'd1 && spi_clk_cnt == 'd3 && ADC_SCLK_i == 'd0) begin
rec_adc_sample_en <= 'd1;
end
else begin
rec_adc_sample_en <= 'd0;
end
end
always@(posedge sys_clk or negedge sys_rst_n)begin
if(sys_rst_n == 'D0)begin
sample_en_delay <= 'd0;
adc_stop_delay <= 'd0;
end
else begin
sample_en_delay <= {sample_en_delay[30:0],rec_adc_sample_en};
adc_stop_delay <= {adc_stop_delay[30:0],sample_en_neg};
end
end
always@(posedge sys_clk or negedge sys_rst_n)begin
if(sys_rst_n == 'D0)begin
s_axis_tdata <= 'd0;
s_axis_tvalid <= 'd0;
end
else if(sample_en_delay[0] == 'd1 && channel_a_en == 'd1)begin
s_axis_tdata <= rec_adc_a_sample_data[15:8];
s_axis_tvalid <= 'd1;
end
else if(sample_en_delay[1] == 'd1 && channel_a_en == 'd1)begin
s_axis_tdata <= rec_adc_a_sample_data[7:0];
s_axis_tvalid <= 'd1;
end
else if(sample_en_delay[2] == 'd1 && channel_b_en == 'd1)begin
s_axis_tdata <= rec_adc_b_sample_data[15:8];
s_axis_tvalid <= 'd1;
end
else if(sample_en_delay[3] == 'd1 && channel_b_en == 'd1)begin
s_axis_tdata <= rec_adc_b_sample_data[7:0];
s_axis_tvalid <= 'd1;
end
else if(sample_en_delay[4] == 'd1 && channel_c_en == 'd1)begin
s_axis_tdata <= rec_adc_c_sample_data[15:8];
s_axis_tvalid <= 'd1;
end
else if(sample_en_delay[5] == 'd1 && channel_c_en == 'd1)begin
s_axis_tdata <= rec_adc_c_sample_data[7:0];
s_axis_tvalid <= 'd1;
end
else if(sample_en_delay[6] == 'd1 && channel_d_en == 'd1)begin
s_axis_tdata <= rec_adc_d_sample_data[15:8];
s_axis_tvalid <= 'd1;
end
else if(sample_en_delay[7] == 'd1 && channel_d_en == 'd1)begin
s_axis_tdata <= rec_adc_d_sample_data[7:0];
s_axis_tvalid <= 'd1;
end
else if(sample_en_delay[8] == 'd1 && channel_e_en == 'd1)begin
s_axis_tdata <= rec_adc_e_sample_data[15:8];
s_axis_tvalid <= 'd1;
end
else if(sample_en_delay[9] == 'd1 && channel_e_en == 'd1)begin
s_axis_tdata <= rec_adc_e_sample_data[7:0];
s_axis_tvalid <= 'd1;
end
else if(sample_en_delay[10] == 'd1 && channel_f_en == 'd1)begin
s_axis_tdata <= rec_adc_f_sample_data[15:8];
s_axis_tvalid <= 'd1;
end
else if(sample_en_delay[11] == 'd1 && channel_f_en == 'd1)begin
s_axis_tdata <= rec_adc_f_sample_data[7:0];
s_axis_tvalid <= 'd1;
end
else if(sample_en_delay[12] == 'd1 && channel_g_en == 'd1)begin
s_axis_tdata <= rec_adc_g_sample_data[15:8];
s_axis_tvalid <= 'd1;
end
else if(sample_en_delay[13] == 'd1 && channel_g_en == 'd1)begin
s_axis_tdata <= rec_adc_g_sample_data[7:0];
s_axis_tvalid <= 'd1;
end
else if(sample_en_delay[14] == 'd1 && channel_h_en == 'd1)begin
s_axis_tdata <= rec_adc_h_sample_data[15:8];
s_axis_tvalid <= 'd1;
end
else if(sample_en_delay[15] == 'd1 && channel_h_en == 'd1)begin
s_axis_tdata <= rec_adc_h_sample_data[7:0];
s_axis_tvalid <= 'd1;
end
else begin
s_axis_tvalid <= 'd0;
end
end
always@(posedge sys_clk or negedge sys_rst_n)begin
if(sys_rst_n == 'D0)begin
s_axis_tvalid_cnt <= 'd0;
end
else if(s_axis_tvalid == 'd1 && s_axis_tvalid_cnt == adc_frame_length - 'd1)begin
s_axis_tvalid_cnt <= 'd0;
end
else if(s_axis_tvalid == 'd1)begin
s_axis_tvalid_cnt <= s_axis_tvalid_cnt + 'd1;
end
end
always@(posedge sys_clk or negedge sys_rst_n)begin
if(sys_rst_n == 'D0)begin
fifo_rst_en <= 'd0;
end
else if(sample_en == 'd0 && axis_adc_tvalid == 'd0 && axis_data_count > 'd0 && axis_data_count <adc_frame_length)begin
fifo_rst_en <= 'd1;
end
else begin
fifo_rst_en <= 'd0;
end
end
always@(posedge sys_clk or negedge sys_rst_n)begin
if(sys_rst_n == 'D0)begin
fifo_rst_n <= 'd1;
fifo_rst_cnt <= 'd0;
end
else if(fifo_rst_en == 'd1)begin
fifo_rst_n <= 'd0;
fifo_rst_cnt <= 'd200;
end
else if(fifo_rst_cnt > 'd0)begin
fifo_rst_n <= 'd0;
fifo_rst_cnt <= fifo_rst_cnt - 'd1;
end
else begin
fifo_rst_n <= 'd1;
fifo_rst_cnt <= 'd0;
end
end
SPI_DRV SPI_DRV_inst(
.sys_rstn(sys_rst_n),
.sys_clk_100m(sys_clk),
.spi_mode(2'b11),
.spi_tvalid(spi_tvalid),
.spi_tdata(spi_tdata),
.spi_rate('d20),
.spi_rd_num('d1),
.DUT_SPI_WR_BIT_reg(8'h04),
.spi_tready(),
.dut_data_out(adc_rec_reg_data),
.dut_data_out_en(adc_rec_reg_data_en),
.spi_fifo_recv_en('d1),
.spi_rd_busy(),
.spi_sck(ADC_SCLK_ii),
.spi_cs(ADC_CS_ii),
.spi_mosi(ADC_SDI),
.spi_miso(ADC_DATA[7])
);
fifo_adc_sample_data fifo_adc_sample_data_inst(
.s_aclk(sys_clk),
.s_aresetn(sys_rst_n && fifo_rst_n),
.s_axis_tvalid(s_axis_tvalid),
.s_axis_tready(),
.s_axis_tdata(s_axis_tdata),
.s_axis_tlast(s_axis_tlast),
.m_axis_tvalid(axis_adc_tvalid),
.m_axis_tready('d1),
.m_axis_tdata(axis_adc_data),
.m_axis_tlast(axis_adc_tlast),
.axis_data_count(axis_data_count)
);
/* test
reg[31:0] cnt_tx;
reg[31:0] delay;
assign rx_data_length = adc_frame_length;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(sys_rst_n == 'd0) begin
axis_adc_data <= 'd0;
axis_adc_tvalid <= 'd0;
axis_adc_tlast <= 'd0;
cnt_tx <= 'd0;
delay <= 'd0;
end
else if(delay <= 'd2000)begin
delay <= delay + 'd1;
end
else if(cnt_tx == adc_frame_length - 'd1)begin
axis_adc_data <= axis_adc_data + 'd1;
axis_adc_tvalid <= 'd1;
axis_adc_tlast <= 'd1;
cnt_tx <= cnt_tx + 'd1;
end
else if(cnt_tx < adc_frame_length - 'd1)begin
axis_adc_data <= axis_adc_data + 'd1;
axis_adc_tvalid <= 'd1;
axis_adc_tlast <= 'd0;
cnt_tx <= cnt_tx + 'd1;
end
///
else if(cnt_tx <= 'd20000)begin
axis_adc_data <= 'd0;
axis_adc_tvalid <= 'd0;
axis_adc_tlast <= 'd0;
cnt_tx <= cnt_tx + 'd1;
end
else begin
axis_adc_data <= 'd0;
axis_adc_tvalid <= 'd0;
axis_adc_tlast <= 'd0;
cnt_tx <= 'd0;
end
end
*/
endmodule
代码接口说明:
|---------------------------|-------------------------|
| 信号名 | 说明 |
| adc_frame_length | 采样数据打包成帧输出,这里设置帧内容长度 |
| adc_tdata_reg | 寄存器地址+数据信息,如果是读只有读地址有效 |
| adc_sample_rate_h | ADC循环采样周期高16位 |
| adc_sample_rate_l | ADC循环采样周期低16位 |
| adc_rec_reg_data | ADC返回的寄存器值 |
| adc_rec_reg_data_en | ADC返回的寄存器值使能位 |
| adc_rw_set | ADC读、写、循环采样控制寄存器 |
| adc_sample_rate_reg | clk速率控制 |
| adc_sample_channel_en_reg | 通道使能寄存器控制,8通道可以做任意通道使能。 |
仿真
代码仿真结果如下:
首先是SPI写操作,放大效果如下:
该操作标识对寄存0x01写0x23值;
ADC采样波形如下:
CONGVST为高电平期间触发芯片同步采集,此时BUSY会至高表示正在转换中,当BUSY拉低代表转换完成,输出SCLK时钟信号并开始同步采样。
如需要完整的驱动代码或者技术支持可以私聊我,谢谢。