一、實現環境
軟件: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。因此理論上由以上三個參數就可以得出任意的 fo 輸出頻率,且可以得出頻率分辨率由時鍾頻率和累加器的位數決定的結論。當參考時鍾頻率越高,累加器位數越高,輸出頻率分辨率就越高。
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
(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
(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
(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
四、上板展示
五、不足之處
1:算輸出頻率的公式沒有算出本實驗中的輸出信號的頻率(懶);
2:通過按鍵計數來實現輸出信號頻率,相位的切換這部分沒有進行按鍵消抖,fword 和 pword 設置也有一定的問題(懶);
3:沒有編寫濾波模塊的代碼,還是有一丟丟的毛刺信號(懶);
4:沒有編寫振幅控制的模塊(懶);
5:沒有驅動DAC芯片的兩個通道(懶);
6:......
六、參考
1、小梅哥的《基於ac620的fpga系統設計與驗證實戰指南20190516》相關章節;
2、明德揚的《FPGA至簡設計原理與應用》;