我在Linux环境下用C/C++开发一个POS软件,现在要写一个程序对钱箱操作的,比如往并口发一个指令弹开钱箱,请问在Linux下如何对并口送指令?
|
*前言*
本文主要是讨论不同平台下对并行端口进行编程的方法。因条件所限,目前只局限于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
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
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