基于Spartan-6的高速數(shù)據(jù)采集、處理和實(shí)時(shí)傳輸研究
掃描二維碼
隨時(shí)隨地手機(jī)看文章
1.前言
隨著信息技術(shù)的飛速發(fā)展,各種數(shù)據(jù)的實(shí)時(shí)采集和處理在現(xiàn)代工業(yè)控制和科學(xué)研究中已成為必不可少的部分,尤其在信號(hào)測(cè)量、圖像處理、音頻信號(hào)處理等一些高速、高精度的測(cè)量中需要對(duì)高性能的數(shù)據(jù)采集技術(shù)。這就為數(shù)據(jù)采集設(shè)備的設(shè)計(jì)提出了兩個(gè)的要求:1)要求接口簡(jiǎn)單靈活且有較高的數(shù)據(jù)傳輸率; 2)由于數(shù)據(jù)量通常都較大,要求主機(jī)能夠?qū)?shù)據(jù)做出快速響應(yīng),并進(jìn)行實(shí)時(shí)分析、處理。
在基于軟件無線電的接收機(jī)架構(gòu)中,數(shù)字下變頻(DDC)技術(shù)起著非常重要的作用,也是軟件無線電的核心技術(shù)之一。數(shù)字下變頻位于模數(shù)轉(zhuǎn)換(ADC)之后,需要處理高速高容量的數(shù)據(jù),因此難度較大,不容易實(shí)現(xiàn)。針對(duì)數(shù)字下變頻中的這一實(shí)際問題以及數(shù)據(jù)采集設(shè)備的兩個(gè)要求,本報(bào)告采用了一種基于FPGA與USB 2.0的數(shù)據(jù)采集與實(shí)時(shí)傳輸方案。
本文所研究的基于Spartan-6的高速數(shù)據(jù)采集、處理和實(shí)時(shí)傳輸系統(tǒng),就是實(shí)現(xiàn)將寬帶中頻數(shù)字接收機(jī)輸出的高速正交IQ數(shù)據(jù)傳輸給FPGA去實(shí)現(xiàn)軟件無線電的后續(xù)信號(hào)處理算法。利用Cypress的EZ-USB FX2高速數(shù)據(jù)傳輸方案實(shí)現(xiàn)將基帶數(shù)據(jù)或者經(jīng)FPGA處理后輸出的數(shù)據(jù)進(jìn)行傳輸,并利用上位機(jī)軟件進(jìn)行上位機(jī)存儲(chǔ)和顯示。本文主要從系統(tǒng)的硬件設(shè)計(jì)和軟件設(shè)計(jì)兩個(gè)方面分別對(duì)高速數(shù)據(jù)采集模塊(寬帶中頻數(shù)字下變頻模塊)、高速數(shù)據(jù)傳輸模塊以及上位機(jī)軟件三個(gè)方面進(jìn)行詳細(xì)介紹。
2.系統(tǒng)總體方案設(shè)計(jì)
整個(gè)系統(tǒng)分成3個(gè)子模塊,分別是:(1)高速數(shù)據(jù)采集模塊(寬帶中頻數(shù)字下變頻模塊);(2)高速數(shù)據(jù)實(shí)時(shí)處理和傳輸模塊;(3)上位機(jī)軟件模塊。整個(gè)系統(tǒng)框圖如圖1所示:
其中高速數(shù)據(jù)采集模塊與EZ-USB高速數(shù)據(jù)處理和傳輸模塊分別由對(duì)應(yīng)的硬件電路和軟件組成。上位機(jī)軟件模塊主要是利用Microsoft Visual2008軟件利用MFC進(jìn)行開發(fā)。系統(tǒng)整體硬件框圖如圖2所示:
系統(tǒng)整體工作原理:首先高速數(shù)據(jù)采集模塊對(duì)70MHz中頻模擬信號(hào)進(jìn)行模數(shù)轉(zhuǎn)換,采樣速率為60MHz(基于帶通采樣定理),然后利用Atmel公司的高性能微控制器Atmega16A作為控制單元以異步控制方式對(duì)專用數(shù)字下變頻進(jìn)行設(shè)置和編程,實(shí)現(xiàn)將中心為70MHz的數(shù)字中頻信號(hào)搬移到數(shù)字基帶,基帶數(shù)據(jù)速率仍然為60MHz,因此需要對(duì)高速的數(shù)字基帶信號(hào)進(jìn)行抽取和濾波得到低速的數(shù)字基帶信號(hào),抽取倍數(shù)可通過編程設(shè)置,抽取倍數(shù)越大得到的數(shù)字基帶信號(hào)速率就越小,低速的數(shù)據(jù)基帶信號(hào)再傳輸給FPGA實(shí)現(xiàn)基帶數(shù)據(jù)的碼元恢復(fù),得到原始信息。EZ-USB既可以對(duì)數(shù)字基帶信號(hào)數(shù)據(jù)進(jìn)行實(shí)時(shí)數(shù)據(jù)采集和傳輸也可以對(duì)FPGA輸出的原始碼元信息進(jìn)行傳輸,最后通過USB2.0接口將這些數(shù)據(jù)傳輸?shù)缴衔粰C(jī)進(jìn)行數(shù)據(jù)實(shí)時(shí)存儲(chǔ)和顯示。
3.高速數(shù)據(jù)采集模塊(寬帶中頻數(shù)字下變頻模塊)
傳統(tǒng)的數(shù)據(jù)采集系統(tǒng)往往采用單片機(jī)或數(shù)字信號(hào)處理器(DSP)作為控制器,控制模/數(shù)轉(zhuǎn)換器(ADC)、存儲(chǔ)器和其他外圍電路的工作。但由于單片機(jī)本身的指令周期以及處理速度的影響,其時(shí)鐘頻率較低,各種功能都要靠軟件的運(yùn)行來實(shí)現(xiàn),軟件運(yùn)行時(shí)間在整個(gè)采樣時(shí)間中占有很大的比例,效率較低,很難滿足系統(tǒng)對(duì)數(shù)據(jù)采集系統(tǒng)實(shí)時(shí)性和同步性的要求?;贒SP的數(shù)據(jù)采集系統(tǒng),雖然處理速度快,但成本較高,過于頻繁的中斷會(huì)使CPU的效率降低,響應(yīng)速度變差。近年來,基于FPGA的數(shù)據(jù)采集方案逐漸成為一種具有特殊優(yōu)勢(shì)的一種方案,其中最主要的一個(gè)優(yōu)點(diǎn)就是可以實(shí)現(xiàn)對(duì)數(shù)據(jù)的并行處理。另外還具有開發(fā)周期短,集成度高,功耗低,工作頻率高,設(shè)計(jì)費(fèi)用低,編程配置靈活等一系列優(yōu)點(diǎn)。
本報(bào)告中采用的高速數(shù)據(jù)采集與實(shí)時(shí)傳輸方案,主要包括以下幾個(gè)部分:1)高速數(shù)據(jù)采集以及數(shù)字下變頻處理部分;(2).高速數(shù)據(jù)傳輸部分;(3).上位機(jī)數(shù)據(jù)采集控制部分。系統(tǒng)首先將外部真實(shí)世界的模擬信號(hào)進(jìn)行數(shù)字化,然后將模數(shù)轉(zhuǎn)換器的高速數(shù)字信號(hào)進(jìn)行數(shù)據(jù)緩沖,然后將緩沖數(shù)據(jù)經(jīng)過數(shù)字下變頻處理后傳輸給FPGA,采用FPGA控制CY7C68013A實(shí)現(xiàn)高速數(shù)據(jù)實(shí)時(shí)傳輸與存儲(chǔ),并進(jìn)行顯示。
3.1 高速數(shù)據(jù)采集及數(shù)字下變頻處理部分硬件設(shè)計(jì)
高速數(shù)據(jù)采集及數(shù)字下變頻模塊是利用ADI的高速模數(shù)轉(zhuǎn)換器AD6640按照奈奎斯特帶通采用定理進(jìn)行數(shù)據(jù)采集(本報(bào)告中選擇的采樣速率為60MHz),然后將60MHz的數(shù)字中頻信號(hào)傳輸給專用數(shù)字下變頻器件AD6620(也可以利用FPGA實(shí)現(xiàn)數(shù)字下變頻以及信號(hào)抽取濾波等)進(jìn)行數(shù)字下變頻和抽取濾波等處理。
高速數(shù)據(jù)采集(主要是采集70MHz的中頻模擬信號(hào))及數(shù)字下變頻(將70MHz中頻搬移到數(shù)字基帶)處理部分的硬件設(shè)計(jì)方案如下圖1所示:
在該部分的硬件設(shè)計(jì)中:1)ADC采用的是ADI公司的高性能AD6640來實(shí)現(xiàn)對(duì)數(shù)據(jù)的高速采集。AD6640具有如下優(yōu)點(diǎn):12位的采樣精度,具有65MSPS最小采樣率,在25MHz帶寬上具有高達(dá)80
的無雜散動(dòng)態(tài)范圍(SFDR),中頻采樣率可達(dá)70MSPS, 功率消耗大約710mW,采用采用5V單電源供電,片上自帶T/R和參考電壓,數(shù)據(jù)以二進(jìn)制補(bǔ)碼形式輸出,CMOS輸出電平兼容3.3V和5V。2)下變頻部分,采用的是美國(guó)ADI公司的中頻數(shù)字接收機(jī)專用數(shù)字信號(hào)處理器AD6620。它的內(nèi)部集成了NCO(數(shù)控晶體振蕩器)、數(shù)字混頻器、二階級(jí)聯(lián)積分梳妝濾波器(CIC2)、五階級(jí)聯(lián)積分梳妝濾波器(CIC5)、系數(shù)可編程的抽取濾波器(RCF)等。3)控制部分,選用了Xilinx公司的Spartan 6芯片來進(jìn)行采集控制、數(shù)據(jù)緩沖、數(shù)據(jù)處理、數(shù)據(jù)傳輸控制及通信等。
3.2 高速數(shù)據(jù)采集及下變頻處理部分軟件設(shè)計(jì)
寬帶中頻數(shù)字下變頻器件AD6620內(nèi)部主要由以下單元組成:頻率變換單元、二階固定系數(shù)級(jí)聯(lián)積分梳狀濾波器(CIC2)單元、五階固定系數(shù)級(jí)聯(lián)積分梳狀濾波器(CIC5)單元以及一個(gè)可變系數(shù)的RAM系數(shù)抽取濾波器(RCF)單元。其中頻率變換模塊實(shí)現(xiàn)中頻到數(shù)字基帶的下變頻,CIC2單元和CIC5單元是完成采樣速率的抽取功能,通過設(shè)置不同的抽取倍數(shù)可得到不同速率的基帶信號(hào),而可變系數(shù)的 RCF單元?jiǎng)t是將抽取后的信號(hào)進(jìn)行整形濾波處理,使得濾波器的通帶紋波、過渡帶帶寬以及阻帶衰減等設(shè)計(jì)參數(shù)設(shè)計(jì)的更優(yōu)化。
下面分別對(duì)頻率轉(zhuǎn)換單元、CIC2和CIC5濾波器單元以及可變系數(shù)RCF濾波器單元分別進(jìn)行編程介紹。
3.2.1頻率變換單元編程設(shè)置
頻率變換單元主要是利用片內(nèi)集成的數(shù)控晶體振蕩器(NCO)來產(chǎn)生一組正交的數(shù)字本振信號(hào)。NCO模塊的目標(biāo)就是產(chǎn)生理想的正弦波和余弦波,以便與高速模數(shù)轉(zhuǎn)換器件AD6640傳輸?shù)闹蓄l實(shí)信號(hào)進(jìn)行頻率轉(zhuǎn)換,把中頻信號(hào)的頻譜搬移到數(shù)字基帶。NCO模塊產(chǎn)生的正交數(shù)字本振信號(hào)頻率是通過式來計(jì)算:
(式 1)
其中, 為NCO模塊的本振頻率, 為相應(yīng)通道IF信號(hào)輸入的頻率,在本文中為70Mhz, 為采樣頻率,本文中應(yīng)為56MHz,實(shí)際為60MHz,
根據(jù)式可計(jì)算得到 的值為:0010 1010 1010 1010 1010 1010 1010 1010共32位二進(jìn)制數(shù),那么通過FPGA向地址為0x303的地址寫入上述32bit的二進(jìn)制數(shù)。
3.2.2 固定系數(shù)CIC濾波器設(shè)計(jì)及其編程
二階和五階級(jí)聯(lián)積分梳狀濾波器都是固定系數(shù)的抽取濾波器。CIC2處理的信號(hào)時(shí)頻率變換后輸出的I、Q兩路數(shù)字基帶信號(hào),
均為60MHz,為了減輕后續(xù)處理器的處理難度,需要利用CIC2和CIC5抽取濾波器進(jìn)行合理抽取濾波,經(jīng)CIC2抽取濾波后的信號(hào)頻率為式所示:
其中, 為經(jīng)過抽取系數(shù) 抽取濾波降速后的信號(hào)頻率。可通過微控制器對(duì)CIC2濾波器抽取系數(shù) 進(jìn)行編程,取值范圍為2~6中得某一個(gè)整數(shù)值。第一級(jí)的抽取系數(shù)取值越大相應(yīng)的整個(gè)芯片功耗就越低。CIC2濾波器的增益和通帶衰減計(jì)算公式如式2和式3所示:
其中, 為CIC2濾波器增益衰減因子,取值范圍為0~6之間的整數(shù)值。為了獲得最優(yōu)的動(dòng)態(tài)范圍需要將 設(shè)置為最小值,但是要注意防止出現(xiàn)溢出。
為輸入信號(hào)的電平。
CIC2濾波器的離散和連續(xù)頻率響應(yīng)公式分別如式4和5所示:
相應(yīng)的CIC5濾波器和CIC2濾波器的功能是一致的,也是實(shí)現(xiàn)抽取和濾波,CIC5濾波器是對(duì)CIC2這一級(jí)處理后的信號(hào)再一次抽取。下面利用ADI公司的SoftCell濾波器設(shè)計(jì)軟件,設(shè)計(jì)CIC2、CIC5、RCF濾波器,以便獲得相應(yīng)濾波器參數(shù),進(jìn)行因?yàn)閷?duì)于70MHz中頻信號(hào)而言,依據(jù)射頻帶通采樣定理,可以確定帶通采樣率為56MSPS就可以滿足要求,但是為了增大信噪比的性能可將降采樣率設(shè)成60MHz,輸出頻率為0.4MSPS,那么抽取倍數(shù)為140。打開濾波器設(shè)計(jì)軟件后的界面如圖3所示:
在軟件的PortSelect菜單中選擇AD6620作為設(shè)計(jì)的對(duì)象,然后在面板的左中間位置輸入信號(hào)頻率60MHz,以及抽取濾波后的輸出頻率0.4MHz,在Passband這個(gè)地方輸入通帶帶寬4MHz,通帶紋波設(shè)置為0.18,另外在Stopband這個(gè)地方設(shè)置阻帶頻率為4.5MHz,衰減為60dB,在5MHz這個(gè)頻率點(diǎn),設(shè)置衰減為80dB。然后點(diǎn)擊compute按鈕后就可以計(jì)算得到設(shè)計(jì)好的濾波器頻率相應(yīng)以及相應(yīng)的各級(jí)濾波器系數(shù)和RCF濾波器的抽頭數(shù)。設(shè)計(jì)完成后CIC2以及CIC5以及RCF濾波器系數(shù)可能有多種組合,這種情況下可以對(duì)比分析濾波器的響應(yīng),選擇一種最佳的設(shè)計(jì)方案。表 1為不同抽取倍數(shù)下的帶寬和混疊衰減規(guī)格:
最后確定選擇CIC2濾波器抽取倍數(shù)為15,CIC5抽取倍數(shù)為2,RCF抽取倍數(shù)為2就可以滿足系統(tǒng)設(shè)計(jì)的要求。
3.2.3 可變系數(shù)RCF濾波器參數(shù)設(shè)置
根據(jù)2.2.1節(jié)設(shè)計(jì)得到的RCF抽取倍數(shù)為30,可以根據(jù)式來計(jì)算得到抽取濾波器的抽頭數(shù)為150。
其中, 為60MHz, 為15, 為CIC5濾波器抽取降速后的頻率也即2MHz,當(dāng)輸入通道模式時(shí)單通道實(shí)模式時(shí)為1。對(duì)可變系數(shù)的RCF濾波器進(jìn)行編程設(shè)置時(shí)需要先向地址0x30C寫入整數(shù) 149( =59)。然后濾波器的響應(yīng) 逐一寫入到地址0x000~0xFF中。這樣就完成了RCF寄存器的編程設(shè)置。
3.2.4 AD6620工作模式寄存器編程
對(duì)頻率變換單元、固定系數(shù)的CIC2和CIC5以及RCF濾波器變化進(jìn)行編程設(shè)置后,要使AD6620正常工作還需要對(duì)其工作方式和相關(guān)寄存器進(jìn)行配置。AD6620的工作方式主要由地址0x300H的寄存器中低四位決定,其中,bit0=1時(shí)系統(tǒng)處于軟復(fù)位狀態(tài),bit0=0則處于正常狀態(tài);bit1=1時(shí)系統(tǒng)處于雙通道實(shí)數(shù)輸入模式;bit2=1時(shí)系統(tǒng)處于單通道復(fù)數(shù)輸入模式;如果bit0~bit2都是0,那么系統(tǒng)處于單通道實(shí)數(shù)輸入模式;bit3=1/0為時(shí)鐘主從工作模式。因此,當(dāng)各寄存器配置完成后需要將寄存器bit0拉低,也即置為0狀態(tài)使其脫離軟復(fù)位狀態(tài),進(jìn)入正常工作模式。另外,在本文中AD6620讀寫內(nèi)部寄存器是采用的異步讀寫時(shí)序,數(shù)據(jù)輸出是并行16位輸出模式。[!--empirenews.page--]
總的來說,采用FPGA實(shí)現(xiàn)AD6620初始化的過程如下:
1)首先判斷AD6620有沒有經(jīng)過硬件復(fù)位,如果沒有經(jīng)過硬件復(fù)位而又要實(shí)現(xiàn)系統(tǒng)復(fù)位,那么可以通過程序去編程地址0x300H的寄存器的bit0位置為1,使得AD6620處于軟件復(fù)位狀態(tài),這樣就可以對(duì)非動(dòng)態(tài)的寄存器進(jìn)行相關(guān)編程;
2)對(duì)NCO單元、CIC2、CIC5濾波器的抽取倍數(shù)和系數(shù)進(jìn)行編程,將相應(yīng)的控制系數(shù)寫入相對(duì)應(yīng)的地址寄存器中;
3)對(duì)RCF濾波器系數(shù)進(jìn)行編程,分別向地址0x000H~0xFF中寫入 這150個(gè)抽頭系數(shù),剩下的寫入0。
4)對(duì)AD6620所有配置工作完成后,必須向地址0x300H的bit0寫入0,使其脫離軟復(fù)位狀態(tài),這樣AD6620才可以在所配置工作方式下對(duì)輸入的數(shù)據(jù)進(jìn)行處理?! ?/p>
4.實(shí)時(shí)數(shù)據(jù)傳輸模塊
基于USB2.0的數(shù)據(jù)傳輸設(shè)計(jì)主要包括以下幾個(gè)部分:(1)基于EZ-USB FX2的硬件電路設(shè)計(jì)(2)EZ-USB FX2系列器件CY7C68013A的固件程序(設(shè)置CY7C68013A工作模式、傳輸方式等,需要對(duì)其內(nèi)部寄存器進(jìn)行相關(guān)配置),主要通過Keil C51軟件進(jìn)行開發(fā)。
4.1 實(shí)時(shí)數(shù)據(jù)傳輸部分的硬件設(shè)計(jì)
實(shí)時(shí)數(shù)據(jù)傳輸部分(USB2.0的數(shù)據(jù)傳輸)的硬件設(shè)計(jì)方案如圖10所示,USB模塊與FPGA間的連線如圖11所示。
在高速數(shù)據(jù)傳輸部分采用了Cypress公司的EZ-USB FX2 CY7C68013A芯片,采用單片USB 2.0外設(shè)可以避免用FPGA直接實(shí)現(xiàn)USB通信協(xié)議時(shí)可靠性低的問題。在數(shù)據(jù)采集、傳輸以及測(cè)試過程中,CY7C68013A作為從機(jī),負(fù)責(zé)將FPGA發(fā)送給它的基帶數(shù)據(jù),利用其16位SLAVE FIFO(關(guān)于SLAVE FIFO的傳輸示意圖如圖所示)將數(shù)據(jù)緩沖發(fā)送給上位機(jī)。在SLAVE FIFO數(shù)據(jù)傳輸中,上位機(jī)應(yīng)用程序、固件程序和FPGA程序都非常關(guān)鍵,要配合好才能完成數(shù)據(jù)的高速數(shù)據(jù)傳輸。在這個(gè)系統(tǒng)中,F(xiàn)PGA起著主控制器作用,CY7C68013A則相當(dāng)于一個(gè)從設(shè)備。具體的工作流程是:FPGA把接收過來的16位數(shù)字基帶信號(hào)發(fā)送給CY7C68013A,CY7C68013A以SLAVE FIFO的方式將這些數(shù)據(jù)緩沖,并以批量數(shù)據(jù)傳輸?shù)姆绞桨l(fā)送給上位機(jī),上位機(jī)接收到這些數(shù)據(jù)后加以存儲(chǔ)和顯示。
4.2 CY7C68013A的固件程序設(shè)計(jì)
固件程序是指運(yùn)行在設(shè)備CPU中的程序,只有在該程序運(yùn)行時(shí),外設(shè)才能稱之為具有給定功能的外部設(shè)備。固件程序負(fù)責(zé)初始化單元,重新設(shè)置設(shè)備。主要包括設(shè)備描述符信息、設(shè)備功能代碼。設(shè)備描述信息描述USB設(shè)備的一半特性和配置,如設(shè)備類別、接口配置、VID和PID等。主機(jī)在設(shè)備列舉時(shí)要獲取USB設(shè)備的描述符,從而獲得設(shè)備的配置信息和相關(guān)驅(qū)動(dòng)信息。用戶可以通過修改固件中的描述符來改變?cè)O(shè)備的特性。設(shè)備功能代碼有設(shè)備的功能需求決定。通信控制功能代碼執(zhí)行主機(jī)請(qǐng)求分析處理和數(shù)據(jù)交換處理功能。
采用ARM公司的Keil C51 uVision4.02開發(fā)CY7C68013A的固件程序。為了簡(jiǎn)化固件編程,CYPRESS提供了固件編程框架,在此基礎(chǔ)上添加我們所需要的配置代碼即可完成軟件編程。
復(fù)位上電時(shí),固件先初始化一些全局變量,然后調(diào)用初始化函數(shù)ID_Init(),初始化設(shè)備一直到?jīng)]有配置的狀態(tài)和打開中斷,循環(huán)1s后枚舉,直到端點(diǎn)0接收到SETUP包退出循環(huán),進(jìn)入循環(huán)語句while,執(zhí)行任務(wù)函數(shù),函數(shù)包括:
(a)TD_POLL()用戶任務(wù)調(diào)度函數(shù);
(b)如果發(fā)現(xiàn)USB設(shè)備請(qǐng)求,則執(zhí)行對(duì)應(yīng)的USB請(qǐng)求;
(c)如果發(fā)現(xiàn)USB空閑置位,則調(diào)用TD_Suspend()這個(gè)掛起函數(shù),調(diào)用成功后則內(nèi)核掛起,直到出現(xiàn)USB遠(yuǎn)程喚醒信號(hào),調(diào)用TD_Resume(),內(nèi)核喚醒后重新進(jìn)入while循環(huán)。
固件框架程序需要以下幾個(gè)文件:
(1)FX2.h:庫函數(shù)申明,以及變量、宏定義、數(shù)據(jù)類型定義;
(2)Fxregs.h:FX2LP寄存器頭文件;
(3)Fw.c:固件框架源文件;
(4)Periph.c:用戶調(diào)度函數(shù)、用戶可以修改,在不同的應(yīng)用中文件名不一樣;
(5)Dscr.a51:USB描述符列表,用戶可以修改;
(6)Ezusb.lib:EZUSB庫文件;
(7)USBJmpTb.OBJ:中斷跳轉(zhuǎn)函數(shù)目標(biāo)文件
這種情況下,需要在固件程序中,進(jìn)行相應(yīng)的修改。在SLAVE FIFO中,特別是自動(dòng)傳輸(CY7C68013A單片機(jī)不干預(yù)數(shù)據(jù)傳輸),固件程序主要完成各個(gè)端口的初始化。因此我們要修改兩個(gè)地方:(1)USB設(shè)備描述符列表Dscr.a51,根據(jù)實(shí)際情況修改里面的端口數(shù)和傳輸方式等;(2)初始化函數(shù)void TD_Init(void)。在SLAVE FIFO這種方式下,設(shè)置EP2為4緩沖的輸出端口,EP6為4緩沖的輸入端口。
5. 上位機(jī)軟件設(shè)計(jì)
上位機(jī)應(yīng)用程序是系統(tǒng)與用戶之間交流的接口,它通過通用驅(qū)動(dòng)程序完成對(duì)外設(shè)的控制和通信。主機(jī)端應(yīng)用程序負(fù)責(zé)向FX2的FIFO發(fā)送或者接收數(shù)據(jù)。本報(bào)告中采用的固件架構(gòu)是EZ-USB FX2/FX2LP(CY7C68013, 驅(qū)動(dòng)程序是Cyusb.sys。用Visual Studio2008軟件進(jìn)行上位機(jī)開發(fā),利用C++/MFC來開發(fā)基于對(duì)話框的應(yīng)用程序,系統(tǒng)的主要功能模塊有:打開USB設(shè)備、復(fù)位USB設(shè)備、系統(tǒng)數(shù)據(jù)測(cè)試與顯示等。
在VS2008中建立一個(gè)MFC 單文檔/對(duì)話框應(yīng)用程序后,在路徑項(xiàng)目中包含頭文件cyapi.h和cyapi.lib所在的路徑。然后手動(dòng)導(dǎo)入cyapi.lib,注意是CV6_7的lib,不要導(dǎo)入BCB的。
開發(fā)USB應(yīng)用程序的一般工作流程如下。
1)首先要?jiǎng)?chuàng)建一個(gè)USB設(shè)備對(duì)象
CCyUSBDevice *USBDevice = new CCyUSBDev(Handle);括號(hào)中的Handle是USB所關(guān)聯(lián)對(duì)象的句柄,一般在MFC中直接就是m_hwnd。
2)打開USB設(shè)備。
可以用到兩個(gè)函數(shù)open();isopen()這兩個(gè)都可以用來打開USB設(shè)備,isopen()還可以判斷能否獲得USB設(shè)備句柄,一般來說,如果只有一個(gè)USB設(shè)備連接,可以這樣打開:
USBDevice->open(0)//打開0號(hào)USB設(shè)備;如果要判斷,可以:
if(!USBDevice->open(0)) //打開失敗
{messagebox("USB未連接");}
或者if(!USBDevice->Isopen())
如果連接有多個(gè)USB設(shè)備,那么可以枚舉所有的USB,用到DeviceCount()函數(shù);具體的可以參考cybulk的例子。執(zhí)行USBDevice->DeviceCount()后,返回所連接的USB設(shè)備個(gè)數(shù):
if (USBDevice->DeviceCount()) //保證至少有一個(gè)USB設(shè)備連接
{
for (i = 0; i DeviceCount(); i++) //枚舉所有USB設(shè)備
{
USBDevice->Open(i);//打開第i號(hào)USB設(shè)備
m_DeviceListComBox.AddString(USBDevice->DeviceName);//所選擇的當(dāng)前設(shè)備名}
}
3)端點(diǎn)枚舉
在cybulk的例子中介紹了如何枚舉固件中使用的所有端點(diǎn),也就是使用多個(gè)端點(diǎn)的情況,其枚舉步驟主要包括一下幾個(gè)端點(diǎn):
(1)創(chuàng)建USB設(shè)備并打開該設(shè)備
CCyUSBDevice *USBDevice=new CCyUSBDevice(m_hWnd);//USB設(shè)備USBDevice->Open(0);//打開0號(hào)USB設(shè)備。
(2)獲取所用的端點(diǎn)數(shù)目
intepts = USBDevice->EndPointCount();
EndPointCount();函數(shù)返回當(dāng)前所用的端點(diǎn)數(shù)+1,也就是包含了控制端點(diǎn)。例如在固件接口描述符Interface Descriptor中設(shè)置Number of end points項(xiàng)(第5項(xiàng))的值為4,則epts的值為4+1=5。
(3)定義端點(diǎn)指針
CCyUSBEndPoint *endpt;CCyUSBEndPoint建立一個(gè)端點(diǎn)對(duì)象,可建立所有的端點(diǎn)類型,控制端點(diǎn),bulk端點(diǎn),ISO端點(diǎn)等;
(4)枚舉端點(diǎn),并獲得其屬性:端點(diǎn)號(hào),傳輸方向
for (i=1; i
{
endpt = USBDevice->EndPoints[i];//EndPoints-端點(diǎn)列表,最大16.EndPoints[0]指向控制端點(diǎn)(CCyControlEndPoint),未使用的端點(diǎn)設(shè)置為NULL。
if (endpt->Attributes == 2) // Bulk Attributes 判斷傳輸類型bulk,control,等。
{
sprintf(s, "0x%02x", endpt->Address);
if (endpt->Address & 0x80) //Address--判斷傳輸方向in or out 0x8_-in;0x0_-out
{
m_InEndptComBox.AddString(s); //最高位為8,in端點(diǎn),添加到in組合框m_InEndptComBox.SetItemData(m_InEndptComBox.GetCount()-1,i);
else
{
m_OutEndptComBox.AddString(s); //否則,最高位為0,out端點(diǎn),添加到out組合框m_OutEndptComBox.SetItemData(m_OutEndptComBox.GetCount()-1,i);
}}}[!--empirenews.page--]
這樣,就完成了某個(gè)具體端點(diǎn)的選擇。如果只需要使用一個(gè)端點(diǎn)的話,那上面的代碼無疑就顯得冗長(zhǎng)不夠簡(jiǎn)潔了。僅使用一個(gè)端點(diǎn),可以使用EndPointOf()函數(shù),該函數(shù)直接使用指定的端點(diǎn),返回其指針;例如,要使用端點(diǎn)2,in傳輸,那么,可以這樣:
CCyUSBDevice *USBDevice=new CCyUSBDevice(m_hWnd); //USB設(shè)備
USBDevice->Open(0); //打開0號(hào)設(shè)備,
CCyUSBEndPoint *endpt = USBDevice->EndPointOf(0X82); //使用端點(diǎn)2,in傳輸可以看到,上面的只需要3行代碼,比枚舉簡(jiǎn)潔方便多了。至于獲取USB其他屬性,這里列出經(jīng)常使用的幾個(gè):(1)USBDevice->DeviceCount()//返回連接到電腦的USB設(shè)備個(gè)數(shù),從0,1,2.開始命名(2)USBDevice->DeviceName()//返回USB設(shè)備名稱,也就是固件中 StringDscr2:字段字符串。(3)USBDevice->VendorID//返回USB設(shè)備VIDUSBDevice->ProductID//返回USB設(shè)備PID。具體的可參考cyapi的文檔,在CCyUSBDevice和CCyUSBEndPoint這兩個(gè)類里可查詢到。
4)傳輸命令控制
可以說,寫USB上位機(jī),要對(duì)固件進(jìn)行自定義命令傳輸,通常都是用輸入輸出控制傳輸進(jìn)行的,即使用控制傳輸in或者out方式實(shí)現(xiàn)對(duì)固件的自定義命令。
(1)out控制傳輸發(fā)送vendor命令
采用out方式應(yīng)該比較符合大家的思維,因?yàn)槭菑纳衔粰C(jī)發(fā)命令到下位機(jī),怎么看都應(yīng)該是out而不是in。例如你在固件里設(shè)置了out的接收buf:
BOOL DR_VendorCmnd(void)
{
switch (SETUPDAT[1])
{
case 0xDD:
{ temp=EP0BUF[0];
EP0BCH=0;
EP0BCL=1;
EP0CS |=bmHSNAK;
break;
}
default:
return(TRUE);
}
return(FALSE);
}
在VS2008的C++/MFC中:
CCyControlEndPoint* CtlEndpoint;//定義一個(gè)控制端點(diǎn)
CtlEndpoint->Target = TGT_DEVICE;//不必關(guān)注,固定
CtlEndpoint->ReqType = REQ_VENDOR; //請(qǐng)求類型:自定義請(qǐng)求(標(biāo)準(zhǔn)請(qǐng)求等)CtlEndpoint->Direction = DIR_TO_DEVICE; //傳輸方向:主機(jī)->usb設(shè)備(out)
CtlEndpoint->ReqCode = 0XDD; //自定義請(qǐng)求碼
CtlEndpoint->Value = 0; //這里的設(shè)定值將傳給setupdat的[2:3]位
CtlEndpoint->Index = 0; //這里的設(shè)定值將傳給setupdat的[4:5];PUCHAR buf=new UCHAR[1];
ZeroMemory(buf,1);//用0填充buf區(qū),填充大小(1字節(jié))
long buflen=1;//傳輸?shù)钠渌止?jié)數(shù):cy控制臺(tái)console中l(wèi)ength的值。
CtlEndpoint->XferData(buf,buflen);
當(dāng)然,沒有規(guī)定說一定必須傳給下位機(jī)至少一個(gè)字節(jié)的數(shù)據(jù);你也可以不傳;不過最好下位機(jī)同樣設(shè)置接收字節(jié)為0,否則小心有莫名奇妙的錯(cuò)誤(XX內(nèi)存不能為只讀等等)。
將temp=EP0BUF[0];去掉,上位機(jī):
PUCHAR buf=new UCHAR; //用0填充buf區(qū),填充大小(1字節(jié))
long buflen=0;
CtlEndpoint->XferData(buf,buflen);
也是可以的,另外,控制傳輸請(qǐng)盡量用同步的xferdata()而不是異步的begindataxfer()。
(2)in控制傳輸發(fā)送vendor命令
基本上,跟out區(qū)別不大。固件中:
BOOL DR_VendorCmnd(void)
{
switch (SETUPDAT[1])
{
case 0xDD:
{
*EP0BUF=0XDD;
EP0BCH=0;
EP0BCL=1;
EP0CS |=bmHSNAK;
break;
}
default:
return(TRUE);
}
return(FALSE);
}
VC中:CCyControlEndPoint* CtlEndpoint; //定義一個(gè)控制端點(diǎn)
CtlEndpoint->Target = TGT_DEVICE;//不必關(guān)注,固定
CtlEndpoint->ReqType= REQ_VENDOR; //請(qǐng)求類型:自定義請(qǐng)求(標(biāo)準(zhǔn)請(qǐng)求等)CtlEndpoint->Direction = DIR_FROM_DEVICE; //傳輸方向:usb設(shè)備->主機(jī)(in)CtlEndpoint->ReqCode = 0XDD; //自定義請(qǐng)求碼CtlEndpoint->Value = 0;//這里的設(shè)定值將傳給setupdat的[2:3]位CtlEndpoint->Index = 0; //這里的設(shè)定值將傳給setupdat的[4:5]位PUCHAR buf=new UCHAR[1];
ZeroMemory(buf,1); //用0填充buf區(qū),填充大小(1字節(jié))
long buflen=1; //傳輸?shù)钠渌止?jié)數(shù):cy控制臺(tái)console中l(wèi)ength的值CtlEndpoint->XferData(buf,buflen);這里的話,推薦盡量設(shè)置接收緩沖,不要將buflen設(shè)置為0.同樣,控制傳輸請(qǐng)盡量用同步的xferdata()而不是異步的begindataxfer()。
(3)另外,還有兩個(gè)簡(jiǎn)化版本的函數(shù)Write(out傳輸)和Read(in傳輸)也可以進(jìn)行控制傳輸。其作用同XferData()是一樣的,因?yàn)橐呀?jīng)明確表示了是in還是out,所以Direction 項(xiàng)的值就省略了。只是看起來代碼更加簡(jiǎn)潔,輸入效率更高而已。基本上,大多數(shù)時(shí)候,使用Write和Read會(huì)方便很多。
CCyControlEndPoint* CtlEndpoint; //定義一個(gè)控制端點(diǎn)
CtlEndpoint->Target = TGT_DEVICE; //不必關(guān)注,固定
CtlEndpoint->ReqType = REQ_VENDOR; //請(qǐng)求類型:自定義請(qǐng)求(標(biāo)準(zhǔn)請(qǐng)求等)CtlEndpoint->ReqCode = 0XDD; //自定義請(qǐng)求碼CtlEndpoint->Value = 0; //這里的設(shè)定值將傳給setupdat的[2:3]位CtlEndpoint->Index = 0; //這里的設(shè)定值將傳給setupdat的[4:5]位PUCHAR buf=new UCHAR[1];ZeroMemory(buf,1);//用0填充buf區(qū),填充大小(1字節(jié)) long buflen=1; //傳輸?shù)钠渌止?jié)數(shù):cy控制臺(tái)console中l(wèi)ength的值CtlEndpoint->Write(buf,buflen);
Read的話同理。