主 題: | [原創]--不同平臺下對並行端口的訪問 | |
作 者: | 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 |