DDS基本原理
DDS(Direct Digitial Synthesis):直接数字信号合成器
从图中我们可以看出DDS是由相位累加器、相位调制器、查找表(数据波形存储表ROM)以及D/A转换器组成。
相位累加器:相位以系统时钟周期为间隔进行等量增加,每个时钟周期相位增加一次,所有时钟周期累加在一起就构成了相位累加,故此将上述结构称为相位累加器。其映射关系式为
上图中为4个相位增量,即经过4个时钟周期相位的增量。
相位调制器:决定了输出信号的初始相位。
数据波形存储表:一个周期内的信号用多少位去存储,例如8位,它使用256个点存储一个周期的信号的幅值。
相位量化器:利用相位累加器输出的相位去寻址波形的查找表,由于在数字计算机中存储的数据类型是数字信息,故需要将模拟波形抽样量化
DDS的Matlab仿真
%% 利用Matalb对DDS进行仿真
clc;clear all;
f_out = 1e6; %输出频率
fs = 100e6 ; %采样率 = 时钟频率
N=256; %256个点,8位
sin_rom = sin(2*pi*(0:N-1)/N);
figure(1);
plot((0:N-1),sin_rom, xlabel('点数'),title('单位周期正弦波'));%给出单位周期的正弦波
%假设用32位去存储单位周期的正弦波 实际只使用了8位存储
F_WORD=f_out/fs*(2^32); %1Mhz 载波的频率控制字 f_out=fs*W/(N)
len = length(sin_rom)*fs/f_out; % 在单位周期内输出信号的数据长度 fs/f_out表示一个周期采样几个点
F_ADDR = 0; %初始相位
rom_addr = 0; %ROM地址初始化
sin_1m = zeros(1,len);
for i = 1:len
F_ADDR = F_ADDR + F_WORD; %相位累加
if(F_ADDR > 2^32)
F_ADDR = F_ADDR - 2^32; %相位溢出
end
rom_addr = round(F_ADDR /2^24);
%因此处F_WORD用32位宽的二进制表示 但ROM只有8位 依据数学计算 其ROM地址仅有高8位变化 所以上式要除以2的24次方
if rom_addr == 0
rom_addr = 1;
end
sin_1m(i) = sin_rom(rom_addr);
end
figure(2);
plot(sin_1m(1:len)), xlabel('点数'),title('一个周期的正弦波');
存储波形如下
通过DDS模拟仿真得图像
FPGA实现——基于ROM的DDS
使用matlab生成存储信号的COE文件
%% 创建一个含有N个点的频率为F的单位周期正弦信号(余弦、方波、锯齿波),用M位宽的二进制文本存储
%此处频率F不应该大于N,若大于就违反了奈奎斯特定理 即一个周期至少采样两个点才能完全恢复信号
%必须满足N/F>2;
clear all;clc
F=1;
N = 256 ;
M=8;
y = zeros(N , 1) ;%生成256行*1列的矩阵
y_integer = zeros(N , 1) ;%生成256行*1列的矩阵
y_hex = zeros(N , 1) ;%生成256行*1列的矩阵,十六进制
x=cos(2*pi*F*(0:N-1)/N);%正弦信号
% x=sin(2*pi*F*(0:N-1)/N);%余弦信号
% x=square(2*pi*F*(0:N-1)/N,50);%square(t,duty_cycle)为产生周期为2*pi,占空比为0~100的方波信号
% x = sawtooth(2*pi*F*(0:N-1)/N,0.5);%sawtooth(t,bia)产生周期为2*pi,峰尖值偏移度为0-1的锯齿波信号
for i = 1:1:N %循环1~256,累加1
y(i,1) = round((2^(M-1)-1)*x(i)) ;%round为四舍五入函数,输出范围为-127~127的余弦波数据
end
plot(y);%画图预览
fid = fopen('cos.coe','wt'); %创建一个名为cos_1024point.coe的文件 wt表示擦除里面内容重新写入数据以文本形式打开
%- COE 文件前置格式
fprintf( fid, 'MEMORY_INITIALIZATION_RADIX = 16;\n');
fprintf( fid, 'MEMORY_INITIALIZATION_VECTOR =\n');
%- 写数据
for i = 1:1:N
if (y(i,1)<0)
y_integer(i,1)=y(i,1)+2^M;%负数用补码表示
else
y_integer(i,1)=y(i,1);%正数的补码为原码
end
end
%进制转换
y_hex= dec2hex(y_integer);%因为dec2hex只能转换正数,因此先将y取补码 因取值-127~127 故用两位16进制的数表示
for i = 1:1:N
if(i == N)
%最后一个点时,标点为分号,其余时候为逗号
fprintf(fid,'%c%c;',y_hex(i,1),y_hex(i,2)); %输出16进制数据
% fprintf(fid,'%d,\n',y(i,1)); %输出10进制数据
else
fprintf(fid,'%c%c,\n',y_hex(i,1),y_hex(i,2)); %输出16进制数据
% fprintf(fid,'%d,\n',y(i,1)); %输出10进制数据
end
end
fclose(fid);%关闭文件
![](https://i-blog.csdnimg.cn/blog_migrate/c4c5ab7f013f936c764611fb3143cca6.jpeg)
顶层文件
module top_dds(
input clk,
input rst_n,
output [7:0]signal
);
parameter F_WORD=32'd42949672,P_WORD=32'd0;
wire [7:0] addr;
reg [31:0] reg_addr;
always@(posedge clk)begin
if(!rst_n)
reg_addr<=P_WORD;
else
reg_addr<=reg_addr+F_WORD;
end
assign addr=reg_addr[31:24];
blk_mem_gen_0 dds_cos (
.clka(clk), // input wire clka
.addra(addr), // input wire [7 : 0] addra
.douta(signal) // output wire [7 : 0] douta
);
endmodule
仿真文件
`timescale 1ns / 1ps
module tb_dds(
);
reg clk;
reg rst_n;
wire [7:0]signal;
parameter PERIOD = 4'd10;
always begin
clk = 1'b0;
#(PERIOD/2) clk = 1'b1;
#(PERIOD/2);
end
initial begin
rst_n=1'b0;
#30
rst_n=1'b1;
end
top_dds u_top_dds(
. clk(clk),
. rst_n(rst_n),
.signal(signal)
);
endmodule
FPGA实现——基于IP核
顶层文件
`timescale 1ns / 1ps
module top_DDS_IP(
input clk,
output [7:0]out_data
);
wire data_tvalid=1'b1;
dds_compiler_0 your_instance_name (
.aclk(clk), // input wire aclk
.m_axis_data_tvalid(data_tvalid), // output wire m_axis_data_tvalid
.m_axis_data_tdata(out_data) // output wire [7 : 0] m_axis_data_tdata
);
endmodule
仿真文件
`timescale 1ns / 1ps
module tb_top_dds_ip(
);
reg CLK;
wire [7:0]out_data;
parameter PERIOD = 5'd20;
initial begin
CLK = 1'b0;
#(PERIOD/2);
forever
#(PERIOD/2) CLK = ~CLK;
end
top_DDS_IP u_top_DDS_IP(
.clk(CLK),
.out_data(out_data)
);
endmodule
本文参考