記錄背景:昨晚快下班時,與同事rk聊起怎么用FPGA實現正弦波的輸出。我第一反應是利用高頻的PWM波去濾波,但感覺這樣的波形精度肯定很差;后來想起之前由看過怎么用FPGA產生正弦波的技術,但怎么都想不起來這個技術的名稱是叫什么了。后來搜索后才知道就是DDS(Direct Digital Synthesizer),即:直接數字頻率合成器。
最近(好吧,不是最近,是一直)發現自己記東西記不牢,究竟是自己老了,記憶力下退;還是自己不用心去記;還是因為看得少,缺少實踐(畢竟紙上得來終覺淺、絕知此事要躬行)。
-----------------------------分割線-----------------------------------
以下內容主要轉自:http://blog.chinaaet.com/lincoding/p/5100050592,如有侵權,請告知刪除。抱歉!
DDS的主要組成部分:相位累加器、相位調制器、波形數據表、DAC和低通濾波器四大部分組成。如下圖:
DDS的原理:
1、首先ROM中要存放好要顯示的正弦波數據;
2、然后由相位累加器(其實就是個計數器)一直累加,這個累加器的值作為ROM的地址
3、DAC根據ROM輸出的數據輸出對應的電壓值
4、由於上述輸出的電壓值是個離散值,無法構成平滑的正弦波,因此需要在后級增加一個低通濾波器才能輸出完美的正弦波。
那么,怎么實現上述的功能呢?
首先,我們要考慮兩個問題:
A、相位累加器(計數器)的位寬是多少?
B、ROM的數據位寬和深度(深度:2^地址位寬)是多少?
對於第一個問題:相位累加器的位寬一般是24~32bits,一般選32bits(因為這樣的位寬能滿足絕大部分的應用場合了);
對於第二個問題:
ROM的數據位寬選擇要看DAC模塊,比如我的DAC模塊的數據輸入數據范圍是0~1023,那么ROM的數據位寬就要選擇10位;
ROM的深度也要取決於你的DAC模塊,因為ROM中只能存儲整數。
相位累加器舉例:
//----------------------------------- //phase adder reg [10:0] fre_cnt; always @ ( posedge clk or negedge rst_n ) begin if ( ! rst_n ) fre_cnt <= 11'd0; else if ( DDS_en ) fre_cnt <= fre_cnt + 1'b1; else fre_cnt <= 11'd0; end
這就是所謂的相位累加器,在DDS_en是能以后就一直技術,直到記滿,然后重新又開始計數。
DDS_rom u_DDS_ddsrom
(
.clock (clk),
.address (fre_cnt),
.q (DAC_data)
);
然后,將計數的值作為ROM的地址送給ROM,ROM輸出相應的正弦波數據,這是,會把2048個點(假設ROM中存了一個正弦波周期的數據,共2048個數據)全部輸出。而2048個點全部輸出需要的實踐為:2048*20ns(假設時鍾為50MHz)=40960ns(24414.0625Hz),這就是DDS的基本頻率,我們將其稱為基頻。
***********************************************************************************************************
如果我們希望能將頻率翻倍,可以這樣:
//----------------------------------- //phase adder reg [10:0] fre_cnt; always @ ( posedge clk or negedge rst_n ) //clk為50Mhz begin if ( ! rst_n ) fre_cnt <= 11'd0; else if ( DDS_en ) fre_cnt <= fre_cnt + 2'd2; else fre_cnt <= 11'd0; end DDS_rom u_DDS_ddsrom ( .clock (clk), .address (fre_cnt), .q (DAC_data) );
如果我們希望把頻率減半,我們可以這樣:
//----------------------------------- //phase adder reg [10:0] fre_cnt; always @ ( posedge clk_ref or negedge rst_n ) //clk_ref為25Mhz begin if ( ! rst_n ) fre_cnt <= 11'd0; else if ( DDS_en ) fre_cnt <= fre_cnt + 1'b1; else fre_cnt <= 11'd0; end DDS_rom u_DDS_ddsrom ( .clock (clk), .address (fre_cnt), .q (DAC_data) );
注意:上述的clk_ref為25MHz;
但由於上述需要用到另外的時鍾,clk_ref,這會讓代碼不好維護,改良代碼如下:
//----------------------------------- //phase adder reg [31:0] fre_cnt; always @ ( posedge clk or negedge rst_n ) //clk為50Mhz begin if ( ! rst_n ) fre_cnt <= 32'd0; else if ( DDS_en ) fre_cnt <= fre_cnt + fre_value; else fre_cnt <= 32'd0; end wire [11:0] rom_addr = fre_cnt[31:20]; DDS_rom u_DDS_ddsrom ( .clock (clk), .address (rom_addr), .q (DAC_data) );
為了更進一步完善DDS,我們可以再增加一個相位調節的功能:
//----------------------------------- //phase adder reg [31:0] fre_cnt; always @ ( posedge clk or negedge rst_n ) //clk為50Mhz begin if ( ! rst_n ) fre_cnt <= 32'd0; else if ( DDS_en ) fre_cnt <= fre_cnt + fre_value; else fre_cnt <= 32'd0; end wire [11:0] rom_addr = fre_cnt[31:20] + pha_value; DDS_rom u_DDS_ddsrom ( .clock (clk), .address (rom_addr), .q (DAC_data) );
就是增加一個pha_value的相位控制字,它的位寬需與ROM中DAC模塊的位寬相同。
Summary:
1、相位累加器的位寬為24~32bites,一般選32bits;
2、頻率控制字位寬與相位累加器位寬相同;
3、ROM的數據位寬選擇取決於DAC模塊;
4、ROM的深度(2^地址位寬)有標准深度,但可任意;
5、相位控制字位寬選擇取決於DAC模塊。