Hi everyone,
I am using a LM3S5951 controllers for one of my projects in which I need to implement a host side driver for Dot Matrix printer .
I am done with enumeration of the printer, but now I would like to know how can I send data to printers using bulk transfers. I am using the Stellaries USB Library for the same where I found some host side codes for keyboard and pendrive. I would like to know, how to go about this.
Regards
Dhanush
A) Before starting firmware coding, you have to examine the printer on a PC, to know the printer protocols better.
1) Read out these information from the printer, using USB sniffer on the PC. - Device and Configuration descriptor set. The descriptor set is used to check if your firmware reads them out correctly or not.
- Printer’s Device ID string In the enumeration, PC reads out Device ID string using GET_DEVICE_ID request (control transfer), of this SETUP packet layout.
bmRequestType: 0xA1 bRequest : 0x00 (GET_DEVICE_ID) wValue : 0x01 (config index) wIndex : 0x00 (Interface / Alternate) wLength : length of ID string
Here is the Device ID string of an old laser printer (EPSON LP-2400) on my desktop.
00 6B 4D 46 47 3A 45 50 53 4F 4E 3B 43 4D 44 3A .kMFG:EPSON;CMD: 45 4A 4C 2C 45 53 43 50 32 34 4A 2D 38 34 2C 45 EJL,ESCP24J-84,E 53 43 50 41 47 45 4A 2D 30 34 2C 45 53 43 50 53 SCPAGEJ-04,ESCPS 55 50 45 52 2D 30 30 2C 50 52 32 30 31 2D 30 30 UPER-00,PR201-00 3B 4D 44 4C 3A 4C 50 2D 32 34 30 30 3B 43 4C 53 ;MDL:LP-2400;CLS 3A 50 52 49 4E 54 45 52 3B 44 45 53 3A 45 50 53 :PRINTER;DES:EPS 4F 4E 20 4C 50 2D 32 34 30 30 3B ON LP-2400;
"CMD:" tag on the Device ID string shows the PDL (Page Description Language), applied to print data. In above example,
CMD:EJL,ESCP24J-84,ESCPAGEJ-04,ESCPSUPER-00,PR201-00;
This printer supports Epson ESC/P and PC-PR201 enumeration. You may use these sniffers on Windows, USBlyzer (commercial, 33 days trial) http://www.usblyzer.com/ USBTrace (commercial, 15 days trial) http://www.sysnucleus.com/
To read out device/config descriptors and Device ID string, the enumeration sequence of the printer is traced. To catch enumeration on USBlyzer, you'll need to enable "Capture menu -> Capture Hot-plugged" before plugging in the target printer to the PC.
2) Make a "test page" data on the PC - First of all, your PC should be installed the correct driver of the printer. - Make a "test page" on a text editor, like WordPad, as close to your required page layout as possible. - Print the page on the printer from the PC, tuning the page layout until you are satisfied. - When the page finishes, switch to "Print to File"
On Windows7, bottom Start button > Device and printers > right click on the target printer > Printer’s properties > Ports tab forums.techguy.org...er-properties-port.jpg Check "FILE: Print to File"
And then, print the "test page" on the editor. Now that you have "test page" data on a file.
To be continued..
Tsuneo
Hi Tsuneo,
I successfully intterfaced the dot matrix printer using the details provided by you for LM3S5951 controller. I am using an epson LX-300 printer which supports esc/p2 pdl. now when eveytime I send data to printer preceded by an init printer command , the printer prints data. But now when I send data w/o init printer command it prints data once in every two tries. Please provide some solution.
Regards Sanket K
Answered to Dhanush in TI Stellaris forum, e2e.ti.com/.../1172109.aspx
Sanket, ESC/P line printer prints data on its buffer to paper, when it receives LF (0x0A). On your code, you may terminate a string with LF, like "ABCD\n"
Now that I have successfully interfaced the printer using USB, everything is working fine. I would also like to check whether the printer is busy or not like it has been checked using the 25 pin centronics using LPT. How could I achieve this I have one IN endpoint for the printer.
Regards Dhanush
> I have one IN endpoint for the printer.
The bulk IN endpoint of the printer interface is optional. It’s role is specific to each printer model. Unfortunately, Epson doesn’t open the information of the role (protocol) over the bulk IN. You may get some printer status, or nothing. Try it.
ulBytes = USBHCDPipeRead( g_USBHPRNDevice.ulBulkInPipe, pBuffer, sizeof(pBuffer) );
> I would also like to check whether the printer is busy or not like it has been checked using the 25 pin centronics using LPT.
NAK on the bulk OUT endpoint corresponds to the BUSY signal of Centronics.
While the endpoint is NAKing, USBHCDPipeWrite() (and USBHCDPipeRead()) blocks the caller thread. I believe this is the reason why you need "BUSY" signal.
Instead of these blocking calls, you may apply non-blocking USBHCDPipeSchedule() to start transfer over the bulk IN or OUT endpoint.
USBHCDPipeSchedule( g_USBHPRNDevice.ulBulkOutPipe, pBuffer, sizeof(pBuffer) ); or USBHCDPipeSchedule( g_USBHPRNDevice.ulBulkInPipe, pBuffer, sizeof(pBuffer) );
When the transfer finishes, callback is called for each endpoint. This callback is registered at USBHCDPipeAllocSize() in your USBHPRNOpen()
static void PRNEndpointCallback(uint32_t ui32Pipe, uint32_t ui32Event); static void * USBHPRNOpen(tUSBHostDevice *pDevice) { ... ... g_sUSBHMSCDevice.ui32BulkInPipe = USBHCDPipeAllocSize(0, USBHCD_PIPE_BULK_IN_DMA, psDevice, psEndpointDescriptor->wMaxPacketSize, PRNEndpointCallback); // <------- ... ... g_sUSBHMSCDevice.ui32BulkOutPipe = USBHCDPipeAllocSize(0, USBHCD_PIPE_BULK_OUT_DMA, psDevice, psEndpointDescriptor->wMaxPacketSize, PRNEndpointCallback); // <-------
In the callback,
static void PRNEndpointCallback(uint32_t ui32Pipe, uint32_t ui32Event) { switch ( ui32Event ) { case USB_EVENT_TX_COMPLETE: // transfer on OUT endpoint finishes // // notify to the main thread by a flag, or start another transfer // break; case USB_EVENT_RX_AVAILABLE: // transfer on IN endpoint finishes // read out data into a buffer USBHCDPipeReadNonBlocking( g_USBHPRNDevice.ulBulkInPipe, pBuffer, sizeof(pBuffer) ); break; default: break; } }
Hi Tsuneo ,
I wish to interface a Laser printer (high USB speed device) to my ARM controller.But TIVA supports only low speed and full-speed devices . Is there any solution .Is there any interfacing circuitry ???
Regards, Sanket
> But TIVA supports only low speed and full-speed devices .
Surely USB host on Tiva TM4C123x supports just low-/full-speed. But TM4C129x does high-speed, too, with an external ULPI PHY chip. Unfortunately, TivaWare doesn't implement external PHY chip, yet.
> I wish to interface a Laser printer (high USB speed device) to my ARM controller.
You may consider on other MCUs.
a) STM32F2/F4 with an external ULPI PHY chip The low-cost pair, STM32F4 Discovery board and "USB3300 USB HS Board", is handy to play high-speed USB. www.wvshare.com/.../USB3300-USB-HS-Board.htm
ST's STM32_USB-Host-Device_Lib_V2.1.0 doesn't have printer host. But you may write it, based on Mass-Storage host example In this link, we discussed on modification for CDC/RNDIS host. Similar modification is applied to printer host. my.st.com/.../DispForm.aspx
b) LPC18xx/LPC43xx (with on-chip high-speed PHY) - USB host library in LPCOpen supports printer host, but no example. You may write it fairly easily, based on HID keyboard host example. www.lpcware.com/.../lpcopen-software-development-platform-lpc18xx-packages-0
c) Raspberry PI and BeagleBone Black Raspbian / Angstrom Linux supports printer host.
I am sending data to the printer using usbhcdpipewrite. now i dont have the paper in the printer, the printer pauses, but its buffer is now full with the data that is sent. Now whenever I insert the paper it starts printing the data, but I dont want this to happen. But I have no other solution as busy line in LPT. Whether there is a solution to this or not.
Hi,
I am also facing the same problem with the printer. I want to check the paper out and set a flag in my code whenever there is no paper in the printer. My requirement is like this so I have to achieve this in any case . There may be a solution to this problem.
regards Sagar
GET_PORT_STATUS class-specific request retrieves printer status, including Paper Empty.
USB Printing Device Class spec www.usb.org/.../usbprint11a021811.pdf 4.2.2 GET_PORT_STATUS (bRequest = 1) (page 7) This class-specific request returns the printer's current status, in a format which is compatible with the status register of a standard PC parallel port.
Bit(s) Field Description 7..6 Reserved; Reserved for future use; device shall return these bits reset to zero. 5 Paper Empty; 1 = Paper Empty, 0 = Paper Not Empty 4 Select; 1 = Selected, 0 = Not Selected 3 Not Error; 1 = No Error, 0 = Error 2..0 Reserved; Reserved for future use; device shall return these bits reset to zero.
This request is carried over Control Transfer. The implementation is,
unsigned long GetPortStatus(unsigned long ulInstance, unsigned long ulInterface) { #define PRN_GET_PORT_STATUS 0x01 unsigned long ulStatus = 0; tUSBHostPRNInstance *pPRNDevice = (tUSBHostPRNInstance *)ulInstance; tUSBRequest SetupPacket; SetupPacket.bmRequestType = USB_RTYPE_DIR_IN | USB_RTYPE_CLASS | USB_RTYPE_INTERFACE; SetupPacket.bRequest = PRN_GET_PORT_STATUS; SetupPacket.wValue = 0x0000; SetupPacket.wIndex = (unsigned short)ulInterface; // interface number SetupPacket.wLength = 0x01; // one-byte status returns USBHCDControlTransfer(0, &SetupPacket, pPRNDevice->pDevice, (unsigned char *)&ulStatus, sizeof(ulStatus), pPRNDevice->pDevice->DeviceDescriptor.bMaxPacketSize0); return(ulStatus); }
To poll the printer status, your firmware regularly calls GetPortStatus() in every 100ms or so.
unsigned long ulStatus = GetPortStatus( g_USBHPRNDevice.pDevice, g_USBHPRNDevice.pDevice->ulInterface );
Thank you for your responses. I have implemented the above things as said but whenever I read the status using GetPortStatus function described above I get result byte as '0' even if the paper is not present in the printer.
The procedure I followed for the same is as follows:
1: I added the implementation as discussed above to my printerhostclass.c file as it is. I understood the things mentioned in it.
2: I have created an instance of the printer driver g_ulPRNinstance.
3: unsigned long status=GetPortStatus(g_ulPRNinstance,0) I call the function using the above line from my main function. if((status & 0x20)== 1) do this else do that.
Now when I start the printer w/o paper and call this function then always it executes the "else" part, where as it should execute "if" part. I check the value of status on uart but it shows 0. Am I doing something wrong here, if yes then what should I do
First off, you say you get the status '0'. But the status is a number - not a character. While character '0' happens to be 0x20 + 0x10;
Next thing - your expression:
if((status & 0x20)== 1) { ... }
is broken. It can never be true.
It should either be:
if (status & 0x20) { ... }
or
if ((status & 0x20) != 0) { ... }
Ok. But after calling function Getportstatus, I get ulstatus I check this thing by displaying it on serial terminal after converting long to Ascii, still I get 0. I want to know whether I am calling the func correctly using the correct parameters, and what should be the wvalue parameter.
There is note in USB Printing Device Class spec stating that some printer do not support this information. And what does benign status, what does it mean. My printer is Epson-LX300-II does this support the thing or not.
As mentioned by Mr. Dhanush, when I tried it the printer was returning some status. When I converted to ascii and sent it serially to hyper terminal I saw data as 87(10000111) which I guess is wrong since all the bits which are high are reserved, which should not be the case.
Still I am debugging this thing whether I am doing somethin wrong. Hoping for some hints.
Regards Sagar
Connect your printer to a PC, on which genuine printer driver is installed, and trace the USB traffic using a sniffer. The driver should poll the printer status.
a) How does the driver poll the status, over GET_PORT_STATUS request, or over the bulk IN endpoint?
b) How does the status change, at on/off of "Select" button, and Paper empty?
HI,
Here instead of calling the getportstatus every 100 ms i call the func before printing the data.
Now when I first send data to be printed w/o paper inserted i get no error i.e. 00011000 .
Second time if I do the same i get error i.e. 00110000
My question is why it does not give an error for the first time.
And my requirement is once i give data to be printed and no paper is inserted it the code should wait till the paper is not inserted and once the paper is inserted it should start printing.
For parallel port I do this using the busy flag of the centronics port. I keep on checking the busy flag till it gets low, Once the paper is inserted the printer makes the busy flag zero and then I start sending data to be printed.
How can I achieve this on USB . I Know the only way I have is polling the printer status continuously.
Did you trace the USB traffic of the "genuine" PC driver for the printer? You can trace it with any free USB sniffer, like http://freeusbanalyzer.com/ sourceforge.net/.../ etc.
The "genuine" PC driver should regularly poll the printer status. It may apply bulk IN endpoint or any vendor-specific request other than GET_PORT_STATUS.
I want to implement a USB module as V drive for pendrive where I can get commands through uart such as make directory ,open file etc and then dump the file onto pendrive. I am aware of how USB works, I want to know the feasibility of this project and a starting guide to how can it be achieved.
Regards Sanket
I am using a TM4c123 series controllers with 1 USB host port. Now I am using the same port using the a USB switch to use the keyboard and pendrive. But i want to use them both without switching. I guess this is possible if a USB HUB controller with 2 or 3 downstream ports is available. So how should go about it, are there any controllers which such capabilities available.
Sanket
View all questions in Keil forum