摘? 要: 介紹了在VB開發(fā)環(huán)境下,對PCI設備進行底層訪問的兩種方法:一是通過調用用戶自己編寫的動態(tài)連接庫(DLL)實現(xiàn),二是利用WINDRIVER提供的VB運行庫編寫直接訪問硬件的接口函數(shù),并對兩種方法進行了比較。
關鍵詞: WINDRIVER? PCI 動態(tài)連接庫 應用程序接口
?
VB集成化編程語言是一種功能強大而又容易上手的開發(fā)工具。在用戶界面、數(shù)據(jù)庫、多媒體、網(wǎng)絡編程等方面,VB可謂得心應手。然而VB有限的硬件編程能力又使得許多硬件開發(fā)者對此深感無奈。尤其在工業(yè)控制,測控技術等領域,自行設計開發(fā)的I/O卡,數(shù)據(jù)采集卡等在WIN32下的驅動常常需要借助DDK, VtoolsD等工具進行艱苦而又長期的內核模式開發(fā)。本文介紹了在VB開發(fā)環(huán)境下訪問PCI設備的方法。對于其他設備,方法與此大同小異。
在VB開發(fā)環(huán)境下,用戶要訪問諸如數(shù)據(jù)采集卡之類硬件上的PCI設備,一般來說有兩種途徑:一是直接訪問,即用VB直接編寫訪問PCI設備的接口函數(shù)(這種方法需要有相關軟件的支持);二是間接訪問,即VB調用其它編程語言(如匯編,C/C++等)寫的底層驅動模塊(一般封裝成動態(tài)連接庫DLL的形式)實現(xiàn)。
1 PCI總線的配置空間
PCI規(guī)范定義了三種地址空間,除了存儲器和I/O地址空間外,為支持PCI設備系統(tǒng)資源的自動配置,還定義了配置地址空間。
PCI總線的配置空間由256個字節(jié)組成,分為預定首區(qū)和設備關聯(lián)區(qū)。預定首區(qū)包括開始64個字節(jié),對所有的PCI設備來說,都必須支持該區(qū)的設置;設備關聯(lián)區(qū)的寄存器有不同的PCI設備廠家自己定義。
配置空間的預定首區(qū)分兩個部分,前16個字節(jié)的定義對各類PCI設備而言都是相同的,后48個字節(jié)空間根據(jù)設備支持的功能有不同的分配。首區(qū)類型定義了該空間的分配情況(目前只有一種類型00H)。表1是首區(qū)的組織結構。
?
所有的PCI設備必須支持首區(qū)中的供應商ID、設備ID、指令和狀態(tài)區(qū)。對于其他寄存器的使用可根據(jù)設備的功能來選擇。對于不同的PCI設備,其供應商ID由PCI SIG分配以確保唯一性,而設備ID則由供應商自己分配。
2 PCI設備的配置過程
PCI總線的配置空間規(guī)范保證了所有PCI設備對“即插即用”的支持。
系統(tǒng)在上電后,“即插即用”BIOS通過隔離算法讀取每一個“即插即用”設備的資源申請數(shù)據(jù),并分配相應的系統(tǒng)資源,同時檢查資源的沖突情況,然后引導、加載操作系統(tǒng),并將控制權交給操作系統(tǒng);如果加載的是“即插即用”操作系統(tǒng)(WINDOWS 95及以后版本),那么操作系統(tǒng)將接管系統(tǒng)的資源管理權,它首先從BIOS讀取“即插即用”設備的資源配置信息,并仲裁資源沖突情況,然后配置BIOS尚未配置的“即插即用”設備,將設備的配置信息寫入配置管理器,最后激活無資源沖突的“即插即用”設備,裝載相應的設備驅動程序。???
對于PCI設備來說,系統(tǒng)完成引導之后,除了將資源的分配寫入系統(tǒng)的配置管理器外,還寫入了相應的PCI配置寄存器。程序可以通過直接讀取設備的配置寄存器來得到設備的I/O,存儲器等資源配置情況。
3 VB下PCI設備的訪問
驅動程序訪問PCI設備的過程一般包括掃描PCI總線,查找指定的PCI設備,確定I/O等資源分配情況,進行I/O、存儲器、中斷以及DMA等操作。VB本身并不能實現(xiàn)上述對PCI設備的訪問過程,下面介紹在VB下通過其他途徑實現(xiàn)對PCI設備的訪問。
3.1 VB直接訪問
由于VB只提供了非常有限的I/O訪問能力(如串口通信),在VB下直接訪問PCI設備時需要借助其它軟件。目前WINDRIVER提供這方面的支持。WINDRIVER是KRFTech公司主推產(chǎn)品,是許多PCI廠家所推薦的首選驅動程序開發(fā)工具。??
WINDRIVER為VB 4.0以上版本提供了一個類模塊(WINDRAR.CLS),利用這個類模塊,用戶可以手工編寫自己所需的接口函數(shù)來訪問相應的設備。下面以具體例子來說明WINDRAR.CLS的使用方法。
3.1.1 掃描PCI總線得到指定設備的數(shù)目
利用WINDRAR.CLS提供的應用程序接口函數(shù)(APIs),編寫一個掃描PCI總線,獲得指定PCI設備數(shù)目的函數(shù)如下:
Function GetCardsNum(dwVendorID As
Long, dwDeviceID As Long) As Integer
Dim pciScan As WD_PCI_SCAN_CARDS
Dim hWD As Long
hWD = WD_Open()
If hWD = INVALID_HANDLE_VALUE Then?
?????? MsgBox ″設備打開出錯!″
?????? Exit Function
End If
pciScan.searchId.dwVendorID =
?????? dwVendorID
?????? pciScan.searchId.dwDeviceID =
?????? dwDeviceID
WD_PciScanCards hWD, pciScan
WD_Close (hWD)
GetCardsNum = pciScan.dwCards
End Function???????????????
該函數(shù)可以通過輸入?yún)?shù):PCI設備的供應商ID和設備ID得到所需的PCI設備數(shù)目。如查找AMCC公司的PCI適配芯片S5933,則輸入?yún)?shù)為:&H10E8和&H4750。
下面例子用于讀寫S5933的PCI配置寄存器。在工程的全局模塊中需要先定義下列數(shù)據(jù)結構,同時設備必須處于打開狀態(tài)。
Type??AMCC_INTERRUPT
?????? Int As WD_INTERRUPT????
?????? hThread As Long
?????? Trans(0 To 1) As WD_Transfer
End Type
Type AMCC_ADDR_DESC
? dwLocalBase As Long
? dwMask As Long
? dwBytes As Long
? dwAddr As Long
? dwAddrDirect As Long
? fIsMemory As Boolean
End Type
Type AMCC_STRUCT
? hWD As Long
? cardLock As WD_CARD
? pciSlot As WD_PCI_SLOT
? cardReg As WD_CARD_REGISTER
? addrDesc(0 To AD_PCI_BARS - 1) As
??????????AMCC_ADDR_DESC
? fUseInt As Boolean
? Int As AMCC_INTERRUPT
End Type
3.1.2 讀寫PCI配置寄存器
完成以上數(shù)據(jù)結構的定義后,用下面的函數(shù)可讀寫S5933的PCI配置寄存器內容。
Function AMCC_ReadPCIReg( hAmcc As
AMCC_STRUCT, dwReg As Long)
Dim pciCnf As WD_PCI_CONFIG_DUMP?
Dim dwVal As PVOID?
pciCnf.pciSlot = hAmcc.pciSlot?
pciCnf.pBuffer = dwVal?
pciCnf.dwOffset = dwReg?
pciCnf.dwBytes = 4?
pciCnf.fIsRead = True?
WD_PciConfigDump hAmcc.hWD, pciCnf?
AMCC_ReadPCIReg = dwVal?
End Function? ?? ‘讀函數(shù)?
Sub AMCC_WritePCIReg(hAmcc As ?
AMCC_STRUCT, dwReg As Long, dwData As PVOID)
????????????? Dim pciCnf As WD_PCI_CONFIG_DUMP?
????????????? pciCnf.pciSlot = hAmcc.pciSlot?
?????? ?????? pciCnf.pBuffer = dwData?
????????????? pciCnf.dwOffset = dwReg?
????????????? pciCnf.dwBytes = 4?
????????????? pciCnf.fIsRead = False?
????????????? WD_PciConfigDump hAmcc.hWD, pciCnf?
End Sub???????? ‘寫過程
· 參數(shù)說明:
hAMCC????? 設備打開后系統(tǒng)分配的句柄
dwReg????? 讀寫的PCI配置寄存器
????dwVal????? 讀出的寄存器數(shù)據(jù)??????
dwData?? 寫入寄存器的數(shù)據(jù)
以上例子僅僅是拋磚引玉。WINDRAR.CLS類模塊提供了功能極為強大的底層驅動的API函數(shù),用戶通過編寫相應的驅動模塊可以方便地實現(xiàn)對各類硬件的I/O、存儲器映射、中斷以及DMA等操作,同時可以實現(xiàn)WIN32下物理內存空間的申請、讀寫等處理。另外對于實時性要求較高的設備,WINDRIVER提供的“內插”(Plug-In)特性可以讓程序的相關模塊運行于Ring 0內核模式(Kernel mode),以提高性能。
開發(fā)完成的底層驅動模塊既可直接為VB的應用程序調用,也可以在VB下封裝成DLLs供其它的WIN32開發(fā)工具調用。
3.2 自定義DLL訪問
DLL使VB的功能得到極大的增強,使得VB的應用范圍不斷擴大,使用更加靈活。VB通過調用自定義DLL可以實現(xiàn)對硬件的底層訪問。下面用例子說明VB對DLL的調用及DLL的編寫過程。
3.2.1 DLL的功能和編寫
本例中的DLL通過掃描PCI總線,得到總線上S5933接口芯片的數(shù)目,打開指定設備,向S5933的輸入郵箱1中寫入命令字,然后從輸出郵箱1中讀取返回數(shù)據(jù),最后關閉設備。
extern “C” _declspec(dllexport) int _stdcall GetCardsNum()
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
?????? int cards;
?????? cards=AMCC_CountCards(0x10e8,0x4750);
?????? return cards;
}?????????????????? // 此函數(shù)得到S5933的數(shù)目;
extern “C” _declspec(dllexport) DWORD_stdcall SendCommand(int CardNum,DWORD dwCmd)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
DWORD data;?????????????
If(AMCC_Open(&hAMCC,0x10e8,0x4750,?? CardNum,0))??? // 打開指定設備
{?
AMCC_WriteRegDWord(hAMCC,OMB1_ADDR,
dwCmd);??????? // 寫入命令字
?????? do{
?????? data=AMCC_ReadRegDWord(hAMCC,MBEF_ADDR);
}while((data&0x000f0000)==0x00000000);????
?????????????????????????????????? // 等待輸入郵箱1滿
data=AMCC_ReadRegDWord(hAMCC,IMB1_ADDR);???
?????????????????????????????????? // 讀取返回數(shù)據(jù)
?????? if(hAMCC)???? AMCC_Close(hAMCC);
??????????????????????????? // 關閉設備
?????? return data;
else
{ AfxMessageBox(“打開設備失敗?選”);
?????? ? return 0;}
程序中用到的函數(shù)包含在WINDRIVER的API函數(shù)庫中,在VC++下編譯時加上頭文件:
#include “amcclib.h”
#include “amcclib.c”
同時在DEF文件中列出DLL的導出函數(shù)名,生成的DLL即可為VB所調用。讀者也可用其它工具編寫驅動模塊,最后封裝成DLL即可。
3.2.2 VB調用DLL
VB調用動態(tài)連接庫(DLL)時,首先聲明DLL,然后即可像調用VB的語句或函數(shù)一樣使用DLL中的例程。下面介紹VB調用上例生成的DLL(假設文件名為Test.dll)。
· 聲明
Public Declare Function GetCardsNum Lib “Test.dll” () As Integer
Public Declare Function SendCommand Lib “Test.dll” (ByVal dwCmd as Long) As Long
在聲明時需要注意:DLL的路徑;參數(shù)傳遞的方式;參數(shù)的類型。
另外,VB遵從 _stdcall的參數(shù)傳遞約定,而VC++默認_cdecl的傳遞約定,因此在DLL中的導出聲明需采用_stdcall的修飾符。
· 調用
一旦聲明后,在VB的應用程序中就可調用DLL中的例程。如:
Private Sub Form_Load()
Dim CardsNum As Integer?
CardsNum = GetCardsNum()?
?????? MsgBox “系統(tǒng)中有”+ Str(CardsNum)+“塊S5933插卡!”?
End Sub?
WINDRIVER包括了諸如AMCC、Altera、PLX、Galileo、V3、PLDA等公司PCI芯片的專用C/C++的API函數(shù)庫,其中包含了I/O讀寫,內存映射,中斷處理以及DMA等底層驅動的函數(shù),可以非常方便地用VC++,BC++以及C++ Builder等工具編譯成DLLs供VB調用。?
本文提供了兩種在VB的開發(fā)環(huán)境下訪問PCI設備的方法。第一種方法需要有WINDRIVER的VB運行庫支持,可以在VB環(huán)境下直接編寫所需的接口函數(shù),但對WINDRAR.CLS類模塊中定義的內核數(shù)據(jù)結構要有較深的了解;第二種方法具有一定的靈活性、普遍性,編寫DLL的工具較多,DLL除了可用于VB外,還可用于其他的WIN32開發(fā)工具,有較強的適應性。
以上方法在北京航空航天大學測控技術研究所研制的PHD2000高速并行數(shù)據(jù)采集系統(tǒng)中得到實際應用,取得了良好的效果。
參考文獻
1 WinDriver V4 Developer's Guide.KRFTech 1997~1999
2 S5933 PCI Matchmaker Controller Data Book. Applied?Micro Circuits Corporation,1996
3 余永進譯.“即插即用”技術大全.北京:電子工業(yè)出版社