搞這個高速數(shù)據(jù)傳輸,真可謂“一波三十折”,僅方案都試了好幾個。分別使用了:示波器方案;NiosII方案;ARM方案。最后直接使用fpga+dp83848實現(xiàn)了高速數(shù)據(jù)采集。 系統(tǒng)的硬件成本較低,使用EP4CE6芯片,外加ADC芯片和DP83848模塊就可以了,連外置ram都省了。淘寶上的虛擬示波器方案,則是用usb的phy傳輸?shù)诫娔X的,但考慮到上位機程序編寫,使用網(wǎng)絡的udp協(xié)議還是非常方便的。當然在fpga上從底層實現(xiàn)udp協(xié)議還是費了很多功夫的。
硬件上,ADC芯片使用TI的AD7476,12bit,1M轉(zhuǎn)換率,spi接口,3根線到fpga即可。當擴展多片adc時,可以將cs和clk共用,多用幾個miso就可以了,后來用這個系統(tǒng)采集8顆ad7884,就是用了8個miso,實現(xiàn)ADC芯片組的同步采集。
網(wǎng)絡芯片dp83848,在很多板子上見到過,單芯片僅幾元錢,網(wǎng)上也有現(xiàn)成的單獨模塊出售,卻要賣50大洋。這里使用rmii接口,也是直接連接fpga,一共9根線,時鐘從網(wǎng)絡模塊上提供。
實際應用中發(fā)現(xiàn),MDIO和MDC用于控制寄存器的兩根線可以不要的,直接用rmii的7根線就可以了,而且RX和TX可以完全分開來搞,這個系統(tǒng)主要搞的當然是TX了,不過TX搞定后,RX一兩天就也搞定了。
對fpga系統(tǒng),直接淘寶上買最小系統(tǒng)就可以了:時鐘是必不可缺的,最好帶兩個按鍵,再帶兩個指示燈,再把所有的io引出,嗯,完美,我買的就這樣的,自帶50M時鐘,帶兩個觸點開關和兩個紅色led,IO從板子兩側全部引出,不帶sram/sdram/flash,唯一遺憾是沒有2.5V的IO塊,不能實現(xiàn)lvds接口,否則可以使用更高速的ADC芯片了。 硬件上需要注意的地方是,RMII接口的時鐘是用網(wǎng)絡模塊提供的,頻率50MHz,而不是用fpga提供,好處是保證網(wǎng)絡模塊送出的數(shù)據(jù)和時鐘同步,但缺陷也是明顯的,線絕不能太長,我用杜邦接口作的線束連接,線長不超過10公分是沒有問題的,同時送給網(wǎng)絡模塊的數(shù)據(jù)也要保證和時鐘的同步,可能是我做的線短,還沒有出現(xiàn)發(fā)送數(shù)據(jù)和時鐘不同步導致的問題。 以上是硬件上的操作。 ********************************************************************************* 硬件完成,就要開始做軟件這個大頭戲了,當然需要一步一步,一步兩步是爪牙,是魔鬼的步伐了。建議最開始先實現(xiàn)數(shù)據(jù)報的發(fā)送,然后再考慮數(shù)據(jù)流結構,最后實現(xiàn)各個模塊功能。 對于數(shù)據(jù)采集,我用的乒乓操作,將ADC數(shù)據(jù)存儲到ram中,再轉(zhuǎn)給rmii接口。這里一定要分清各個模塊的功能,不能越位。
數(shù)據(jù)流:ADC接口模塊->fifo模塊->發(fā)送控制器->內(nèi)部ram1/內(nèi)部ram2->RMII發(fā)送模塊。ADC接口模塊采集模數(shù)轉(zhuǎn)換芯片的數(shù)據(jù),fifo用于緩存數(shù)據(jù),發(fā)送控制器將fifo中的數(shù)據(jù)打包成標準的udp數(shù)據(jù)包并保存在ram中,兩個ram實現(xiàn)乒乓操作,RMII實現(xiàn)將ram中的數(shù)據(jù)送到phy芯片。
第一個是ADC芯片的接口模塊,也就是spi接口,功能非常單一:采集spi數(shù)據(jù),送入到fifo;這也是最長改動的模塊,每換一款adc芯片就要改動,同時控制信號也有這個模塊產(chǎn)生,在合適的時候產(chǎn)生合適的電平,經(jīng)常需要結合示波器調(diào)試,特別是adc的取樣時間確定,也是非常麻煩的。 我的編程風格是將各個模塊生成框圖,再連接起來,這樣看著方便,如下圖這樣,考慮到后期擴展,直接將miso作成了16根,想用幾根用幾根。
數(shù)據(jù)送入fifo,fifo是系統(tǒng)自帶的模塊,由于數(shù)據(jù)實時傳輸,不用太深,64words就可以了,我用的16bits寬度,畢竟告訴數(shù)據(jù)采集很少用高于16位ADC芯片的。
最復雜的就是發(fā)送控制器了,將原始數(shù)據(jù),打包成udp包。包括但不限于:生成Ethernet包的頭部ip包的頭部udp包的頭部,計算udp的checksum,計算ip的headchecksum,將fifo中的數(shù)據(jù)按特定格式存放,告知RMII模塊發(fā)送數(shù)據(jù)長度及數(shù)據(jù)有效標識,還有一個乒乓操作的控制信號產(chǎn)生??傊?,這個是最麻煩的,同時完成后也盡量不要改動這里面的代碼。 下圖中的fifo interface是從fifo中讀取數(shù)據(jù),ram interface則是存放完整的udp包,由于udp包含了各種head,生成這些head會占用時鐘,這也是fifo存在的意義。而最下側的rmii control則是告知rmii接口數(shù)據(jù)的長度,已經(jīng)是否可以發(fā)送了。
正常的,發(fā)送控制器將數(shù)據(jù)打包后保存到ram,通知rmii發(fā)送就可以了,但這里用的單口ram,rmii讀取的時候就不能保存udp包了,會將網(wǎng)絡利用率降低一半,因此還需要一個乒乓操作控制器,一個ram寫入udp數(shù)據(jù)的時候,另一個正在讀取并通過rmii接口發(fā)送。這里通過一個ram_chose信號實現(xiàn)控制。 框圖很復雜,其實就是將兩個ram變成了一個而已。 ram模塊,當然使用8位寬度,至于深度,由于最大的網(wǎng)絡數(shù)據(jù)包長度為1518字節(jié),這里選擇了2048長度,足夠。
最后就是rmii的發(fā)送模塊了,該模塊只是將ram中的數(shù)據(jù),按照rmii的協(xié)議發(fā)送,也就是將8位的byte,轉(zhuǎn)成2位的tx1/tx0。外加一點工作是,產(chǎn)生前導向符,也就是一串的10101010,和最后一個10101011。再加一點工作是,計算數(shù)據(jù)包的crc32,追加后數(shù)據(jù)后面,這里crc32計算,要選對多項式,務必請使用:`define POLYNOMIAL 32'h04C11DB7,計算crc32也是個無比蛋疼的過程,首先是實時計算,每發(fā)送一個字節(jié)就要更新CRC,計算到最后還要取反才能發(fā)送。最后再加一點工作是,從wiki百科得知,數(shù)據(jù)包發(fā)送需要有12個字節(jié)的間隔,也就是不能一直發(fā)。由于現(xiàn)在網(wǎng)卡都是全雙工的了,也就不需要考慮碰撞檢測了。 網(wǎng)絡模塊的4根線就到這個上面的,注意時鐘是網(wǎng)絡模塊提供的oscin。
最后發(fā)一張整體的bdf圖
****************************************************************************************** 由于這個方案設計的時候重要工作已經(jīng)放在fpga上了,故上位機的程序就比較好辦了。使用udp協(xié)議監(jiān)聽制定的端口,將收到的數(shù)據(jù)包按格式解碼并顯示就可以了,使用任何一門語言都可以方便實現(xiàn),也不涉及底層代碼編寫。這里使用labVIEW實現(xiàn)。 下圖是實現(xiàn)數(shù)據(jù)的采集。
當然要實現(xiàn)更過的功能,上位機還是要費點功夫的,比如實現(xiàn)觸發(fā)功能,多通道信號的恢復,數(shù)字信號處理識別等。其實相當于做了一套dmq設備,并將接口進行了簡化,在udp數(shù)據(jù)實時傳輸實現(xiàn)的基礎上,按客戶需求實現(xiàn)測量功能則已沒有瓶頸。 最后放一張當時調(diào)試成功的界面,用了1顆AD7874實現(xiàn)3M采樣率時的網(wǎng)絡傳輸截圖,由于我按照16位寬度做的,故網(wǎng)絡使用率約48%多一些。
********************************************************************* 后面再更新RMII接收的方法,比較容易實現(xiàn)的。 接口上,將RMII的時鐘和rx1/rx0以及crs/dv接入fpga,并進行對應的解碼即可。 解碼時已將數(shù)據(jù)包放入到ram中,不過似乎沒什么用,直接進行數(shù)據(jù)包的解碼了,再根據(jù)接口確定命令就可以了。