在FPGA上设计一个DDS模块,在DE0 开发板上运行,在FPGA芯片内部合成出数字波形即可。不用输出模拟信号,本模块满足以下条件:
使用板载晶振的50MHz时钟,合成以下频率的信号
- 1 、500KHz 正弦波信号。 2、1MHz 正弦波信号。 3、3MHz 正弦波信号。
-频率字字长32位,波表ROM尺寸为 10比特地址,1024个word
- 波形格式为2补码格式,12比特量化
- 每个CLK输出一个有效样点。
- 输入信号为频率字和频率字输入使能信号
- 使用板载的拨码开关(Switch)控制生成的波形信号的不同频率。
设计中根据DDS输出波形频率的计算公式:
确定输出频率。
上式中为输出频率,为输入频率,即为系统基准时钟频率50MHz。m为地址加法器的宽度,K为频率控制字。设计中通过控制K的大小控制输出频率,由于K只能取整数,输出频率可能会有一定偏差。
module addr_cnt(CLK,sel_K,CLR,En,ROMaddr);
input CLK;
input [1:0] sel_K;
input En,CLR;
output [9:0] ROMaddr;
reg [9:0] cnt_out;
assign ROMaddr=cnt_out;
always @ (posedge CLK or negedge CLR)
if(~CLR)
cnt_out<=10'd0;
else if(~En)
cnt_out<=cnt_out;
else begin
case(sel_K)
2'b00:cnt_out<=cnt_out+10'b00000_01010;
2'b01:cnt_out<=cnt_out+10'b00000_10100;
default:cnt_out<=cnt_out+10'b00001_11101;
endcase
end
endmodule
module Sine_Signal (Dout,ROMaddr,CLK,CLR,En,sel_K); //顶层模块
output [11:0] Dout; //离散的正弦波形输出
output [9:0] ROMaddr; //ROM的地址
input CLK /* synthesis chip_pin="G21" */ ; //50MHz时钟
input CLR /* synthesis chip_pin="H2" */ ; //清零KEY0
input En /* synthesis chip_pin="J6" */; //使能SW0
input [1:0] sel_K /* synthesis chip_pin="H6,H5" */; //输出频率控制字选择SW2、SW1
addr_cnt U0_inst( //实例引用地址计数器模块
.CLK(CLK),
.CLR(CLR),
.En(En),
.sel_K(sel_K),
.ROMaddr(ROMaddr)
);
myROM myROM_inst( //实例引用上面定制的ROM模块
.address(ROMaddr), //ROM的地址输入端
.clken(En),
.clock(CLK), //时钟输入端
.q(Dout) //数据输出端
);
endmodule
#include
#include
#define PI 3.141593
#define DEPTH 1024 /* 数据深度,即存储单元的个数 */
#define WIDTH 12 /* 存储单元的宽度 */
int main(void)
{
int n,temp;
float v;
FILE * fp;
/* 建立文件名为sine1024.mif新文件,允许写入数据,
文件名随意,但扩展名必须为.mif */
fp = fopen("sine1024.mif","w+");
if(NULL==fp)
printf("Can not creat file!\r\n");
else
{
printf("File created successfully!\n");
/* 生成文件头,注意不要忘了";" */
fprintf(fp,"DEPTH=%d;\n",DEPTH);
fprintf(fp,"WIDTH=%d;\n",WIDTH);
fprintf(fp,"ADDRESS_RADIX = HEX;\n");
fprintf(fp,"DATA_RADIX = HEX;\n");
fprintf(fp,"CONTENT\n");
fprintf(fp,"BEGIN\n");
/* 以十六进制输出地址和数据 */
for(n=0;n/* 周期为1024个点的正弦波 */
v=sin(2*PI*n/DEPTH);
/* 将-1~1之间的正弦波的值扩展到0~4095之间 */
temp=(int)((v+1)*4095/2); //v+1将数值平移到0~2之间
/* 以十六进制输出地址和数据 */
fprintf(fp,"%x\t:\t%x;\n",n,temp);
}
fprintf(fp,"END;\n");
fclose(fp); //关闭文件
}
}
将signal tap采集的正弦波数据生成.txt文件保存,导入到matlab中分析频谱。
clear all; close all;
varnum; %调用signal tap 采集的正弦波数据,列矢量
signal=transpose(VarName); %转置
fs=50E6; %采样频率
N=1024; %采样点数
t=[0:1/fs:(N-1)/fs]; %采样时刻
figure(1);plot(t,signal);
title('正弦波信号');
xlabel('Time (s)');
ylabel('Magnitude');
Y = fft(signal,N); %做FFT变换
Ayy = abs(Y); %取模
Ayy=Ayy/(N/2); %换算成实际的幅度
Ayy(1)=Ayy(1)/2;
F=([1:N]-1)*fs/N; %换算成实际的频率值,Fn=(n-1)*Fs/N
figure(2);
stem(F(1:N/2),Ayy(1:N/2)); %显示换算后的FFT模值结果
axis([0 5E6 0 2500]);
title('正弦信号频谱图');
xlabel('Frequency (Hz)');
ylabel('Magnitude');
(1)f1=500kHz正弦波,地址位宽m=10,频率控制字K=10
(2)f2=1MHz正弦波,地址位宽m=10,频率控制字K=20
(2)f2=3MHz正弦波,地址位宽m=10,频率控制字K=61
由频谱图可以看出DDS输出的频率略小于实验要求的频率。这是由于实验中频率控制字只能取整数,略去了小数位,实际取值小于理论计算值。实验结果与理论推算结果是一致的。