原码代码 源码.rar
1 项目背景
1.1 AD转换
AD转换就是模数转换。顾名思义,就是把模拟信号转换成数字信号。主要包括积分型、逐次逼近型、并行比较型/串并行型、Σ-Δ调制型、电容阵列逐次比较型及压频变换型。
A/D转换器是用来通过一定的电路将模拟量转变为数字量。模拟量可以是电压、电流等电信号,也可以是压力、温度、湿度、位移、声音等非电信号。但在A/D转换前,输入到A/D转换器的输入信号必须经各种传感器把各种物理量转换成电压信号。
AD转换的技术指标,一般有如下几个:
1. 分辨率(Resolution) 指数字量变化一个最小量时模拟信号的变化量,定义为满刻度与2^n的比值。分辨率又称精度,通常以数字信号的位数来表示。
2. 转换速率(Conversion Rate)是指完成一次从模拟转换到数字的AD转换所需的时间的倒数。积分型AD的转换时间是毫秒级属低速AD,逐次比较型AD是微秒级属中速AD,全并行/串并行型AD可达到纳秒级。采样时间则是另外一个概念,是指两次转换的间隔。为了保证转换的正确完成,采样速率 (Sample Rate)必须小于或等于转换速率。因此有人习惯上将转换速率在数值上等同于采样速率也是可以接受的。常用单位是ksps和Msps,表 示每秒采样千/百万次(kilo / Million Samples per Second)。
3. 量化误差 (Quantizing Error) 由于AD的有限分辨率而引起的误差,即有限分辨率AD的阶梯状转移特性曲线与无限分辨率AD(理想AD)的转移特 性曲线(直线)之间的最大偏差。通常是1个或半个最小数字量的模拟变化量,表示为1LSB、1/2LSB。
4. 偏移误差(Offset Error) 输入信号为零时输出信号不为零的值,可外接电位器调至最小。
5. 满刻度误差(Full Scale Error) 满度输出时对应的输入信号与理想输入信号值之差。
6. 线性度(Linearity) 实际转换器的转移函数与理想直线的最大偏移,不包括以上三种误差。
其他指标还有:绝对精度(Absolute Accuracy) ,相对精度(Relative Accuracy),微分非线性,单调性和无错码,总谐波失真(Total Harmonic Distotortion缩写THD)和积分非线性。
1.2 教学板AD原理图
明德扬教学板上板载板载32Mhz 转换速率、8bit高速AD芯片AD9280,满足各种信号的采集,满足用户实现各种常见滤波算法的实现。实际位置如下所示:
图 599
图 600
上面是AD9280的原理图。与FPGA相连的信号有:AD_D0~7、AD_OTR、AD_CLK。
AD9280管脚 | 原理图信号 | FPGA管脚 | 作用 |
CLK | AD_CLK | AD9280的工作时钟,最大是32MHz。 | |
OTR | AD_OTR | 超过电压范围指示信号 | |
D7 | AD_D7 | AD转换后的数字值。 | |
D6 | AD_D6 | ||
D5 | AD_D5 | ||
D4 | AD_D4 | ||
D3 | AD_D3 | ||
D2 | AD_D2 | ||
D1 | AD_D1 | ||
D0 | AD_D0 |
1.3 AD9280的控制时序
AD9280的控制时序如下图。
图 601
图 602
图 603
由时钟图可以看出,每个时钟就可以做AD转换一次,但会延迟3个时钟才输出。例如时序图中第一个时钟采集到S1,并对S1进行模数转换,经过3个时钟后,输出DATA1,这个DATA1就是S1所应对的数字值。
由参数时序可以看出,时钟最大是32MHz。
由上面时序可以看出,控制AD9280非常简单,只要给出不超过32MHz的时钟,然后对其采集就行了。
3 设计实现
3.1 顶层信号
新建目录:D:\mdy_book\ad_prj。在该目录中,新建一个名为ad_prj.v的文件,并用GVIM打开,开始编写代码。
我们要实现的功能,概括起来就是FPGA产生控制AD9709,让其中的通道A产生正弦波所对应的电压,同时采集AD9280的数据并观察。为了控制AD9709的通道A,就需要控制AD9709的MODE、SLEEP、CLK1、WRT1、DB7~0P1管脚。为了采集AD9280,那么就需要控制AD9280的CLK、D0~D7管脚。根据设计目标的要求,整个工程需要以下信号:
1. 使用clk连接到晶振,表示50M时钟的输入。
2. 使用rst_n连接到按键,表示复位信号。
3. 使用3位信号key,表示三位拨码开关。
4. 使用dac_mode信号连接到AD9709的MODE管脚,用来控制其工作模式。
5. 使用dac_sleep信号连接到AD9709的SLEEP管脚,用来控制其睡眠模式。
6. 使用dac_clka信号连接到AD9709的CLK1管脚,用来控制通道A的时钟。
7. 使用dac_wra信号连接到AD9709的WRT1管脚,用来控制通道A的写使能。
8. 使用8位信号dac_da连接到AD9709的DB7~0P1管脚,用来控制通道A的写数据。
9. 使用ad_clk信号连接到AD9280的CLK管脚,用来作来采样时钟。
10. 使用8位ad_in信号连接到AD9280的D7~0管脚,用来采集数据。
综上所述,我们这个工程需要10个信号,时钟clk,复位rst_n,dac_mode、dac_sleep、dac_clka、dac_wra、dac_da、ad_clk和ad_in,其中dac_da 、ad_in是8位信号,key是3位信号,其他都是1位信号。
器件 | AD9709管脚 | AD9280管脚 | 原理图信号 | FPGA管脚 | FPGA工程信号 |
U8 | MODE | DAC_MODE | Y4 | dac_mode | |
SLEEP | DAC_SLEEP | H2 | dac_sleep | ||
CLK1 | DA_CLKA | R2 | dac_clka | ||
WRT1 | DA_WRA | U1 | dac_wra | ||
DB7P1 | DAC_DA7 | AA1 | dac_da[7] | ||
DB6P1 | DAC_DA6 | Y2 | dac_da[6] | ||
DB5P1 | DAC_DA5 | Y1 | dac_da[5] | ||
DB4P1 | DAC_DA4 | W2 | dac_da[4] | ||
DB3P1 | DAC_DA3 | W1 | dac_da[3] | ||
DB2P1 | DAC_DA2 | V2 | dac_da[2] | ||
DB1P1 | DAC_DA1 | V1 | dac_da[1] | ||
DB0P1 | DAC_DA0 | U2 | dac_da[0] | ||
U1 | CLK | AD_CLK | L6 | ad_clk | |
D7 | AD_D7 | N5 | ad_in[7] | ||
D6 | AD_D6 | M4 | ad_in[6] | ||
D5 | AD_D5 | M5 | ad_in[5] | ||
D4 | AD_D4 | R6 | ad_in[4] | ||
D3 | AD_D3 | T5 | ad_in[3] | ||
D2 | AD_D2 | U7 | ad_in[2] | ||
D1 | AD_D1 | V5 | ad_in[1] | ||
D0 | AD_D0 | V6 | ad_in[0] | ||
X1 | SYS_CLK | G1 | clk | ||
K1 | SYS_RST | AB12 | rst_n | ||
sw0 | SW_D0 | AA3 | key[2] | ||
sw1 | SW_D1 | AB3 | key[1] | ||
sw2 | SW_D2 | AB5 | key[0] |
将module的名称定义为ad_prj,代码如下:
1 2 3 4 5 6 7 8 9 | module ad_prj( clk , rst_n , key , dac_mode , dac_clka , dac_da , dac_wra , dac_sleep, ad_clk , ad_in ); |
其中clk、rst_n是1位的输入信号,dac_da是8位的输出信号,dac_mode,dac_clka,dac_wra,dac_sleep是1位输出信号,ad_clk是1位输出信号,ad_in是8位输入信号,key是3位输入信号。
1 2 3 4 5 6 7 | input clk ; input rst_n ; input [ 3-1:0] key ; output dac_mode ; output dac_clka ; output [ 8-1:0] dac_da ; output dac_wra ; output dac_sleep ; output ad_clk ; input [8-1:0] ad_in ; |
3.2 信号设计
由于正弦信号的产生要求,与“FIR滤波器设计”相同,所以不再详细描述原因。现在把相关代码整理如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | always @(*)begin case(addr) 0: sin_data = 8'h7F; 1: sin_data = 8'h85; 2: sin_data = 8'h8C; 3: sin_data = 8'h92; 4: sin_data = 8'h98; 5: sin_data = 8'h9E; 6: sin_data = 8'hA4; 7: sin_data = 8'hAA; 8: sin_data = 8'hB0; 9: sin_data = 8'hB6; 10: sin_data = 8'hBC; 11: sin_data = 8'hC1; 12: sin_data = 8'hC6; 13: sin_data = 8'hCB; 14: sin_data = 8'hD0; 15: sin_data = 8'hD5; 16: sin_data = 8'hDA; 17: sin_data = 8'hDE; 18: sin_data = 8'hE2; 19: sin_data = 8'hE6; 20: sin_data = 8'hEA; 21: sin_data = 8'hED; 22: sin_data = 8'hF0; 23: sin_data = 8'hF3; 24: sin_data = 8'hF5; 25: sin_data = 8'hF7; 26: sin_data = 8'hF9; 27: sin_data = 8'hFB; 28: sin_data = 8'hFC; 29: sin_data = 8'hFD; 30: sin_data = 8'hFE; 31: sin_data = 8'hFE; 32: sin_data = 8'hFE; 33: sin_data = 8'hFE; 34: sin_data = 8'hFE; 35: sin_data = 8'hFD; 36: sin_data = 8'hFC; 37: sin_data = 8'hFA; 38: sin_data = 8'hF8; 39: sin_data = 8'hF6; 40: sin_data = 8'hF4; 41: sin_data = 8'hF1; 42: sin_data = 8'hEF; 43: sin_data = 8'hEB; 44: sin_data = 8'hE8; 45: sin_data = 8'hE4; 46: sin_data = 8'hE0; 47: sin_data = 8'hDC; 48: sin_data = 8'hD8; 49: sin_data = 8'hD3; 50: sin_data = 8'hCE; 51: sin_data = 8'hC9; 52: sin_data = 8'hC4; 53: sin_data = 8'hBE; 54: sin_data = 8'hB9; 55: sin_data = 8'hB3; 56: sin_data = 8'hAD; 57: sin_data = 8'hA7; 58: sin_data = 8'hA1; 59: sin_data = 8'h9B; 60: sin_data = 8'h95; 61: sin_data = 8'h8F; 62: sin_data = 8'h89; 63: sin_data = 8'h82; 64: sin_data = 8'h7D; 65: sin_data = 8'h77; 66: sin_data = 8'h70; 67: sin_data = 8'h6A; 68: sin_data = 8'h64; 69: sin_data = 8'h5E; 70: sin_data = 8'h58; 71: sin_data = 8'h52; 72: sin_data = 8'h4C; 73: sin_data = 8'h46; 74: sin_data = 8'h41; 75: sin_data = 8'h3C; 76: sin_data = 8'h36; 77: sin_data = 8'h31; 78: sin_data = 8'h2C; 79: sin_data = 8'h28; 80: sin_data = 8'h23; 81: sin_data = 8'h1F; 82: sin_data = 8'h1B; 83: sin_data = 8'h17; 84: sin_data = 8'h14; 85: sin_data = 8'h11; 86: sin_data = 8'hE ; 87: sin_data = 8'hB ; 88: sin_data = 8'h9 ; 89: sin_data = 8'h7 ; 90: sin_data = 8'h5 ; 91: sin_data = 8'h3 ; 92: sin_data = 8'h2 ; 93: sin_data = 8'h1 ; 94: sin_data = 8'h1 ; 95: sin_data = 8'h1 ; 96: sin_data = 8'h1 ; 97: sin_data = 8'h1 ; 98: sin_data = 8'h2 ; 99: sin_data = 8'h3 ; 100: sin_data = 8'h4 ; 101: sin_data = 8'h6 ; 102: sin_data = 8'h7 ; 103: sin_data = 8'hA ; 104: sin_data = 8'hC ; 105: sin_data = 8'hF ; 106: sin_data = 8'h12; 107: sin_data = 8'h15; 108: sin_data = 8'h19; 109: sin_data = 8'h1D; 110: sin_data = 8'h21; 111: sin_data = 8'h25; 112: sin_data = 8'h2A; 113: sin_data = 8'h2E; 114: sin_data = 8'h33; 115: sin_data = 8'h38; 116: sin_data = 8'h3E; 117: sin_data = 8'h43; 118: sin_data = 8'h49; 119: sin_data = 8'h4E; 120: sin_data = 8'h54; 121: sin_data = 8'h5A; 122: sin_data = 8'h60; 123: sin_data = 8'h67; 124: sin_data = 8'h6D; 125: sin_data = 8'h73; 126: sin_data = 8'h79; 127: sin_data = 8'h7F; endcase end
always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin addr_tmp <= 0; end else if(key==0) begin addr_tmp <= addr_tmp + 262; end else if(key==1) begin addr_tmp <= addr_tmp + 524; end else if(key==2) begin addr_tmp <= addr_tmp + 786; end else if(key==3) begin addr_tmp <= addr_tmp + 1029; end else if(key==4) begin addr_tmp <= addr_tmp + 1311; end else if(key==5) begin addr_tmp <= addr_tmp + 1573; end else if(key==6) begin addr_tmp <= addr_tmp + 1835; end else begin addr_tmp <= addr_tmp + 2097; end end
assign addr = addr_tmp >>10 ;
always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin dac_da <= 0; end else begin dac_da <= 255 - sin_data; end end
assign dac_sleep = 0 ; assign dac_wra = dac_clka ; assign dac_clka = ~clk ; |
还有最后一个信号要设计:ad_clk。由设计目标可知,要求是25MHz的时钟。为了产生时钟,我们就要用到PLL。
3.3 生成PLL IP核
1.打开IP核管理工具
打开quartus软件,然后选择Tools ->IPcatalog,在右侧弹出如下界面
图 607
在搜索框中,填下ALTPLL,就会出现如下界面。
图 608
ALTPLL就是我们需要使用的PLL IP核。双击ALTPLL,在弹出的对话框中起个文件名,如vga_pll,记得选择verilog。然后点OK就可以开始设置参数了。
图 609
2.设置IP参数
图 610
上图中,主要是设置输入的时钟频率,明德扬开发板输入时钟是固定的50MHz,因此可填写50,注意旁边的单位是选择MHz。其他默认,按next。
图 611
上图的option inputs中,全部取消勾选,不需要产生复位信号。上图中的lock output,全部取消勾选,不需要产生locked指示信号。然后选Next。
图 612
不用做任何更改,直接Next。
图 613
不用做任何更改,直接Next。
图 614
增加输入时钟,由于我们只有一个输入时钟,所以只用inclk0即可,无需增加。直接Next。
图 615
不用做任何更改,直接Next。
图 616
设置c0的输出频率。我们选择Enter output clock frequest中,直接填入25,单位选择MHz,就是表示要产生25MHz的时钟。然后点击Finish,弹出如下窗口。
图 617
取消my_pll_bb.v的勾选,然后选择Finish。QUARTUS就会产生PLL IP的代码了。
稍等片刻,到工程目录 D:\mdy_book\ad_prj,可以看到生成一个my_pll.v文件,用GVIM打开后,可以看到PLL模块的模块输入输出接口。其中inclk0是50MHz输入时钟,c0是25MHz的输出时钟。顶层模块直接例化就可以使用了。
图 618
1 2 3 4 | my_pll u_my_pll( .inclk0(clk ) , .c0 (ad_clk ) ); |
至此,主体程序已经完成。接下来是将module补充完整。
3.4 信号定义
接下来定义信号类型。
在“FIR滤波器案例”中已经说明了部分信号的说明,现补充如下。
1 2 3 4 | wire [6:0] addr ; reg [7:0] sin_data ;
reg [7:0] dac_da ; wire dac_sleep ; wire dac_wra ; wire dac_clka ; wire dac_mode ; reg [16:0] addr_tmp ; |
ad_clk是由例化模块输出信号,非always产生,可以定义为wire型,只有1位。
1 | wire ad_clk ; |