1、前言
采用 Verilog 设计了一个简单的信号发生器,可输出正弦波、方波、三角波、锯齿波这 4 种频率、相位和幅值可调的波形。
2、具体功能
module dds(
input wire clk,
input wire rst_n,
input wire [19:0] freq,
input wire [1:0] wave_sel,
input wire [1:0] amp_adj,
output wire [9:0] wave_adj
);
wire [31:0] step_val;
wire [9:0] addr;
wire [7:0] q;
freq_ctrl_v2 freq_ctrl_inst(
.clk (clk),
.rst_n (rst_n),
.wave_sel (wave_sel),
.freq (freq),
.step_val (step_val)
);
phase_adder phase_adder_inst(
.clk (clk),
.rst_n (rst_n),
.wave_sel (wave_sel),
.step_val (step_val),
.addr (addr)
);
rom_ip rom_ip_inst (
.address ( addr ),
.clock ( clk ),
.q ( q )
);
amp_adj amp_adj_inst(
.clk (clk),
.rst_n (rst_n),
.wave (q),
.amp_factor (amp_adj),
.wave_adj (wave_adj)
);
endmodule
在设计的模块中,包含以下功能:
(1)通过 freq 信号输入需要的频率的值;
(2)通过 wave_sel 信号选择所需的波形;
(3)通过 amp_adj 信号选择波形放大的倍数。
在该设计中,包含 3 个模块:频率控制器,根据输入的频率值输出步进值 step_val 。
相位累加器,根据步进值 step_val 控制对应地址的变化。
波形放大器,对 rom 输出的数据进行放大。
3、设计实现
1、由于正弦波无法直接通过 FPGA 直接产生,因此在 FPGA 中例化一个位宽为 10bit、深度为 1024 的 rom ip核,采用 matlab 产生一个由 256 个采样点构成的正弦波形、一个由 256 个采样点构成的方波波形、一个由 256 个采样点构成的三角波波形、一个由 256 个采样点构成的锯齿波的数据点作为该 rom 的初始化文件。FPGA只需要从 ROM 中读取相应地址中的数据即可输出对应波形。
2、在产生正弦波的过程中,FPGA 读取 ROM 地址中的数据速率决定了正弦波的周期,例如,当 FPGA 的主时钟为 50MHz,在每个时钟上升沿读取完地址 0~255 这 256 个地址的数据后输出一个完整的正弦波,即经过了一个正弦波周期,此时波形周期为 20ns x 256 = 5120ns。如果每 2 个上升沿读取的地址才加 1, 则周期为 40ns x 256 = 10240ns 。如果每个上升沿读取的地址加 2, 则周期为 40ns x128= 2560ns 。因此通过这种改变方法可以改变输出波形的周期。
但是,此时只能输出某些频率的波形,因此在这里使用了 32 位的计数器cnt[31:0],把高 8 位给 addr[7:0],这样一来,如果 cnt 在上升沿加 1 ,需要 2^32 个时钟周期才能产生一个周期的正弦波,此时频率为 50MHz /(2^32),约为 0.01164Hz,即可以产生的正弦波最小频率为 0.01164Hz,通过增大 cnt 在每隔上升沿加的值 step_val 即可改变频率。
3、amp_adj[1:0] 信号可改变波形,将其赋给 addr[9:8],即可改变 rom 的基地址,通过这种方式改变波形。
4、amp_adj[1:0] 信号对波形进行放大,只要将得到的波形信号与该信号加 1 的值相乘即可。
4、总结
以上是 DDS 的设计思想,具体的实现源码可关注我的公众号:深入浅出玩儿转FPGA(fpgafun)即可获取。