不同平臺下對並口的訪問

主  題: [原創]--不同平臺下對並行端口的訪問
作  者: yangsongx (達到"國際領先",填補"國內空白")
等  級:
信 譽 值: 100
所屬論壇: C/C++ C語言
問題點數: 100
回覆次數: 19
發表時間: 2004-10-13 10:49:33

引子
+++++

這幾天把以前關於並口訪問的程序整理了一下,寫了一點東西,希望提出好的意見。

+++++ 正文開始++++++


*前言*

本文主要是討論不同平臺下對並行端口進行編程的方法。因條件所限,目前只侷限於Intel386體系下的
PC微機結構。



第一章 *簡介*

並口在步進電機控制、數據採集等方面有廣泛應用。本文就是討論不同平臺下,對並行端口的訪問。
到目前爲止,在DOS、Windows 95/98、Windows NT/2000/XP和Linux下實現了並口訪問程序。

PC機上一般都至少有一個25針的並口,我們可以很容易在機箱後面找到它。當中,第18~25針是
地線,第2~9針是數據輸出線,剩下的是狀態線和輸入線。



第二章 *實現*

在PC機上,並行端口有相應的寄存器(Registers),這些寄存器會被映射到系統地址空間當中去。
根據當初IBM設計PC時的方法,PC上的並口基址在系統地址一般是0x378和0x278(如果有第二個並口的話)。

端口映射地址都存放在PC機的BIOS中。根據BIOS的設計,從低端40[0]H處開始,共有256(100H)Byte的空間,
用來存放PC機外部設備的地址。所以我們也可以通過工具debug來查看這些內容,在命令行下:

C:/>debug
進入debug環境,用d指令查看0x40[0]處的內容:

-d 40:00
0040:0000 F8 03 F8 02 E8 03 E8 02 BC 03 78 03 78 02 C0 9F
0040:0010 22 C8 20 80 02 85 00 20 00 00 2E 00 2E 00 64 20
0040:0020 20 39 34 4B 30 52 3A 27 30 52 30 52 0D E0 00 00
... ...

上面以16-bit(2Byte)爲單位存放設備地址。偏移量0x00~0x07的,是4個串口地址(對應着COM1~COM4)。
接下來的0x08~0x0F偏移量中,是4個並口地址(對應着LPT1~LPT4)。在Intel的X86體系上,內存的存放
是Little-Endian順序,也即小字節反而放在前面。這樣,我們可以從上面的內存內容看出,LPT1的地址
是0x3BC,LPT2地址是0x378。


2.1 DOS、Win95/98下並口的訪問
在DOS、Win95/98下訪問並口,相對而言較簡單,只需要調用下面兩個函數:

#include <conio.h>

int _outp(unsigned short port, int databyte);
int _inp(unsigned short port);

其中_outp是輸出,_inp是輸入。比如我要對8個輸出數據線都寫高電平(即0xFF),則可以用:

/* 讓8個輸出數據線均爲高電平1 */
_outp(0xFF,0x378);

要讓第1、2、3數據線爲高電平,則可用:

_outp(0x07,0x378);

其餘類推。

2.2 Win NT/2000/XP下並口的訪問

由於Windows NT/2000內核與Windows 98/95的不同,所以訪問並口的方法也要作相應的調整。也
就是說,前面所講的方法在NT內核下,已經不再實用啦!大家可以在NT下調用前面的_outp和_inp試試
看,會發現,在執行這兩個函數時,程序出現“執行非法程序”的錯誤框,然後程序就結束了。這是因爲
在Windows NT/2000下,從安全性出發,程序會被系統授予一定的特權(Privilege)和限制(Restriction)。
通常,這種特權級會分作兩個:用戶模式(User Mode)和核心模式(Kernel Mode)。用戶模式下的運行的
程序等級是Ring 3。在覈心模式下運行的程序等級是Ring 0。而在Ring 3下系統是不允許直接訪問端口的。
所以前面所用調用庫函數_outp等的方法在Windows NT/2000下是行不通的。

既然瞭解了上面的原因,我們就明白了:要在Windows NT/2000下實現對硬件端口的訪問,就要通過
一定的途徑獲得Ring 0下的權限。這個工作可以通過Windows的DDK(Device Drive Kits)來實現。Windows
下的DDK也是一系列的API函數,只是這些函數是專門用於Windows下硬件驅動程序編寫的。由於驅動程序可
以運行在Ring 0的權限下面,所以也就解決了用戶程序不能進入Kernel模式下的矛盾。另一個要解決的問
題是驅動程序和用戶程序之間要定義一個通信接口。這樣,用戶程序通過自己定義的驅動程序接口來實現
對並口的訪問。我們運用的方法是先利用DDK編寫一個可訪問並口的驅動程序(一般是XXXX.sys模樣的文件),
之後,由一個DLL文件指定外露接口。

幸運的是,已經有人在這方面爲我們做好了相應的可訪問並口的驅動程序,而且提供了源代碼讓我們
學習。大家可以在參考文獻[1]上找到源代碼和使用文檔說明。文檔寫得很清楚、詳細,這裏我就不多說啦。

2.3 Linux下並口的訪問

由於Linux的開放性和高度的用戶自定義性,要讓Linux支持並口,首先要讓Linux的內核支持並口。
一般我們在從光盤安裝Linux時,都會把並口支持功能放進內核當中。但如果是自己升級內核、編譯內核時
就要注意了:不要把並口支持功能給去掉。不然,你會發現並口死活“不肯”產生信號。要讓內核支持並口
(如果大家是從光盤安裝Linux的話,則不要下面的步驟,因爲系統會自動加載並口支持功能的),就要在
設置系統內核時選擇上"Parallel port support"這個選項。這個選項可以在make config或make xconfig
或make gconfig或make menuconfig時找到。如果大家以上面幾個命令不太瞭解,可以參考編譯內核的文檔。
這樣,在編譯內核時,我們就預定義了宏CONFIG_PARPORT,編譯器就被通知到要把並口支持部分代碼給編
進內核當中去。當然,我們也可以把並口支持功能編譯成模塊(Module)形式,這些模塊的存放位置在
"/lib/module/內核版本號/kernel/drivers/parport"目錄下,相應的文件是parport.o、parport_pc.o等。
在需要使用並口時,用命令insmod、rmmod來加載、去除並口支持功能。其中insmod是指"Insert Module",
rmmod是指"Remove Module"。

在確定Linux內核支持並口功能後,我們就可以進入下面的主題了。

訪問Linux下並口的函數,和前面介紹的Windows95/98下的_outp是極其類似的:outb、inb函數,當中
的參數含義也和前面講到的_outp、_inp一樣。

但需要指出的是,Linux也採用了與Windows NT/2000相似的保護措施,禁止用戶進程直接對硬件端口
訪問(用戶空間程序不允許訪問內核空間中的內容)。爲了簡化並口訪問,Linux又提供了一個Helper Function,
這就是ioperm。這個函數可以告訴內核:給出一定的控制權與用戶進程,當用戶程序結束後,再把控制權給收
回來。如果我們不事先調用ioperm的話,訪問並口的程序也可以編譯成功,但在運行時會出錯,一般會在屏幕
上出現"Segment Failure"的出錯提示。

使用到的兩個函數原型如下:

#include <sys/io.h>
int ioperm(unsigned long from, unsigned long num, int turn_on);
void outb(unsigned char byte, unsigned port);

當中的ioperm是讓從from開始,直到num個字節,這段範圍內的I/O映射地址可以爲普通用戶程序訪問。上
述行爲是在當turn_on參數爲"真"(非零)時允許的。當爲"假"時,就禁止這種行爲。這可以在程序完成,
不再要訪問硬件端口時調用。

下面給出Linux下訪問並口的示例代碼:

... ...
ioperm(0x000,0x3FF,1); //把0x000~0x3FF之間空間開放給用戶程序
//這段範圍對訪問並口而言,已經足夠了。

outb(0xFF,0x378); //向並口數據位寫數據。這裏是將8個數據位全置爲1
... ...
ioperm(0x000,0x3FF,0); //收回控制權,用戶程序在這之後就不再可以
//訪問內核空間內容了。


第三章 *總結*

目前爲止,已經在如下平臺實現了對並口的編程:
*Windows 98 SE
*Windows 2000 Professional
*Linux(RedHat 9.0 Kernel 2-4-20)
*Linux(Slackware 9.1 Kernle 2-4-22)

*參考文獻*

[1] Accessing Parallel Port. http://www.logix4u.net/index.htm
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章