基于FPGA的DDS信号发生器

前言

此处仅为基于Vivado实现DDS信号发生器的仿真实现,Vivado的安装请看下面的文章,这里我只是安装了一个标准版本,只要能够仿真波形即可。

FPGA开发Vivado安装教程_vivado安装 csdn-CSDN博客

DDS原理

DDS技术是一种通过数字计算生成波形信号的方法,其核心原理是利用数字相位累加器和波形查找表(ROM)生成高精度、频率可调的波形信号。DDS系统的主要组成部分包括频率控制字(Fword)、相位累加器、相位控制字(Pword)和波形查找表。

DDS的基本结构图如下所示:

在DDS系统中,频率控制字决定了输出波形的频率。频率控制字越大,相位累加器每个时钟周期增加的相位值就越大,从而输出波形的频率越高。相位累加器是DDS系统的核心部件,用于累加频率控制字。在每个时钟周期,相位累加器会将上一个周期的累加值与频率控制字相加,生成新的相位值。这个相位值用于波形查找表的地址生成。

要理解这个频率字对应的输出频率,可以使用以下公式:

其中,是输出频率,是频率控制字,是驱动DDS的时钟频率,N是相位累加器的位宽,通常是DDS设计中的一个常数。假设这里为50MHz,N为32位,输出频率位1MHz,那么频率控制字即为85899345(仅去整数部分)。

这里还比较了1MHz、500KHz、100KHz的正弦波信号,如下图所示。

相位控制字用于实现相位偏移,通过将相位控制字加到相位累加器的输出中,可以实现输出波形的相位偏移,从而便于同步或相位调制等应用。波形查找表存储了一个周期波形的数据,例如正弦波、方波和三角波。相位累加器的输出作为地址输入到波形查找表,查找到相应的波形数据输出。

使用IP核生成ROM表

波形ROM模块通过查找表方式存储和输出波形数据。每种波形的数据表根据相应的波形公式预先计算并存储在ROM中。在系统运行过程中,DDS模块根据当前相位值读取ROM中的波形数据。

你可以使用软件去生成波形数据文件.coe文件。

在IP Catalog中找到ROM IP核,直接搜索即可。

修改名字,并且讲Memory Type类型改为Single Port ROM。

点击Port A Options修改宽度和深度。

在换到Other Options选择我们刚刚生成的.coe文件路径。

接下来和之前的一样对方波和正弦波做同样的处理。然后切换到IP Sources,点击每个的.v文件

这里应当以你自己的标准为准,然后进行实例化。

DDS波形仿真

模块中实例化了三个波形生成子模块,这里应该按照你自己的方式来。

python 复制代码
`timescale 1ns / 1ps

module DDS(
        Clk,
        Reset_n,
        Fword,
        Pword,
        mode,
        Data_out
    );
        input                         Clk;
        input                         Reset_n; 
        input [31:0]                  Fword;
        input [11:0]                  Pword;
        input [1:0]                   mode;  // 2位模式输入,用于选择波形
        output reg [13:0]             Data_out;  // 输出选择的波形数据
         
        // 频率控制字同步寄存器
        reg [31:0] Fword_r;
        always @(posedge Clk)
            Fword_r <= Fword;
        
        // 相位控制字同步寄存器
        reg [11:0] Pword_r;
        always @(posedge Clk)
            Pword_r <= Pword; 
        
        // 相位累加器    
        reg [31:0] Freq_ACC;
        always @(posedge Clk or negedge Reset_n)
            if (!Reset_n)
                Freq_ACC <= 0;
            else
                Freq_ACC <= Fword_r + Freq_ACC;
    
        // 波形数据表地址
        wire [11:0] Rom_Addr;      
        assign Rom_Addr = Freq_ACC[31:20] + Pword_r;
        
        // 波形数据输出
        wire [13:0] Data_sine;
        wire [13:0] Data_square;
        wire [13:0] Data_transqure;
        
        // 实例化正弦波模块
        sine_wav sine_wav (
            .clka(Clk),         // 输入时钟
            .ena(1'b1),         // 使能信号置高
            .addra(Rom_Addr),   // 输入地址
            .douta(Data_sine)   // 输出正弦波数据
        );
        
        // 实例化方波模块    
        square_wav square_wav (
            .clka(Clk),         // 输入时钟
            .ena(1'b1),         // 使能信号置高
            .addra(Rom_Addr),   // 输入地址
            .douta(Data_square) // 输出方波数据
        );
        
        // 实例化三角波模块    
        triangular_wav triangular_wav (
            .clka(Clk),            // 输入时钟
            .ena(1'b1),            // 使能信号置高
            .addra(Rom_Addr),      // 输入地址
            .douta(Data_transqure) // 输出三角波数据
        );
        
        // 多路复用器根据 mode 选择波形数据输出
        always @(*) begin
            case (mode)
                2'b00: Data_out = Data_sine;      // mode = 00 时输出正弦波
                2'b01: Data_out = Data_square;    // mode = 01 时输出方波
                2'b10: Data_out = Data_transqure; // mode = 10 时输出三角波
                default: Data_out = 14'b0;        // 默认情况下输出0
            endcase
        end

endmodule

仿真使用的tb文件

python 复制代码
`timescale 1ns / 1ps

module DDS_tb;

       reg                         Clk;
       reg                         Reset_n; 
       reg [31:0]                  Fword;
       reg [11:0]                  Pword;
       reg [1:0]                   mode;
       wire [13:0]                 Data_out;

    DDS DDS(
        .Clk(Clk),
        .Reset_n(Reset_n),
        .Fword(Fword),
        .Pword(Pword),
        .mode(mode),
        .Data_out(Data_out)
    );

    initial Clk = 1;
    always #10 Clk = ~Clk;
    
    initial begin
        Reset_n = 0;
        Fword = 85899345;  // 1M初始频率控制字设置为较大值
        Pword = 0;
        mode = 2'b00;  // 选择正弦波
        #201
        Reset_n = 1;
        #20000
        
        Fword = 42949673;  // 500k更改频率控制字,降低频率
        #20000
        
        Fword = 8589935;  // 100k更改频率控制字,进一步降低频率
        #20000
        
        $stop;  
        
//     initial begin
//        Reset_n = 0;
//        Fword = 85899345;
//        Pword = 0;
//        mode = 2'b00;  // 正弦波
//        #201
//        Reset_n = 1;
//        #100000
        
//        mode = 2'b01;  // 方波
//        #100000
        
//        mode = 2'b10;  // 三角波
//        #100000
        
//        $stop;  
    end

endmodule

修改波的类型为模拟信号即可,对于方波这种还需要进行以下设置。

完整工程资源

按理来说,可以通过博客就完成了,但如果你比较懒,你可以从这里下载完整工程。

基于FPGA的DDS信号仿真资源-CSDN文库

因为这个模块只是一个课程设计的一部分,涉及到通过串口控制的部分由其他同学负责,因此这里仅记录我所完成的部分。我对FPGA的理解还有限,后续不会对这方面进行答疑。

参考文章

DDS原理及FPGA实现_dds fpga-CSDN博客

基于FPGA的DDS算法实现(可调幅值,附ISE联合Modelsim仿真结果)-CSDN博客

基于FPGA的DDS信号发生器-CSDN博客

相关推荐
北城笑笑8 小时前
FPGA 14 ,硬件开发板分类详解,FPGA开发板与普通开发板烧录的区别
fpga开发·fpga
2202_754421548 小时前
一个计算频率的模块
驱动开发·fpga开发
小灰灰的FPGA9 小时前
低速接口项目之串口Uart开发(七)——如何在FPGA项目中实现自适应波特率串口功能
fpga开发
fei_sun1 天前
【Verilog】第一章作业
fpga开发·verilog
深圳市雷龙发展有限公司longsto1 天前
基于FPGA(现场可编程门阵列)的SD NAND图片显示系统是一个复杂的项目,它涉及硬件设计、FPGA编程、SD卡接口、NAND闪存控制以及图像显示等多个方面
fpga开发
9527华安1 天前
FPGA实现PCIE3.0视频采集转10G万兆UDP网络输出,基于XDMA+GTH架构,提供工程源码和技术支持
网络·fpga开发·udp·音视频·xdma·pcie3.0·万兆网
able陈1 天前
为什么verilog中递归函数需要定义为automatic?
fpga开发
fei_sun1 天前
【Verilog】第二章作业
fpga开发·verilog
碎碎思1 天前
如何使用 Vivado 从源码构建 Infinite-ISP FPGA 项目
fpga开发·接口隔离原则
江山如画,佳人北望2 天前
fpga-状态机的设计及应用
fpga开发