基於FPGA(DDS)的正弦波發生器


記錄背景:昨晚快下班時,與同事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模塊。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM