Hi
I'm using USB HID library to implement an small HID device. I already have read this library and USB 1.1 specification, but I still have a small problem. I still cannot understand relation between 3 sizes in descriptor: 1- EP0 packet size(that you can specify in usbcfg.h) 2- Endpoint's wMaxPacketSize 3- maximum size of reports
In my case, I need to transfer 128 bytes of data to and from device. Then I have defined my reports like this: ReportSize(8) ReportCount(128) But I still cannot get how wMaxPacketSize and EP0 packet size are related to these values. From some samples, I saw that people set wMaxPacketSize to 64. But how I should transfer my reports if wMaxPacketsize is 64? or are they related at all?
Regards
I'm using a custom board,but I should say when I program that board with provided HID example with no change, everything works fine. But when I program it with my own code, that problem shows up.
In addition, my MCU is AT91SAM7S256.
I will test my board with this revised report descriptor and tell you what happens.
Regards.
> I'm using a custom board,but I should say when I program that board with provided HID example with no change, everything works fine.
Do you mean debug function also works fine with the example? Anyway, the descriptor has problem, but it isn't the cause of USBlyzer / debugger trouble.
Fixed / controlled D+ (or D-) pull-up. Here is a bad example.
Keil MCB2140 schematic http://www.keil.com/mcb2140/mcb2140-schematics.pdf
At the bottom right corner of the first page, you'll see USB connector. On the D+ line from the connector, R35 (1k5) pull-up resistor is connected. This resistor is fixed one, not controlled by the MCU port.
This pull-up configuration is bad for the development of USB application. You have to plug-off/plug-in USB connector to re-start USB enumeration, every time the debugger puts reset. Also, this board is supplied just by USB connector. Plug-off/plug-in connector powers down the board, the debugger is disturbed.
The workaround of this board is, insert a self-powered hub (supplied by wall-mount adapter) between this board and PC USB port.The self-powered hub supplies power to the board regardless of PC connection. But you still have to plug-off/plug-in the hub - PC USB port connection for re-enumeration.
Here is a good example.
Keil MCB2300 schematic http://www.keil.com/mcb2300/mcb2300-schematics.pdf
On this schematic, USB connector is found at the right hand of the second page. R3 (1k5) pull-up resistor is connected to the D+ line (connected to pin 3 of the connector). This pull-up is controlled by Q1 PNP transistor, which connects to P2.9 MCU port through J5.
This configuration is fine for USB development. When debugger puts reset, the port goes to initial state, and the D+ pull-up is disabled automatically. After reset, the firmware enables the pull-up, and re-enumeration starts smoothly without connector plug-in/off.
Which type of D+ pull-up on your borad?
Tsuneo
Thanks for schematics. I should say that my board is using bad mode of pull-up resistor for D+, but I have used a simple way to solve enumeration problem. I power up my board with an additional 5v(for JTAG), and start debugging board. In this mode,USB is not enumerated yet. Then I attach USB cable from my board to PC and enumeration starts with no problem. But this is not my problem for sure. When I program my board with base HID example from USB library, there is no problem with anything. my board works both normally(attaching device to PC), or when I debug my board with ULINK. then there should not be any problem with my board. But when I program board with my own USB codes(customized from those libraries), if I attach device to PC it says "USB device not recognized", but when I start debugging my board(then there are some delays between configuration steps when I break on breakpoints), my configuration of USB device proceeds several steps(not to a exact steps, once only till getting report descriptor, once till fully recognizing device). Then I think there is a synchronization problem in my code that leads to this behavior.
In addition I tested my code using new report descriptor, but nothing changed. EP0 is a 2 way control endpoint, is it possible that configuration packets via this endpoint are transferred incorrectly?
> When I program my board with base HID example from USB library, there is no problem with anything.
Then, go step by step. 1) Firstly, copy the example project to your working folder. Build it and run it once, to ensure the copy works fine. Of course, it'll run without any problem, but do it just for double check.
2) And then, replace just the descriptors (Device, Config set and Report descriptors) on the copy. The other source code are left untouched. It should still be enumerated, unless the descriptors have any problem.
For different configuration, assign different VID/PID (Vendor/Product ID) on the Device descriptor. While development, you can use any VID/PID, unless it doesn't conflict with other USB devices on your PC. I usually apply VID = 0xFFxx (xx is arbitrary number), and arbitrary PID for development. Before release of the product, get official VID/PID. USBDeview lists up USB deviceson your PC, with their VID/PID.
USBDeview www.nirsoft.net/.../usb_devices_view.html
3) When you finish the descriptors, modify endpoint handler a little (report size, etc). Then check the result. Jan Axelson's "generic_hid_vb" or "generic_hid_cs" will work as a test bench on the PC side. http://www.lvr.com/hidpage.htm Up to this phase, I think you don't see so much trouble on debugging.
4) When you satisfy with the USB communication, modify the example processes and add your own tasks to the source code. If some trouble would occur on debugging, it's here. - When you declare large array or numerous global variables, it delays start-up initialization. - You may access to USB endpoints, before USB connection is established.
In this way, you can narrow down the source of trouble, easily.
Sorry for late replay, I was on my weekend. :)
After working more on my device, I noticed what the problem was. It seems that my code itself had no problems but there are some problem with USBlyzer on my PC. Because after uninstalling it, my HID device is recognized with no problem. But I currently have another problem that I'm working on it. I can send data to my HID usb device, but I cannot read anything from it. I don't think it that the problem comes from my PC side test code, but I need to investigate more. But there is some more small questions if you have time for them too.. :) 1- Is it ok to send 2 packets of data both equal to wMaxPacketSize with no delay? I mean can I simply split my buffer which is bigger than wMaxPacketSize to smaller buffers and send then like this(it is pseudo-code):
Send_Buffer_To_Host(buf, len) while (len > 0) { tmplen = len > wMaxPacketSize? wMaxPacketSize : len USB_SendEP(0x81, tmplen of buf, tmplen) len -= tmplen buf += tmplen }
I mean several calls for sending data will not cause any problem? 2-There are quite a lot unimplemented event callbacks in HID library. Is there any special one which is recommended to implement? 3-Should I make sure to protect my write and read function with SWI for interrupt protecting? Is there any other functions that is needed to protect too or not?
> It seems that my code itself had no problems but there are some problem with USBlyzer on my PC. Because after uninstalling it, my HID device is recognized with no problem.
Did you install two or more software sniffers on the PC? Depending on the sniffers, it causes conflict among sniffers each other. > 1- Is it ok to send 2 packets of data both equal to wMaxPacketSize with no delay?
As the interrupt endpoint doesn't have double buffer, the firmware has to wait until the first packet finishes to transfer to host, before it passes next packet to the endpoint.
For request/response pair over HID OUT/IN endpoint, typical code pattern is as follows.
The size of request / response affects the descriptors as follows. request (4 bytes) - output report - OUT endpoint response (128 bytes) - input report IN endpoint On the report descriptor, the size of the input and output reports exactly match to the requirement. On the OUT endpoint descriptor, set to equal or more size than the requirement - any value from 4 to 64 bytes will do. For IN endpoint, set it to 64 (maximum for full-speed). The input report is split into two 64 bytes packets.
usbdesc.c /* HID Report Descriptor */ const BYTE HID_ReportDescriptor[] = { HID_UsagePageVendor(0x00), HID_Usage(0x01), HID_Collection(HID_Application), // ---------- common global items ---------- HID_LogicalMin(0), HID_LogicalMaxS(0xFF), HID_ReportSize(8), // ---------- input report ---------- HID_ReportCount(128), // <------ set to 128 bytes HID_Usage(0x01), HID_Input(HID_Data | HID_Variable | HID_Absolute), // ---------- output report ---------- HID_ReportCount(4), // <------ set to 4 bytes HID_Usage(0x01), HID_Output(HID_Data | HID_Variable | HID_Absolute), HID_EndCollection, }; const BYTE USB_ConfigDescriptor[] = { ... ... /* Endpoint, HID Interrupt In */ USB_ENDPOINT_DESC_SIZE, /* bLength */ USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ USB_ENDPOINT_IN(1), /* bEndpointAddress */ USB_ENDPOINT_TYPE_INTERRUPT, /* bmAttributes */ WBVAL(0x0040), /* wMaxPacketSize */ <------- set to 64 bytes 0x20, /* 32ms */ /* bInterval */ /* Endpoint, HID Interrupt Out */ USB_ENDPOINT_DESC_SIZE, /* bLength */ USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ USB_ENDPOINT_OUT(2), /* bEndpointAddress */ USB_ENDPOINT_TYPE_INTERRUPT, /* bmAttributes */ WBVAL(0x0004), /* wMaxPacketSize */ <------- set to 4 - 64 bytes 0x20, /* 32ms */ /* bInterval */ ...
Simple implementation is here. - When output report comes from host, is it caught in EP2 interrupt - EP2 ISR (Interrupt Service Routine) parses the request. As the result, an input report is generated on a buffer. The first packet of IN transfer is sent to the IN EP1 in this interrupt. - When the transaction of the first packet finishes, EP1 ISR is called. In this ISR, the latter half of the input report is passed to the endpoint.
usbcfg.h #define USB_EP_EVENT 0x0007 // enable EP0, 1, 2 interrupts usbuser.c BYTE request_buf[4]; BYTE response_buf[128]; BOOL send_next = FALSE; void USB_EndPoint1 (DWORD event) { // IN EP if ( send_next ) { send_next = FALSE; USB_WriteEP( 0x81, &response_buf[64], 64 ); // send the second packet } } void USB_EndPoint2 (DWORD event) { // OUT EP DWORD byte_count; byte_count = USB_ReadEP( 0x02, request_buf ); // read output report // // parse the request here // and fill response_buf // USB_WriteEP( 0x81, response_buf, 64 ); // send the first packet send_next = TRUE; } void USB_EndPoint3 (DWORD event) { // EP3 is not used // Comment these lines // GetInReport(); // USB_WriteEP(0x83, &InReport, sizeof(InReport)); }
In above code, USB_EndPoint2() is called twice, when both of the first and second transaction finishes. send_next flag distinguishes these calls. This kind of flags and variables, which relate to USB communication, are initialized in USB_Configure_Event() - Set_Configuration request handler.
usbuser.c #if USB_CONFIGURE_EVENT void USB_Configure_Event (void) { if (USB_Configuration) { /* Check if USB is configured */ // For request / response exchange, the first input report is not required. // Comment these lines // GetInReport(); // USB_WriteEP(0x83, &InReport, sizeof(InReport)); // // Instead other initialization is placed here // send_next = FALSE; } } #endif
In above post, the first packet is passed to IN EP using USB_WriteEP() in the OUT EP ISR. This scheme is fine when it doesn't take so much time to generate the input report. But if it takes so long, waiting in the ISR is not a good idea. In such case, USB_WriteEP() moves to elsewhere, at the place where the report generation finishes.
Suppose that the request orders 64 x 2 bytes samples from ADC, starting at the arrival of the request. - The parser on the OUT EP ISR starts ADC. The ISR just finishes. - In the ADC ISR, the report buffer is filled one sample by one. - In the ADC ISR, USB_WriteEP() is called for the first packet, when the 64th sample is filled to the buffer. - The second packet is sent in the IN EP ISR, like above.
In this way, you can move USB_WriteEP() to anywhere, on the place it fits best.
Similarly, USB_ReadEP() is placed on anywhere. Suppose that your firmware doesn't want to be disturbed by the next request, until current process finishes. - In the OUT EP ISR, just raise a flog, without calling USB_ReadEP(). - The flag is checked in timer-ed polling or main-loop polling, when current process finishes. In this polling routine, USB_ReadEP() is called to starts next process.
While the OUT endpoint is occupied by the last packet, the USB engine returns NAK (not ready) to the host. Host waits until the endpoint becomes empty (NAK flow control). No packet is lost. When you move USB_ReadEP() and/or USB_WriteEP() out of the endpoint ISR, you have to care of atomic access of these functions, so as not to re-enter to these routines each other. USB_ReadEP() and USB_WriteEP() are used in the several part of USB ISR in the USB stack.
To use these functions in an ISR of ADC, timer, etc., the interrupt priority of these ISRs should be same as those of USB interrupt. To use them on main-loop task, SWI is applied for these functions. For SWI, see the discussion on this topic, from the post on 26-Sep-2009 03:46 GMT http://www.keil.com/forum/docs/thread15613.asp
Thanks for quick and great replies. :)
In above code, USB_EndPoint2() is called twice Why it is called twice? Shouldn't it get called only once when I receive data on EP2?
Is there any flag that shows a buffer in an IN endpoint has been sent to host(and host is received it)?
And regarding processing time, device has a really wide range of processing time, from a few milliseconds to even 1.5 mins, But I don't think it really makes problems for my application side, if this delay won't make any problem in MCU itself(for example these process delays will happen in ISR because of library code).
>> In above code, USB_EndPoint2() is called twice > Why it is called twice? > Shouldn't it get called only once when I receive data on EP2?
Auu, my bad ... It's EP1 (IN endpoint), instead of EP2 (OUT endpoint) The OUT EP ISR is called once, when the OUT endpoint receives the request from host. The IN EP ISR is called twice, when the IN endpoint sends each packet to the host. > Is there any flag that shows a buffer in an IN endpoint has been sent to host(and host is received it)?
These Bits on USB engine registers are available for polling, when interrupt is not enabled on the endpoint.
UDP_ISR (Interrupt Status Register) - EPxINT bit (IN and OUT endpoint) or UDP_CSRx (Endpoint Control and Status Register) - TXCOMP (IN endpoint), RX_DATA_BK0 (OUT endpoint)
Hi Tsuneo
Thanks for your great replies. :) I worked on my code for a lot and now I have no problem on sending and receiving small buffers(less than wMaxPacketSize). But I should say that I were not able to send any report that is bigger than wMaxPacketSize. Assume that I have set my INPUT and OUTPUT report size to 8 and report count to 128(so buffer is 128 bytes) and my wMaxPacketSize is 64. When I try to send data from host to device,I need to send them in 128 byte buffers(and trying to send less than this much generates errors), but I only receive first 64 bytes in my device. Similarly, when I need to read 128 bytes from device(less than that much is not allowed), I can only send first 64 bytes from device to endpoint, then because host side has not received enough data, it goes into deadlock for receiving reminder of data that never happens. Any idea how I can repair this problem? In addition, the is a flag for INPUT and OUTPUT reports called 'Byte Stream'. is it good to set this flag if all my report data is byte aligned?
I should say That was able to send 128 Byte buffer (bigger than wMaxPacketSize) from host to device on my OUTPUT report, so no problem for this part anymore... :) But my problem for INPUT report persists, and I cannot send data to host... :(
> When I try to send data from host to device,I need to send them in 128 byte buffers
Are you working on Windows? Then, the buffer size to pass to ReadFile()/Writefile() is 129 bytes (1 + 128). Report ID (1 byte) precedes before the 128 bytes body.
When the report descriptor on the device doesn't have any report ID, - Windows app: default report ID (0) precedes. - HID device: no report ID is attached.
Yes,I'm working on windows. And as I told in 2nd post, I have finally successfully transfered 128 bytes of data from host to device with no problem. But sending 128 bytes of data from device to host is still a problem that I cannot solve. My windows application needs to use a ReadFile with a 129 byte buffer length for getting data from device, but I cannot find a way to send a 128 byte report from device to host when wMaxPacketSize is 64.
> but I cannot find a way to send a 128 byte report from device to host when wMaxPacketSize is 64.
**sigh** I've already explained about the way in above post. See above code.
128 bytes input report is split into two 64 bytes packets. 1) The first packet is passed to the IN endpoint outside of the IN EP ISR. Usually, at the place where the input report is made up.
2) When the USB engine finishes to send the first packet, IN EP interrupt occurs. Then, the second packet is passed to the IN EP inside of the IN EP ISR.
3) When the USB engine finishes the second packet, another interrupt occurs, the IN EP ISR is called again. But this time, there is nothing to send. Therefore, a flag (or a counter) is required to do control this behavior.
This flag is set when the first packet is sent in 1) stage The IN EP ISR sees this flag. When it is set, it drops the flag and does 2) stage.
It seems that I forgot to say, but I have already tried this way of sending 128 Bytes of data to host by splitting it into two 64 bytes part and it didn't work.
How didn't it work?
Place a break point in the USB_EndPoint2() Is this ISR visited?