基于FPGA的AD采集

LVDS(Low-Voltage Differential Signaling)低电压差分信号,是一种低功耗、低误码率、低串扰和低辐射的差分信号技术。
KSPS(Kilo Samples per Second),采样千次每秒,是转化速率的单位。

逐次逼近型ADC由比较器包括n位逐次比较型AD转换器,它由控制逻辑电路、时序产生器、移位寄存器、DA转换器及电压比较器组成。

逐次逼近转换过程和用天平秤重相似,从最重的开始试放与被秤物体进行比较,若物体重于砝码,则该砝码保留,否则移去。再加上第二个次重砝码,由物体的重量是否大于砝码的重量决定第二个砝码是否保留。
逐次逼近AD转换器,就是将输入模拟信号与不同的参考电压作多次比较,使转换所得的数字量在数值上逐次逼近模拟量对应值。如上图,它由脉冲启动后,在第一个时钟脉冲作用下,控制电路使时序产生器的最高位置1,其他位置0,其输出数据寄存器将100…000送到DA转换器,输入电压首先与D/A器输出电压(VREF/2)进行比较,如果V1 ≥ (VREF/2),比较器输出为1,否则为0.比较结果存于数据的寄存器的Dn-1位。然后在第二个时钟脉冲(CP)下,移位寄存器的次高位置1,其他低位置0. 如最高位已存1,则此时vO=(3/4)VREF。于是v1再与(3/4)VREF相比较,如v1≥(3/4)VREF,则次高位Dn-2存1,否则Dn-2=0;如最高位为0,则vO=VREF/4,与vO比较,如v1≥VREF/4,则Dn-2位存1,否则存0……。以此类推,逐次比较得到输出数字量。
基准电压VREF。

举例如下:电路为8位AD转换器,输入模拟量vA=6.84v,DA转换器基准电压VREF=10v。当启动脉冲低电平到来后转换开始,在第一个CP作用下,数据寄存器将D7~D0=10000000送入D/A转换器,其输出电压v0=5V,vA与v0比较,vA>v0存1;第二个CP到来时,寄存器输出D7~D0=11000000,v0为7.5V,vA再与7.5V比较,因vA<7.5V,所以D6存0;输入第三个CP时,D7~D0=10100000,v0=6.25V;vA再与v0比较,……如此重复比较下去,经8个时钟周期,转换结束。由图中v0的波形可见,在逐次比较过程中,与输出数字量对应的模拟电压v0逐渐逼近vA值,最后得到A/D转换器转换结果D7~D0为10101111。该数字量所对应的模拟电压为6.8359375V,与实际输入的模拟电压6.84V 的相对误差仅为 0.06%

本实验是打算采用AD7928这个数据采集设备,这是一个12位高速、低功耗、8通道逐次逼近型ADC。采用单电源工作,电源电压为2.7v~5.25v,最高吞吐速率可以达到1Msps。内置有低噪声、款带宽采样保持放大器,可以处理8Mhz以上的输入频率。

通过配置控制寄存器,器件的模拟输入范围可以在0v到REFin或0v到2REFin间选择,可采用标准二进制或者二进制补码输出编码。AD7928具有八个通道序列器的单端模拟输入,可以通过预先编程选择通道转换顺序。

AD7928的最小输入频率是50Khz。

AD7928的转换时间取决于SCLK频率,该频率同时用作转换控制的主时钟。

灵活的功耗/串行时钟管理。转换速率取决于串行时钟,通过提高串行时钟还可以缩短转换时间。这些器件还提供多种关断模式,可以在较低吞吐量下实现最高功效,完全关断时最大功耗是0.5ua。

VREF = 2.5v
Fclkmax = 20Mhz

时序规格

控制寄存器:

序列选择:

但是现在又有个问题,那就是,是否选择了这个SEQ=1,SHADOW=1的模式,从0到指定的地址,会循环起来呢?

模拟输入选择
8个模拟输入通道中的任何一个都可以选择用来进行转换,方法是控制寄存器中的地址位ADD2至ADD0对多路复用器进行编程。还可配置为在选定的多个通道间自动循环。通过控制寄存器中的SEQ和SHADOW位访问序列器功能。

通过阅读手册第20页,发现有三种方式可以解决上述的问题。
(1)、设置SEQ=0,SHADOW=1,这种方式然后再对SHADOW寄存器进行配置。
(2)、设置SEQ=1,SHADOW=1.也可以实现通道循环,且不需要对对SHADOW寄存器进行配置。但是这个配置必须是从通道0到指定通道依次递增,然后再从0开始。但是WRITE必须是低电平。如果是高电平,那就必须再将SEQ设为1,SHADOW设为0。
(3)、因为上一次转换输出的时候会把下一次转换的通道的地址给出,从而设计可以让8个通道循环起来,这个方法设计出来只要按照时序要求,就基本不会出现错误

目前存在的问题是,这只是8通道的AD采集,但是实验需要用到32个通道,怎么去用这一个AD采集模块去完成4次8通道AD采集???

同时使用8个通道只需要对控制寄存器的参数进行输入配置,即可让8个通道循环使用。

AD7928的数据输出包括一个前置0和3个地址位(表示转换结果对应的的通道),然后就是12个转换数据位(MSB)优先。输出编码方式可选择标准二进制或二进制补码,通过控制寄存器中的CODING位选择。

针对上述的问题,准备在使用这个AD7928之前先给那32路的信号源加上4个八选一的模拟芯片,这样AD7928这个模块就只需要四个通道就可以了,针对这四个通道进行循环。这样的话,FPGA不光要给AD7928和八选一芯片加上控制。max4581芯片(模拟开关)

我想想该怎么加控制?
把这个AD7928的通道选择为四个,这四个通道进行循环。每个通道传输来自一个八选一模块的输出,然后八路数据在这个通道里面依次传输。

这个代码是单纯的启动AD7928八个通道连续循环采集数据。

module ad7928_ctrl32(input clk,input rst_n,//spiinput adc_dout,output reg adc_cs,output reg adc_sclk,output reg adc_din,//othersoutput reg dout_vld, //每个转换结束输出一个标志位output [2:0] adc_sel, //控制8选1output reg [18:0] dout,output reg ad_change_flag //每组(即32个PD)结束之后输出一个标志位);parameter WRITE = 1'b1;parameter PM = 2'b11;parameter SEQ =1'b0;parameter SHADOW = 1'b0;parameter RANGE = 1'b0;parameter CODING = 1'b1;//先设计一个时钟频率为1Mhz的,那么周期就是1000nsreg [5:0] cnt0;wire add_cnt0 = 1'b1;wire end_cnt0 = add_cnt0 && cnt0==6'd49;always @ (posedge clk or negedge rst_n) beginif(!rst_n) begincnt0 <= 6'd0;endelse if(add_cnt0) beginif(end_cnt0) begincnt0 <= 6'd0;endelse begincnt0 <= cnt0 + 1'b1;endendend//每次采集16位数据,并且cs的上升沿与下降沿之间最少50nsreg [4:0] cnt1;wire add_cnt1 = end_cnt0;wire end_cnt1 = add_cnt1 && cnt1==5'd17;always @ (posedge clk or negedge rst_n) beginif(!rst_n) begincnt1 <= 5'b0;endelse if(add_cnt1) beginif(end_cnt1) begincnt1 <= 5'b0;endelse begincnt1 <= cnt1 + 1'b1;endendend//切换四个通道reg [2:0] cnt2;wire add_cnt2 = end_cnt1;wire end_cnt2 = add_cnt2 && cnt2 == 3'd3;always @ (posedge clk or negedge rst_n) beginif(!rst_n) begincnt2 <= 3'b0;endelse if(add_cnt2) beginif(end_cnt2) begincnt2 <= 3'b0;endelse begincnt2 <= cnt2 + 1'b1;endendend//切换那4个8选1通道reg [2:0] cnt3;wire add_cnt3 = end_cnt2;wire end_cnt3 = add_cnt3 && cnt3 == 3'd7;always @ (posedge clk or negedge rst_n) beginif(!rst_n) begincnt3 <= 3'b0;endelse if(add_cnt3) beginif(end_cnt3) begincnt3 <= 3'b0;endelse begincnt3 <= cnt3 + 1'b1;endendend//产生adc_csalways @ (posedge clk or negedge rst_n) beginif(!rst_n) beginadc_cs <= 1'b1;endelse if(add_cnt0 && cnt0==6'd24 && cnt1==5'd0) beginadc_cs <= 1'b0;endelse if(add_cnt0 && cnt0==6'd24 && cnt1==5'd17) beginadc_cs <= 1'b1;endend//产生adc_sclkalways @ (posedge clk or negedge rst_n) beginif(!rst_n) beginadc_sclk <= 1'b1;endelse if(add_cnt0 && cnt0==6'd24 && cnt1>=5'd1 && cnt1<5'd17) beginadc_sclk <= 1'b0;endelse if(end_cnt0) beginadc_sclk <= 1'b1;endend//给ADC7928输入控制寄存器wire [15:0] data;assign data = {WRITE,SEQ,1'b0,cnt2,PM,SHADOW,1'b0,RANGE,CODING,4'b0};always @ (posedge clk or negedge rst_n) beginif(!rst_n) beginadc_din <= 0;endelse if(end_cnt0 && cnt1<16) beginadc_din <= data[15-cnt1];endend//输出ADC7928的16位数据reg [15:0] tmp_dout;always @ (posedge clk or negedge rst_n) beginif(!rst_n) begintmp_dout <= 16'd0;endelse if(add_cnt0 && cnt0==6'd24 && cnt1>=1 && cnt1 <17) begintmp_dout[16-cnt1] <= adc_dout;endendalways @ (posedge clk or negedge rst_n) beginif(!rst_n) begindout <= 19'd0;endelse if(cnt1==5'd16)begindout <= {cnt3,tmp_dout};endendalways @ (posedge clk or negedge rst_n) beginif(!rst_n) beginad_change_flag <= 1'b0;endelse if(cnt1==5'd16 && end_cnt3) beginad_change_flag <= 1'b1;endelse beginad_change_flag <= 1'b0;endendalways @ (posedge clk or negedge rst_n) beginif(!rst_n) begindout_vld <= 1'b0;endelse if(add_cnt0 && cnt0==6'd25 && cnt1==5'd16)begindout_vld <= 1'b1;endelse if(add_cnt0 && cnt0==6'd24 && cnt1==5'd16) begindout_vld <= 1'b0;endendassign adc_sel = cnt3;endmodule

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184

这个代码仿真过了,没错,具体还得下板子
后面有机会再试试其他方法,设计这个循环通道的代码

(0)

相关推荐