|||
最近在做一个嵌入式linux下的应用程序驱动打印机的工作,打印机是58mm的热敏票据打印机,供应商说是免驱动的。
linux开发最不愿意听到的就是免驱。免驱动的意思就是linux下,你自己看着办吧!!!
在网上查了半天,看样子只能使用libusb来自己直接控制打印机了,还好打印机是ESC/POS指令集的,这个东西比较容易找到。另外现在linux默认是UTF-8编码,这个编码不被手头的ESC/POS打印机支持,所以还需要一个将UTF-8转换为GBK的软件包libiconv。libiconv比较简单,就不再本文中多说。
首先下载libusb,配置并编译。在www.libusb.org官网下在最新版本的源代码,目前是libusb 1.0.9。
编辑一个setconf.sh文件,用于对libusb进行编译设置:
#!/bin/sh
./configure --host=arm-linux-gnueabihf \
--prefix=/opt/libusb/ \
CC="arm-linux-gnueabihf-gcc -ldl -fPIC" \
CXX="arm-linux-gnueabihf-g++ -ldl -fPIC"
然后make -j4,编译libusb,成功后make install,将库安装到计算机的/opt/libusb目录中,手动将/opt/libusb目录拷贝到嵌入式linux的nfs文件夹中的相同目录中。
编写一个测试程序,用于测试是否能够通过libusb控制打印机。
首先对libusb初始化,并获取设备列表,然后遍历设备列表,寻找指定的打印机设备:
#define VenderID 0x1cbe
#define ProduceID 0x0a03
libusb_context *ctx = NULL;
struct libusb_device_handle *dev_handle=NULL;
unsigned int uInterfaceNum;
unsigned char ucEpIn=0,ucEpOut=0;
rtn=libusb_init(&ctx); //初始化libusb
if(rtn!=0){
printf("Can't init the libusb.\n");
return 1;
} else {
printf("Open the libusb sucess, rnt code is %d.\n",rtn);
}
cnt=libusb_get_device_list(ctx,&devs); //获取libusb的设备列表
if (cnt<0){
printf("Can't get the USB device list.\n");
return 1;
}
while((dev=devs[i++])!=NULL){ //遍历设备列表,寻找指定的USB打印机设备
struct libusb_device_descriptor desc;
struct libusb_config_descriptor *config_desc;
uint8_t bus_number=libusb_get_bus_number(dev);
uint8_t device_address=libusb_get_device_address(dev);
rtn=libusb_get_device_descriptor(dev,&desc);
if(rtn<0){
printf("Failed to get device descriptor for %d/%d.\n",bus_number,device_address);
continue;
}
printf("Bus number=%u,Device address=0x%x\n",bus_number,device_address);
printf("Vender ID=0x%x,Product ID=0x%x\n",desc.idVendor,desc.idProduct);
if((VenderID==desc.idVendor)&&(ProduceID==desc.idProduct)&&(dev_handle==NULL)){ //比较是否是指定的设备
printf("Find a ESC/POS printer, open it.\n");
rtn=libusb_open(dev,&dev_handle);
if(rtn<0){
printf("Can't open the USB printer, rtn code is %d.\n",rtn);
dev_handle=NULL;
} else {
rtn=libusb_get_active_config_descriptor(dev,&config_desc);
if(rtn<0){
(void)libusb_close(dev_handle);
dev_handle=NULL;
printf("Can't get config descriptor.\n");
} else {
printf("Open the USB printer sucess.\n");
usbPrinter=dev;
for(j=0;j<config_desc->bNumInterfaces;j++){ //已经找到指定设备,访问该设备的描述表,找到interface,endpoint
for(t=0;t<config_desc->interface[j].num_altsetting;t++){
if(config_desc->interface[j].altsetting[t].bInterfaceClass==0x07){
printf("It is a USB printer.\n");
uInterfaceNum=j;
for(m=0;m<config_desc->interface[j].altsetting[t].bNumEndpoints;m++){
if(config_desc->interface[j].altsetting[t].endpoint[m].bmAttributes==0x02){
if(config_desc->interface[j].altsetting[t].endpoint[m].bEndpointAddress&0x80){ //输入endpoint
ucEpIn=config_desc->interface[j].altsetting[t].endpoint[m].bEndpointAddress;
} else { //输出endpoint
ucEpOut=config_desc->interface[j].altsetting[t].endpoint[m].bEndpointAddress;
}
}
.........
}
libusb_free_device_list(devs,1);
上面的程序通过获取device_list,并遍历list,然后比较venderID和produceID,寻找指定的USB打印机。如果找到指定的打印机,使用libusb_open打开设备。
当成功打开指定的USB打印机后,保存打开的设备句柄,然后遍历已经打开的打印机的设备描述表,获取interface以及endpoint。
手头的ESC/POS打印机只有1个interface,这个interface包含2个endpoint,一个输入,一个输出。
打开打印机,并获得interface,endpoint后,下一步通过libusb_claim认领interface:
rtn=libusb_claim_interface(dev_handle,uInterfaceNum);
if(rtn<0){
printf("Can't claim the interface.\n");
}
到这里,就已经获得打印机的控制,可以向打印机发送ESC/POS指令,进行打印操作了。例如控制打印机回车换行并切纸:
ucBuf[0]=0x0d;
ucBuf[1]=0x0a;
ucBuf[2]=0x1b;
ucBuf[3]='i';
rtn=libusb_bulk_transfer(dev_handle,ucEpOut,ucBuf,4,&iLen,USB_WRITE_TIMEOUT);
if(rtn<0){
printf("Can't write cmd, rtn code is %d.\n",rtn);
} else {
printf("Write cmd sucess, real len is %u.\n",iLen);
}
最后关闭打印机并退出libusb:
rtn=libusb_release_interface(dev_handle,uInterfaceNum);
if(rtn<0){
printf("Can't release the interface,rtn code is %d.\n",rtn);
} else {
printf("Release the interface sucess.\n");
}
(void)libusb_close(dev_handle);
if(ctx!=NULL){
libusb_exit(ctx);
ctx=NULL;
}