(1.西安現(xiàn)代控制技術(shù)研究所,西安 710065;2.中國人民解放軍93811部隊 保障部裝備質(zhì)量控制與安全監(jiān)察中心,蘭州 730000)
PCI-1751卡是一塊基于PCI總線的擁有48路并行I/O的板卡。由于可以同時控制多路電平輸入輸出,該板卡廣泛于工業(yè)交流/直流I/O設(shè)備監(jiān)控、繼電器和開關(guān)控制、并行數(shù)據(jù)傳輸、感應(yīng)TTL信號邏輯、驅(qū)動LED指示器等環(huán)境。同時PCI-1751板卡上也集成了2個8254定時器/計數(shù)器,也可用于一些高精度定時計數(shù)的功能場景。
RTX操作系統(tǒng)作為Windows系統(tǒng)的擴(kuò)展系統(tǒng),受到許多高校研究單位的青睞。而很多板卡在出廠時都不提供RTX驅(qū)動程序,PCI-1751板卡也不例外。因此,本文結(jié)合研華科技公司的PCI-1751板卡,介紹RTX系統(tǒng)下板卡驅(qū)動的編寫調(diào)試方法及一些經(jīng)驗,以求RTX驅(qū)動程序的開發(fā)被更多探索。
寄存器是板卡上的具有特定功能的內(nèi)存存儲。用戶可以不了解板卡內(nèi)部的具體硬件實現(xiàn),但只要能理解其意義并通過地址訪問到寄存器,即可實現(xiàn)板卡功能,故稱為板卡與用戶之間的軟件接口。
因此,RTX下PCI板卡驅(qū)動的開發(fā)實質(zhì)就是利用RTX系統(tǒng)函數(shù)操縱板卡上的寄存器。
開發(fā)驅(qū)動的用戶需要關(guān)心兩類寄存器,即PCI配置寄存器與板卡功能寄存器。
1.2.1 PCI配置寄存器
PCI配置寄存器是每塊板卡寄存器的“目錄”,是PCI協(xié)議預(yù)定義的256字節(jié)的內(nèi)存[3]。在該寄存器中,標(biāo)識了該板卡的所有有用信息,其具體內(nèi)容如表1所示。
表1 PCI配置空間
1)DeviceID與VendorID:
每類板卡獨一無二的屬性[1],用戶在遍歷計算機(jī)系統(tǒng)中的所有板卡時,可根據(jù)這兩個值,來判斷該板卡是否存在于該計算機(jī)系統(tǒng)。
2)基地址寄存器:
用于存放寄存器映射的基地址?;刂肥前蹇üδ芗拇嫫鞯钠鹗嫉刂罚脩艨梢愿鶕?jù)基地址和偏移地址計算板卡上所有功能寄存器的地址。
1.2.2 板卡功能寄存器
板卡功能寄存器是板卡功能的軟件接口,用戶只需對這些寄存器置數(shù)取數(shù),即可完成與之對應(yīng)的功能。
板卡在出廠硬件手冊都會附帶寄存器功能說明及地址分布,這些地址都是從基地址開始有規(guī)律累加的,每個寄存器相對基地址的累加量稱作偏移地址。因此,只要找到了板卡I/O基地址,所有寄存器的地址都可以很容易的推算出來。
在圖1所示的計算機(jī)環(huán)境中,PCI-1751的內(nèi)存基地址為0xFEBFF400,這與板卡PCI配置空間的基地址寄存器2中的值所吻合,我們可得到其基地址存放在基地址寄存器2中。感興趣的讀者可以通過該方法自己動手驗證基地址是否存放于PCI配置寄存器中的基地址寄存器2中。
圖1 板卡資源對話框
研華PCI-1751接口卡是一塊具有48路并行DI/O輸入輸出卡,同時該板卡也攜帶3個定時器/計數(shù)器,可以完成高精度的定時計數(shù)功能。
該板卡借鑒了8255芯片的設(shè)計思路,實現(xiàn)了兩塊8255芯片的mode 0模式,共具有24x2=48路DI/O通道。同時該板卡的I/O驅(qū)動能力遠(yuǎn)超出于普通的8255芯片。
同時,PCI-1751板卡提供了斷電保護(hù)功能,當(dāng)所在機(jī)器遭遇突發(fā)斷電又瞬時恢復(fù)的情況,板卡可以保持之前保存的通道輸出值。
PCI-1751板卡上兩個增強的8255芯片的48路DI/O通道被分為6個組,分別為PA0、PB0、PC0(PC0H、PC0L)、PA1、PB1、PC1(PC1H、PC1L)。每個通道組可以單獨配置輸入輸出方向,PC0和PC1組高低字節(jié)也可單獨配置,互不影響。
PCI-1751的寄存器地址列表如表2所示。
板卡上的硬件跳線可以強制配置I/O口輸入輸出方向。當(dāng)跳線配置為軟件配置模式時,需要在使用前先寫入控制字??刂萍拇嫫鞯钠频刂窞?和7,對應(yīng)Port0和Port1,其內(nèi)容格式如表3所示。
表2 PCI-1751寄存器地址
表3 Port0、Port1配置寄存器
對于Port0、Port1配置寄存器,寫1為輸入方向,寫0為輸出方向。例如,只想配置PC0通道組為輸入通道,其他通道均為輸出通道,則應(yīng)將控制字0x09(00001001B)寫入偏移地址為3的寄存器中。
配置好輸入/輸出方向后,對相應(yīng)的通道寄存器進(jìn)行讀/寫即可完成輸入/輸出操作。例如讀取PC0通道組,只需讀取base+2的寄存器值即可。
PCI-1751板卡上攜帶三塊8254計數(shù)器芯片,定時器連接關(guān)系如圖2所示。
板卡在設(shè)計時,為了提供更多的靈活性,Timer1的CLK引腳可以通過跳線連接到外部信號源CLK1,亦可連接到Timer0的輸出端。當(dāng)Timer1的時鐘源連接到Timer0的輸出端時,相當(dāng)于Timer0與Timer1串聯(lián)形成一個32位的計數(shù)器。
板卡內(nèi)部的定時器晶振頻率為10 MHz,使用Timer0與Timer1進(jìn)行定時,最大定時頻率為10 MHz/2=5 MHz;最小定時頻率為10 MHz/65 536/65 536=0.002 328 Hz。
圖2 定時器/計數(shù)器結(jié)構(gòu)圖
在表2所示的寄存器列表中,特別值得關(guān)注的是偏移地址為32的中斷控制/狀態(tài)寄存器。該寄存器在寫入時作為控制寄存器,讀取時作為狀態(tài)寄存器,他們使用相同的偏移地址。
1751板卡將PC00、PC04、Timer1、PC10、PC14、Timer2的輸出引入到板卡的中斷電路中。中斷控制寄存器決定了中斷源的選擇、中斷觸發(fā)模式等設(shè)置,中斷狀態(tài)寄存器顯示當(dāng)前中斷配置與觸發(fā)狀態(tài),其定義如表4所示。
表4 中斷控制/狀態(tài)寄存器
其中:F是中斷標(biāo)志,作為狀態(tài)寄存器時,該位表示中斷是否發(fā)生;作為控制寄存器,寫0是對中斷標(biāo)志的清除。E是上升沿/下降沿的配置,1為上升沿,0為下降沿。M1M0是中斷源的選擇,具體示意如表5所示。
表5 中斷模式配置
例如,在本文的第四章節(jié)所介紹的實驗中,欲檢測PC00的上升沿中斷,需將0x05寫入中斷控制寄存器中即可完成配置;中斷服務(wù)函數(shù)中,就是通過該寄存器的D3位即可檢測PC00中斷是否來臨。
有了這些知識儲備,即可開始進(jìn)行板卡驅(qū)動的開發(fā)。
在本文示例的驅(qū)動程序中,主要提供關(guān)于DI/O操作的幾個重要函數(shù),分別為打開板卡函數(shù)、中斷配置函數(shù)、配置通道組函數(shù)、讀通道組函數(shù)、寫通道組函數(shù)、等待中斷函數(shù)。通過這些函數(shù),該板卡可以完成多路電平的輸入輸出以及上升沿/下降沿中斷采集的功能。
PC機(jī)可能存在很多板卡,因此在打開板卡函數(shù)的實現(xiàn)中,主要操作為根據(jù)DeviceID和VendorID搜索PCI-1751板卡是否存在。如果搜尋到板卡,則保存板卡的I/O映射基地址,方便后續(xù)讀寫板卡內(nèi)部寄存器時使用。
示例代碼如下:
for ( bus=0; bFlag; bus++ )
for(deviceNumber=0;deviceNumber for(functionNumber=0;functionNumber { bytesWritten = RtGetBusDataByOffset(…) if(( PciData->VendorID == vendorID ) && ( PciData->DeviceID == deviceID )) base_add = base_add_register[2];//get add } 這三層循環(huán)會遍歷所在計算機(jī)系統(tǒng)中的所有板卡。通過RTX系統(tǒng)提供的接口RtGetBusDataByOffset,可以獲得PCI配置空間的內(nèi)存指針,即表1所示的內(nèi)存區(qū)域,將該內(nèi)存中的DeviceID和VendorID成員與PCI-1751板卡的進(jìn)行對比,即可驗證當(dāng)前所遍歷板卡是否為1751板卡。如果找到,保存I/O映射基地址。 對于PCI-1751板卡而言,DeviceID為0x1751,VendorID為0x13FE。 打開中斷函數(shù)內(nèi)部完成兩個操作。 首先根據(jù)用戶需求,對中斷控制寄存器進(jìn)行配置,其次使用RTX提供的API函數(shù)RtAttachInterruptVector對PCI中斷進(jìn)行掛接響應(yīng)。 完成上述兩個設(shè)置之后,板卡上被使能的中斷就可以觸發(fā)中斷服務(wù)函數(shù)。 中斷寄存器的配置示例代碼如下: IntCmd = IntMode<<(port*4); RtWritePortUchar(BaseAdd+32, (UCHAR)IntCmd); 其中,IntMode對應(yīng)表5中的中斷模式選擇,取值0~3;port定義為Port口編號,Port0為0,Port1為1。 在配置通道組函數(shù)中,主要操作就是對欲使用的通道組的控制字進(jìn)行設(shè)置,然后將控制字寫入對應(yīng)的寄存器中。 示例代碼如下: dirsetting=PA<<4+PCH<<3+PB<<1+PCL; RtWritePortUchar(BaseAdd+(port+1)*4-1, (UCHAR)dirsetting); 在形參列表中,PA、PCH、PB、PCL是通道組輸入輸出方向,定義為輸出傳0,輸入傳1;port定義為Port口編號,Port0為0,Port1為1。 讀取通道組,就是讀取指定通道對應(yīng)的寄存器。 示例代碼如下: if (channel >= 3) channel += 1; cResult=RtReadPortUchar(BaseAdd+channel); 在形參列表中,channel代表I/O口編號,定義為PortA0、PortB0、PortC0、PortA1、PortB1、PortC1依次為0~5。 輸出通道組,就是向指定通道對應(yīng)的寄存器上寫值。 示例代碼如下: if (channel >= 3) channel += 1; RtWritePortUchar(BaseAdd+channel, (UCHAR)value); 在形參列表中,channel代表I/O口編號,定義為PortA0、PortB0、PortC0、PortA1、PortB1、PortC1依次為0~5。 3.6.1 原理解析 對于像Windows、RTX這樣的多任務(wù)操作系統(tǒng),每個任務(wù)對應(yīng)一個運行的進(jìn)程,每個運行的進(jìn)程中又可以包含很多線程。如果沒有同步機(jī)制,所有的線程會任意運行。然而,多個線程可能會要求同一個資源,這就需要同步處理。 等待中斷函數(shù)就使用到了同步機(jī)制。調(diào)用等待函數(shù)后,其內(nèi)部的等待同步對象的函數(shù),例如WaitForSingleObject函數(shù),就會處于等待狀態(tài),對于用戶,其表征為“卡死”狀態(tài),只有當(dāng)中斷觸發(fā)后,中斷服務(wù)函數(shù)內(nèi)部對該同步對象使能后,等待同步對象的函數(shù)才會釋放線程占有權(quán),等待中斷函數(shù)才能繼續(xù)運行下去。 RTX操作系統(tǒng)提供的等待信號量的函數(shù)為RtWaitForSingleObject,形參和用法兼容Windows操作系統(tǒng)函數(shù)。形參1是信號量的句柄,形參2是等待時間,當(dāng)形參2傳入INFINITE時,永久等待,直至信號量有效。等待中斷函數(shù)就是利用永久等待信號量來實現(xiàn)的。 3.6.2 函數(shù)實現(xiàn) 等待中斷函數(shù)內(nèi)部對兩個port口,3類中斷進(jìn)行等待。當(dāng)用戶調(diào)用該函數(shù)時,先清空對應(yīng)信號量,然后等待信號量,此時該函數(shù)處于阻塞狀態(tài)。 中斷服務(wù)函數(shù)檢測到中斷觸發(fā)后,將對應(yīng)信號量激活。等待中斷函數(shù)才能繼續(xù)進(jìn)行,達(dá)到了“卡死”等待的作用。 這里對port0口的PC00中斷進(jìn)行示意。 IntCmd = 0x01;//中斷源,對應(yīng)表5 RtWritePortUchar(BaseAdd+32, (UCHAR)IntCmd);//寫中斷控制寄存器 RtWaitForSingleObject(hInterHandle[0], INFINITE); Printf(“PC00 Int found/n”);//中斷到達(dá)了 return 0; 在中斷服務(wù)函數(shù)內(nèi)部,其核心代碼如下: temp1=RtReadPortUchar(base+32);//得到中斷狀態(tài)寄存器 if (temp1 & 0x08)//對比表4中的D3位 RtSetEvent(hInterHandle[0]); 對于板卡驅(qū)動性能的測試,這里使用了一個“自發(fā)自收”的閉環(huán)測試模型,即板卡PA00自己產(chǎn)生上升沿,板卡PC00采集該上升沿,通過對比上升沿產(chǎn)生前后的時間間隔來衡量驅(qū)動程序的性能。測試流程如圖3所示。 圖3 驅(qū)動測試流程 板卡硬件上用導(dǎo)線連接引腳1與引腳19,即PA00引腳與PC00引腳。 軟件上將PA口配置為輸出方向,PC口設(shè)置為輸入方向,這樣PA00的電壓會被PC00實時采集。 I/O口方向設(shè)置好后,使PA00口先輸出低電平,再輸出高電平,等待PC00口檢測到該上升沿觸發(fā)中斷。 Windows與RTX的測試程序均按照圖3所示流程進(jìn)行編寫,具體流程如下: 1)打開板卡,配置PA口為輸出方向,PC口為輸入方向; 2)配置中斷控制寄存器,使能PC00上升沿中斷; 3)記錄當(dāng)前時刻t1; 4)PA00輸出低電平; 5)PA00輸出高電平; 6)等待PC00上升沿中斷,記錄中斷觸發(fā)時刻t2; 7)計算“閉環(huán)”時間t2-t1; 8)程序結(jié)束。 本次試驗使用研華610L原裝機(jī)箱作為測試硬件環(huán)境,系統(tǒng)環(huán)境為Windows XP SP3+RTX8.1,編譯器使用Visual Studio 6.0。 Windows與RTX實驗程序均按照4.2節(jié)中的流程開發(fā),t1和t2通過系統(tǒng)函數(shù)獲取,t2與t1的差值作為最終考核指標(biāo)。 實驗100次取平均值作為測試最終結(jié)果,Windows驅(qū)動與RTX驅(qū)動的“閉環(huán)”測試結(jié)果如表6所示。 表6 驅(qū)動測試結(jié)果 ms 通過平均值的對比可以看到,RTX驅(qū)動程序相比Windows驅(qū)動,響應(yīng)時間縮短了68%,性能提升相當(dāng)明顯。 同時,通過極值對比可以看到,RTX驅(qū)動的閉環(huán)時間相對穩(wěn)定,波動保持在0.003 ms之內(nèi);Windows驅(qū)動的閉環(huán)時間相對不穩(wěn)定,波動在0.008 ms之內(nèi)。 本次實驗表明,無論在響應(yīng)時間方面,還是在穩(wěn)定性方面,RTX驅(qū)動的性能都處在領(lǐng)先地位,對于追求實時、穩(wěn)定的環(huán)境而言,RTX驅(qū)動無疑是首選。 由于系統(tǒng)設(shè)計出發(fā)點的不同,無論是在線程調(diào)度算法、線程優(yōu)先級定義、定時器精度方面,Windows系統(tǒng)均不是RTX對手。因此,Windows驅(qū)動的落敗也是在預(yù)料之中的。 這也表明,RTX可以對一個單一的低成本的平臺進(jìn)行擴(kuò)展,使其滿足一個廣泛的嵌入式應(yīng)用程序的要求。之所以很多高校和研究所廣泛使用RTX,確實是有一定依據(jù)的。 本文介紹了PCI-1751接口卡在RTX實時系統(tǒng)下驅(qū)動程序的編寫方法,出色的實現(xiàn)了板卡提供的DI/O功能、中斷采集功能,可以滿足絕大多數(shù)工業(yè)、生產(chǎn)、仿真的實時性要求。同時對于其他類型的接口卡,亦可借鑒本文中列舉的方法和框架進(jìn)行開發(fā)驅(qū)動。對于PCI-1751板卡的定時器/計數(shù)器等功能,由于篇幅所限未能介紹,感興趣的讀者可以參考本文的思路,自己探索嘗試。3.2 打開中斷——EnableInterrupt_1751
3.3 配置通道組——SetPortDirection_1751
3.4 讀通道組——ReadPort_1751
3.5 寫通道組——WritePort_1751
3.6 等待中斷——WaitPortInterrupt_1751
4 RTX驅(qū)動程序測試
4.1 測試原理
4.2 測試方法
4.3 測試環(huán)境與考核指標(biāo)
4.4 測試結(jié)果與分析
5 結(jié)束語