##方案描述:
- 设计一个DDS,50MHz的时钟速率,输出波形频率为1MHz,输出数据格式为2补码
- 波表ROM由MATLAB生成,8bit地址,10bit量化
- 相位累加器为32bit
- 首先在SignalTap里面观察波形的正确性,然后把SignalTap的数据导入至MATLAB进行分析
##DDS原理:
其中fo为输出频率,fc为时钟频率,N为相位累加器的位数,K为频率控制字。其中频率控制字作为相位累加器的输入,相位累加器的高P位输出作为ROM表的输入,输出为相位对应的幅值。从未达到频率对应幅值的效果,产生数字的正弦信号。
##Verilog代码及输出文件:
###顶层文件
/*-----------------------------------------------------
Author : madezhuang Communication University of China
Technology blogs : http://blog.csdn.net/proton_boke
Email address : mdz800@cuc.edu.cn
module function :
Simulate the function of dds to generate a sine wave
Data : 2017-04-01
Version : 1.0
-----------------------------------------------------*/
module Top
(
CLK , // Global clock 50MHz
RST , // Global reset
FWEN, // inputfrequency word update enable
RD , // read data
CLKOUT // Accumulator overflow flag
);
input CLK;
input RST;
input FWEN;
output CLKOUT;
output [9:0] RD;
reg [9:0] RA;
wire [31:0] FQWD; // output frequency word
reg [32-1:0] FWIN;
wire [7 :0] ADD_R; // ROM_dress
//--------------------------------------------
//generate_freq_word
generate_freq_word u_generate_freq_word
(
FQWD,
FWEN
);
//--------------------------------------------
always @(FQWD)
begin
FWIN <= FQWD;
end
//--------------------------------------------
//phase_adder
phase_adder u_phase_adder
(
CLK ,
RST ,
FWIN ,
ADD_R ,
CLKOUT
);
//--------------------------------------------
always @(ADD_R)
begin
RA <= ADD_R;
end
//--------------------------------------------
//Output discrete sine waveforms
DDS_CORE_ROM u_DDS_CORE_ROM
(
CLK ,
RA ,
RD
);
//--------------------------------------------
endmodule
###产生频率控制字
module generate_freq_word(
FQWD , // output frequency word
FWEN ); // input frequency word update enable
parameter VAL_FREQ = 32'h051E_B851;
// 50MHz clock, 1MHz out wave freqword value
output [31:0] FQWD;
input FWEN;
reg [31:0] FQWD;
always @(FWEN)
begin
if(FWEN)
FQWD <= VAL_FREQ;
else
FQWD <= 0;
end
endmodule
###相位累加器
module phase_adder(
CLK , // clock, posedge valid
RST , // reset, high level reset
FWIN , // input frequency word
ADD_R , // output ROM_dress
CLKOUT // output clock
);
input CLK;
input RST;
input [32-1:0] FWIN;
output[8 -1:0] ADD_R;
output CLKOUT;
reg [32 -1:0] fwin_R; // freq word DFF
reg [32 -1:0] acc_R; // phase ACC DFF
reg [8 -1:0] ADD_R; // ROM_dress
reg CLKOUT;
always @ (posedge CLK or posedge RST) begin
if(RST) begin
fwin_R <= 0;
acc_R <= 0;
end
else begin
// update fwin_R DFF
fwin_R <= FWIN;
// update acc_R
acc_R <= fwin_R + acc_R;
// update addr_R, the acc_R high RA_WL is rom address
ADD_R <= acc_R[32-1:24];
end
end
always @(posedge CLK or posedge RST) begin
if(RST)
CLKOUT <= 1'b0;
else if(acc_R < 32'h7FFF_FFFF)
CLKOUT <= 1'b0;
else
CLKOUT <= 1'b1;
end
endmodule
###DDS波表ROM
module DDS_CORE_ROM(
CLK , // clock
RA , // read address
RD ); // read data
input CLK;
input [7 :0] RA;
output [9 :0] RD;
reg [9 :0] RD;
always @ (posedge CLK)
case(RA)
8 'd 0 :RD = #1 10'b 0000000000; // 0 0x0
8 'd 1 :RD = #1 10'b 0000001100; // 12 0xC
8 'd 2 :RD = #1 10'b 0000011001; // 25 0x19
8 'd 3 :RD = #1 10'b 0000100101; // 37 0x25
8 'd 4 :RD = #1 10'b 0000110010; // 50 0x32
8 'd 5 :RD = #1 10'b 0000111110; // 62 0x3E
8 'd 6 :RD = #1 10'b 0001001010; // 74 0x4A
8 'd 7 :RD = #1 10'b 0001010111; // 87 0x57
...............//省略了
8 'd 169 :RD = #1 10'b 1001010001; // -431 0x251
8 'd 170 :RD = #1 10'b 1001001010; // -438 0x24A
8 'd 171 :RD = #1 10'b 1001000100; // -444 0x244
8 'd 172 :RD = #1 10'b 1000111110; // -450 0x23E
8 'd 173 :RD = #1 10'b 1000111000; // -456 0x238
8 'd 174 :RD = #1 10'b 1000110011; // -461 0x233
8 'd 175 :RD = #1 10'b 1000101101; // -467 0x22D
...............//省略了
8 'd 249 :RD = #1 10'b 1110101001; // -87 0x3A9
8 'd 250 :RD = #1 10'b 1110110110; // -74 0x3B6
8 'd 251 :RD = #1 10'b 1111000010; // -62 0x3C2
8 'd 252 :RD = #1 10'b 1111001110; // -50 0x3CE
8 'd 253 :RD = #1 10'b 1111011011; // -37 0x3DB
8 'd 254 :RD = #1 10'b 1111100111; // -25 0x3E7
8 'd 255 :RD = #1 10'b 1111110100; // -12 0x3F4
default : RD = #1 0;
endcase
endmodule
###Block Diagram File
###SignalTap
##将SignalTap中的数据导入至MATLAB分析:
在SignalTap文件sin_wave_out位置单击鼠标右键选择Create signal tapⅡ list file,生成如下图所示的数据
利用文件编辑器Utradit文件编辑器,选择需要的数据,保存为.m文件。之后就可以在matlab中对此文件进行调用。
##通用正弦信号频谱分析的MATLAB代码:
- 生成一个1k的标准正弦信号,然后由32k去采样,采样点数为64
- 此时包含 64/(32kHz/1kHz) = 2个周期的样点
- 结果非常之好,好的接近理想程度了,现实世界中的电子热运动噪声才-180dB,案例1中的噪声被压制到了-300dB以下,已经非常满足工程上面的测量需求了。
%/
%用途:正确的正弦信号频谱分析的matlab代码
%方案,设计生成一个标准的正弦信号,对其作fft变换分析频谱,若与实际情况一致,则证明matlab代码可用
%作者:中国传媒大学 马德壮 PS:几乎未改动的来自杜伟韬杜老师
%/
clc;
clear all;
fc = 1e3; %正弦信号频率
fs = 32e3; %采样频率
N = 64; %采样点数,则此时采样点数共包含N/(fs/fc) = 2 个周期正弦信号
idx = [0:N-1];
x1 = sin(2*pi*fc/fs*idx); %t = 1/fs*[0:N-1]
figure;
subplot(2,2,1:2); %将绘图窗口分为两行两列的,其中第一幅图占据第一行的第一、二列
stem(x1); %将以火柴柱状图画出
grid on; %添加网格线
title('Sampled Sine Signal', 'fontsize',14); %改变标题字体的大小至14
%频谱实现左右对称,横轴为频率,纵轴为幅度值
y1 = fftshift ( fft(x1) );
y1_abs = abs(y1);
subplot(2,2,3);
f = linspace(-(fs/2),(fs/2), N);
stem(f,y1_abs,'LineWidth',1,'MarkerSize',6);
xlim([-(fs/2) (fs/2)]);
grid on;
title('DFT Amplitude in Linear scale', 'fontsize',14);
xlabel('Frequency(Hz)');
ylabel('Magnitude');
y1_abs_dB = 20*log10(y1_abs);
min_y1 = min(y1_abs_dB);
subplot(2,2,4);
% f = linspace(-(fs/2),(fs/2), N);
stem(f,y1_abs_dB, 'LineWidth',1,'MarkerSize',6,'BaseValue',min_y1);
xlim([-(fs/2) (fs/2)]);
grid on;
title('DFT Amplitude in dB scale','fontsize',14);
xlabel('Frequency(Hz)');
ylabel('Magnitude(dB)');
% %频谱未搬移,即频谱不对称
% y1 = fft(x1);
% y1_abs = abs(y1);
% subplot(2,2,3);
% stem(y1_abs);
% grid on;
% title('DFT Amplitude in Linear scale', 'fontsize',14);
%
% %纵轴以分贝显示,频谱未搬移,即频谱不对称
% y1_abs_dB = 20*log10(y1_abs);
% min_y1 = min(y1_abs_dB);
% subplot(2,2,4);
% stem(y1_abs_dB, 'LineWidth',1,'MarkerSize',6,'BaseValue',min_y1);
% % stem(y1_abs_dB); 采用这条代码时会发生错误,错误原因暂不去查明
% grid on;
% title('DFT Amplitude in dB scale', 'fontsize',14);
##将上述代码稍加改动的分析来自SignalTap中的数据:
###第一次代码
%/
%用途:对DDS产生的正弦信号进行FFT变换,频谱分析
%作者:中国传媒大学 马德壮
%/
clc;
clear all;
sin_wave_out; %调用sin_wave_out里面的数据
signal = reshape(sin_wave_data, 1024, 1)/512;
fc = 1e6; %正弦信号频率
fs = 50e6; %采样频率
N = 1024; %采样点数,则此时采样点数共包含N/(fs/fc) = 10.24 个周期正弦信号
figure; subplot(4,4,1:4); %将绘图窗口分为两行两列的,其中第一幅图占据第一行的第一、二、三、四列
plot([0:N-1],signal);
title('Sine Signal', 'fontsize',14); %改变标题字体的大小至14
subplot(4,4,5:8);
stem(signal);
xlim([100,200]);
grid on; %添加网格线
title('Sampled Sine Signal(Partially displayed)', 'fontsize',14); %改变标题字体的大小至14
%频谱实现左右对称,横轴为频率,纵轴为幅度值
y1 = fftshift ( fft(signal) );
y1_abs = abs(y1);
subplot(4,4,9:12);
f = linspace(-(fs/2),(fs/2), N);
plot(f,y1_abs);
grid on;
title('DFT Amplitude in Linear scale', 'fontsize',14);
xlabel('Frequency(Hz)'); ylabel('Magnitude');
y1_abs_dB = 20*log10(y1_abs);
subplot(4,4,13:16);
plot(f,y1_abs_dB,'linewidth',1);
grid on;
title('DFT Amplitude in dB scale','fontsize',14);
xlabel('Frequency(Hz)'); ylabel('Magnitude(dB)');
###第一次波形
###第一波小分析
%///
%结果显示:(单一频率正弦波,强度适中)噪声太大,即频谱泄露严重
%结果分析:采样点数不是整数个周期造成频谱泄露
%改进方法:不管是否乐意,我们拿过来一段有限长序列,等价于对序列在时域乘了一个矩形窗,于是等价于在频域对信号的频谱与矩形窗的频谱做卷积。而我们知道,矩形窗的频域即为抽样函数,这就导致正弦信号的尖峰在整个频域扩展开来。因此,可以选择一个合适的窗,以改进频谱泄露。
%///
###第二次代码(加kaiser窗)
clc;
clear all;
sin_wave_out; %调用sin_wave_out里面的数据
signal = reshape(sin_wave_data, 1024, 1)/512;
kaiser_Beta = 12;
N = 1024; %采样点数,则此时采样点数共包含N/(fs/fc) = 20.48 个周期正弦信号
win = kaiser(N,kaiser_Beta); %凯撒窗
x1 = signal.*win;
fc = 1e6; %正弦信号频率
fs = 50e6; %采样频率
figure; subplot(3,3,1:3);
plot([0:N-1],x1);
grid on; %添加网格线
title('Sine Signal(Kaiser window,Beta 14)', 'fontsize',10); %改变标题字体的大小至14
%频谱实现左右对称,横轴为频率,纵轴为幅度值
y1 = fftshift ( fft(x1) );
y1_abs = abs(y1);
subplot(3,3,4:6);
f = linspace(-(fs/2),(fs/2), N);
plot(f,y1_abs);
xlim([-(fs/2) (fs/2)]);
grid on;
title('DFT Amplitude in Linear scale', 'fontsize',14);
xlabel('Frequency(Hz)'); ylabel('Magnitude');
y1_abs_dB = 20*log10(y1_abs);
subplot(3,3,7:9);
f = linspace(-(fs/2),(fs/2), N);
plot(f,y1_abs_dB);
xlim([-(fs/2) (fs/2)]);
grid on;
title('DFT Amplitude in dB scale','fontsize',14);
xlabel('Frequency(Hz)'); ylabel('Magnitude(dB)');
###第二次波形
###第二波小分析
%///
%结果显示:(单一频率正弦波,强度适中)噪声仍然很大
%结果分析:采用的是kaiser窗,Beta为14,比较第一次波形虽有改善,但噪声仍未被压制到-40dB以下,不明白原因
%///