概述
ADC简介
位数n:一定范围的模拟量能分成2^n份;其中某个模拟量由n位二进制数表示。n越大,精度越高。
采样率:
原理分析
-
FPGA通过什么管脚控制ADC
-
用什么时序来控制管脚?
依据SPI时序图
DOUT输出这一次通道转换的数据,DIN控制下一次要转换数据的通道和其他设置( ADD控制通道序数)
留意时序规格:结合两张图,可以看出sclk下降沿ADC自动采集DIN,所以需要在上升沿送入DIN。同样sclk下降沿读取DOUT。
-
控制通道与工作模式
通过控制寄存器DIN,根据需求选择;如果不懂,或者没有要求,就用默认,最简单的形式。
50分频;PM=11; SEO SHADOW=00; RANGE=0,响应0-5V; CODING=1;
代码实现
实现步骤:
1、根据功能要求,在给出clk的波形图上,画出输入和输出的波形;(时序图已给出)
2、标出需要计数的信号,在给出clk的波形图上画出计数器
3、确认各计数器的加1条件、结束条件(加1条件:计数器数什么;结束条件:计数器周期数多少个,不同值时用变量法。 )。
4、其它信号变化点条件(其他信号即输出或内容信号;变化点∶ 0变 1、1变0的点,何时写入/读取)。
5、写出计数器代码和其他信号变量代码
参数声明部分
module ad7928_ctr(
input clk , //系统时钟
input rst_n , //复位信号
input adc_dout //adc输出给fpga的串行信号 ,
output reg adc_cs , //控制adc是否工作的片选信号
output reg adc_sclk , //adc的工作时钟
output reg adc_din , //fpga控制adc工作参数的信号,勿混淆成要转换的信号
output reg [15:0] dout , //fpga最终输出的16位并行信号
output reg dout_vld ,//转换数据有效指示信号
);
parameter WRITE = 1'b1;
parameter PM = 2'b11;
parameter SEQ = 1'b0;
parameter SHADOW= 1'b0;
parameter RANGE = 1'b0;
parameter CODING= 1'b1;
计数器部分
这三个计数器的时钟(周期)实际上是不同的,但这里并没有用不同频率的时钟信号分别生成计数器,而是统一在系统时钟下,保证了时序上不出问题
加1条件 | 结束条件 | 用途 | |
cnt0 | 一个sysclk | 计数到50-1 | 计数系统时钟个数,50分频产生sclk |
cnt1 | 一个sclk=cnt0结束条件 | 计数到18-1 | 计数sclk个数,18个sclk对应一整段片选信号CS |
cnt2 | 一整段转换过程(CS)=cnt1结束条件 | 计数到8-1 | 计数ADC转换次数,1个cnt2的值控制一次ADD通道 |
reg [ 7:0] cnt0 ;
wire add_cnt0 ;
wire end_cnt0 ;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt0 <= 0;
end
else if(add_cnt0)begin
if(end_cnt0)
cnt0 <= 0;
else
cnt0 <= cnt0 + 1;
end
end
assign add_cnt0 = 1; //计数器加1条件;可以不要
assign end_cnt0 = cnt0==50-1; //计数器中止条件
//assign end_cnt0 = add_cnt0 && (cnt0==50-1) ;
reg [ 5:0] cnt1 ;
wire add_cnt1 ;
wire end_cnt1 ;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt1 <= 0;
end
else if(add_cnt1)begin
if(end_cnt1)
cnt1 <= 0;
else
cnt1 <= cnt1 + 1;
end
end
assign add_cnt1 = end_cnt0; //cnt0 ==49
assign end_cnt1 = add_cnt1 && (cnt1==18-1) ; //不用前半部分add_cnt1也行,因为就是在add_cnt1=1的条件下才做的end_cnt1判断
reg [ 3:0] cnt2 ;
wire add_cnt2 ;
wire end_cnt2 ;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt2 <= 0;
end
else if(add_cnt2)begin
if(end_cnt2)
cnt2 <= 0;
else
cnt2 <= cnt2 + 1;
end
end
assign add_cnt2 = end_cnt1;
assign end_cnt2 = add_cnt2 && cnt2==8-1 ;
其他信号
按照时序要求来,1MHz的sclk,无论是半个还是一个周期,都完全满足时序要求。
复位 | 1变0 | 0变1 | |
Sclk | 1 | 1~16cnt1,cnt0的一半 | 1~16cnt1, Cnt0的结束; |
0,17cnt1一直保持1 | |||
CS | 1 | Cnt1=0,cnt0的一半 | Cnt1=17,cnt0的一半 |
DIN 16
时序逻辑:前一个sclk结束时写入,中间ADC自动采。因此0~15cnt1,cnt0结束时写入
注意:对每个sclk单独弄,很麻烦,先拼出一个data,每次讲data的一位写给DIN;cnt1=0写入的WRITE是data最高位[15]
DOUT 16
时序逻辑:ADC自动发送,sclk一半时FPGA采集,因此1~16cnt1,cnt0一半时读取
assign middle = add_cnt0 && cnt0==25-1;
//生成sclk
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
adc_sclk <= 1; //注意这里复位是1不是0
end
else if(middle && (cnt1>=1) &&(cnt1<17))begin
adc_sclk <= 0;
end //每个cnt0计数周期的一半变低,除了sclk第0个,第17个周期
else if(end_cnt0)begin
adc_sclk <= 1;
end //每个cnt0计数周期结束变高,sclk第0,第17个周期保持高位
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
adc_cs <= 1;
end
else if(middle && cnt1==0)begin
adc_cs <= 0;
end //sclk第0个周期的一半拉低,比时序图的要求更宽松
else if(middle && cnt1==17 )begin
adc_cs <= 1;
end
end
//生成din
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)begin
if(rst_n==1'b0)begin
adc_din <= 0;
end
else if(end_cnt0 && cnt1<16)begin
adc_din <= data[15-cnt1];
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
dout <= 0;
end
else if(middle && cnt1>=1 && cnt1<17)begin
dout[16-cnt1] <= adc_dout;
end
end
//8个通道循环一次,输出一个有效位
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
dout_vld <= 0;
end
else begin
dout_vld <= end_cnt2;
end
end
最后
以上就是俊秀汉堡为你收集整理的基于FPGA的AD7928驱动ADC简介原理分析代码实现的全部内容,希望文章能够帮你解决基于FPGA的AD7928驱动ADC简介原理分析代码实现所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复