打印
[FPGA]

基于FPGA的AD/DA采集(附件源码代码)

[复制链接]
10931|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
源码代码下载 源码.rar (7.06 KB)
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  ;


评论
WYHLTT 2020-10-29 17:21 回复TA
博主写的很好,要是再加上仿真结果就更好了。 

相关帖子

发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:官网:www.mdy-edu.com

30

主题

43

帖子

3

粉丝