1. DDS 基础:从概念到 FPGA 实现
直接数字频率合成(DDS)是现代数字信号处理中的核心技术,它通过纯数字方式生成精确控制的模拟波形。例如,使用 50MHz 时钟产生 1MHz 正弦波,传统方法可能需要复杂的模拟电路,而 DDS 只需要几个数字模块就能优雅地实现。
DDS 的核心思想其实很简单:用一个相位累加器跟踪波形当前的位置,然后用查找表(LUT)把相位转换成实际的幅度值。相位累加器就像是一个指针,不断在波形周期内循环移动;LUT 则像是一张地图,告诉指针每个位置对应的幅度值。通过控制相位累加器的步长,我们就能控制波形生成的速度,从而控制输出频率。
在 FPGA 中实现 DDS 特别有优势。FPGA 的并行处理能力让 DDS 可以实时生成波形,而且通过编程可以轻松调整频率、相位甚至波形形状。下面我们先来看看最简单的 DDS 实现——方波生成。
2. 方波生成:最简单的 DDS 实现
让我们从最简单的波形开始——方波。虽然方波看起来简单,但它能很好地展示 DDS 的基本原理。下面这段 Verilog 代码就是一个最简单的 DDS 方波发生器:
module SimpleDDS(
input DAC_clk, // 时钟输入,比如 50MHz
output reg [9:0] DAC_data // 10 位 DAC 输出
);
// 16 位自由运行的计数器
reg [15:0] cnt;
always @(posedge DAC_clk)
cnt <= cnt + 16'h1;
// 取出计数器的第 8 位作为方波输出
wire cnt_tap = cnt[7];
// 将这一位复制 10 次形成 10 位 DAC 输出
assign DAC_data = {10{cnt_tap}};
endmodule
这段代码的工作原理是:计数器 cnt 每个时钟周期加 1,相当于相位在不断累加。当我们取 cnt[7](第 8 位)时,实际上是在每个计数周期内,前 128 个时钟周期输出 0,后 128 个时钟周期输出 1,这样就产生了占空比 50% 的方波。
输出频率的计算也很直观:如果时钟频率是 50MHz,计数器的第 8 位翻转频率是 50MHz/256 ≈ 195.3kHz。因为 256(2⁸)个时钟周期才完成一个完整的方波周期。这种方法的妙处在于,通过选择不同的计数器位,我们可以得到不同频率的方波。比如选择 cnt[6] 会得到约 390.6kHz 的方波,频率正好翻倍。
不过要注意,这种方法产生的方波频率分辨率有限,而且只能产生特定频率的方波。想要更精确的频率控制和更复杂的波形,就需要更完整的 DDS 结构。
3. 相位累加器:DDS 的心脏
相位累加器是 DDS 系统的核心,它决定了输出信号的频率精度和稳定性。可以把相位累加器想象成一个不断转动的圆盘,圆盘的周长就是波形的整个周期(360 度或 2π弧度),而累加器的步长决定了圆盘转动的速度。
在 FPGA 中,相位累加器通常由一个寄存器和一个加法器组成。每个时钟周期,加法器将当前相位值加上一个预设的相位增量(Frequency Control Word,频率控制字),结果存回寄存器。数学上可以表示为:
相位[n] = (相位[n-1] + Δ相位) mod 2^N
其中 N 是相位累加器的位宽,mod 运算保证了相位在达到最大值后自动回绕,形成周期性的波形。
输出频率的计算公式很直观:
f_out = (Δ相位 × f_clk) / 2^N
这里 f_clk 是系统时钟频率,N 是相位累加器位宽。举个例子,如果 f_clk=50MHz,N=32,那么频率分辨率就是 50MHz/2³² ≈ 0.0116Hz!这意味着你可以产生极其精确的频率,这是模拟方法难以实现的。
在实际实现中,相位累加器的位宽选择很重要。位宽太小时频率分辨率不够,太大时又浪费资源。通常根据项目需求来选择:对于一般应用,24-32 位是不错的选择;对于需要极高精度的场合,比如专业测试设备,可能会用到 48 位甚至更高。

