1 項目背景 源碼下載技術交流群:544453837 暗號:fpga1.1 信號發生器 信號發生器又稱信號源或振蕩器,是一種能提供各種頻率、波形和輸出電平電信號的設備,在測量各種電信系統或電信設備的振幅特性、頻率特性、傳輸特性及其它電參數時,以及測量元器件的特性與參數時,用作測試的信號源或激勵源,在生產實踐和科技領域中有著廣泛的應用。 直接數字式頻率合成器(DDS)是將先進的數字處理理論與方法引入頻率合成的一項新技術,它把一系列數字量形式的信號通過數/模轉換器轉換成模擬量形式的信號。 上圖是一個典型的DDS工程。DDS一般可分為相位累加器、信號轉換器和DAC。 DDS的輸入是頻率控制字,它用來控制相位累加器每次增加的相位值,相當于一個步進值。 相位累加器:每來一個時鐘脈沖,就會在原來相位值的基礎上,加上頻率控制字的值,得到最新的相位值,將相位值將輸出給信號轉換器。 信號轉換器:一般轉換器內部有一片ROM,事先保存了要產生波形的幅度值。根據輸入的的相位值,就能輸出該相位值所對應的信號幅度值。例如將一個完整周期的正弦波等距離分成128份,并保存到轉換器的ROM當中。當相位值為0時,就輸出相位為0所應對的幅度值;當相位為100時,就輸出相位為100所對應的幅度值。 的具體工作過程是由N位相位累加器、N位加法器和N位累加寄存器組成。每來一個時鐘脈沖,N位加法器將頻率控制字K與N位累加寄存器輸出的累加相位數據相加,并把相加后的結果送至累加寄存器的輸入端。累加寄存器一方面將上一時鐘周期作用后所產生的新的相位數據反饋到加法器的輸入端,使加法器在下一時鐘的作用下繼續與頻率控制字K相加;另一方面將這個值作為取樣地址送入幅度/相位轉換電路,幅度/相位轉換電路根據這個地址輸出相應的波形數據。最后經D/A轉換器和 LPF將波形數據轉換成所需要的模擬波形。 1.2 DA轉換 明德揚教學板板載雙通道、125MHz 轉換速率、8bi的高速DA芯片,滿足常用信號發生器、濾波信號輸出等需求。實際位置如下所示: 芯片型號是AD9709,AD9709是一款雙端口、高速、雙通道、8位CMOS DAC,其中集成兩個高品質8位TxDAC+®內核、一個基準電壓源和數字接口電路,采用48引腳小型LQFP封裝。它提供出色的交流和直流性能,同時支持最高125 MSPS的更新速率。 與FPGA相連的信號有:DA_CLKA、DA_CLKB、DAC_DB7~0、DAC_DA7~0,DAC_MODE、DAC_SLEEP、DA_WRA和DA_WRB。 1.3 AD9709的時序 AD9709的控制時序如下圖 在雙通道模式中,通道A和通道B就如兩個獨立的DA芯片。其中DA_CLKA、DAC_DA7~0、DAC_WR_A用于控制通道A,DA_CLKB、DA_DB7~0、DA_WRB用于控制通道B。 以控制通道A為例,時序圖要求,要先將數據輸出到DAC_DA7~0,然后經過ts時間后,將DAC_WRA和DA_CLKA變高,此時DAC就將數據鎖住,經過一段時間后,就會輸出數據所對應的電流,經過電路轉換后就變成對應電壓了。 時序圖中要注意幾點(數據手冊有詳細說明) 1. DA_CLKA并且超前于或者同時與DA_WRA由0變1。 2. 圖中tS(DAC_WRA上升沿前數據保持不變的時間)、tH(DAC_WRA上升沿后數據保值不變的時間)、tLPW(DAC_WRA的高電平時間)、tCPW(DAC_CLKA的高電平時間)等參數,查詢數據手冊,可以得到如下參數表。從表中可以看到tS的時間至少是2ns;tH時間至少是1.5ns;tLPW、tCPW時間至少是3.5ns。圖中規定了至少,只要大于要求都是可以的。 通道B的時序要求和通道A是相同的,僅是控制信號不同。 明德揚教學板的AD9709的兩個通道,均支持0.48~2.2V的電壓輸出,這個輸出電壓與輸入數據的關系,可用下面的公式表示: 通道A的輸出電壓 = -1.72 * (DAC_DA /255) + 2.2 V 通道B的輸出電壓 = -1.72 * (DAC_DB/255) + 2.2 V 由公式可見,輸出電壓與DAC_DA/B的值是成線性反比例關系,最低電壓為0.48V,最高為2.2V。需要指出的是,由于電路原理圖的原因才導致電壓在此范圍,不同電路實現是不相同的。 2 設計目標 本次案例將使用到采樣率大于100M的示波器。將示波器和教學板上的通道1連接,如下圖所 本案例是要讓DA輸出不同頻率的正弦波。共輸出方式如下: 1. 連續輸出2個周期為6.25MHz的正弦波,其中每個正弦波輸出16個采樣點; 2. 連續輸出2個周期為3.125MHz的正弦波,其中每個正弦波輸出32個采樣點; 3. 連續輸出2個周期為1.5625MHz的正弦波,其中每個正弦波輸出128個采樣點; 4. 連續輸出2個周期為781250Hz的正弦波,其中每個正弦波輸出128個采樣點; 5. 連續輸出2個周期為390625Hz的正弦波,其中每個正弦波輸出128個采樣點; 6. 連續輸出2個周期為195312.5Hz的正弦波,其中每個正弦波輸出128個采樣點。 重復以上的1~7的步驟。 正弦波的最高電壓是2.2V,最低電壓是0.48V 示波器的顯示結果如下圖 上圖是整體效果圖,每種頻率的正弦波連續出現2次,并且正弦波的周期越來越大。 下圖是捕捉到的,頻率為6.25MHz的正弦波,最高電壓是2.2V,最低電壓是0.48V。 下圖是捕捉到的,頻率為3.125MHz的正弦波,最高電壓是2.2V,最低電壓是0.48V。 下圖是捕捉到的,頻率為1.5625MHz的正弦波,最高電壓是2.2V,最低電壓是0.48V。 下圖是捕捉到的,頻率為390625Hz的正弦波,最高電壓是2.2V,最低電壓是0.48V。 下圖是捕捉到的,頻率為195312.5Hz的正弦波,最高電壓是2.2V,最低電壓是0.48V。 3 設計實現3.1 頂層接口 新建目錄:D:\mdy_book\dds_da。在該目錄中,新建一個名為dds_da.v的文件,并用GVIM打開,開始編寫代碼。 我們要實現的功能,概括起來就是FPGA產生控制AD9709,讓其中的通道A產生正弦波所對應的電壓。為了控制AD9709的通道A,就需要控制AD9709的MODE、SLEEP、CLK1、WRT1、DB7~0P1管腳。根據設計目標的要求,整個工程需要以下信號: 1. 使用clk連接到晶振,表示50M時鐘的輸入。 2. 使用rst_n連接到按鍵,表示復位信號。 3. 使用dac_mode信號連接到AD9709的MODE管腳,用來控制其工作模式。 4. 使用dac_sleep信號連接到AD9709的SLEEP管腳,用來控制其睡眠模式。 5. 使用dac_clka信號連接到AD9709的CLK1管腳,用來控制通道A的時鐘。 6. 使用dac_wra信號連接到AD9709的WRT1管腳,用來控制通道A的寫使能。 7. 使用8位信號dac_da連接到AD9709的DB7~0P1管腳,用來控制通道A的寫數據。 綜上所述,我們這個工程需要7個信號,時鐘clk,復位rst_n,dac_mode、dac_sleep、dac_clka、dac_wra和dac_da,其中dac_da是8位信號,其他都是1位信號。 下面表格表示了硬件電路圖的連接關系。 將module的名稱定義為dds_da,代碼如下: 其中clk、rst_n是1位的輸入信號,dac_da是8位的輸出信號,dac_mode,dac_clka,dac_wra,dac_sleep是一位輸出信號。 3.2 信號設計 我們先分析下DAC的輸出。以頻率為195312.5Hz的正弦波為例,如下圖。頻率為195312.5Hz,也就是一個正弦波的周期是5120ns。案例要求一個周期要輸出128個點,那就是每隔5120/128=40ns要輸出一個點。考慮到工程輸入的時鐘是50MHz,周期是20ns,那就意味著每隔2個時鐘就要輸出一個點。 綜上所述,產生頻率頻率為195312.5Hz的正弦波,就是每隔2個時鐘輸出一個電壓值,一共輸出128個點,組成一個正弦波。我們要連續產生2個正弦波。 現在進一步分析下,這128個點所對應電壓值是多少?由于教學板的輸出電壓在0.48~2.2V之間,最低值是0.48V,最高值是2.2V。 先將一個標準的正弦波向上平穩1個單位,使得范圍變成0~2。然后等間隔取128個點(間隔為2*pi/128),獲取其幅度值。我們再用8位信號sin_data表示這些幅度值,其表示方式為: sin_data = (sin(2*pi/128) + 1) * (255/2),i為0~127 (公式1) 通道A的輸出電壓 = -1.72 * (DAC /255) + 2.2 V 公式中可以看到,通道A的輸出電壓是與DAC_DA成線性反比例關系。為了讓通道A的電壓正確地展現出正弦波,我們還需要做如下調整。 DAC_DA = 255 - sin_data 上面的DAC_DA就是最終輸出給DA芯片的數據值,即dac_da信號。 綜上所述,產生頻率為195312.5Hz的正弦波,就是每隔2個時鐘輸出一個電壓值dac_da。先按表XX每隔1個選出sin_data,再用(255-sin_data)得到dac_da。一共輸出128個點,組成一個正弦波,并且連續輸出2個正弦波。 以相同的分析方法,分析頻率為6.25MHz的正弦波。 頻率為6.25MHz,也就是一個正弦波的周期是160ns。案例要求一個周期要輸出8個點,那就是每隔160/8=20ns要輸出一個點。考慮到工程輸入的時鐘是50MHz,周期是20ns,那就意味著每隔1個時鐘就要輸出一個點。 先將一個標準的正弦波向上平穩1個單位,使得范圍變成0~2。然后等間隔取8個點(間隔為2*pi/8),獲取其幅度值。我們再用8位信號sin_data表示這些幅度值,其表示方式為: sin_data = (sin(2*pi/8) + 1) * (255/2),i為0~7 = (sin(2*pi*16/128) + 1) * (255/2),i為0~7 (公式2) 對比公式1和公式2,發現同樣可以由表XX得到相應的sin_data,只是此時間隔16個點取一個值,一共取8個。 綜上所述,產生頻率為6.25MHz的正弦波,就是每隔1個時鐘輸出一個電壓值dac_da,按表XX中每隔16個點輸出一個值,再用(255-sin_data)得到dac_da。一共輸出8個點,組成一個正弦波,并且連續產生2個正弦波 . 按同樣的分析方法,分析其他頻率。最終總結如下: 1. 連續輸出2個周期為6.25MHz的正弦波,其中每個正弦波輸出8個采樣點。 等價于:每隔1個時鐘輸出一個電壓值dac_da,一共輸出8個點,組成一個正弦波,連續產生2個正弦波。 dac_da的產生方式:表XXX每隔16個選出得到sin_data,通過(255-sin_data)得到dac_da。 2. 連續輸出2個周期為3.125MHz的正弦波,其中每個正弦波輸出16個采樣點。 等價于:每隔1個時鐘輸出一個電壓值dac_da,一共輸出16個,組成一個正弦波,連續產生2個正弦波。 dac_da的產生方式:表XXX每隔8個選出得到sin_data,通過(255-sin_data)得到dac_da。 3. 連續輸出2個周期為1.5625MHz的正弦波,其中每個正弦波輸出32個采樣點。 等價于:每隔1個時鐘輸出一個電壓值dac_da,一共輸出32個,組成一個正弦波,連續產生2個正弦波。 dac_da的產生方式:表XXX每隔4個選出得到sin_data,通過(255-sin_data)得到dac_da。 4. 連續輸出2個周期為781250Hz的正弦波,其中每個正弦波輸出64個采樣點。 等價于:每隔1個時鐘輸出一個電壓值dac_da,一共輸出64個點,組成一個正弦波,連續產生2個正弦波。 dac_da的產生方式:表XXX每隔2個選出得到sin_data,通過(255-sin_data)得到dac_da。 5. 連續輸出2個周期為390625Hz的正弦波,其中每個正弦波輸出128個采樣點。 等價于:每隔1個時鐘輸出一個電壓值dac_da,一共輸出128個點,組成一個正弦波,連續產生2個正弦波。 dac_da的產生方式:表XXX每隔1個選出得到sin_data,通過(255-sin_data)得到dac_da。 6. 連續輸出2個周期為195312.5Hz的正弦波,其中每個正弦波輸出128個采樣點。 等價于:每隔2個時鐘輸出一個電壓值dac_da,一共輸出128個點,組成一個正弦波,連續產生2個正弦波。 dac_da的產生方式:表XXX每隔1個選出得到sin_data,通過(255-sin_data)得到dac_da。 按照至簡設計法中的變量法思想,那么可以概括上面的功能:每隔x個時鐘輸出一個電壓值,一共輸出y個點,組成一個正弦波,每個要產生要連續產生2個正弦波。由于一共要產生6種不同頻率的正弦波,所以還需要一個計數器來數6個。 總結出上面的內容后,我們開始設計代碼。 “每隔x個時鐘輸出一個電壓值”,所以這需要一個計數器cnt0,加1條件是“1”,結束條件是數到x個,可以得到cnt0的代碼。 “一共輸出y個點”,這同樣需要一個計數器cnt1。注意的是,由于每個點維持x個時鐘,也就是cnt1的加1條件是“數到x個時鐘”,即end_cnt0。結束條件是:數到y下。可以得到cnt1的代碼。 “每個要產生要連續產生2個正弦波”,這也需要一個計數器cnt2。一個正弦波由y個點組成,所以cnt2的加1條件是“數到y個”,即end_cnt1,結束條件是“數到2個”。可以得到cnt2的代碼: 由于一共要產生6種不同頻率的正弦波,所以還需要一個計數器cnt3來數6個。這個cnt3的加1條件是“產生完2個正弦波”,即end_cnt2,結束條件是“數到6個”。可以得到cnt3的代碼。 我們定義了變量x和y,其中x表示相隔的時鐘數,y表示一個正弦波的采樣點數。具體的x和y是與正弦波的不同頻率相關的,也就是與cnt3相關。根據題意和至簡設計法中的變量設計方法,可以得到x和y的代碼。 有了計數器之后,其他信號就可以根據計數器設計出來了。 首先看信號dac_da。dac_da都是按(255-sin_data)得到。那么可以寫出dac_da的代碼 接下來看sin_data信號。sin_data是從表XX中選擇出來的值,不同的頻率,選擇的方式不同。那么很自然是定義一個選擇信號addr。我們只要控制好addr,就能方便得到sin_data。 接下來設計信號addr。addr是用來控制選擇數據的地址,不同頻率的正弦波要求地址控制方式不同。頻率為6.25MHz(cnt3=0)是每隔16個選擇一個;頻率為3.125MHz(cnt3=1)是每隔8個點選擇一個;頻率為1.5625MHz(cnt3=2)是每隔4個點選擇一個;頻率為781250Hz(cnt3=3)是每隔2個選擇一個;頻率為390625Hz(cnt3=4)是每隔1個點選擇一個;頻率為195312.5Hz(cnt3=5)是每隔1個選擇一個,一共發送128個。 我們用cnt1表示發送的第幾個數。 cnt3==0 時,addr = cnt1*16; cnt3==1時,addr = cnt1*8; cnt3==2時,addr = cnt1*4; cnt3==3時,addr = cnt1*2; cnt3==4時,addr = cnt1*1; cnt3==5時,addr = cnt1*1。 因此,可以寫得addr的代碼 接下來是信號dac_sleep,AD是一直工作的,所以要讓dac_sleep一直為0。 dac_clka為了滿足tS的時間要求,可以讓dac_clka = ~clk。 dac_wra可以與dac_clka相同。 dac_mode是控制AD9709的模式,當為高電平時,表示雙通道模式,此時通過DA、DB兩組信號分別獨立控制兩個通道。在能實現功能的前提下,越簡單越好,就使用雙通道模式,因此令dac_mode一直為1。 至此,主體程序已經完成。接下來是將module補充完整。 3.3 信號定義 接下來定義信號類型。 cnt0是用always產生的信號,因此類型為reg。cnt0計數的最大值為15,需要用5根線表示,即位寬是5位。add_cnt0和end_cnt0都是用assign方式設計的,因此類型為wire。并且其值是0或者1,1個線表示即可。因此代碼如下: cnt1是用always產生的信號,因此類型為reg。cnt1計數的最大值為127,需要用8根線表示,即位寬是8位。add_cnt1和end_cnt1都是用assign方式設計的,因此類型為wire。并且其值是0或者1,1根線表示即可。因此代碼如下: cnt2是用always產生的信號,因此類型為reg。cnt2計數的最大值為7,需要用3根線表示,即位寬是8位。add_cnt2和end_cnt2都是用assign方式設計的,因此類型為wire。并且其值是0或者1,1根線表示即可。因此代碼如下: |