我之前用過的CPLD有Altera公司的MAX和MAX-II系列,主要有兩個優點:1、程式儲存在片上Flash,上電即行,保密性高。2、CPLD器件規模小,成本和功耗低,時序不收斂情況也不容易出現。缺點也很明顯:1、沒有片上RAM,無法對資料進行高速暫存和處理;2、沒有PLL,使用一個以上的高頻時鐘非常不方便;3、沒有小封裝產品,MAX-II最小的EPM240也是LQFP100封裝。近年來,隨著Altera被Intel收購,對MAX-II的支援力度不斷降低,當前EPM240的價格也達到了百元左右。
最近在B站關注到一種國產CPLD——AG1280Q48,幾乎滿足了我對CPLD的一切幻想:1、片上Flash,上電即行。2、有1280個LUT和觸發器,資源足以媲美小型FPGA,而工作電流僅為幾個mA——MAX-II的幾分之一。3、成本低到數元。3、QFN48小封裝,節約嵌入式系統空間,焊接又相對BGA封裝簡單。4、擁有和FPGA類似(包括PLL和片上RAM塊)的資源,可用於完成以往CPLD無法完成的任務,如訊號緩衝,高速通訊等。
AG1280和STM32、GD32等低成本MCU聯合使用時,能將只有專用解決方案才能完成的功能帶給通用嵌入式系統。個人感覺,AG1280的最佳應用場景是在低成本應用領域和MCU協同工作, MCU+AG1280能部分替代Xilinx的Zynq方案。(注:AGM公司也有類似Zynq的ARM Cortex-M3 + FPGA方案,但我個人不看好這些方案,原因在於嵌入式工程師使用這些新MCU的開發環境和韌體庫的學習成本過高,沒有學習動力。)
為驗證這一設計思路,我自己動手做了一個MCU+AG1280+DAC的DDS(直接頻率合成器)系統,跑通了STM32+AG1280的開發過程。其中AG1280除了完成DDS演演算法所需的地址累加、資料表格儲存和查詢之外,還實現了與STM32之間的同步序列資料接收和波形表格存入。現將開發全過程分享給各位網友,相信會對大家有一定的參考價值,也供大神批評指正。
有網友可能會較真質疑方案的意義:STM32有片上DAC,且還支援DMA,可以構成任意型號發生器,為什麼還要用附加的可程式化邏輯裝置和DAC晶片?其原因在於STM32的DMA不支援記憶體的地址遞增值變化,因此不用中斷無法實現DDS演演算法——而這也正好體現了AG1280在系統中的價值。
以下原創內容歡迎網友轉載,但請註明出處: https://www.cnblogs.com/helesheng
一、硬體電路
MCU開發板很多,這裡就不「重複發明輪子」了——直接用手頭STM32開發板上的PMOD介面來和AG1280子板連線。系統硬體方案框圖如下圖所示。
(注1:PMOD介面是由Xilinx官方定義的一種用於其FPGA開發板的低速介面,現在很多FPGA和嵌入式處理器開發板上都有這種介面。PMOD僅使用12芯2.54mm間距的兩排普通聯結器,其中含有8個GPIO以及電源、地。
注2:有PMOD介面的微控制器開發板可以自行搜尋購買,我們教研室自己也開發過自編教材的配套Innovation-STM32開發板。)
圖1 系統硬體框圖
用STM32開發板的PMOD連線一塊自制的,具有一主一從兩個PMOD介面的AG1280板子,該板子的另一個PMOD介面用於連線DAC板子。至於DAC我選用了常見的低成本同步序列晶片DAC7512。AG1280板子和DAC板子的電路圖如下所示。
圖2 兩個PMOD介面的AG1280電路原理圖
圖3 PMOD介面的DAC7512電路原理圖
圖2是AG1280基本電路,值得注意的有幾點:
1、IO_GLOBE_S1(位於第9腳)、IO_GLOBE_S2(位於第13腳)、IO_GLOBE_S3(位於第15腳)、IO_GLOBE_S4(位於第19腳);IO_GLOBE_N1(位於第41腳) 、IO_GLOBE_N2(位於第44腳) 、IO_GLOBE_N3(位於第46腳)可以作為全域性時鐘輸入管腳,可用於輸入全域性時鐘。但若要使用PLL,則只能從13、15和19管腳輸入。
2、圖2電路板載一個20MHz有源晶振,另外還可以通過PMOD介面從STM32的MCO時鐘輸出管腳獲得時鐘,它們被連線到具有PLL輸入功能的管腳13、15上。
3、AG1280的GPIO分為North和South兩組,可以使用不同IO電平,以實現不同電平邏輯的轉換。另外AG1280還需要3.3V電源作為片上Flash電源,且該電源域North組的IO電源共用,因此North組也只能使用3.3V的IO電源電壓。South組卻可以任選電源電壓。
4、AG1280還需要1.2V核心電源電壓,且該電源應略遲於Flash電源上電,以方便Flash載入程式。我的圖2電路通過PMOD介面從STM32開發板獲得3.3V電源,再用LDO晶片XC6206P122MR從3.3V向下穩壓到1.2V核心電源,LDO後帶有100uF電容,1.2V上電時間自然要落後於3.3V上電。
二、基於Supera和Quartus II的AG1280開發流程
AG1280的開發EDA軟體Supera還不具備分析和綜合電路的能力,但能實現其特有的PLL和片上RAM的IP核打包、綜合後的佈局佈線、下載檔案打包及下載等功能。我計劃完成的DDS系統,完整的包含了PLL、片上RAM以及全部開發流程。
1、開發平臺搭建
到百度網路硬碟http://pan.baidu.com/s/1eQxc6XG 提取密碼:q59e下載AGM公司EDA開發軟體Supra(網路硬碟上有多個版本的Supra,選擇需要的一種即可)。Supra無需安裝,下載後將其放置在不含中文的路徑下,直接執行Bin目錄下的Supra.exe即可。
目前版本的Supra還無法進行硬體描述語言及原理框圖的開發和電路綜合,使用者只能在Supra下建立工程並完成AGM公司特有IP(包括PLL和RAM)的設定,再通過Supra建立Quartus-II工程檔案,在轉到Quartus-II下完成硬體描述語言和原理框圖開發和電路綜合,最後再回到Supra中完成器件內部的佈局佈線、下載檔案的打包和器件燒寫(具體流程在後續會詳細介紹)。
綜上,進行AG1280的開發一定需要安裝一個順手的Quartus-II。這裡特別提醒網友注意,Supra只支援Quartus-II 13.0以上,且不支援Web與Lite版本,必須安裝Full或Standard版本(本人掉到過坑裡,因此特別提醒大家注意)。至於Quartus-II的安裝方法,網上資料較多,這裡不再贅述。
AG1280可以使用Intel的USB-Blaster進行下載和軟體偵錯,但淘寶網上USB-Blaster版本較多,價格差異較大。據網傳,有的版本USB-Blaster不支援AG1280的Flash下載,大家可自行注意避坑。
2、開發流程
1)新建Supra工程
執行 Supra,選擇 File - Project - New Project。
圖4 新建Supra工程
隨後選擇工程存放路徑並填入工程名稱,注意不要使用中文路徑、國產路徑,也不要在路徑中使用空格。
2)設定AGM自有硬體IP
AGM公司自有的硬體及其相關電路的IP只能在Supra中設定。先設定PLL:選擇Supra中Tools - CreateIP – Create Pll選單,在彈出的下列介面中設定PLL。
圖5 設定PLL IP
分別設定模組名稱、輸入頻率、PLL型別、反饋模式、輸出時鐘路數、輸出頻率後,單擊Generate按鈕產生IP和頂層封裝HDL檔案(模組名稱.v和模組名稱.ip)。
其中值得注意的是:AG1280有兩種時鐘源模式:片內RC振盪器模式和片外有源振盪器模式。可以在反饋模式(Feedback mode)選項中選擇EXT_FEEDBACK,以選擇外部有源振盪器模式;選擇NO_REFERENCE,以選擇片上RC振盪器模式。片上RC振盪器振盪頻率不會太準確,供不需要精確定時的系統使用。若選擇片上振盪器則應在輸入頻率(Input frequency)處輸入8MHz,否則真實輸出頻率將與你輸入的頻率成比例變化。
另外,Supra中AG1280的PLL設定中的PLL型別(PLL type),只能選擇PLLX。而輸出路數(PLL output count),相位移動(Phase shift)等設定參照字面意思理解即可。
繼續設定AG1280的片上記憶體RAM IP:選擇Supra中Tools - CreateIP – Create memory選單,在彈出的下列介面中設定片上BRAM。
圖6 設定片上記憶體IP
根據我的DDS系統設計思路,AG1280實現的DDS控制器能從MCU接收波形資料,同時向DAC輸出實時波形資料,因此,需要實現一個雙口RAM。如圖6所示,將記憶體IP設定為2個埠,每個埠的資料寬度都是12bits(DAC7512的解析度為12位元),記憶體深度為256(DDS演演算法要求波形表深度為2的整數次冪)。AG1280片上RAM較多,也可以選擇更大的記憶體深度,以獲得更高訊雜比。
另外,我還選擇了復位後從Flash向雙口RAM內部載入初始資料,因此指定了初始化資料檔案(Select init file)。根據DAC7512對資料資料格式的要求,這些資料是0-4095之間的無符號數,你可以通過MATLAB或Python等工具計算產生。注意,Supra資料檔案的格式與Quartus-II的MIF、HEX都不一樣;Supra要求資料檔案中,每個資料佔一行,且採用ASCII碼錶示十六進位制數。例如下面一樣。
862
893
8C4
8F5
925
956
986
9B6
完成IP設定後,單擊Generate按鈕,Supra將自動生成可在Quartus-II中呼叫的IP檔案和Verilog HDL檔案。
3)在Quartus-II中完成功能模組開發
單擊Supra選單中Tools – Migrate,將工程移植為Quartus-II工程。在下圖所示的設定視窗中輸入工程名稱、器件、需要使用的之前建立的IP等資訊後,單擊Next按鈕。
注:最下面一個對話欄中,需要輸入所有需要轉換到Quartus-II工程的IP名稱,如果有多餘一個IP需要轉換,可以在兩個路徑之間用半形逗號分隔開來,也可以點選Browse按鈕後同時多選多個IP檔案。
圖7 移植為Quartus-II工程
Supra彈出如下介面,可轉入Quartus-II進行開發和綜合電路。
圖8 移植為Quartus-II工程
此時Supra已經在工程目錄下建了與其工程同名的Quartus-II工程(xxxxx.qpf),以及頂層Verilog HDL檔案,下一步需要在Quartus-II中打工程完成所有電路模組程式碼或原理圖的開發。下圖是我建立的DDS系統工程——DDS_SPI_dualRAM下的各個模組層次關係。
圖9 Quartus-II工程
可以看到,在預設情況下Quartus-II工程使用的器件是Cyclone IV系列的EP4CE75F29C8,我們不需要修改該工程使用的器件。但可以限定Quartus-II通過增量編譯模式不編譯Supra已經生成的IP:右鍵單擊PLL IP和memory IP,並將其設定為Design Partition(該功能只有Prime和Standard版本的Quartus-II才有)。
圖10 設定IP為Design Partition
在Quartus-II中執行Supra生成的指令碼檔案af_quartus.tcl:選擇Quartus-II的Tools選單下的Tcl scripts,並在彈出的視窗中執行工程目錄下的af_quartus.tcl檔案。
圖11 Quartus-II執行Tcl指令碼
Quartus-II將彈出Windows命令列視窗,等待綜合工程中的所有模組,綜合成功後Quartus-II的任務就完成了(這裡可能需要幾分鐘)。
此時Supra已經自動生成了一個檔名與工程名相同,使用字尾asf的管教約束檔案。該檔案目前還是空的,可以其中新增約束器件管腳的指令碼。asf檔案的語法與Quartus-II的tcl指令碼管腳約束語法相同,例如:
set_location_assignment -to cs_host1 PIN_43
set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to cs_host1
上面的指令碼將cs_host1約束到了AG1280的43腳,並將其設定成了弱上拉模式,以防止干擾拉低cs_host1,造成資料誤傳輸。
4)在Supra中完成AG1280的佈局佈線和程式下載燒寫
根據圖2的硬體電路編輯完asf檔案,即可返回Supra的圖8所示介面,單擊Next按鈕。(注:若你已經關閉了Supra也不要緊,再次開啟Supra工程後,單擊Migrate按鈕重新進入圖8所示介面即可)。在隨後的介面中檢查設定,並單擊Finish按鈕,Supra將完成佈局佈線。
若一切順利,在Supra下部的Message視窗中間看到編譯成功的提示資訊,即可進入下載檔案打包和USB Blaster下載組態檔階段:選擇Supra中的Tools – Program,連線電腦、USB Blaster以及AG1280板子的JTAG介面,在彈出的視窗中單擊Query Device ID來查詢Supra是否連線到AG1280。若正確連線了硬體,AG1280的ID應該是0x00120010。隨後,可以在Program from file中選擇需要下載的檔案,單擊Browse按鈕選擇所需的下載檔案。其中xxxxx_sram.prg是直接燒寫到AG1280的SRAM中;xxxxx_hybird.prg是燒寫到Flash中,可以實現程式掉電不丟程式,上電即行。(檔名中的xxxxx是Supra工程的名稱)。
圖12 程式下載燒寫介面
單擊Program按鈕,即可燒寫剛才完成的程式。
三、在AG1280上實現基於DDS演演算法的任意波形發生器
1、DDS原理
DDS演演算法的功能特點是可以獲得非常高的頻率解析度,以取得近乎連續的頻率調節效果。下面簡述一下DDS演演算法的核心思想,幫助讀者理解AG1280上的硬體模組的原理和相互關係。一時無法理解的初學者,可以進一步閱讀其他介紹DDS原理的書籍和貼文。
假設記憶體中有個長度為N=2^n的波形資料表格,這些資料為1個週期的波形。若用頻率fc從該表格中每間隔K個點取出一個放入DAC中變為模擬訊號,則DAC輸出模擬訊號的頻率將是:
fo = fc * K / N (1)
從(1)式中可以看出輸出模擬訊號的頻率fo和每次在資料表格中跳躍的地址數k成正比:k每增加1,輸出頻率fo就增加fc/N。為獲得足夠搞得頻率解析度,即使得fo輸出的頻率能夠接近連續的調節,N的數值應該越大越好。一些ADI公司的商業DDS晶片,N甚至達到了2^40 ≈ 1T,這樣大的表格顯然是無法直接儲存在AG1280中的。好在用1T個點來儲存一個週期的訊號其實並沒有多大意義:在表格中相臨的很多點大概率都是相同的內容。既然相鄰點的資料都差不多,還不如不儲存——在波形表格中每相臨2^p個點,只儲存第一個點的數值,如果用到後續2^p - 1個點,則都用第一個點的數值來代替。則AG1280所需的資料表格長度僅為2^(N-p) = 2^n(其中,設n = N-p),這樣在AG1280中只需要長度N位的累加器和頻率控制字,他們在時鐘控制下不斷相加新的表格地址(也是N位),但在讀波形表時,只使用這個N位地址的高n位,低p位地址則直接捨棄。
我們的系統使用的DAC是DAC7512,其輸出模擬訊號的重新整理率fc為100KSPS,精度為12bits,取N為16,n為8。即所需的記憶體為256(2^8)個12位元(具體設定參見圖6所示),地址累加器和頻率控制字為16位元。獲得的頻率解析度為100K / 2^16 ≈ 1.53Hz,理論上頻率調節範圍上限為:100K / 2 = 50KHz(奈奎斯特頻率),頻率下限為:100K / 2^16 ≈ 1.53Hz。
2、硬體總體設計
AG1280中的整個電路以雙口RAM為核心,其左側介面與MCU相連,用於接收MCU下發的波形資料和頻率控制字K的數值;右側介面和DAC7512相連,用於依次輸出DDS演演算法輸出的資料。
雙口RAM左側地址由一個每次加1的計數器產生,以遍歷所有的村春單元;右側地址由一個每次加K的加法器產生,以實現DDS演演算法的頻率控制。由於雙口RAM的深度256剛好是2的整數次冪,所以無需特意處理計數器和加法器的溢位問題,溢位後自然返回地址開頭即可。
AG1280中還應實現兩個SPI介面:一個SPI從機介面(通過左側的PMOD介面)用於接收來自MCU的資料;另一個SPI主機介面(通過右側的PMOD介面)用於向同步序列介面的DAC7512下發資料。
左側與MCU的介面除了需要接收波形資料之外,還需要接收頻率控制字K。我通過兩個管腳分別輸出兩個低電平選通訊號cs1和cs2來區分當前下發的資料是波形資料還是頻率控制字。
3、硬體實現
3.1 雙口RAM右側與DAC介面的電路部分
1)DAC輸出同步時鐘生成電路
DAC7512需要使用100KSPS的重新整理率,需要25MHz的主時鐘分配產生輸出同步時鐘訊號sap_syc_sig,該訊號還將同步雙口RAM右側的讀取和DDS地址累加器的不斷疊加。
1 module samp_syc_sig( 2 input clk_25m, 3 input nrst, 4 output samp_syc_sig 5 ); 6 reg[9:0] time_cnt; 7 wire comp; 8 reg samp_syc_sig_reg; 9 assign samp_syc_sig = samp_syc_sig_reg; 10 always @ (negedge clk_25m or negedge nrst) 11 begin 12 if(!nrst) 13 time_cnt[9:0] <= 10'd0; 14 else 15 if(time_cnt[9:0] < 10'd249) 16 time_cnt[9:0] <= time_cnt[9:0] + 10'd1; 17 else 18 time_cnt[9:0] <= 10'd0; 19 end 20 //以下組合邏輯用於產生start訊號 21 assign comp = (time_cnt[9:0] < 10'd20) ? 1'b1 : 1'b0; 22 always @ (negedge clk_25m or negedge nrst) 23 begin 24 if(!nrst) 25 samp_syc_sig_reg <= 1'b0; 26 else 27 samp_syc_sig_reg <= comp; 28 end 29 endmodule
2)與DAC7512通訊的SPI主機電路
1 module DAC7512( 2 input clk_25m,//25MHz左右的時鐘訊號 3 input nrst,//低電平復位 4 input syc_sig,//外部產生的同步輸出訊號,當其出現上升沿時重新整理輸出電壓 5 output cs_da, 6 output mosi_da, 7 output sck_da, 8 input [11:0] data_in//待轉換的資料,在每次開始轉換之前被鎖存到模組中,只有低12位元有效 9 ); 10 reg[7:0] cnt_cs; 11 reg cs_da_reg; 12 reg sck_da_reg; 13 reg[2:0] sck_timer;//用於測量單個DAC通訊時鐘長度的計數器 14 /////////以下用25MHz時鐘,產生片選訊號//////// 15 assign cs_da = cs_da_reg; 16 always @ (posedge clk_25m or negedge nrst) 17 begin 18 if(!nrst) 19 begin 20 cnt_cs[7:0] <= 8'd0; 21 cs_da_reg <= 1'b1; 22 end 23 else begin 24 if(syc_sig == 1'b1)//同步訊號高電平時,回覆到計數初始狀態 25 begin 26 cnt_cs[7:0] <= 8'd0; 27 cs_da_reg <= 1'b1; 28 end 29 else 30 begin 31 if(cnt_cs[7:0] < 8'd98) //98個脈衝,少一個防止競爭現象 32 begin 33 cnt_cs[7:0] <= cnt_cs[7:0] + 8'd1; 34 cs_da_reg <= 1'b0; 35 end 36 else begin 37 cnt_cs[7:0] <= cnt_cs[7:0]; 38 cs_da_reg <= 1'b1; 39 end 40 end 41 end 42 end 43 ////////以下產生序列同步輸出時鐘//////////// 44 assign sck_da = sck_da_reg; 45 always @(posedge clk_25m or posedge cs_da) 46 begin 47 if(cs_da) 48 begin 49 sck_timer[2:0] <= 3'd0; 50 sck_da_reg <= 1'b0; 51 end 52 else begin 53 if(sck_timer[2:0] < 3'd2) 54 begin 55 sck_timer[2:0] <= sck_timer[2:0] + 3'd1; 56 sck_da_reg <= sck_da_reg; 57 end 58 else begin 59 sck_timer[2:0] <= 3'd0; 60 sck_da_reg <= !sck_da_reg; 61 end 62 end 63 end 64 ///////////產生序列輸出訊號,需要序列輸出的資料在CS下降沿在並行輸入口被鎖存///////// 65 reg[16:0] shift_reg; 66 //注意這裡多一個位,是因為DAC7512在下降沿讀取資料,而我們的資料在第一個SCK脈衝上升沿就開始移出,因此會多移出一個位(第一位) 67 assign mosi_da = shift_reg[16]; 68 always @ (posedge sck_da or posedge cs_da) 69 begin 70 if(cs_da) 71 shift_reg[16:0] <= {5'b00000,data_in[11:0]};//對於dac7512而言,最高四位為0000,表示輸出使能,且連線1KΩ負載 72 else 73 shift_reg[16:0] <= shift_reg[16:0]<<1; 74 end 75 endmodule
上面的程式碼,使用獨立的計數器分別產生SCK和CS訊號,避免了由於組合邏輯訊號鏈過長可能造成的建立時間不收斂問題。另外輸入資料data_in只有12位元,其內容就是模擬訊號大小。DAC7512的SPI通訊中需要使用16位元資料,其高四位被上面的模組自動補充為控制字4’b0000,需要使用其他控制模式的網友,可以根據DAC7512手冊自行修改。
3)DDS地址累加器電路
1 module addr_adder( 2 input clk, 3 input nrst, 4 input[15:0] delta_addr, 5 output[7:0] high_byte_addr 6 ); 7 reg[15:0] inc_addr_reg; 8 assign high_byte_addr[7:0] = inc_addr_reg[15:8]; 9 always @(posedge clk or negedge nrst) 10 begin 11 if(!nrst) 12 inc_addr_reg[15:0] <= 16'H0; 13 else 14 inc_addr_reg[15:0] <= inc_addr_reg[15:0] + delta_addr[15:0]; 15 end 16 endmodule
注意,根據DDS演演算法,16位元的累加器每次增加的值也是16位元的頻率控制字delta_addr(相當於公式(1)中的K),但每次只輸出累加器的高8位元作為雙口RAM右側的地址。
3.2 雙口RAM左側與MCU介面的電路部分。
4)根據不同片選訊號,將MCU的SPI訊號分解為波形資料和頻率控制字的資料多路器電路
1 module mux_HostSpi( 2 input mosi, 3 input sck, 4 input cs1, 5 input cs2, 6 output mosi_ram, 7 output mosi_DdsDelta, 8 output sck_ram, 9 output sck_DdsDelta 10 ); 11 assign mosi_ram = mosi & (!cs1);//輸入CS1為低電平時,spi訊號被送到雙口ram一側 12 assign mosi_DdsDelta = mosi & (!cs2);//輸入CS2為低電平時,spi訊號被送到DDS頻率控制字 13 assign sck_ram = sck & (!cs1);//輸入CS1為低電平時,spi訊號被送到雙口ram一側 14 assign sck_DdsDelta = sck & (!cs2);//輸入CS2為低電平時,spi訊號被送到DDS頻率控制字 15 endmodule
上述訊號可以根據MCU輸出的兩個不同的片選訊號cs1和cs2將MCU輸出的mosi、sck訊號分解為連線波形資料雙口RAM的mosi_ram、sck_ram,以及與頻率控制字向量的mosi_DdsDelta和sck_DdsDelta。
5)SPI從機接收暫存器電路
1 module shift_reg( 2 input mosi, 3 input sck, 4 input nrst,//低電平復位 5 input[15:0] ini_data,//復位後的初始化值 6 output[15:0] data_out 7 ); 8 reg[15:0] shft_r; 9 assign data_out[15:0] = shft_r[15:0]; 10 always @ ( posedge sck or negedge nrst) 11 begin 12 if(!nrst)//復位後初始化為初始值 13 shft_r[15:0] <= ini_data[15:0]; 14 else 15 shft_r[15:0] <= {shft_r[14:0],mosi}; 16 end 17 endmodule
這個電路被例化為兩個範例,分別用於接收波形資料或頻率控制字。讀者也可以只例化一個SPI從機移位暫存器,但這樣做資料多路器就要放在移位暫存器並行化之後。這一點可以根據器件資源消耗情況靈活決定。
6)雙口RAM左側地址生成電路
1 module addr_generator( 2 input clk, 3 input nrst, 4 output[7:0] inc_addr 5 ); 6 reg[7:0] inc_addr_reg; 7 assign inc_addr[7:0] = inc_addr_reg[7:0]; 8 always @(posedge clk or negedge nrst) 9 begin 10 if(!nrst) 11 inc_addr_reg[7:0] <= 8'd0; 12 else 13 inc_addr_reg[7:0] <= inc_addr_reg[7:0] +1'b1; 14 end 15 endmodule
這個地址生成電路用於產生將接收到的波形資料依次存入雙口RAM所需的地址,是一個簡單的帶非同步復位訊號的加1增計數器。
注意:1、該計數器的時鐘應使用MCU產生的波形資料片選訊號cs1,這樣在每次一個地址單元的資料傳輸後,可以產生下一個單元的地址。2、該電路需要一個非同步復位,以實現雙方地址的同步,MCU可以在每次開始一個新的波形資料傳輸之前,輸出該復位訊號,以實現MCU和AG1280之間的地址同步。
7)LED閃爍電路
LED閃爍電路並不是必須的,加上該電路是為了監測系統時鐘是否正常工作。讀者可以根據實際情況自信選擇,程式碼不再給出。
8)頂層例化檔案
頂層例化檔案用於連線上述各個模組電路
1 module DDS_SPI_dualRAM( 2 input clk, 3 input nrst, 4 output led, 5 output samp_syc_sig, 6 output mosi, 7 output sck, 8 output cs, 9 //以下連線主機接收波形資料的SPI線 10 input sck_host, 11 input mosi_host, 12 input cs_host1,//主機輸出的第一個cs,用於選通和資料RAM通訊 13 input cs_host2,//主機輸出的第而個cs,用於選通和DDS累加器增加值暫存器 14 //以下測試管腳 15 output test_pin 16 ); 17 //wire samp_syc_sig; 18 wire[7:0] dds_addr; 19 wire[11:0] tab_data; 20 wire[15:0] delta_addr; 21 wire clk_25m; 22 assign test_pin = inc_addr_host[0]; 23 //assign clk_25m = clk; 24 //////////////從板載20MHz有源振盪通過PLL產生25MHz時鐘///////// 25 expll_2_25M i_ex_pll_25M( 26 .clkin(clk), 27 .clkfb(clk_25m), 28 .pllen(1'b1), 29 .resetn(nrst), 30 .clkout0en(1'b1), 31 .clkout1en(1'b0), 32 .clkout2en(1'b0), 33 .clkout3en(1'b0), 34 .clkout0(clk_25m), 35 .clkout1(), 36 .clkout2(), 37 .clkout3(), 38 .lock() 39 ); 40 //////////////以下DAC側的電路描述///////////// 41 samp_syc_sig i_syc_sig(//產生DAC輸出的時鐘 42 .clk_25m(clk_25m), 43 .nrst(nrst), 44 .samp_syc_sig(samp_syc_sig) 45 ); 46 47 run_led i_led( 48 .clk(clk_25m), 49 .nrst(nrst), 50 .led(led) 51 ); 52 DAC7512 iDAC7512( 53 .clk_25m(clk_25m), 54 .nrst(nrst), 55 .syc_sig(samp_syc_sig), 56 .cs_da(cs), 57 .mosi_da(mosi), 58 .sck_da(sck), 59 .data_in(tab_data[11:0]) 60 ); 61 //////////////以下雙口RAM側的電路描述///////////// 62 wire[15:0] data_host;//從主機接收的資料的並行介面 63 wire[7:0] inc_addr_host;//連線到主機端記憶體埠的地址線,由計數器自動產生 64 wire mosi_ram_host,mosi_DdsDelta_host,sck_ram_host,sck_DdsDelta_host; 65 wire[15:0] delta_val; 66 dualRAM_DATA i_dualRAM_DATA( 67 .Clk0(cs_host1),//用接收端序列接收選通訊號作為儲存訊號的時鐘 68 .Clk1(samp_syc_sig), 69 .ClkEn0(1'b1), 70 .ClkEn1(1'b1),//時鐘有效訊號,可以一直為高 71 .AsyncReset0(1'b0),//復位訊號,高有效,可以一直不復位 72 .AsyncReset1(1'b0),//復位訊號,高有效,可以一直不復位 73 .WeRenA(1'b1),//寫資料有效訊號,高有效 74 .ReB(1'b1 ), //埠1讀資料有效訊號,高有效 75 .DataInA(data_host[11:0]),//只使用接收資料的低12位元 76 .AddressA(inc_addr_host[7:0]), 77 .AddressB(dds_addr[7:0]), 78 .DataOutB(tab_data[11:0]) 79 ); 80 //////////////以下主控MCU側的電路描述///////////// 81 mux_HostSpi i_mux_HostSpi(//資料多路器,用於選擇SPI口初始化的是RAM還是DDS累加地址 82 .mosi(mosi_host), 83 .sck(sck_host), 84 .cs1(cs_host1), 85 .cs2(cs_host2), 86 .mosi_ram(mosi_ram_host), 87 .mosi_DdsDelta(mosi_DdsDelta_host), 88 .sck_ram(sck_ram_host), 89 .sck_DdsDelta(sck_DdsDelta_host) 90 ); 91 //以下移位暫存器用於通過CS1選通後接收主機發來的波形資料 92 shift_reg i_ram_shift_reg_host( 93 .mosi(mosi_ram_host), 94 .sck(sck_ram_host), 95 .nrst(nrst),//低電平復位 96 .ini_data(16'd0), 97 .data_out(data_host[15:0]) 98 ); 99 addr_generator i_addr_generator_host(//每次加一的方式遍歷所有地址 100 .clk(cs_host1), 101 .nrst(nrst), 102 .inc_addr(inc_addr_host[7:0]) 103 ); 104 //以下移位暫存器用於通過CS2選通後接收主機發來的地址累加值 105 shift_reg i_delta_shift_reg_host( 106 .mosi(mosi_DdsDelta_host), 107 .sck(sck_DdsDelta_host), 108 .nrst(nrst),//低電平復位 109 .ini_data(16'd328),//復位後的初始化為500Hz輸出 110 .data_out(delta_val[15:0]) 111 ); 112 addr_adder i_dds_addr_adder(//DDS演演算法產生下一個需要DAC資料資料的地址 113 .clk(samp_syc_sig), 114 .nrst(nrst), 115 .delta_addr(delta_val[15:0]), 116 .high_byte_addr(dds_addr[7:0]) 117 ); 118 endmodule
4、MCU軟體實現
MCU軟體通過SPI口完成波形資料和頻率控制字的下載。SPI應設定為時鐘空閒時低電平(CPOL=0),第一個脈衝邊沿讀取資料(CPHA=0)的模式。此處不再給出MCU初始化程式碼,僅給出下載波形表格和頻率控制字函數的程式碼。
1 //設定波形函數 2 void set_wave(unsigned char wave_type) 3 //波形型別0:正弦;1:三角;2:墨西哥草帽 4 { 5 unsigned short i; 6 switch(wave_type) 7 { 8 case 0://選擇正弦波 9 for(i = 0; i<256 ; i++) 10 { 11 CS_wave_reg = 0; 12 SPIx_ReadWrite16bit(sin_tl[i]); 13 CS_wave_reg = 1; 14 delay_us(2); 15 } 16 break; 17 case 1://選擇三角波 18 for(i = 0; i<256 ; i++) 19 { 20 CS_wave_reg = 0; 21 SPIx_ReadWrite16bit(trg_tl[i]); 22 CS_wave_reg = 1; 23 delay_us(2); 24 } 25 break; 26 case 2://選擇墨西哥草帽小波基 27 for(i = 0; i<256 ; i++) 28 { 29 CS_wave_reg = 0; 30 SPIx_ReadWrite16bit(mexhat_tl[i]); 31 CS_wave_reg = 1; 32 delay_us(2); 33 } 34 break; 35 default: 36 break; 37 } 38 } 39 40 //設定頻率控制字函數 41 #define DAC_CLK 100000 //DAC的輸出頻率對DAC7512為100K 42 //設定輸出頻率 43 void set_frq(unsigned short frq) 44 //輸出頻率範圍必須在1-50_000Hz 45 { 46 unsigned short temp_short; 47 temp_short = (float)65536/DAC_CLK * frq + 0.5;//加0.5是為了消除捨棄誤差 48 CS_delta_reg = 0;//選通累加器增加值設定暫存器 49 SPIx_ReadWrite16bit(temp_short); 50 CS_delta_reg = 1; 51 }
三、總結
1、實際測試
1)系統
下圖是本文使用國產CPLD器件AG1280設計的DDS系統。實驗系統看起來有點醜,請大家海涵。
圖13 實際製作的DDS系統
2)功能實測
本文設計的DDS系統相比於ADI公司商業化的DDS晶片,最大的特點在於可以自己下載所需的波形,我測試了一個訊號處理領域常用的小波基——墨西哥草帽小波基作。需要使用下面的MATLAB程式碼產生所需的資料。
1 y = mexihat(-4,4,256);%呼叫matlab小波函數 2 %%%%以下程式碼將浮點結果標準化為12位元的DAC7512可以接收的資料 3 y = y - min(y); 4 y = y / max(y); 5 y=round(4000*y);
用STM32的SPI1口把上面MATLAB指令碼產生的資料下發到AG1280中的DDS系統後得到頻率/週期可以調節的墨西哥草帽函數週期波形,用示波器觀察1.5KHz的波形如下圖所示。
圖14 用本DDS系統產生的頻率可調墨西哥草帽小波基函數
可以看到不論是頻率的準確性,還是波形的完整性均達到了設計預期。
3)AG1280佔用資源、功耗和上電執行時間實測
本文設計的DDS系統佔用AG1280資源如下:
圖15 AG1280資源佔用情況
可以看到邏輯資源僅使用了10-20%,而記憶體資源僅使用了10%左右(當然有可能隨著DDS波形表格增長而變大)。可以想見AG1280的資源能夠滿足一般的MCU+FPGA的嵌入式系統需要。
另外,據實測本系統中的AG1280在25MHz工作頻率,PLL開啟的情況下,功耗約為5mA左右。
本次測試中唯一讓我覺得意外的是AG1280的上電設定時間——可達100ms-300ms。仔細想來也可以理解:AG1280需要從其片上Flash中讀取程式,在設定到內部的查詢表中方能正常使用,肯定要比STM32一類直接在Flash中執行程式的器件要慢。但在使用中就需要如果與STM32等MCU配合,在MCU上電後延時一段時間再和AG1280通訊。
2、AG1280和Supra使用感慨
國產PLD器件一路走來不容易,雖然存在套用國外大廠EDA軟體,支援檔案不足,工具不夠專業等等問題——但瑕不掩瑜,AG1280已經具有相當的可用性和市場競爭能力。