zoukankan      html  css  js  c++  java
  • 基于FPGA(DDS)的正弦波发生器

    记录背景:昨晚快下班时,与同事rk聊起怎么用FPGA实现正弦波的输出。我第一反应是利用高频的PWM波去滤波,但感觉这样的波形精度肯定很差;后来想起之前由看过怎么用FPGA产生正弦波的技术,但怎么都想不起来这个技术的名称是叫什么了。后来搜索后才知道就是DDS(Direct Digital Synthesizer),即:直接数字频率合成器。

    最近(好吧,不是最近,是一直)发现自己记东西记不牢,究竟是自己老了,记忆力下退;还是自己不用心去记;还是因为看得少,缺少实践(毕竟纸上得来终觉浅、绝知此事要躬行)。

    -----------------------------分割线-----------------------------------

    以下内容主要转自:http://blog.chinaaet.com/lincoding/p/5100050592,如有侵权,请告知删除。抱歉!

    DDS的主要组成部分:相位累加器、相位调制器、波形数据表、DAC和低通滤波器四大部分组成。如下图:

    DDS的原理:

    1、首先ROM中要存放好要显示的正弦波数据;

    2、然后由相位累加器(其实就是个计数器)一直累加,这个累加器的值作为ROM的地址

    3、DAC根据ROM输出的数据输出对应的电压值

    4、由于上述输出的电压值是个离散值,无法构成平滑的正弦波,因此需要在后级增加一个低通滤波器才能输出完美的正弦波。

    那么,怎么实现上述的功能呢?

    首先,我们要考虑两个问题:

    A、相位累加器(计数器)的位宽是多少?

    B、ROM的数据位宽和深度(深度:2^地址位宽)是多少?

    对于第一个问题:相位累加器的位宽一般是24~32bits,一般选32bits(因为这样的位宽能满足绝大部分的应用场合了);

    对于第二个问题:

    ROM的数据位宽选择要看DAC模块,比如我的DAC模块的数据输入数据范围是0~1023,那么ROM的数据位宽就要选择10位;

    ROM的深度也要取决于你的DAC模块,因为ROM中只能存储整数。

    相位累加器举例:

    //-----------------------------------
    //phase adder
    reg    [10:0]    fre_cnt;
    always @ ( posedge clk or negedge rst_n )
    begin
        if ( ! rst_n )
            fre_cnt        <= 11'd0;
        else if ( DDS_en )
            fre_cnt        <= fre_cnt + 1'b1;
        else
            fre_cnt        <= 11'd0;
    end

    这就是所谓的相位累加器,在DDS_en是能以后就一直技术,直到记满,然后重新又开始计数。

    DDS_rom u_DDS_ddsrom
    (    
        .clock            (clk),
        .address          (fre_cnt),
        .q                (DAC_data)
    );

    然后,将计数的值作为ROM的地址送给ROM,ROM输出相应的正弦波数据,这是,会把2048个点(假设ROM中存了一个正弦波周期的数据,共2048个数据)全部输出。而2048个点全部输出需要的实践为:2048*20ns(假设时钟为50MHz)=40960ns(24414.0625Hz),这就是DDS的基本频率,我们将其称为基频。

    ***********************************************************************************************************

    如果我们希望能将频率翻倍,可以这样:

    //-----------------------------------
    //phase adder
    reg    [10:0]    fre_cnt;
    always @ ( posedge clk or negedge rst_n )       //clk为50Mhz
    begin
        if ( ! rst_n )
            fre_cnt        <= 11'd0;
        else if ( DDS_en )
            fre_cnt        <= fre_cnt + 2'd2;
        else
            fre_cnt        <= 11'd0;
    end
    
    DDS_rom u_DDS_ddsrom
    (    
        .clock            (clk),
        .address          (fre_cnt),
        .q                (DAC_data)
    );

    如果我们希望把频率减半,我们可以这样:

    //-----------------------------------
    //phase adder
    reg    [10:0]    fre_cnt;
    always @ ( posedge clk_ref or negedge rst_n )    //clk_ref为25Mhz
    begin
        if ( ! rst_n )
            fre_cnt        <= 11'd0;
        else if ( DDS_en )
            fre_cnt        <= fre_cnt + 1'b1;
        else
            fre_cnt        <= 11'd0;
    end
    
    DDS_rom u_DDS_ddsrom
    (    
        .clock            (clk),
        .address          (fre_cnt),
        .q                (DAC_data)
    );

    注意:上述的clk_ref为25MHz;

    但由于上述需要用到另外的时钟,clk_ref,这会让代码不好维护,改良代码如下:

    //-----------------------------------
    //phase adder
    reg    [31:0]    fre_cnt;
    always @ ( posedge clk or negedge rst_n )    //clk为50Mhz
    begin
        if ( ! rst_n )
            fre_cnt        <= 32'd0;
        else if ( DDS_en )
            fre_cnt        <= fre_cnt + fre_value;
        else
            fre_cnt        <= 32'd0;
    end
    
    wire    [11:0]    rom_addr    = fre_cnt[31:20];
    
    DDS_rom u_DDS_ddsrom
    (    
        .clock            (clk),
        .address          (rom_addr),
        .q                (DAC_data)
    );

    为了更进一步完善DDS,我们可以再增加一个相位调节的功能:

    //-----------------------------------
    //phase adder
    reg    [31:0]    fre_cnt;
    always @ ( posedge clk or negedge rst_n )    //clk为50Mhz
    begin
        if ( ! rst_n )
            fre_cnt        <= 32'd0;
        else if ( DDS_en )
            fre_cnt        <= fre_cnt + fre_value;
        else
            fre_cnt        <= 32'd0;
    end
    
    wire    [11:0]    rom_addr    = fre_cnt[31:20] + pha_value;
    
    DDS_rom u_DDS_ddsrom
    (    
        .clock            (clk),
        .address          (rom_addr),
        .q                (DAC_data)
    );

    就是增加一个pha_value的相位控制字,它的位宽需与ROM中DAC模块的位宽相同。

    Summary:

    1、相位累加器的位宽为24~32bites,一般选32bits;

    2、频率控制字位宽与相位累加器位宽相同;

    3、ROM的数据位宽选择取决于DAC模块;

    4、ROM的深度(2^地址位宽)有标准深度,但可任意;

    5、相位控制字位宽选择取决于DAC模块。

  • 相关阅读:
    h5实现 微信的授权登录
    js判断浏览器的环境(pc端,移动端,还是微信浏览器)
    动态判断时间插件显示到年月日时分秒
    H5发起微信支付
    Vue项目结合vux使用
    Swift学习笔记一:常量和变量
    iOS开发之解决系统数字键盘无文字时delete键无法监听的技巧
    Swift3.0之获取设备识别号deviceNo和保存账户AccountId
    Swift3.0之自定义debug阶段控制台打印
    Xcode之command+/快捷键添加注释不起作用
  • 原文地址:https://www.cnblogs.com/lifei-chan/p/9216272.html
Copyright © 2011-2022 走看看