DDS原理及简单应用
DDS原理学习
何为DDS
DDS定义与优点
DDS: Direct digital synthesis 直接数字频率合成,可以用来合成周期性的信号序列。
优点:频率转换时间短;频率分辨率极高;相位变化连续;输出波形灵活
DDS原理
在参考时钟的驱动下,相位累加器对频率控制字进行线性累加,得到的相位码对波形存储器寻址,使之输出相应的幅度码,经过模数转换器得到相应的阶梯波。

也就是,我们事先将想要的波形所对应的数值按照一定的要求存储于ROM 中,然后在硬件工作的时候,对 ROM 进行寻址,得到相应的波形数据,再通过 D/A 输出,以此获得我们目标波形。
角度与波形
从 DDS 的原理可以看出,DDS 可以产生任意周期性的波形,比如 sine,cosine,三角波等。

以 sine 为例,可以看出来,波形电平最大值等于圆的半径,产生的波形的频率等价于扫过 2π 角度的速度。
频率控制字

从上图可以看出来,输出信号的频率与扫描的角速度是有关系的。
$$
频率=\frac{2π}{角速度}
$$
但由于波形数据是存储在 ROM 中的,这就意味着其相位的步进是离散的,这时候需要引入频率控制字的概念。

-
举例

- 频率控制字与输出信号频率之间的关系
当前你在 ROM 中存储了 1024 个 sine 波形数据,而你要读取哪个波形数据是完全取决于相位累加器的输出的,比如当前相位累加器输出是 10,则你会读取 ROM 中地址为 10 的数据。相位累加器的步进值又取决于频率控制字 M,如果 M = 1,则系统会按照顺序读取所有 ROM 中的数据,那如果频率控制字 M = 10,则系统会在 ROM 中每 10 个数据读取一次,可想而知,波形没那么好看了(可能会产生锯齿),但是由于扫描一周的时间变短了,所以对应频率更高。
- 频率控制字与输出信号频率之间的关系
硬件实现
思考
-
求 $131.072MHz $系统时钟,要求精度为 $1kHz$ 时的 $N$。输出精度相当于$M = 1$$ $时的输出频率。
答:$N = 17$,意味着 $ROM$ 核的地址有 $17bit$,因此深度为$2 ^ {17} = 131, 072$。若要求精度为 $1Hz$,深度就会更大。
-
如果我想获得更大的可调频率范围,那频率控制字 $M$ 有可能为小数吗?或者是当前 $ROM $的寻址范围有限,那我有可能产生比取 $M = 1$还低的输出信号频率吗?如果可以,那频率控制字$ M = 0.5, M = 0.25$ 在$ Verilog$ 中该怎么实现呢?
答:可以通过对相位累加器的输出进行截断来实现。
地址截断

一般舍去较低为位数,只取 N 的高 A 位(如高 10 位或 14 位)做为存储器地址,使得相位的低位被截断(即相位截尾)。可以参考如下代码。
1 | reg [19:0] addr = 20'd0; |
具体实现步骤

仿真设计实现
MATLAB
生成coe文件
1 | clear;clc;close all; |
上述代码,$N = 1024$,正弦函数,量化时乘以$2^{13}-1$,其原因在于量化$14Bit$,有符号数的范围为$-2^{13}$~$2^{13}-1$;正数少一个,所以只能量化到$2^{13}-1$

Vivado
仿真中遇到的问题及注意事项
无地址截断仿真
- Design文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
module dds_work(
input clk,
input rst_n,
output [13:0] dds_out
);
reg [9:0] addr = 10'd0;
//addr
always @(posedge clk or negedge rst_n) begin
if (rst_n) begin
addr <= 10'd0;
end
else begin
addr <= addr + 10'd1;
end
end
sin_rom sin_rom_m (
.clka(clk), // input wire clka
.addra(addr), // input wire [9 : 0] addra
.douta(dds_out) // output wire [13 : 0] douta
);
endmodule - testbench文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
module tb_sinrom(
);
reg clk;
reg rst_n;
wire [13:0] dds_out;
initial begin
clk = 1'b0;
end
always #10 clk = ~clk; //50MHz clock
initial begin
rst_n = 1'b1;
#200
rst_n = 1'b0;
end
dds_work dds_work_m (
.clk(clk),
.rst_n(rst_n),
.dds_out(dds_out)
);
endmodule-
仿真波形

$T = 20.66us$(稍有误差,手拖动的)
$$
f_o={M×f_{clk} \over 2^N}
$$
即$fo={1×50×10^6 \over 2^{10} }$; 所以 $T =1/f_o=1024/50us=20.48us $
故而可以认为输出正确
-
有地址截断仿真
- Design文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23module dds_top(
input clk,
input rst_n,
output [13:0] dds_out
);
reg [19:0] addr = 20'd0;
parameter add_M = 20'd1;
//addr
always @(posedge clk) begin
if (rst_n == 1'b1) begin
addr[19:0] <= 20'd0;
end
else begin
addr[19:0] <= addr[19:0] + add_M;
end
end
sine_rom sin_rom_m (
.clka(clk), // input wire clka
.addra(addr[19:10]), // input wire [9 : 0] addra
.douta(dds_out) // output wire [13 : 0] douta
);
endmodule - Testbench文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22module tb_sinrom(
);
reg clk;
reg rst_n;
wire [13:0] dds_out;
initial begin
clk = 1'b0;
end
always #10 clk = ~clk; //50MHz clock
initial begin
rst_n = 1'b1;
#200
rst_n = 1'b0;
end
dds_top dds_top_m (
.clk(clk),
.rst_n(rst_n),
.dds_out(dds_out)
);
endmodule-
仿真输出

上述图看到,周期大约为21ms
计算验证:$ T = 1/f_o = 2 ^ {20} \times1 \times10 ^ 3 / 50 \times 10 ^ 6 ms = 20.97152 ms $
该设计是为了证明可通过截取地址来实现更低频率的信号,解决了 $N$ 有限导致的问题
-







