基于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
这个代码仿真过了,没错,具体还得下板子
后面有机会再试试其他方法,设计这个循环通道的代码