FPGA 29 ADC(数据采集)模数字转换驱动设计
模块名称 : adc_128s022 模数驱动模块设计
主要功能 :本实验设计了adc_128s022 数模驱动芯片,通过在ADC_DIN输出端发送配置数据来完成芯片通道的配置和数字电压信号的传输,ADC_DOUT 来获取信号的输入,最终内部转换得到输出采集的数字信号。
设计流程:我们在这次实验中,在内部通过CLK的时钟分频,来输出时钟ADC_SCLK信号,根据ADC_SCLK 的时钟,通过ADC_DIN 来发送通道的配置引脚,随后ADC_DOUT 来接收模拟信号的采样数据点。最终完成在内部进行转换得到电压。通过该时钟,我们进而来设计线性序列机(可以理解一种比较特殊的状态机)来编写ADC模数芯片的SPI的时序.
AD128S022芯片的线性序列逻辑:
12位分辨率
CS_N : 片选信号,低电平有效
SCLK : 时钟
DIN(FPGA输出) : 信号输入引脚,SCLK 上升沿采样输入,【对于FPGA,需要下降沿改变输出的数据】
DOUT(对应FPGA输入): 转换结构输出脚,SCLK下降沿输出,【对于FPGA,还是上升沿读取】。
模块实现:
数据手册的基本信息:
ksps (kilo samples per second) : 每秒多少K次采样。
500ksps -1Msps : 表示的是采样速率是,500k采样/1s - 1000k采样/1s.
功能描述:
注:这里主要关注的是一些关于dac的位数,工作电压,支持的采样速率,低功耗模式等较为重要的信息。
这个是内部电路给出的时序操作图:
实现过程可以看到,在CS片选信号拉低以后,我们将以SCLK的下降来进行计数,DIN在第四个下降沿到来时,开始连续的发送3个采样的信道地址ADDR[2:0]。同时我们也统计上升沿的个数,我们在SCLK的第5个上升沿开始接收采样的数据DATA[11:0],统计16个上升沿数据。随后完成一次数据的采集。
在上述图片中,描述的是一个连续的信号采集过程,一次完整的信号采集一共有16个上升沿和16个下降沿,我们以下降作为一次的开始(实际的情况也是下降沿作为信号采集的起始信号)计数,一共有16个时钟周期。随后又是16个时钟周期开始执行相同的操作。
数据典型电路:
布局布线设计电路:
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:这个是来统计上升沿和下降沿的计数。