X86体系结构对并行端口的操作

来源:互联网 发布:mysql多主多从 编辑:程序博客网 时间:2024/05/01 05:02

*前言*

        本文主要是讨论不同平台下对并行端口进行编程的方法。因条件所限,目前只局限于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

原创粉丝点击