基于USB和多線程的實時數(shù)據(jù)采集系統(tǒng)
關(guān)鍵詞:USB PDIUSBD12 多線程 實時數(shù)據(jù)采集
1 問題的提出
隨著信息技術(shù)的飛速發(fā)展,各種數(shù)據(jù)的實時采集和處理在現(xiàn)代工業(yè)控制中已成為必不可少的。這就為我們的設(shè)計提出了兩個方面的要求:一方面,要求接口簡單靈活且有較高的數(shù)據(jù)傳輸率;另一方面,由于數(shù)據(jù)量通常都較大,要求主機能夠?qū)?a class="contentlabel" href="http://m.butianyuan.cn/news/listbylabel/label/實時數(shù)據(jù)">實時數(shù)據(jù)做出快速響應(yīng),并及時進(jìn)行分析和處理。
傳統(tǒng)的外設(shè)與主機的通信接口難以滿足上述第一個方面的要求。這些接口一般采用PCI部線或RS-232串行總線。PCI總線雖然有很高的傳輸率(可達(dá)132Mbps),還能“即插即用”,但是它們的擴充槽相當(dāng)有限,且插拔并不方便。RS-232串行總線雖然連接方便,可是它的帶寬非常有限,傳輸速度太慢,而且1條RS-232串口通信電纜只能連接1個物理設(shè)備。USB技術(shù)正是順序這一要求提出的,它集PCI和RS-232的優(yōu)點于一身:具有較高的傳輸速率(USB協(xié)議1.1支持最高傳輸速度達(dá)12Mbps,USB協(xié)議2.0支持最高傳輸速度可達(dá)148Mbps),實現(xiàn)了真正意義上的“即插即用”(Plug Play),同時USB上最多可以連接127個外設(shè)。因此,將USB技術(shù)應(yīng)用于數(shù)據(jù)的實時采集是非常適合的。
實時系統(tǒng)對多任務(wù)的要求比較普遍。往往在后臺采集數(shù)據(jù)、進(jìn)行數(shù)據(jù)顯示的同時,還要在前臺界面對用戶的操作做出響應(yīng)。在實時系統(tǒng)中,對實時數(shù)據(jù)做出及時而準(zhǔn)確的反應(yīng)是十分重要的。由于受A/D采集速度等因素的限制,從硬件上采用USB接口技術(shù)的確可以提高速度,但畢竟?jié)摿τ邢?,因此在現(xiàn)有硬件設(shè)計基礎(chǔ)上充分發(fā)揮軟件的作用就能進(jìn)一步提升速度。使用傳統(tǒng)的單線程編程技術(shù)效率較低,無法及時處理,必須充分利用Windows的多任務(wù)處理功能,采用多線程編程技術(shù)來處理數(shù)據(jù)。
在這個實時采集系統(tǒng)的設(shè)計上,我們將這兩種技術(shù)結(jié)合起來:在硬件上采用USB技術(shù);軟件用VC++進(jìn)行開發(fā),采用多線程編程,使系統(tǒng)的效率從這兩方面都得到提升。
2 系統(tǒng)體系結(jié)構(gòu)
2.1 硬件結(jié)構(gòu)
整個系統(tǒng)硬件結(jié)構(gòu)如圖1所示。
實時數(shù)據(jù)采集系統(tǒng)主要由多路選擇開關(guān)、A/D轉(zhuǎn)換、單片機系統(tǒng)、PDIUSBD12、微機組成。多路選擇開關(guān)對多路信號進(jìn)行選擇,使其分時輸入;A/D轉(zhuǎn)換實現(xiàn)模擬信號的數(shù)字化;單片機系統(tǒng)主要完成信號采集、數(shù)據(jù)通信;PDIUSBD12實現(xiàn)USB接口;微機完成數(shù)據(jù)接收、存入數(shù)據(jù)庫、數(shù)據(jù)處理、計算、顯示等功能。
其中PDIUSBD12是系統(tǒng)USB技術(shù)得以實現(xiàn)的關(guān)鍵。它是Philips公司的一個帶并行總線的USB接口器件,支持本地的DMA傳輸。它完全符合USB1.1版的規(guī)范,同時集成了SIE(串行接口引擎)、FIFO存儲器、收發(fā)器以及電壓調(diào)整器。其主端點的雙緩沖配置增加了數(shù)據(jù)吞吐量并輕松實現(xiàn)實時數(shù)據(jù)傳輸,功能框圖如圖2所示。
在這個系統(tǒng)中,單片機采用的是80C52。PDIUSBD12與80C52的接口有2種方式:多路地址/數(shù)據(jù)總線方式、單地址/數(shù)據(jù)總線方式。我們采用的是前一種方式:使用了80C52的INT0、ALE、WR、RD和P0口,當(dāng)PDIUSBD12接收到主機的有效信息時,會產(chǎn)生一個中斷通知80C52進(jìn)行處理。在此種方式下,PDIUSBD12在ALE下降沿的時候,對單片機的輸出地址進(jìn)行鎖存。若輸出地址為奇數(shù),則表示對PDIUSBD12發(fā)送指令;若輸出地址為偶數(shù),則表示對PDIUSBD12進(jìn)行數(shù)據(jù)傳輸。接口電路如圖3所示。
80C52將A/D采集的數(shù)據(jù)經(jīng)PDIUSBD12的并行接口送入FIFO存儲器。當(dāng)USB的傳輸速率達(dá)到12Mbps時,MMU(存儲器管理單元)和集成RAM作為USB之間速度差異的緩沖區(qū),這就允許單片機以它自己的速率對USB信息包進(jìn)行讀寫。若FIFO中數(shù)據(jù)已滿,SIE會立即對數(shù)據(jù)做處理:同步模式的識別、并行/串行轉(zhuǎn)換、位填充/解除填充、CRC校驗/產(chǎn)生、PID校驗/產(chǎn)生、地址識別和握手評估/產(chǎn)生。SIE實現(xiàn)了全部的USB協(xié)議層,完全由硬件實現(xiàn)而不需要固件的參與。數(shù)據(jù)經(jīng)處理后由收發(fā)器通過數(shù)據(jù)線D+、D-傳送到主機。對一個單片機而言,PDIUSBD12看起來就像1個帶8位數(shù)據(jù)總線和1個地址位的存儲器件。
2.2 軟件結(jié)構(gòu)
USB的軟件系統(tǒng)包括三部分:客戶應(yīng)用軟件、設(shè)備固件以及USB設(shè)備驅(qū)動程序。其中,設(shè)備固件和USB設(shè)備驅(qū)動程序又被稱為主機軟件。軟件層次如圖4所示。
2.2.1 固件設(shè)備
設(shè)備固件(firmware)是儲存在程序內(nèi)存中的代碼。它使得USB接口芯片與主機和外設(shè)中其它電路能夠通信。固件由USB驅(qū)動程序(USBD)、主控制器驅(qū)動程序(HCD)兩部分組成。USBD的功能可以概括為:配置管理、總線管理、數(shù)據(jù)傳輸管理、提供客戶服務(wù)。USBD把IRP劃分為USB和設(shè)備需要大小的塊,確保每一個設(shè)備能分配到它所要求的USB資源,這樣它就可以支持USB設(shè)備配置。USBD提供了一個編程接口USBDI(USB驅(qū)動程序接口),給客戶驅(qū)動程序一種方式,用于傳輸請求,傳輸?shù)姆较蚩梢允莵碜曰虬l(fā)往USB的功能單元。大量的客戶服務(wù)是由USB的驅(qū)動程序提供的,它幫助USB的客戶控制和訪問它們的功能單元。HCD提供了對USB的低級支持,通過把IRP轉(zhuǎn)換成為單獨的事務(wù)處理后在USB上執(zhí)行。
基于這種結(jié)構(gòu),主循環(huán)不關(guān)心數(shù)據(jù)是來自USB、串口還是并口,只檢查循環(huán)緩沖區(qū)內(nèi)需要處理的新數(shù)據(jù)。這樣,主循環(huán)程序?qū)W⒂跀?shù)據(jù)的處理而ISR能夠以盡可能高的可能高的速度進(jìn)行數(shù)據(jù)的傳輸。
這部分程序結(jié)構(gòu)可包括:
主循環(huán)程序――發(fā)送USB請求,處理USB總線事件和用戶功能處理等;
硬件提以層――對單片機的I/O口、數(shù)據(jù)總線等硬件接口進(jìn)行操作;
PDIUSBD12命令接口――對PDIUSBD12器件進(jìn)行操作的模塊子程序集;
請求處理程序――對USB的標(biāo)準(zhǔn)設(shè)備請求進(jìn)行處理和對用戶添加的廠商請求進(jìn)行處理;
中斷服務(wù)程序――當(dāng)PDIUSBD12向單片機發(fā)出中斷請求時,讀取PDIUSBD12的中斷傳輸來的數(shù)據(jù),并設(shè)定事件標(biāo)志和Setup包數(shù)據(jù)緩沖區(qū),傳輸給主循環(huán)。
2.2.2 USB設(shè)備驅(qū)動設(shè)計
在Windows下,與USB外設(shè)的任何通信必須通過USB設(shè)備驅(qū)動,這個驅(qū)動知道如何與系統(tǒng)的USB驅(qū)動和訪問設(shè)備的應(yīng)用程序通信。設(shè)備驅(qū)動是保證應(yīng)用程序訪問硬件設(shè)備的軟件組件,使得應(yīng)用程序不必知道物理連接、信號和與一個設(shè)備通信需要的協(xié)議等的細(xì)節(jié),可以保證應(yīng)用程序代碼只通過外設(shè)名字訪問外設(shè)或端口目的地。應(yīng)用程序不需要知道外設(shè)連接端口的物理地址,不需要精確監(jiān)視和控制外設(shè)需要的交換信號。
設(shè)備驅(qū)動通過在應(yīng)用層和硬件專用代碼之間的轉(zhuǎn)化來完成它的任務(wù)。應(yīng)用層代碼一般使用一套操作系統(tǒng)支持的函數(shù),硬件代碼則處理那些訪問外設(shè)電路的必要協(xié)議。設(shè)備驅(qū)動能與應(yīng)用程序之間相互通信是通過Windows提供的API函數(shù),這些函數(shù)使應(yīng)用程序能夠控制顯示器、處理信息、訪問存儲器、讀寫磁盤和其它設(shè)備。對于一些標(biāo)準(zhǔn)設(shè)備,Windows提供通用驅(qū)動;不過,這個實時數(shù)據(jù)采集系統(tǒng)是自定義的設(shè)備,對此Windows并不提供通用的驅(qū)動,需要對設(shè)備編寫自定義的驅(qū)動,并且必須遵循微軟在Windows98和更新版本中為用戶定義的Win32驅(qū)動模式。Windows98和Windows2000中,USB總線驅(qū)動是WDM驅(qū)動,擴展名為.sys。編寫USB設(shè)備驅(qū)動需要使用Visual C++,此外還需要Windows 98或2000設(shè)備開發(fā)包(98DDK/NTDDK)。USB設(shè)備驅(qū)動的編寫通常不是一項簡單的任務(wù),驅(qū)動開發(fā)包就提供一種途徑,通過做盡可能多的工作為跳過驅(qū)動開發(fā),這些開發(fā)庫有Blue Water Systems的WinDK和Compuware NuMega的DriverWorks。這些工具包能夠集成到Visual C++編程環(huán)境中。運用這些工具包只需很少的時間就能生成一個高效的驅(qū)動程序。
這一部分可以包括4個模塊:初始化模塊、即插即用管理模塊、電源管理模塊和I/O功能模塊。初始化模塊提供一個DriverEntery入口點來執(zhí)行大量的初始化函數(shù)。即插用模塊實現(xiàn)USB設(shè)備的動態(tài)插拔及配置。當(dāng)硬件檢測到USB設(shè)備接入時,Windows查找相應(yīng)的驅(qū)動程序,并且調(diào)用它的DriverEntery例程,PnP(即插即用)管理器調(diào)用驅(qū)動程序的AddDevice例程,通知它添加了一個設(shè)備。驅(qū)動程序會收到一個包含有設(shè)備分配資源信息的啟動設(shè)備的IRP,在對設(shè)備進(jìn)行正確配置后,開始與硬件的對話。在運行過程中,如果設(shè)備被拔除,PnP會發(fā)出相應(yīng)的IRP,驅(qū)動程序會進(jìn)行盯應(yīng)處理。USB設(shè)備的掛起和喚醒是由電源管理模塊進(jìn)行管理的。I/O功能模塊完成I/O請求的工作。
2.2.3 應(yīng)用程序設(shè)計
固件程序和USB設(shè)備驅(qū)動程序的設(shè)計是USB設(shè)備開發(fā)者的工作,對于廣大用戶而言,與系統(tǒng)的交互是通過應(yīng)用程序?qū)崿F(xiàn),而且整個實時采集系統(tǒng)的主要數(shù)據(jù)處理都是在這里完成的。因此,運行效率高、界面友好、具有強大數(shù)據(jù)分析和處理的應(yīng)用程序的設(shè)計,也是系統(tǒng)設(shè)計上一個不容忽視的關(guān)鍵因素。應(yīng)用程序的主要功能有:啟動/關(guān)閉USB設(shè)備,檢測USB設(shè)備,設(shè)置USB數(shù)據(jù)傳輸管道/端口,設(shè)置A/D,采集數(shù)據(jù),顯示/分析數(shù)據(jù)。這里,我們采用Visual C++6.0作為程序的開發(fā)環(huán)境,并且充分運用了多線程的編程思想。
在這個設(shè)備中,設(shè)置4個線程:首先是1個主線程,負(fù)責(zé)用戶界面,并保持中樞地位。它的生存周期也就是整個用戶程序的主存期,用戶的動作(例如鼠標(biāo)事件、鍵盤事件)都會觸發(fā)主線程的消息機制,從而完成對用戶的響應(yīng);而3個分離的輔助線程分別負(fù)責(zé)數(shù)據(jù)的采集、數(shù)據(jù)的分析處理以及數(shù)據(jù)的顯示這3個不同的任務(wù)。輔助線程是在主線程運行過程中產(chǎn)生的,它的生命就是線程函數(shù)本身,函數(shù)一旦return,線程就結(jié)束了。因此,輔助線程的生存周期只是整個程序生存期的一部分。
MFC程序只會有一個CwinApp對象,而CwinApp派生自CwinThread,即產(chǎn)生了應(yīng)用程序的主線程。每當(dāng)需要一個額外的線程時,應(yīng)先產(chǎn)生一個CwinThread對象,再調(diào)用全局函數(shù)AfxBeginThread(),將線程產(chǎn)生出來。
對于輔助線程(worker thread),要為它準(zhǔn)備一個線程函數(shù),然后調(diào)用AfxBeginThread()。例如:CWinThread* pThread=AfxBeginThread(ThreadFunc,param);
UINT ThreadFunc(LPVOID pParam); //線程函數(shù)
對于用戶界面線程(UI thread),不能夠光由一個線程函數(shù)來代表,因為它要處理消息,它需要一個消息循環(huán)。應(yīng)該先從CWinThread派生一個自己的類,再調(diào)用AfxBeginThread()產(chǎn)生一個CWinThread對象。具體程序見本刊網(wǎng)站(http://www.dpj.com.cn)。
結(jié)語
基于USB技術(shù)的實時數(shù)據(jù)采集系統(tǒng),在編程上運用了多線程思想;從硬件和軟件兩方面錄求較佳的解決途徑,并將二者結(jié)合起來,在實際中取得了良好的運行效果。
評論