tag 标签: 并口打印

相关博文
  • 热度 13
    2012-1-12 18:36
    2802 次阅读|
    0 个评论
    兄弟我在精华发现了一些关于并口打印的文章,但是都是用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 有一些文档讲并口,我并不是很熟。 ========================================