DDS发生器的verilog实现(三)
DDS發(fā)生器的verilog實現(xiàn)(三)
前面講解了正弦波發(fā)生器和DDS基礎(chǔ)知識,這篇文章主要講解如何在fpga上實現(xiàn)DDS發(fā)生器,同時對上一篇的文章進(jìn)行補(bǔ)充。
確定頻率控制字的DDS發(fā)生器
首先對初始條件做一假設(shè):相位累加寄存器的位寬為n=32位,用來存儲離散正弦波的RAM規(guī)格為256*8bit,使用的采樣頻率fc=50Mhz,要求DDS輸出波形的頻率為f0=1Mhz。則可以通過前面的公式f0 = fc * M / 2^n,可以計算出頻率控制字M = 85899345,直接在正弦波發(fā)生器的代碼上進(jìn)行修改即可:
module ex_dds(input wire clk, //50Mhzinput wire rst_n,output wire [7:0] o_wave );parameter FRQ_W = 32'd85899346;//頻率控制字M,現(xiàn)在要求目標(biāo)頻率為1Mhz,相位累加器為32位,所以M=85899346; reg [31:0] phase_sum; wire [7:0] addr;//相位累加器 always @(posedge clk)beginif(!rst_n)phase_sum <= 32'd0;else phase_sum <= phase_sum + FRQ_W; endassign addr = phase_sum[31:24];sp_ram_256x8 sp_ram_256x8_inst (.clka(clk), // input clka.wea(1'b0), // input [0 : 0] wea.addra(addr), // input [7 : 0] addra.dina(8'd0), // input [7 : 0] dina.douta(o_wave) // output [7 : 0] douta );endmodule注:在這里我們使用的離散正弦波和RAM IP核仍然是正弦波發(fā)生器部分的。
在modelsim中進(jìn)行仿真,可以看到一個周期剛好1us:
頻率控制字變化的DDS發(fā)生器
在很多地方,我們可能需要使用不同頻率的輸出波形,比如在FSK調(diào)制等地方,對信號進(jìn)行調(diào)制時,使用的不是單一頻率的波形,而是使用不同頻率的波形對信號進(jìn)行調(diào)制。此時,我們就需要DDS發(fā)生器輸出的波形頻率按照我們的需求變化,而不是一個確定的頻率。因為頻率控制字控制著DDS 所輸出的正弦波的頻率。因此,我們可以通過改變頻率控制字的大小來改變輸出波形的頻率。
我們?nèi)匀辉谏厦娲a的基礎(chǔ)上進(jìn)行修改,每隔100個時鐘周期改變一次輸出波形的頻率:
module ex_dds2(input wire clk, //50Mhzinput wire rst_n,output wire [7:0] o_wave );parameter FRQ_W = 32'd85899346; //頻率控制字M初始值,現(xiàn)在要求目標(biāo)頻率為1Mhz,相位累加器為32位 parameter FRQ_ADD = 32'd85899346/2; //每次給頻率控制字增加的量(自己根據(jù)需求設(shè)置) reg [31:0] phase_sum; //相位累加器 wire [7:0] addr; reg [31:0] frq_word; //變化的頻率控制字 reg [6:0] div_cnt; reg div_flag;//分頻器 always @(posedge clk)beginif(!rst_n)div_cnt <= 7'd0;else beginif(div_cnt == 7'd99)div_cnt <= 7'd0;else div_cnt <= div_cnt + 1'b1;end end always @(posedge clk)beginif(!rst_n)div_flag <= 1'b0;else beginif(div_cnt == 7'd99)div_flag <= 1'b1;else div_flag <= 1'b0;end endalways @(posedge clk)beginif(!rst_n)frq_word <= FRQ_W;else if(div_flag)frq_word <= frq_word + FRQ_ADD; end//相位累加器 always @(posedge clk)beginif(!rst_n)phase_sum <= 32'd0;else phase_sum <= phase_sum + frq_word; endassign addr = phase_sum[31:24];sp_ram_256x8 sp_ram_256x8_inst (.clka(clk), // input clka.wea(1'b0), // input [0 : 0] wea.addra(addr), // input [7 : 0] addra.dina(8'd0), // input [7 : 0] dina.douta(o_wave) // output [7 : 0] douta );endmodule在modelsim中進(jìn)行仿真:
我們只關(guān)注div_flag信號和o_wave信號。可以看到,div_flag每來一個高脈沖,o_wave的頻率就會改變一次。
初始相位控制字
在前面的所有討論中,都沒有提及初始相位控制字,因為在前面的實現(xiàn)中,采用的初始相位都是0,即phase <= 32'd0,也可以通過仿真圖看到o_wave波形都是從相位為零的位置開始輸出。那么,當(dāng)需要波形從相位為90度、180度等任意位置輸出時該如何實現(xiàn)呢?
上面的框圖就是帶有初始相位控制字的DDS結(jié)構(gòu)圖。先對初始相位控制字、相位累加寄存器和RAM中存儲的正弦波的關(guān)系進(jìn)行理解。RAM中存儲的是一個周期的正弦波,又因為正弦波就是一個圓周運(yùn)動在一條直線上的投影,在這里引入一個動圖便于理解:
所以我們將正弦波對應(yīng)到一個圓上,因為相位累加器為32位,可以理解為將圓周等分成了2^32,從0到232-1,當(dāng)在開始的時候,我們給相位累加器加上初始值0,正弦波就從相位為0的位置開始輸出。因此當(dāng)我們在開始時給相位累加器加上任意值,就可以讓波形從任意相位開始輸出。在開始時給相位累加器加的初始值就是這里的初始相位控制字。假設(shè)我們此時想讓正弦波從相位為π的位置輸出,對應(yīng)到圓上,就是圓的180度的位置,此時是圓的相位的中間位置,因為相位累加器的位數(shù)決定了圓周等分的個數(shù)。如果將圓等分為232份,則180度對應(yīng)的就是0到232-1的中間值,所以設(shè)置初始相位控制字為2^32/2 = 2^31;如果想讓波形從相位為90度的位置開始輸出,則對應(yīng)的就是圓的相位的四分之一的位置,也就是0到232-1的四分之一的位置,可以設(shè)置初始相位累加器的值為2^32/4 = 2^30;其他位置計算方法相同。
下面用verilog實現(xiàn)帶有初始相位控制字的DDS,繼續(xù)在上面的代碼上進(jìn)行修改:
module ex_dds3(input wire clk, //50Mhzinput wire rst_n,output wire [7:0] o_wave );parameter FRQ_W = 32'd85899346; //頻率控制字M,現(xiàn)在要求目標(biāo)頻率為1Mhz,相位累加器為32位, parameter FRQ_ADD = 32'd85899346/2; //每次給頻率控制字增加的量(自己根據(jù)需求設(shè)置) parameter INIT_PHASE = 32'd2147483648; //初始相位,180度 //parameter INIT_PHASE = 32'd1073741824;//初始相位,90度 reg [31:0] phase_sum; wire [7:0] addr; reg [31:0] frq_word; //變化的頻率控制字 reg [6:0] div_cnt; reg div_flag;//分頻器 always @(posedge clk)beginif(!rst_n)div_cnt <= 7'd0;else beginif(div_cnt == 7'd99)div_cnt <= 7'd0;else div_cnt <= div_cnt + 1'b1;end end always @(posedge clk)beginif(!rst_n)div_flag <= 1'b0;else beginif(div_cnt == 7'd99)div_flag <= 1'b1;else div_flag <= 1'b0;end endalways @(posedge clk)beginif(!rst_n)frq_word <= FRQ_W;else if(div_flag)frq_word <= frq_word + FRQ_ADD; end//相位累加器 always @(posedge clk)beginif(!rst_n)phase_sum <= INIT_PHASE;else phase_sum <= phase_sum + frq_word; endassign addr = phase_sum[31:24];sp_ram_256x8 sp_ram_256x8_inst (.clka(clk), // input clka.wea(1'b0), // input [0 : 0] wea.addra(addr), // input [7 : 0] addra.dina(8'd0), // input [7 : 0] dina.douta(o_wave) // output [7 : 0] douta );endmodule在modelsim中進(jìn)行仿真:
從圖中可以看到輸出波形o_wave初始相位發(fā)生了180度的翻轉(zhuǎn),
當(dāng)INIT_PHASE = 32'd1073741824時,輸出波形的相位從90度開始:
總結(jié)
以上是生活随笔為你收集整理的DDS发生器的verilog实现(三)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SharePoint 2013 Nint
- 下一篇: 奇分频和偶分频