前言:
DDS的频率精度受限于\(fs/2^N\),其中,\(2^N\)为查找表深度。假设采样率12MHz,地址位宽16位,则最小频率步进为\(\Delta f=12e6/2^{16}=183Hz\)。也就是我们只能产生\(183Hz,183*2Hz\)等频率的信号。如果由于项目需求,我们需要产生更精细的频率,比如10Hz分辨率的信号,由于存储受限,也无法增加存储深度,那该怎么做呢?
上面精度范围有限的原因是固定了频率控制字的数值。如果每次频率控制字可变,则其精度可以更高。通常来说,DDS会在FPGA上实现,固定精度的DDS 很容易实现,那可变精度的呢?说起来容易,在Verilog上怎么实现呢?
首先,假定我们需要产生模拟域频率为\(f_c\)的正弦信号,时钟频率为\(f_s\),采样周期为\(T_s=1/fs\)。
如果查找表存了整个周期的正弦值,那么地址范围从\([0:1:2^N-1]\)对应了相位的\([0:1/2^N:1-2^{-N}]\),在式\(\eqref{signal_expression}\)中,相位为
那么查找表的地址就应该是
当时间无限延展,地址也会无限扩展,显然,由于信号是周期的,我们可以对地址进行绕回,也就是
如此生成的正弦波,将会是一个更为精确的数字。
算法中有一个关键的问题:
1.n是无限延展的,如何用定点数表示?
2.如何进行mod操作?
先来解答第一个问题,分析式\(\eqref{addr}\),可以看出,每次addr的增量是固定的,即fc/fs2^N,那么我们可以把乘法替代为累加运算,也就是改写为addr = addr + fc/fs2^N的形式,发生溢出时正常溢出即可,因为无符号数溢出不会影响低位的正确性。
第二个问题,如何进行mod操作?这个也简单,直接取整数部分的低位即可。
给出一个设计的例子。
\(fs=12MHz,\ fc = 18Hz\),查找表深度为\(2^16\)。
首先,对 1/fs 进行定点化。
>> fi(1/12e6,0,12)ans = 8.3324e-08DataTypeMode: Fixed-point: binary point scalingSignedness: UnsignedWordLength: 12FractionLength: 35
>> bin(ans)ans ='101100101111'
对输入频率进行量化,假定指定输入频率定点方案为u(16,0),即输入范围为0Hz~2^17-1Hz
对其进行全精度运算,const0=u(12,19)*u(16,0)=u(28,19)
计算地址增量,对const0乘2^16,相当于小数点右移16bit,结果为addr_delta = u(28,3),二进制表示不变
对addr_temp做加法,addr_temp仍然为u(28,3)
addr为取了整数位的addr_temp,即量化方式位wrap,量化结果位u(16,0)
给出伪Verilog代码:
module dds_inftyPrecise
(
input clk,rstn
input [15:0] fc,
output [15:0] addr
)
wire [11:0] 1_fs = 12'b101100101111;
wire [27:0] const0 = fc*1_fs;
wire [27:0] addr_delta = const0;
reg [27:0] addr_temp;
always @(posedge clk or negedge rstn)
if(rstn==0)addr_temp<=0;
elseaddr_temp<=addr_temp+addr_delta;assign addr = addr_temp[18:3];
endmodule