PC并行端口作為數(shù)字I/O口的應(yīng)用
三、PC并行口數(shù)字輸入/輸出
所謂的數(shù)字輸出就是在程序要求某一個(gè)設(shè)備的某一開關(guān)點(diǎn)開或關(guān),產(chǎn)生高電位或低電位。從計(jì)算機(jī)的觀點(diǎn)來說,低電位就是0.7V以下(邏輯0),而高電位是2.1V以上(邏輯1),若電位處在0.7~2.1V時(shí),電位的邏輯狀態(tài)是不確定的。想要通過計(jì)算機(jī)去控制外部設(shè)備,最簡單的方法就是控制數(shù)字輸出。
所謂的數(shù)字輸入,也就是外界的狀況被計(jì)算機(jī)用0或1的數(shù)值予以記錄下來而儲(chǔ)存,此0與1就代表了外界某一個(gè)設(shè)備的某一開關(guān)點(diǎn)開或關(guān)的兩種情形。
PC并行口即可以作數(shù)字輸出口,也可以作數(shù)字輸入口。其中的數(shù)據(jù)端口、控制端口都可以作為數(shù)字輸出端口,數(shù)據(jù)端口共8位,控制端口共4位,兩個(gè)端口可以組成1~12位的任意數(shù)字輸出端口;其中的狀態(tài)端口、控制端口都可以作為數(shù)字輸入端口,狀態(tài)端口共5位,控制端口共4位,兩個(gè)端口可以組成1~9位的任意數(shù)字輸入端口。本文給出了并行端口3種寄存器的讀寫方法,如下圖所示:
四、PC并行口數(shù)字輸入/輸出的VC實(shí)現(xiàn)
由于Windows對(duì)系統(tǒng)底層操作采取了屏蔽的策略,因而對(duì)用戶而言,系統(tǒng)變得更為安全,但這卻給眾多的硬件或者系統(tǒng)軟件開發(fā)人員帶來了不小的困難,因?yàn)橹灰獞?yīng)用中涉及到底層的操作,開發(fā)人員就不得不深入到Windows的內(nèi)核去編寫屬于系統(tǒng)級(jí)的設(shè)備驅(qū)動(dòng)程序。對(duì)并行口的讀寫操作就是如此,由于Windows對(duì)系統(tǒng)的保護(hù),絕對(duì)不允許任何的直接I/O動(dòng)作發(fā)生,所以必須帶上*.dll、*.sys或*.vxd文件,這些文件用來讓操作系統(tǒng)知道有一個(gè)特定的I/O可能會(huì)被調(diào)用。系統(tǒng)開機(jī)后,這些文件中的內(nèi)容就會(huì)加載到內(nèi)存中,一旦有對(duì)應(yīng)的動(dòng)作發(fā)生,就會(huì)引發(fā)I/O的實(shí)際動(dòng)作。
本文只是介紹并行口作為數(shù)字I/O口的使用,不在于介紹并行I/O口驅(qū)動(dòng)的編寫。故本文中直接使用由 Yariv Kaplan 編寫的 WinIo 庫,它有如下特點(diǎn):WinIo 庫通過使用內(nèi)核模式下設(shè)備驅(qū)動(dòng)程序和 其它一些底層編程技巧繞過 Windows 安全保護(hù)機(jī)制,允許32位 Windows 程序直接對(duì) I/O 口進(jìn)行操作。
支持Windows 9x、Windows NT、Windows2000、WindowsXP環(huán)境;在Windows NT/2000/XP下,允許非 Administrator 用戶應(yīng)用 WinIo 應(yīng)用程序;不支持中斷。
注意事項(xiàng):使用這個(gè)類代碼時(shí)請(qǐng)確保不要與其它使用常規(guī) Win32 調(diào)用操作并行端口的程序發(fā)生沖突。
WinIo庫在VC應(yīng)用程序中的使用(WinIo庫下載)
為了在VC中能正常使用WinIo庫,必須按以下步驟進(jìn)行配置:
(1):將WinIo.dll、WinIo.sys、WINIO.VXD三個(gè)文件放在程序可執(zhí)行文件所在目錄下;
(2):將WinIo.lib添加到工程中,WinIo.lib及winio.h文件必須放在工程目錄下;
(3):在StdAfx.h頭文件中加入#include "winio.h"語句;
(4):調(diào)用InitializeWinIo函數(shù)初始化WinIo驅(qū)動(dòng)庫;
(5):調(diào)用讀寫IO口的GetPortVal或SetPortVal函數(shù);
(6):調(diào)用ShutdownWinIo函數(shù);
在非管理員權(quán)限下運(yùn)行,必須首先完成以下步驟:
(1):將WinIo.dll、WinIo.sys、WINIO.VXD三個(gè)文件放在任一WinIo應(yīng)用程序可執(zhí)行文件所在目錄下;
(2):以管理員或其它具有管理員權(quán)限的用戶身份登陸;
(3):調(diào)用InstallWinIoDriver函數(shù),第一個(gè)參數(shù)設(shè)置為WinIo.sys文件所在目錄路徑,第二個(gè)參數(shù)設(shè)置為false;
(4):重新啟動(dòng)系統(tǒng);
(5):以普通用戶身份登錄,現(xiàn)在可以調(diào)用WinIo庫函數(shù);
(6):當(dāng)不再需要WinIo庫時(shí),可以再次以管理員身份或其它具有管理員權(quán)限的用戶身份登陸系統(tǒng),調(diào)用RemoveWinIoDriver卸載該庫;
WinIo庫中幾個(gè)函數(shù)說明:
(1):初始化與終止
bool _stdcall InitializeWinIo();
void _stdcall ShutdownWinIo();
(2):安裝與卸載
bool _stdcall InstallWinIoDriver(PSTR pszWinIoDriverPath, bool IsDemandLoaded = false);
bool _stdcall RemoveWinIoDriver();
(3):讀寫I/O口
bool _stdcall GetPortVal(WORD wPortAddr, PDWORD pdwPortVal, BYTE bSize);
bool _stdcall SetPortVal(WORD wPortAddr, DWORD dwPortVal, BYTE bSize);
GetPortVal函數(shù)從指定端口讀取一個(gè)BYTE/WORD/DWORD類型的值;
wPortAddr是指定一個(gè)端口地址值;
pdwPortVal為指向一雙字節(jié)型變量的指針,該變量存儲(chǔ)從wPortAddr端口讀取的值;
bSize指定讀取字節(jié)數(shù),值可以為1,2或4。
SetPortVal函數(shù)向指定端口寫入一個(gè)BYTE/WORD/DWORD類型的值;
除dwPortVal為輸入?yún)?shù),表示待寫入外,其余個(gè)變量含義與GetPortVal相似。
PC并行口數(shù)字輸出的VC實(shí)現(xiàn)(示例工程下載)
為了測(cè)試并行口的數(shù)字輸出,可以準(zhǔn)備12支LED發(fā)光二極管,將LED的陽極分別與數(shù)據(jù)端口引腳Pin2~Pin9和控制端口引腳Pin1、Pin14、Pin16、Pin17相連接;將LED的陰極連接在一起與并行口的歸地引腳GND相連即可。在實(shí)際控制應(yīng)用中不能這樣連接,因?yàn)閿?shù)據(jù)端口引腳、控制端口引腳輸出的電流非常小,只有10mA左右,必須添加 其它硬件電路。
(1):數(shù)據(jù)端口數(shù)字輸出的VC實(shí)現(xiàn)
//獲得數(shù)據(jù)端口地址
WORD m_nport=(WORD)0x378;
//獲得要寫入數(shù)據(jù)端口的值WriteValue(數(shù)據(jù)范圍為0~255)
DWORD m_nValue=(DWORD)WriteValue;
//調(diào)用WinIo庫函數(shù)SetPortVal寫端口值
SetPortVal(m_nport, m_nValue, 1);//write a BYTE value to an I/O port
(2):控制端口數(shù)字輸出的VC實(shí)現(xiàn)
//獲得控制端口地址
WORD m_nport=(WORD)0x37A;
//獲得控制端口的值,保持高位值不變,將要輸出的值從低4位輸出,且使連接器上的電位狀態(tài)與想輸出的值一致
DWORD temp_dwPortVal;
unsigned int temp_aa;
GetPortVal(m_nport, temp_dwPortVal, 1); //reads a BYTE value from an I/O port
temp_aa=(unsigned int)temp_dwPortVal;
temp_aa=temp_aa0x0F0; //取低8位值,將低4位置為0;高4位不變;
temp_aa=temp_aa^0x0B; //將低4位中C0、C1、C3置為1,C2置為0;高4位不變;
//獲得要寫入控制端口的值WriteValue(數(shù)據(jù)范圍為0~15)
unsigned int WriValue;
WriValue=WriteValue0x0F; //取低4位;
temp_aa=temp_aa^WriValue; //將寫入值的低4位中的C0、C1、C3取反,C2位不變,高4位保持端口值不變
SetPortVal(m_nport, (DWORD)temp_aa, 1); //寫出的值中,高4位保持端口原來的值不變,
//低4位是寫入什么電平,連接器上既是什么電平
(3):數(shù)據(jù)端口及控制端口組合成12位數(shù)字輸出的VC實(shí)現(xiàn)
//獲得端口地址
WORD m_nportData=(WORD)0x378;
WORD m_nportControl=(WORD)0x37A;
//獲得要寫入端口的值WriteValue(數(shù)據(jù)范圍為0~4095)
DWORD m_nValue=(DWORD)(WriteValue0x0FF);//取低8位值
SetPortVal(m_nportData, m_nValue, 1);//write a BYTE value to Data port
DWORD temp_dwPortVal;
unsigned int temp_aa;
GetPortVal(m_nportControl, temp_dwPortVal, 1); //reads a BYTE value from an I/O port
temp_aa=(unsigned int)temp_dwPortVal;
temp_aa=temp_aa0x0F0; //取低8位值,將低4位置為0;高4位不變;
temp_aa=temp_aa^0x0B; //將低4位中C0、C1、C3置為1,C2置為0;高4位不變;
unsigned int WriValue;
WriValue=WriValue>>8;//取高4位值
temp_aa=temp_aa^WriValue; //將寫入值的低4位中的C0、C1、C3取反,C2位不變,高4位保持端口值不變
SetPortVal(m_nportControl, (DWORD)temp_aa, 1); //寫出的值中,高4位保持端口原來的值不變,
//低4位是寫入什么電平,連接器上既是什么電平
評(píng)論