DDS直接數(shù)字式頻率合成器(Direct Digital Synthesizer)。本文實(shí)現(xiàn)一個(gè)具有可以頻率可調(diào)、相位可調(diào)的正余弦、方波、三角波的DDS。
DDS的原理如下圖,累加器每次累加一個(gè)頻率控制字,調(diào)節(jié)頻率控制字的數(shù)值,可以改變累加器的累加速度,進(jìn)而可以調(diào)節(jié)從ROM查找表中讀取波形數(shù)據(jù)的速度。即頻率控制字越大,頻率越高。相位控制字可以用來(lái)調(diào)節(jié)初始相位,即ROM地址自加的初始值。
從查找表讀取出來(lái)的數(shù)據(jù),經(jīng)DA轉(zhuǎn)換芯片可以直接輸出進(jìn)行濾波或其他操作,最后可使用示波器進(jìn)行觀(guān)察波形變化。
DDS 模塊的輸出頻率是系統(tǒng)工作頻率、相位累加器比特?cái)?shù)N以及頻率控制字K三者的一個(gè)函數(shù),其數(shù)學(xué)關(guān)系由式如下。
DDS的頻率分辨率,即頻率變化間隔。
本設(shè)計(jì)是調(diào)用Xilinx Vivado提供的ROM IP Core來(lái)存儲(chǔ)波形數(shù)據(jù),首先要生成波形數(shù)據(jù)并添加至IP Core中,Xilinx的文件類(lèi)型為coe格式。
MATLAB代碼
此MATLAB代碼提供正余弦、方波、三角波四種波形的coe文件輸出,MATLAB腳本語(yǔ)言并不是很難,有編程基礎(chǔ)很快就能看懂,MATLAB提供的函數(shù)眾多,哪里不會(huì)Google哪里。
x=linspace(0,2*pi,4096);%6.28為2pi,一個(gè)周期采樣點(diǎn)取4096個(gè)
y1=cos(x)+1; %將函數(shù)平移到縱軸的正半軸。
y2=sin(x)+1;
y3=ceil(y1*511);
y4=ceil(y2*511);
%生成cos函數(shù)coe文件
fid = fopen(‘cos_coe.coe’,‘wt’);
fprintf(fid,‘MEMORY_INITIALIZATION_RADIX=10; ’);
fprintf(fid,‘MEMORY_INITIALIZATION_VECTOR= ’);
%fprintf(fid,‘%16.0f ’,y3);
for i = 1:1:2^12
fprintf(fid,‘%16.0f’,y3(i));
if i==2^12
fprintf(fid,‘;’);
else
fprintf(fid,‘,’);
end
if i%15==0
fprintf(fid,‘ ’);
end
end
fclose(fid);
%生成sin函數(shù)coe文件
fid = fopen(‘sin_coe.coe’,‘wt’);
fprintf(fid,‘MEMORY_INITIALIZATION_RADIX=10; ’);
fprintf(fid,‘MEMORY_INITIALIZATION_VECTOR= ’);
for i = 1:1:2^12
fprintf(fid,‘%16.0f’,y4(i));
if i==2^12
fprintf(fid,‘;’);
else
fprintf(fid,‘,’);
end
if i%15==0
fprintf(fid,‘ ’);
end
end
fclose(fid);
%生成方波
t=1:1:2^12;
y=(t r=ceil(y*(2^9-1));
fid = fopen(‘square.coe’,‘w’); %寫(xiě)到square.coe,用來(lái)初始化rom_square
fprintf(fid,‘MEMORY_INITIALIZATION_RADIX=10; ’);
fprintf(fid,‘MEMORY_INITIALIZATION_VECTOR= ’);
for i = 1:1:2^12
fprintf(fid,‘%d’,r(i));
if i==2^12
fprintf(fid,‘;’);
else
fprintf(fid,‘,’);
end
if i%15==0
fprintf(fid,‘ ’);
end
end
fclose(fid);
%生成三角波
t=1:1:2^12;
y=[0.5:0.5/1024:1-0.5/1024, 1-0.5/1024:-0.5/1024:0, 0.5/1024:0.5/1024:0.5];
r=ceil(y*(2^9-1));
fid = fopen(‘triangular.coe’,‘w’); %寫(xiě)到triangular.coe,初始化三角波rom
fprintf(fid,‘MEMORY_INITIALIZATION_RADIX=10; ’);
fprintf(fid,‘MEMORY_INITIALIZATION_VECTOR= ’);
for i = 1:1:2^12
fprintf(fid,‘%d’,r(i));
if i==2^12
fprintf(fid,‘;’);
else
fprintf(fid,‘,’);
end
if i%15==0
fprintf(fid,‘ ’);
end
end
fclose(fid);
linspace函數(shù)
生成一個(gè)等差數(shù)列
y = linspace(x1,x2)
y = linspace(x1,x2,n)
y = linspace(x1,x2) 返回包含 x1 和 x2 之間的 100 個(gè)等間距點(diǎn)的行向量。
示例
y = linspace(x1,x2,n) 生成 n 個(gè)點(diǎn)。這些點(diǎn)的間距為 (x2-x1)/(n-1)。
y1 = linspace(-5,5,7)
y1 = 1×7
-5.0000 -3.3333 -1.6667 0 1.6667 3.3333 5.0000 ?
https://ww2.mathworks.cn/help/matlab/ref/linspace.html?requestedDomain=zh
Verilog實(shí)現(xiàn)
生成coe文件后調(diào)用ROM IP Core,需要生成個(gè)ROM的讀取地址,主要代碼如下。
//freq_data為頻率控制字,phase_data為相位控制字。
always @(posedge clk_50MHz or negedge rst)begin
if(!rst)
fcnt else
fcnt end
assign addra = fcnt[31:20] + phase_data;
blk_mem_sin blk_mem_sin_inst (
.clka(clk_50MHz), // input wire clka
.addra(addra), // input wire [11 : 0] addra
.douta(sin) // output wire [11 : 0] douta
);
本設(shè)計(jì)調(diào)用了四個(gè)IP,將四種波形同時(shí)出處,然后用一個(gè)四選一的選擇器輸出給DA模塊至示波器顯示,仿真截圖如下。
DA轉(zhuǎn)換
D/A轉(zhuǎn)換器采用的是ADI公司的雙通道12位21.3MSPS高速DAC轉(zhuǎn)換器AD5447,FPGA 輸出的12位數(shù)字信號(hào)進(jìn)入AD5447后,經(jīng)過(guò)兩個(gè)鎖存器,轉(zhuǎn)換為差分信號(hào)輸出芯片內(nèi)部結(jié)構(gòu)圖如圖所示。
時(shí)序圖如圖所示??梢钥吹綌?shù)據(jù)寫(xiě)入芯片的的過(guò)程也是十分簡(jiǎn)單,只要CS拉低就可以進(jìn)行讀操作或?qū)懖僮鳌?/p>
芯片的輸入數(shù)據(jù)是并行的,我這里輸入的數(shù)據(jù)就是MATLAB的直接生成的12bit的數(shù)據(jù),輸出電壓的幅值手冊(cè)給出了計(jì)算公式,清晰明了。
Sin和Cos的波形是一樣的,將三種波形輸出用示波器查看。波形很漂亮。
這個(gè)工程,我放在GitHub上了。訂閱號(hào)后臺(tái)回復(fù)“DDS”即可獲得這個(gè)工程。
GitHub 正式宣布了 GitHub Free 以及 GitHub Enterprise。GitHub Free:包含不限量的私有倉(cāng)庫(kù),每個(gè)私有倉(cāng)庫(kù)里的項(xiàng)目最多可以與 3 人分享協(xié)作。
評(píng)論
查看更多