兄弟我在精华发现了一些关于并口打印的文章,但是都是用inb outb操作的,不知道各路高人能否给小弟一个用open write ioctl read close控制的判断状态并读写的例子,尤其是ioctl,该怎么使用才能判断出并口的状态,如未准备好,缺纸等等
还有一个问题是如果用上面的函数,那对并口操作与对串口的操作除了在ioctl上有所不同外,还有什么地方不一样呢,我对这方面很陌生,对于并口,用设置那些类似串口操作时的什么波特率,校验位等等吗?还是只需要open("/dev/lp0",O_RDWR | O_NOCTTY) 就ok了,而不用再设置什么了??
竟然没有一个人回复,真悲哀!
这个问题我在别的论坛也问了,那里多少给了点建议,这里却一点动静也没有,我把我自己这两天摸索的心得写出来吧,这个东东我在昨天给我回复的那个论坛发了,想了想,决定在这里也发一个吧,但是真的不希望看到这里死气沉沉的样子
声明:所有东东都是我自己的理解,又告人觉得不对,希望尽量批评 所有原码,函数名,.h文件名都是在Red Hat Linux上的东东 其它平台可能会有所变化,程序是完全编译通过并正常运行
首先,用C语言操作并口,本身就存在两种方法
一种是:ioperm inb outb
另外一种是:open read write ioctl
第一种方法是操作并口的内存地址
第二种方法是对文件描述符操作(是人就知道)
先说第一种
ioperm是获取地址操作权限的函数,使用前后必须两次调用,
类似于open 和 close 但是他仅仅是获得权限,且一旦写入程序
,该程序就必须用root用户来运行,无论你对其他用户如何授权,其他用户就是调用不了该函数(该点理解给予我的实践,希望有异议者多多批评)。
inb是读取寄存器种数据的,outb就是写入,但是对于并口来说,有些特性,即
数据位地址可读写 控制位地址可写 状态位地址可读 通常/dev/lp0的地址是0x378,所以控制位就是0x378+2 状态位就是0x378+2
状态位有busy ack pe select error
控制位有select_in init autofeed strobe
数据位有D0--D7
如果只熟悉C语言的哥们,估计看到这里就觉得很烦了,建议去看看并口电路的基本针脚介绍就知道我在说什么了
一句话,通过上面的这些东东我们就可以知道打印机是什么状态,是否可以发数据,是否缺纸,并且可以传递数据给打印机,但是要清楚,我们说的事并口,并不是打印机,打印机在这里只是个例子,当然并口很多东西当初也是从打印机那里慢慢演化而来的。
这些控制位和状态位都是做什么的,我研究的也不是很清楚,但是网上有这些文章可查,我这里不多说了,总之如果用ioperm inb outb就要知道确切的作用,否则你控制不了打印机的动作,优点是直接操作地址,速度够快,不过如果操作打印机的话,通常瓶颈在打印机上,而不会是对并口的读写上。
说了这么多屁话,我想不如我下面的原码来的直观
大家请看
#include
#include
#include
#define BASEPORT 0x378 /* lp1 */
int main()
{
/* Get access to the ports */
if (ioperm(BASEPORT, 3, 1)) {perror("ioperm"); exit(1);}
/* Set the data signals (D0-7) of the port to all low (0) */
outb(0, BASEPORT);
/* Sleep for a while (100 ms) */
usleep(100000);
/* Read from the status port (BASE+1) and display the result */
printf("status: %d ", inb(BASEPORT + 1));
outb(0x80,BASEPORT+2);
system("/home/ShenLin/ParPort/Check");
usleep(10000000);
system("/home/ShenLin/ParPort/Check");
outb(0x40,BASEPORT+2);
system("/home/ShenLin/ParPort/Check");
usleep(10000000);
system("/home/ShenLin/ParPort/Check");
outb(0x20,BASEPORT+2);
system("/home/ShenLin/ParPort/Check");
usleep(10000000);
system("/home/ShenLin/ParPort/Check");
outb(0x10,BASEPORT+2);
system("/home/ShenLin/ParPort/Check");
usleep(10000000);
system("/home/ShenLin/ParPort/Check");
outb(0x08,BASEPORT+2);
system("/home/ShenLin/ParPort/Check");
usleep(10000000);
system("/home/ShenLin/ParPort/Check");
/* We don't need the ports anymore */
if (ioperm(BASEPORT, 3, 0)) {perror("ioperm"); exit(1);}
exit(0);
}
上面的程序要注意的是下面这几点
ioperm(BASEPORT, 3, 1) 这个函数就如我说的调了两次,注意区别
system("/home/ShenLin/ParPort/Check");这里面调用的Check程序是我自己写的(就是用open ioctl写的),这个明天再介绍,我要吃饭去了
inb(BASEPORT + 1));这个就是在读取状态地址的值,用它就可以判断是忙还是缺纸还是什么别的(在Check程序中,我用ioctl来获得)
至于打印机的字符集,输出格式等等,不再并口操作讨论范围内,我个人理解他们应该和串口的是一样的,即串口的fd被write了什么,并口就应该被write什么,不知道我理解得对不对,换句话说如果有一个串口的打印机驱动程序,那么我要移植到成并口的,理论上讲应该只替换掉其中打开设备的部分,即open的部分,还有就是替换掉状态判断部分,即ioctl部分,至于别的,我理解应该不需要替换的,这一点我不敢肯定,请各位高人给予分析,多谢!!
补充一下昨天方法中要注意的,就是如果用到了ioperm这些函数,编译的时候要加入 O2或者O 。
即gcc -O2 -o test test.c,不要这样gcc -o test test.c
我接着把昨天的写完
用操作文件描述符的方法主要问题在于ioctl函数上,这个函数实在是变化无穷,第一个参数是文件描述符,但是该文件描述符是如何获得的,将直接影响第二个参数用什么,而第二个参数又直接影响第三个参数的指针类型
总之,就第二个参数而言,其针对串口,并口,socket,普通文件等等是各有一套定义的,而且分别存放在不同的.h中的,如果用错了(假如把socket的用到并口上),编译的时候不会有问题,但是运行的时候,肯定会告诉你参数无效的。
对于并口来说,首先你的系统就要支持并口的那种,如果各位裁减内核的时候把这个东西裁掉了,那么,你用open 打开/dev/lp0的时候会告诉你没有这个设备,昨天我下午曾经一度陷入了这个设备中/dev/parport0,这也是个并口,但是不对应硬件,我不敢肯定的说他可能是个虚拟的并口(希望研究比较明白的人给个答案),对于这个并口的操作,我们必须用到
#include <linux/parport.h>
#include <linux/ppdev.h>
这两个库文件,这里的名字和路径,我在强调一下,是Red Hat Linux上的,其它平台应该会有少许差别。这两个库里是什么东西呢,只要看了,就会明白,其实这里定义了ioctl中第二个参数可以用哪些,以及对应第三个参数是什么类型的指针,包括各种并口状态位的宏定义,以及并口的模式标准(EPP,IEEE1284等)等等,但是问题在于这个设备对应的不是打印机口,所以虽然可以操作了,但是也没用,操作这个东东的ioctl函数的第二个参数可谓十分丰富,只是跟下面将要说的/dev/lp0比起来,/dev/lp0提供给ioctl函数的第二个参数就少得可怜了。
这也是我很疑惑的问题,难道对于/dev/lp0来说,只要我open了,就不需要对这个被open的文件描述符进行任何初始化了吗?
下面我们先看一下操作lp0得源码,强调一下,虽然有了
#include <linux/parport.h>
#include <linux/ppdev.h>
这两个库中定义的那么多的东东,很可惜他们全都不能用到lp0上,否则告诉你参数无效
要操作lp0关键在于下面的库,上面两个可以不要
#include <linux/lp.h>
源码如下:
#include
#include
#include
#include
#include
#include
#include <linux/lp.h>
int CheckParPortState(int fd, int m_bits)
{
int status;
/* Get Par Port State */
if (ioctl(fd, LPGETSTATUS, (char*)&status) < 0) {
return (0);
}
if (m_bits != 0x20) return ( status & m_bits );
else return( !(status & m_bits) );
}
main()
{
int fd;
if ( (fd = open("/dev/lp0", O_WRONLY , 0)) < 0) {
perror("/dev/lp0 error");
exit(-1);
};
if (!CheckParPortState(fd, LP_PBUSY))
printf("printer is busy ");
if (!CheckParPortState(fd, LP_PACK))
printf("printer answer ");
if (!CheckParPortState(fd, LP_POUTPA))
printf("printer paper out ");
if (!CheckParPortState(fd, LP_PERRORP))
printf("printer error ");
close(fd);
}
上面这个程序就是我昨天说到的那个Check的源码
程序要注意的:
1.判断paper out的时候,其逻辑与其他判断相反
2.我到现在也不知道打开lp0这个并口后要不要做什么初始化,
好像这个工作bios里设定了,包括并口传输模式,物理地址,中断等等
3.lp.h中提供的东东比较少,目前我觉得有用的就是这个获得状态位的
command,以及这些状态位判断用的宏。
其他的,如果哪位高人领悟了含义和具体用法,请告诉我,谢谢!
4.某种状态不是唯一出现,比如我关闭打印机,可能同时报忙.缺纸.Error
如果缺纸,可能同时报缺纸.忙两个状态
5.状态以打印机的指示灯为准,即,有的打印机没有纸的时候不会提醒缺纸,只有当你触发换行或换页,缺纸指示灯才会亮,程序也才会受到这个状态
到这里我们只是了解了个大概,希望能有高人能告诉我打印机对应的lp0是否只要open了就可以了,还是要类似于串口一样设置一堆东西(如校验位,波特率等)
此外,对lp0的控制位的操作,我猜想可能被write屏蔽掉了,即它不需要像操作地址一样改变不同的控制位,以便控制打印机能否接受数据等等动作,调用write就会默认去修改一些控制位等。这是我的猜想,希望有高人能证明
====================================
去弄本apue看看,里面有很清楚的讲解,如果看不懂可以再提问。
======================
一般来说直接通过inb/outb访问目的是把并口作为GPIO,每个脚都可以独立使用,其含义由并口上接的设备而定。
通过open等访问的是打印机这样的设备,这时并口是按8位数据线传输的,同时有一些状态线控制,有一定的逻辑关系,EPP什么的是并口的传输模式,在http://www.beyondlogic.org有一些文档讲并口,我并不是很熟。
========================================
文章评论(0条评论)
登录后参与讨论