【FPGA】——DDS信号发生器设计

发布于:2025-04-16 ⋅ 阅读:(19) ⋅ 点赞:(0)

目录

一 、IP核的使用

(1)RAM IP核

(2)FIFO IP核

二、DDS信号发生器设计

(1)代码

(2)仿真波形


一 、IP核的使用

IP核:ASIC或FPGA中预先设计好具有某种功能的电路模块,参数可修改,目的在于提高开发效率。

QuartusII软件下IP核调用方式:Mega Wizard插件管理器(常用)、SOPC构造器、DSP构造器、Qsys设计系统例化。

(1)RAM IP核

RAM的英文全称是Random Access Memory,即随机存取存储器,它可以随时把数据写入任一指定地址的存储单元,也可以随时从任一指定地址中读出数据。其读写速度是由时钟频率决定的。RAM主要用来存放程序及程序执行过程中产生的中间数据、运算结果等。其特点适合双向交换数据。

打开Quartus软件,新建一个项目后,在界面的最右侧,可以看见一个IP Catalog的栏目,在这里有很多的IP核可以供我们选择使用

 在IP Catalog中搜索RAM

双击选择RAM:1-PORT,保存在ip文件夹下,命名为RAM_1port.v

选择数据位大小,以及数据深度

之后选择NEXT

勾选RAM_1port_inst.v和RAM_1port_bb.v文件

(2)FIFO IP核

FPGA使用的FIFO一般指的是对数据的存储具有先进先出特性的一个缓存器,常被用于数据的缓存或者高速异步数据的交互,也即所谓的跨时钟域信号传递。

FIFO的IP核调用(读写共用时钟)

选择读写使用同一个时钟,勾选下面的信号

之后选择Next

二、DDS信号发生器设计

(1)代码

DDS技术的基本原理是利用相位累加器和波形查找表来合成所需频率的波形:

  1. 相位累加器:在每个时钟周期累加频率控制字(Frequency Control Word, FCW)

  2. 相位-幅度转换:通过查找表将相位信息转换为对应的幅度值

  3. 数模转换:将数字幅度值转换为模拟信号

输出频率计算公式:fout = (FCW × fclk) / 2^N,其中N为相位累加器位数。

顶层模块DDS_top .v

module DDS_top (
    input CLOCK_50,
    input [1:0] KEY,          // KEY[0]-复位,KEY[1]-波形选择
    input [3:0] SW,           // SW[0]-增频,SW[1]-减频
    output [7:0] LEDG,        // 显示当前状态
    output [7:0] DAC_DATA,    // 波形数据输出
    output DAC_CLK,           // DAC时钟
    output DAC_CS,
    output DAC_MODE,
    // 用于SignalTap II监测的信号
    output [9:0] DEBUG_PHASE,
    output [7:0] DEBUG_WAVE
);

    wire reset_n = KEY[0];
    wire wave_select = KEY[1];
    
    wire [31:0] freq_word;
    wire [15:0] freq_display;
    
    // 频率控制模块
    frequency_control freq_ctrl (
        .clk_50MHz(CLOCK_50),
        .reset_n(reset_n),
        .key_in(SW[1:0]),
        .freq_word(freq_word),
        .freq_display(freq_display)
    );
    
    // DDS核心模块
    enhanced_dds dds_core (
        .clk_50MHz(CLOCK_50),
        .reset_n(reset_n),
        .wave_select(wave_select),
        .freq_word(freq_word),
        .wave_out(DAC_DATA),
        .phase_out(DEBUG_PHASE)
    );
    
    // DAC控制信号
    assign DAC_CLK = CLOCK_50;
    assign DAC_CS = 1'b0;
    assign DAC_MODE = 1'b1;
    
    // 调试信号连接
    assign DEBUG_WAVE = DAC_DATA;
    
    // LED显示
    assign LEDG = {wave_select, 3'b0, freq_display[15:12]};

endmodule

debounce .v

module debounce (
    input clk,
    input button_in,
    output reg button_out
);

    reg [19:0] counter;
    reg button_state;

    always @(posedge clk) begin
        if (button_in != button_state) begin
            button_state <= button_in;
            counter <= 20'd0;
        end
        else if (counter < 20'd1_000_000) begin
            counter <= counter + 1'b1;
        end
        else begin
            button_out <= button_state;
        end
    end

endmodule

enhanced_dds.v

module enhanced_dds (
    input clk_50MHz,          // 50MHz系统时钟
    input reset_n,            // 低电平复位
    input wave_select,        // 0-正弦波,1-方波
    input [31:0] freq_word,   // 频率控制字
    output reg [7:0] wave_out, // 波形输出
    output reg [9:0] phase_out // 相位输出(用于SignalTap)
);

    // 32位相位累加器
    reg [31:0] phase_accum;
    
    // 正弦波ROM(10位地址,8位数据)
    wire [9:0] rom_addr = phase_accum[31:22]; // 取高10位
    wire [7:0] sine_data;
    
    // 方波信号定义(修正位宽)
    wire [7:0] square_data = phase_accum[31] ? 8'hFF : 8'h00;
    
    // 相位累加
    always @(posedge clk_50MHz or negedge reset_n) begin
        if (!reset_n)
            phase_accum <= 32'd0;
        else
            phase_accum <= phase_accum + freq_word;
    end
    
    // 正弦波ROM实例化
    sine_rom_1024 sine_rom (
        .address(rom_addr),
        .clock(clk_50MHz),
        .q(sine_data)
    );
    
    // 波形选择
    always @(*) begin
        case (wave_select)
            1'b0: wave_out = sine_data;
            1'b1: wave_out = square_data;
            default: wave_out = sine_data;
        endcase
    end
    
    // 相位输出用于SignalTap监测
    always @(posedge clk_50MHz) begin
        phase_out <= rom_addr;
    end

endmodule

frequency_control.v

module frequency_control (
    input clk_50MHz,
    input reset_n,
    input [1:0] key_in,       // 按键输入[0]-增,[1]-减
    output reg [31:0] freq_word,
    output reg [15:0] freq_display // 频率显示值(Hz)
);

    // 频率控制参数
    parameter MIN_FREQ = 10;      // 10Hz
    parameter MAX_FREQ = 5_000_000; // 5MHz
    parameter STEP_1KHZ = 85899;  // 1kHz对应的FCW
    
    // 按键消抖实例化
    wire [1:0] key_pressed;
    debounce deb0(.clk(clk_50MHz), .button_in(key_in[0]), .button_out(key_pressed[0]));
    debounce deb1(.clk(clk_50MHz), .button_in(key_in[1]), .button_out(key_pressed[1]));
    
    // 频率控制
    always @(posedge clk_50MHz or negedge reset_n) begin
        if (!reset_n) begin
            freq_word <= 32'd85899; // 默认1kHz
            freq_display <= 16'd1000;
        end
        else begin
            if (key_pressed[0] && freq_display < MAX_FREQ) begin
                freq_word <= freq_word + STEP_1KHZ;
                freq_display <= freq_display + 16'd1000;
            end
            else if (key_pressed[1] && freq_display > MIN_FREQ) begin
                freq_word <= freq_word - STEP_1KHZ;
                freq_display <= freq_display - 16'd1000;
            end
        end
    end

endmodule

sine_rom_1024 .v

module sine_rom_1024 (
    input [9:0] address,
    input clock,
    output reg [7:0] q
);

    reg [7:0] rom [0:1023];
    
    initial begin
        // 预计算的1024点正弦波表(8位有符号)
        rom[0] = 8'd128; rom[1] = 8'd131; rom[2] = 8'd134; rom[3] = 8'd137;
        rom[4] = 8'd140; rom[5] = 8'd143; rom[6] = 8'd146; rom[7] = 8'd149;
        rom[8] = 8'd152; rom[9] = 8'd155; rom[10] = 8'd158; rom[11] = 8'd162;
        rom[12] = 8'd165; rom[13] = 8'd168; rom[14] = 8'd171; rom[15] = 8'd174;
        // ... 填写完整的1024个点 ...
        rom[1020] = 8'd114; rom[1021] = 8'd117; rom[1022] = 8'd120; rom[1023] = 8'd123;
    end
    
    always @(posedge clock) begin
        q <= rom[address];
    end

endmodule

引脚配置

(2)仿真波形

Tools-》SignalTap ll Logic Analyzer,查看波形

 

总结

IP核的运用是一个新接触的内容,第一次使用时需要借鉴不同资料,明白其道理。