DDS直接數字式頻率合成器(Direct Digital Synthesizer)。本文實現一個具有可以頻率可調、相位可調的正餘弦、方波、三角波的DDS。

DDS的原理如下圖,累加器每次累加一個頻率控制字,調節頻率控制字的數值,可以改變累加器的累加速度,進而可以調節從ROM查找表中讀取波形數據的速度。即頻率控制字越大,頻率越高。相位控制字可以用來調節初始相位,即ROM地址自加的初始值。

從查找表讀取出來的數據,經DA轉換晶元可以直接輸出進行濾波或其他操作,最後可使用示波器進行觀察波形變化。

DDS 模塊的輸出頻率是系統工作頻率、相位累加器比特數N以及頻率控制字K三者的一個函數,其數學關係由式如下。

DDS的頻率解析度,即頻率變化間隔。

本設計是調用Xilinx Vivado提供的ROM IP Core來存儲波形數據,首先要生成波形數據並添加至IP Core中,Xilinx的文件類型為coe格式。

MATLAB代碼

此MATLAB代碼提供正餘弦、方波、三角波四種波形的coe文件輸出,MATLAB腳本語言並不是很難,有編程基礎很快就能看懂,MATLAB提供的函數眾多,哪裡不會Google哪裡。

x=linspace(0,2*pi,4096);%6.28為2pi,一個周期採樣點取4096個
y1=cos(x)+1; %將函數平移到縱軸的正半軸。
y2=sin(x)+1;
y3=ceil(y1*511);
y4=ceil(y2*511);
?
%生成cos函數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函數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<=2047);
r=ceil(y*(2^9-1));
fid = fopen(square.coe,w); %寫到square.coe,用來初始化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); %寫到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函數

生成一個等差數列

y = linspace(x1,x2)
y = linspace(x1,x2,n)
?
y = linspace(x1,x2) 返回包含 x1x2 之間的 100 個等間距點的行向量。
?
示例
y = linspace(x1,x2,n) 生成 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 ?

生成線性間距向量 - MATLAB linspace - MathWorks 中國?

ww2.mathworks.cn

Verilog實現

生成coe文件後調用ROM IP Core,需要生成個ROM的讀取地址,主要代碼如下。

//freq_data為頻率控制字,phase_data為相位控制字。
always @(posedge clk_50MHz or negedge rst)begin
if(!rst)
fcnt <= 32d0;
else
fcnt <= fcnt + freq_data;
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
);

本設計調用了四個IP,將四種波形同時出處,然後用一個四選一的選擇器輸出給DA模塊至示波器顯示,模擬截圖如下。

DA轉換

D/A轉換器採用的是ADI公司的雙通道12位21.3MSPS高速DAC轉換器AD5447,FPGA 輸出的12位數字信號進入AD5447後,經過兩個鎖存器,轉換為差分信號輸出晶元內部結構圖如圖所示。

時序圖如圖所示。可以看到數據寫入晶元的的過程也是十分簡單,只要CS拉低就可以進行讀操作或寫操作。

晶元的輸入數據是並行的,我這裡輸入的數據就是MATLAB的直接生成的12bit的數據,輸出電壓的幅值手冊給出了計算公式,清晰明了。

sin和cos的波形是一樣的,將三種波形輸出用示波器查看。波形很漂亮。

這個工程,我放在GitHub上了。訂閱號後台回復「DDS」即可獲得這個工程。

歡迎持續關注 硅農 訂閱號,隨後持續推出 基於FPGA的無線通信基礎 系列文章。點擊訂閱號左下角,子菜單欄 無線通信基礎,可以把查看所有文章及目錄。


推薦閱讀:
相关文章