基於FPGA的DDS信號發生器的設計與實現


一、實現環境
  軟件:Quartus II 13.0
  硬件:MP801

二、DDS基本原理
  DDS(Direct Digital Synthesizer)即數字合成器,是一種新型的頻率合成技術,具有相對帶寬大,頻率轉換時間短,分辨率高和相位連續性好等優點。較容易實現頻率、相位及幅度的數控調制,廣泛應用於通信領域。DDS的實現示意圖如下圖所示:

  1、將需要合成的信號的數據存儲在rom中,合成待輸出信號的方法請參考:https://www.cnblogs.com/qidaiymm/p/5692253.html

  2、dds_control實現的功能是將存儲在rom中的待合成的信號的數據按照一定的規則取出來:

    dds_control主要由相位累加和頻率累加來實現,簡單的說,通過控制相位累加和頻率累加來實現從rom中取出不同時刻的數據。

    (1)相位累加器位數為N位(24~32),相位累加器把正弦信號在相位上的精度定義為N位,其分辨率位1/2N ,決定一個波形的起始時刻在哪個點;

    (2)頻率累加器用來控制每隔幾個點從rom中取一個數據,決定一個波形的頻率;

    (3)若DDS的時鍾頻率為Fclk ,頻率控制字fword = 1,則輸出頻率為 Fout = Fclk/2N,這個頻率相當於“基頻”,若fword = B,則輸出頻率  Fout = B * Fclk/2N。因此理論上由以上三個參數就可以得出任意的 f輸出頻率,且可以得出頻率分辨率由時鍾頻率和累加器的位數決定的結論。當參考時鍾頻率越高,累加器位數越高,輸出頻率分辨率就越高。

  3、從FPGA中出來的信號都是數字信號(dds_control輸出的信號都為數字信號),需要通過dac芯片來將數字信號轉換為模擬信號,這樣將dac芯片輸出的信號接入到示波器中,才能看到波形;

  4、舉例說明頻率控制和相位控制:

 

    如上圖所示,這個是一個由33個點構成的正弦波信號,(rom_addr,rom_data),縱坐標為存儲在rom中的正弦波信號,橫坐標為 dds_control 生成的地址信號。fre_acc = fre_acc + fword,當 fword = 1 時,fre_acc 則會將從 1 -33 全部地址取到(如 1、2、3、4、5、6......33),而將對應的縱坐標的點全部輸出到DAC芯片,假設此時DAC芯片輸出的正弦波信號頻率為  f。那么,當 fword = 2,則每隔一個地址單位輸出一個存儲在rom中的正弦波數據(如1、3、5、7、9......33),此時DAC芯片輸出的正弦波信號的頻率則變為 2f。

    相位累加器則是將整個波形按照一定的地址信號的位置向右平移來改變相位,體現在數值上則是加上每個地址信號加上一個pword。

三、工程實現

  設計一個單通道的DDS信號發生器,根據改變FPGA開發板上的撥碼開關來控制DAC輸出的信號是正弦波、方波、三角波、鋸齒波,通過按鍵來控制DAC芯片輸出的信號的頻率和相位。

  1、RTL視圖

 

 

   2、按照RTL視圖分析各個模塊的代碼

  (1)select 模塊:

      撥碼開關 sw0: 往上撥則輸出  正弦波      key1 : 調節頻率,有四個檔位,每按一下換一個頻率;      sclk:系統時鍾50MHz

      撥碼開關 sw1: 往上撥則輸出  方波       key2:調節相位,有四個檔位,每按一下換一個相位;      s_rst_n:系統復位

      撥碼開關 sw2: 往上撥則輸出  鋸齒波            en :使能信號

      撥碼開關 sw3: 往上撥則輸出  三角波  

  select 代碼

 1 // *********************************************************************************
 2 // Project Name : select
 3 // Email        : 
 4 // Create Time  : 2020/06/24  18:44
 5 // Module Name  : select.v
 6 // editor         : qing
 7 // Version         : 
 8 // *********************************************************************************
 9 
10 module select(
11     input                sclk                    ,
12     input                s_rst_n                ,
13         
14     input                sw0                    ,
15     input                sw1                    ,
16     input                sw2                    ,
17     input                sw3                    ,
18         
19     input                key1                    ,
20     input                key2                    ,
21 
22     output    reg[ 3:0]    rom_select                    ,
23     output    reg[31:0]    fword                    ,
24     output    reg[31:0]    pword                
25     );
26 
27 //========================================================================\
28 // =========== Define Parameter and Internal signals =========== 
29 //========================================================================/
30 
31 reg            [3:0]        cnt1        ;
32 reg            [3:0]        cnt2        ;
33 
34 //=============================================================================
35 //****************************     Main Code    *******************************
36 //=============================================================================
37 
38 always @(posedge sclk or negedge s_rst_n) begin  // cnt1
39     if(!s_rst_n)
40         cnt1 <= 0;
41     else if(cnt1 == 3)
42         cnt1 <= 0;
43     else if(key1 == 1'b0)
44         cnt1 <= cnt1 + 1'b1;
45 end
46 
47 always @(posedge sclk or negedge s_rst_n) begin  // cnt2
48     if(!s_rst_n)
49         cnt2 <= 0;
50     else if(cnt2 == 3)
51         cnt2 <= 0;
52     else if(key2 == 1'b0)
53         cnt2 <= cnt2 + 1'b1;
54 end
55 
56 always @(*) begin  // rom_select
57     case({sw3,sw2,sw1,sw0})
58         4'b0001:rom_select = 4'd0;
59         4'b0010:rom_select = 4'd1;
60         4'b0100:rom_select = 4'd2;
61         4'b1000:rom_select = 4'd3;
62         default:rom_select = 4'd0;
63     endcase
64 end
65 
66 always @(*) begin  // fword
67     case(cnt1)
68         0:fword = 2000;
69         1:fword = 3000;
70         2:fword = 4000;
71         3:fword = 5000;
72         default:fword = 5000;
73     endcase
74 end
75 
76 
77 always @(*) begin  // pword
78     case(cnt2)
79         0:pword = 0;
80         1:pword = 64;
81         2:pword = 128;
82         3:pword = 192;
83         default:pword = 0;
84     endcase
85 end
86 
87 endmodule
88 
89             
View Code

  (2)dds_control模塊:這個模塊主要是按照一定的規則將存儲在rom中的四種信號的數據取出來,代碼比較簡單,這里就不詳細講解了。

    幾個注意事項:

      a、在本次實驗中,rom 的位寬為 8 ,存儲為 256,在用 Mif_Maker2010 生成  . mif 文件之后,一定要打開這個新生成的文件,看里面是否有數據,數據是不是全部為零,如果沒有數據或者數據為                零,則需重新用 Mif_Maker2010 重新生成數據(位寬為 8 bit,數據量為 256);

      b、新生成的 .mif 文件要放在 工程文件下面,如下圖所示,不然,可能會無法提取存儲在 rom 中的數據;

 

 

 

   dds_control代碼:

 1 // *********************************************************************************
 2 // Project Name : dds
 3 // Email        : 
 4 // Create Time  : 2020/06/24  09:54
 5 // Module Name  : dds_control
 6 // editor         : qing
 7 // Version         : Rev1.0.0
 8 // *********************************************************************************
 9 
10 module dds_control(
11     input                    sclk            ,        // 50M
12     input                    s_rst_n        ,
13     input                    en                ,
14 
15     input        [31:0]    fword           ,        // 頻率控制字
16     input        [11:0]    pword           ,        // 相位控制字
17     input        [ 3:0]    rom_select    ,
18 
19     output                da_clk        ,
20     output reg[ 7:0]  rom_data    
21 );
22 
23 //========================================================================\
24 // =========== Define Parameter and Internal signals =========== 
25 //========================================================================/
26 
27 reg        [31:0]        fre_acc        ;
28 reg        [ 7:0]        rom_addr        ;
29 
30 wire        [ 7:0]        q1                ;
31 wire        [ 7:0]        q2                ;
32 wire        [ 7:0]        q3                ;
33 wire        [ 7:0]        q4                ;
34 
35 //=============================================================================
36 //****************************     Main Code    *******************************
37 //=============================================================================
38 
39 /*
40 parameter         fword = 5000        ;
41 parameter          pword = 0            ;
42 */
43 
44 always @(posedge sclk or negedge s_rst_n) begin  // fword
45     if(!s_rst_n)
46         fre_acc <= 0;
47     else if(en == 1'b0)
48         fre_acc <= 0;
49     else
50         fre_acc <= fre_acc + fword;
51 end
52 
53 always @(posedge sclk or negedge s_rst_n) begin  // rom_addr
54     if(!s_rst_n)
55         rom_addr <= 0;
56     else if(en == 1'b0)
57         rom_addr <= 0;
58     else
59         rom_addr <= fre_acc[31:24] + pword;
60 end
61 
62 assign da_clk = en ? sclk : 1'b1 ;
63 
64 always @(*) begin
65     case(rom_select)
66         0:rom_data = q1;
67         1:rom_data = q2;
68         2:rom_data = q3;
69         3:rom_data = q4;
70         default:rom_data = q1;
71     endcase
72 end
73 
74 rom u1 (
75     .address ( rom_addr     ),
76     .clock   ( sclk            ),
77     .q        ( q1           )
78     );
79     
80 fagnbo u2 (
81     .address ( rom_addr    ),
82     .clock   ( sclk        ),
83     .q       ( q2          )
84     );
85     
86 jucibo u3 (
87     .address ( rom_addr    ),
88     .clock   ( sclk        ),
89     .q       ( q3          )
90     );
91     
92 sanjiao u4 (
93     .address ( rom_addr    ),
94     .clock   ( sclk        ),
95     .q       ( q4              )
96     );
97     
98 
99 endmodule
View Code

  (3)ad9709_driver模塊:AD9709是一個DAC芯片,雙端口、高速、雙通道、8位CMOS  DAC。在本次實驗中只使用了通道 A。這個DAC芯片的驅動是目前見過的最簡單的一款DAC。

    dac_clka  :控制通道 A 工作時鍾;

    dac_mode:AD9709的工作模式選擇。為高電平時,表示雙通道模式;為低電平時,表示為單通道模式;

    dac_sleep :為高電平時,AD9709進入睡眠模式,此時不工作;為低電平時,AD9709為正常工作模式;

               dac_wra :通道 A的寫使能信號;

    注意事項:時鍾要同步,dds_control 模塊輸出數據的速率和提供給 ad9709_driver 模塊數據轉換的速度要保持一致;

  ad9709_driver  代碼:

 1 // *********************************************************************************
 2 // Project Name : ad9709_driver
 3 // Email        : 
 4 // Create Time  : 2020/06/24
 5 // Module Name  : ad9709_driver
 6 // editor         : qing
 7 // Version         : 
 8 // *********************************************************************************
 9 
10 module ad9709_driver(
11     input                  da_clk        ,
12     input                  rst_n            ,
13     input       [7:0]       rom_data        ,
14    
15     output               dac_mode        ,
16     output               dac_clka        ,
17     output reg[7:0]   dac_da        ,
18     output               dac_wra        ,
19     output               dac_sleep
20     );
21 
22 always @ (posedge da_clk or negedge rst_n) begin  // dac_da
23     if(rst_n == 1'b0)
24         dac_da <= 0;
25     else    
26       dac_da <= rom_data;  // 255 - rom_data
27 end
28 
29 assign dac_sleep = 0 ;
30 assign dac_wra   = dac_clka ;
31 assign dac_clka  = -da_clk;
32 assign dac_mode  = 1;
33 
34 endmodule
View Code

  (4)頂層模塊:

  頂層模塊代碼:例化各個子模塊

 1 // *********************************************************************************
 2 // Project Name : dds
 3 // Email        : 
 4 // Create Time  : 2020/06/24 10:26
 5 // Module Name  : dds
 6 // editor         : qing
 7 // Version         : Rev1.0.0
 8 // *********************************************************************************
 9 
10 module dds(
11     input                    sclk                ,
12     input                    s_rst_n            ,
13     input                    en                    ,
14     
15     input                    sw0                ,
16     input                    sw1                ,
17     input                    sw2                ,
18     input                    sw3                ,
19     input                    key1                ,
20     input             key2                ,
21                             
22     output                dac_mode            ,
23     output                dac_clka            ,
24     output                dac_wra            ,
25     output                dac_sleep        ,
26 
27     output    [7:0]        dac_da
28 );
29 
30 wire                da_clk                    ;
31 wire    [7:0]        rom_data                   ;
32 
33 wire    [31:0]    fword                        ;
34 wire    [31:0]    pword                        ;
35 wire    [3:0]        rom_select                ;
36 
37 dds_control u0(
38     .sclk            (sclk            ),        // 50M
39     .s_rst_n        (s_rst_n        ),
40     .en            (en            ),
41     .fword        (fword        ),
42     .pword        (pword        ),
43     .rom_select (rom_select ),
44     .da_clk        (da_clk        ),
45     .rom_data    (rom_data    )
46 );
47 
48 ad9709_driver u1(
49     .da_clk       (da_clk       ),
50     .rst_n       (s_rst_n       ),
51     .rom_data   (rom_data   ),
52     .dac_mode   (dac_mode   ),
53     .dac_clka   (dac_clka   ),
54     .dac_da       (dac_da       ),
55     .dac_wra       (dac_wra       ),
56     .dac_sleep  (dac_sleep  )
57     );
58     
59 select u2(
60     .sclk            ( sclk        ),
61     .s_rst_n        ( s_rst_n    ),
62     .sw0            ( sw0            ),
63     .sw1            ( sw1            ),
64     .sw2            ( sw2            ),
65     .sw3            ( sw3            ),    
66     .key1            ( key1        ),
67     .key2            ( key2        ),
68     .rom_select    ( rom_select),
69     .fword        ( fword        ),
70     .pword        ( pword        )
71     );
72 
73 endmodule
View Code

  

四、上板展示

     

          

五、不足之處

  1:算輸出頻率的公式沒有算出本實驗中的輸出信號的頻率(懶);

  2:通過按鍵計數來實現輸出信號頻率,相位的切換這部分沒有進行按鍵消抖,fword  和  pword 設置也有一定的問題(懶);

  3:沒有編寫濾波模塊的代碼,還是有一丟丟的毛刺信號(懶);

  4:沒有編寫振幅控制的模塊(懶);

  5:沒有驅動DAC芯片的兩個通道(懶);

  6:......

六、參考

  1、小梅哥的《基於ac620的fpga系統設計與驗證實戰指南20190516》相關章節;

  2、明德揚的《FPGA至簡設計原理與應用》;

  

 

    

 

 

  

 


免責聲明!

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



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