? ?最近在學習基于FPGA的DDS設計,借此機會把學習過程記錄下來,當作自己的學習筆記也希望能夠幫助到學習DDS的小伙伴。
?
DDS(Direct Digital Synthesizer)直接數字合成器,這是直譯過來的名字。設計人員一般把它叫做信號發生器,用它來產生一些數字意義上的波形。它的意義還是挺大的,例如我們學習濾波器,就需要一個高低頻率疊加的波形,現時生活中到處都是,可以在設計中,怎么能做出這樣的波形呢?學習各種載波調制,需要將信息加載到載波上,而載波也一般都是一定頻率的正弦波。DDS就是能夠產生這種波形,對于學習數字信號處理以及信號調制解調等有很大的作用。
?
FPGA是數字電路,怎么產生模擬的波形呢?如果單純的使用FPGA是無論如何也產生不了連續不斷的波形,一般都是使用FPGA產生數字信號,再利用DAC(Digital -to- Analog Converter)轉化成為模擬的波形(如圖1)。DAC芯片是一堆模擬電路的東西了,如果感興趣的小伙伴可以在自己學習一下數字信號轉化為模擬信號的過程。
?
?
?
?
圖1
?
??????? FPGA是數字信號,通過DAC轉化為模擬值,筆者在進行學習的時候一直有個疑問:既然FPGA是數字信號,那么也就只能輸出數字信號,輸出相鄰的兩個點中間一定是有一定距離的,輸出的模擬波形應該像樓梯一樣(有一定的幅值跳躍),而不是連續變化的,像這種情況下還是我們還可以用嗎?答案是肯定的,如果“樓梯”的高度跳躍很小,并且在一個周期中有很多小的跳躍,那么就是可以用的。其實微積分的概念和這個道理應該差不多就是用無限多的矩形來近似曲線(如圖2)。
圖2
所以在設計時,只要將波形的一個周期(周期波形)中分成N段輸出就可以了。
?
??????? DDS的實現一般都是由頻率控制器、相位累加器和波形存儲器構成(如圖3)。
?
圖3
?
剛剛開始的時候百度到這個信息,筆者也不太理解,經過一段的時間思考,大概明白了其中的設計原理。我們直接從最終的輸出開始分析,要輸出的一個波形(數字意義上),例如正弦波。那么怎么輸出呢,可以給出一個公式sin(2*pi*n/256),將n從0增長到255,就可以輸出一個波形。如果這么寫的話,就已經把一個周期分成了256份,也可以分成任意份,為了方便數字電路設計,一般都是2的整數次冪份。以下分析都當作一個周期分為256份。n等于一個數值就會輸出一個值,把n所對應的位置看作是相位。例如n等于64時,輸出的值正好對應于相位pi/2時。在FPGA中去計算sin,一般都是利用查表法,也就是說將一個周期分為N份,當輸入的值比較接近與某一個相位時,就把這個相位點的值輸出。那其實也就是把波形等分多份,存儲在存儲器中,而此時存儲器就叫做波形存儲器。存儲器的輸出是按照輸入地址來決定的,如果想要輸出一個完整的不斷的波形,就需要地址從最小到最大不變循環,而存儲器就會不斷的輸出波形。地址從最小到最大的累加,就被成為相位累加器。如果相位累加器1S從最小到最大循環了1000次,那么輸出的波形的頻率就為1KHz。故而相位累加器累加的速度決定輸出波形的頻率,而累加速度是由外部控制器來決定,所以此控制器被稱作頻率控制器。
?
??????? 原理就是上述的那樣,下面可以考慮如何來實現上述的過程。
??????? 在設計中,筆者采用INTEL FPGA來實現,故此采用Quartus Prime 18。首先制作波形存儲器,在FPGA中有很多存儲器資源可以來用,在此選擇ROM來當作存儲器。利用ROM當存儲需要制作初始化文件.mif或者.hex,這兩種都可以,在這里采用.mif文件。
??????? .mif在Quartus 中顯示為一個一個小區域(如圖4),但是其實就是一種文件格式(如圖5)。
圖4
圖5
??????? 利用某些計算工具將數值計算出來,然后填入表格中,這種速度太慢,也太麻煩。我們一般采用下面所述的兩種方法:
??????? 利用mifmaker軟件生成(QQ群:173560979群文件中),此軟件用起來比較簡單。只需要簡單的設置“設定波形”即可。在設定成功后,點擊“文件->保存”即可生成對應的.mif文件(如圖6)。
圖6
??????? 另外一種方法就是利用matlab實現,此方法要求設計者要有一定的matlab基礎。源碼如下:
clear;
clc;
?
width = 8;
depth = 256;
?
file_handle = fopen('sin.mif','w+');
fprintf(file_handle,'--Created by Author xxx\r\n');
fprintf(file_handle,'WIDTH = %d;\r\n',width);
fprintf(file_handle,'DEPTH = %d;\r\n',depth);
fprintf(file_handle,'ADDRESS_RADIX = HEX;\r\n');
fprintf(file_handle,'DATA_RADIX = HEX;\r\n');
fprintf(file_handle,'CONTENT BEGIN\r\n');
for i = 0 : depth-1
??? fprintf(file_handle,'%4x?? :?? %4x? ;? \r\n',i,floor((0.5+0.5*sin(2*pi*i/depth) ) *(2^width-1)));
end
fprintf(file_handle,'END;\r\n');
fclose(file_handle);
??????? 產生了.mif后,就可以加載到ROM中。在quartus工程中,采用50MHz的時鐘來進行驅動整個設計,讓rom的地址每個時鐘周期增加1。經過仿真可以得到正弦波(如圖7)。
圖7
??????? 產生了正弦波,在設置modelsim時,因為波形是按照無符號生成的數據,所以在設置成為模擬之前要先把進制改為無符號類型。即可產生圖7波形。經過使用modelsim工具測試,產生的波形的頻率為195.313KHz。我們自己也算以下:系統的驅動時鐘為50MHz,一個周期為20ns,256個周期輸出一個完整的波形。所以波形的周期為256x20ns=5120ns,經過計算頻率為195.3125KHz,與上述使用modelsim工具測試結果一直,只是modelsim測試保留了頻率的兩位小數而已。
相位累加器每次累加1,產生的波形的頻率為195.313KHz,那么只要改變每次累加的數字就可以得到其他頻率。如果每次增加的是整數比較好辦,那如果每次增加的是帶有小數呢?又該如何實現呢?
?
??????? 在DDS設計中,如果相位累加器每個時鐘周期累加1,就會輸出頻率為195.313KHz的波形。如果每個時鐘周期累加2,就會輸出頻率為2*195.313KHz的波形·······,如果每兩個時鐘周期累加1,就會輸出195.313/2KHz的波形······,如果按照這樣來設計話,不太方便并且輸出波形的頻率是不連續的,只能輸出一些特殊的頻率。
?
首先我們可以一起考慮一個問題,如果我們想要得到一個累加數字0~9,但是想要每次累加的數字有可能是整數也有可能是小數,那么此時整個計算又不讓出現小數,應該怎么做呢?我們可以做一個三位數,然后把百位當作我們的輸出,然后每次累加的數字自己可以隨意調整,也就是相當于之前可以累加小數,小數最小精度為兩位小數。000+011=011,123+001=124。如果按照此中方式,讓數字一直不斷的累加,數字會從最小到最大,不斷循環。但是最高位依然是0~9的變化,只不過0~9變化的周期是受到每次累加的值的影響。如果每次累加001,那么百位0~9的循環就需要1000個累加(不要僅僅想0~900,要不斷循環),如果每次累加010,那么百位0~9的循環就需要100個累加。
?
如果考慮明白上述的問題,那么在DDS中,我們做了一個相位累加器,但是累加器只是負責ROM地址的輸出也就是8位的。如果按照上述思想我們可以設計一個位寬為32位的計數器,把計數器的高八位當作ROM的地址。讓計數器每個時鐘周期累加1,地址從0增長到255,也就是8‘b00000000~8’b11111111,需要整個32位從全0到全1,一共需要2的32次冪個時鐘周期,而這個也就是輸出整個波形用的時間----波形周期。我們可以算出周期:2的32次冪*20ns,頻率為0.01164153218Hz。(這個數字太長了,用f0代替)。每次加1,頻率為f0,如果每次加2,頻率為2倍的f0。那么我們可以任意整數倍的f0,如果f0足夠小(此時f0已經足夠小),我們認為可以得到任意頻率。如果f0的頻率不夠小,可以通過增加計數器的位寬來實現。
?
建議讀者不要每次加1去仿真,仿真時間太長,并且有可能導致整個仿真器卡主。筆者在此仿真每次累加100K。
module addr_ctrl (
?
??? input? wire????????????? clk_50m,
??? input? wire????????????? rst_n,
???
??? output wire?? [7:0]????? addr
);
?
??? reg?????????? [31:0]???? cnt;
???
??? assign ?addr = cnt[31:24];
???
??? always @ (posedge clk_50m, negedge rst_n) begin
?????? if (rst_n == 0)
?????????? cnt <= 0;
?????? else
?????????? cnt <= cnt + 100_000;
??? end
?
endmodule
?
仿真結果如圖8。
圖8
?
??????? 每次累加100K,仿真測試頻率為1.164KHz。每次累加1,得出的頻率為0.01164153218Hz。正好擴大了100K倍。現在我們只要知道做每次累加多少,就能夠通過算基礎這個頻率的多少倍就能算出來頻率,但是這么算是會有誤差,因為上述得到的頻率就是一個近似值。我們可以按照公式去計算頻率,假如每次增加N,那么輸出波形的周期應該是2的32次冪除以N乘以時鐘周期(20ns),用1s除以周期就可以得到頻率(如圖9)。
?
圖9
??????? 通過上面的方法,我們可以輕易知道,累加N所對應的頻率。但是在實際應用中,往往是想要某一個頻率的波形,而不是有了波形算波形的頻率是多少。根據外部要求頻率,算出來每次累加N的具體數值,這就是頻率控制器。其實也是比較簡單的,我們把上述公式轉換以下就可以了(如圖10)。
圖10
??????? 把圖10當中的公式在FPGA實現,輸入F得到N就是頻率控制器。在計算中會有可能存在一些誤差,因為有可能計算的數值為12345.24,但是在累加時,會按照12345來進行累加,.24會被忽略,往往會帶來一個誤差,但是此誤差一般都是可以接受的。
?
好了DDS的設計到此結束了,但是如果想要真正輸出一個模擬的波形還要驅動一個DAC器件,來達到真正的模擬波形的輸出。
?
筆者水平有限,如果設計中,有什么不妥的地方,懇請大佬們指出來。
歡迎加好友探討QQ:746833924,QQ群:173560979。
評論
查看更多