I want to develop mass storage device using LPC2148.I am just referring code from Keil for USBMem. My problem is - I get Get_descriptor command from PC to which I respond with device descriptor. On receiving 8th byte it generates Reset. Then I get Set_address command. On response to it, I execute Set Address command in protocol engine. Then again I get Get_descriptor command (It should actually have index field of 18 but I receive with 64). Again after 8th byte it generates Reset and again gives Set_address command instead of Get_descriptor command for Configuration descriptor. What can be the problem?
I am just referring code from Keil for USBMem.
This one?
LPC2148 USB Mass Storage Device Example http://www.keil.com/download/docs/307.asp
> I get Get_descriptor command from PC to which I respond with device descriptor. On receiving 8th byte it generates Reset. Then I get Set_address command. On response to it, I execute Set Address command in protocol engine. Then again I get Get_descriptor command (It should actually have index field of 18 but I receive with 64).
Up to this point, it's a usual enumeration course of Windows, even though it sounds like somewhat weird. Just because Microsoft doesn't know USB well.
> Again after 8th byte it generates Reset and again gives Set_address command instead of Get_descriptor command for Configuration descriptor.
At this point, enumeration goes out of the rail.
Did you change USB_MAX_PACKET0 to 8? If so, recover it to 64.
usbcfg.h #define USB_MAX_PACKET0 64
Tsuneo
Yes. I am referring the same code. And also I have set Max packet size to 64. When the host sends Get_descriptor command for the second time, length field should be 18. But I am not getting that.
> When the host sends Get_descriptor command for the second time, length field should be 18. But I am not getting that.
Your log misses bus reset before the Get_descriptor
Are you monitoring the enumeration sequence with debug output on LPC2148? Debug output often causes timing error on enumeration, especially when it is inserted on status stage process. Disable debug output once. Monitor the enumeration sequence with a software sniffer,
USBlyzer (1 month trial) http://www.usblyzer.com/ SourceUSB (2 wk trial) http://www.sourcequest.com/
Actually, a hardware bus analyzer is better.
Reset is occurring but I am paying attention to Set_address command only. Am I supposed to take any action on reset? I just send zero-length packet for acknowledgment to host and then execute Set Address command in protocol engine.I am not getting where exactly I am going wrong. On doing this I expect Get_descriptor command with length field 18. Can it be timing problem? i.e. am i not able to set device address within 2 seconds?
> Reset is occurring but I am paying attention to Set_address command only.
If your device gets bus reset after Set_Address, it suggests that the firmware fails to respond to the STATUS stage of Set_Address request.
> I just send zero-length packet for acknowledgment to host and then execute Set Address command in protocol engine.
The order is reversed. The USB engine of LPC family is aware of control transfer sequence, as follows
UM10139 LPC214x User Manual ics.nxp.com/.../user.manual.lpc2141.lpc2142.lpc2144.lpc2146.lpc2148.pdf
9.1 Set Address (Command: 0xD0, Data: write 1 byte) The address set in the device will take effect after the status phase of the setup token. (Alternately, issuing the Set Address command twice will set the address in the device immediately).
Then, the order of Set_Address process is, 1) execute Set Address command in protocol engine 2) send zero-length packet for acknowledgment to host (STATUS stage)
If you do it in reversed order, your device misses next request after Set_Address > Can it be timing problem?
USB spec defines timeout for request process in details as follows. The shortest timeout (50 ms) applies to Set_Address and Set_Configuration on enumeration. If the firmware put debug output in 9600 baud, 48 characters of debug output causes 50 ms timeout.
9.2.6.3 Set Address Processing (usb_20.pdf p246) After the reset/resume recovery interval, if a device receives a SetAddress() request, the device must be able to complete processing of the request and be able to successfully complete the Status stage of the request within 50 ms. In the case of the SetAddress() request, the Status stage successfully completes when the device sends the zero-length Status packet or when the device sees the ACK in response to the Status stage data packet.
9.2.6.4 Standard Device Requests For standard device requests that require no Data stage, a device must be able to complete the request and be able to successfully complete the Status stage of the request within 50 ms of receipt of the request. This limitation applies to requests to the device, interface, or endpoint.
For standard device requests that require data stage transfer to the host, the device must be able to return the first data packet to the host within 500 ms of receipt of the request. For subsequent data packets, if any, the device must be able to return them within 500 ms of successful completion of the transmission of the previous packet. The device must then be able to successfully complete the status stage within 50 ms after returning the last data packet.
For standard device requests that require a data stage transfer to the device, the 5-second limit applies. This means that the device must be capable of accepting all data packets from the host and successfully completing the Status stage if the host provides the data at the maximum rate at which the device can accept it. Delays between packets introduced by the host add to the time allowed for the device to complete the request.
What is meant by debug output? I am just setting or resetting some of the port pins
> What is meant by debug output?
A type of printf() debug Put debug string over UART. If you don't use it, it's OK.
Anyway, reverse the process order in Set_Address handler. What occurs?
I changed the sequence for setting address. But still getting Set Address command twice. When Get_descriptor command comes for 2nd time, length field should be only 18 or not?
> I changed the sequence for setting address. But still getting Set Address command twice.
Then, there are another bugs.
> When Get_descriptor command comes for 2nd time, length field should be only 18 or not?
See it using a sniffer. Windows don't always request the exact number.
KEIL original example is enumerated successfully. Compare sniffer log of yours with the original. And then, you'll find immediately the place where your code goes out of the rail.
At this stage of USB stack development, monitoring enumeration just on the MCU side isn't enough. Even when your code fails, USB engine doesn't report any error. Host puts bus reset on error, and enumeration starts over again. In this reason, monitoring bus is required.
Ok. I am trying to debug my program now. May be something is wrong with it. Will let u know as soon as I find it.
In LPC2148_USBMem example, instead of setting address of device on reception of Set_address command, they are setting it, on reception of IN packet. Is there any reason for that? I am setting it on reception of Set_address command only.
I'll show you the implementation details of Set_Address request handling, using two successful examples.
1) The first one is taken from KEIL implementation. KEIL follows traditional way, switch the device address after STATUS stage of Set_Address request - (USB_EndPoint0() - USB_EVT_SETUP - USB_REQUEST_SET_ADDRESS) At the SETUP stage handling, after parsing it as Set_Address request, - (USB_ReqSetAddress()) the device address is kept in a variable, USB_DeviceAddress. A flag on USB_DeviceAddress is enabled to show further process is required. - (USB_EndPoint0() - USB_EVT_IN) At the STATUS stage of Set_Address request, the firmware registers USB_DeviceAddress to the USB engine. SET_ADDRESS command is passed to the USB engine twice, to make it effective immediately (specific to NXP LPC family)
I suppose that KEIL has chosen this implementation for stack compatibility of other USB engine than NXP LPC family, like STM32, etc.
Excerpt from KEIL USB device code usbcore.c /* * USB Endpoint 0 Event Callback * Parameters: event * Return Value: none */ void USB_EndPoint0 (U32 event) { switch (event) { case USB_EVT_SETUP: // <----- SETUP transaction comes USB_SetupStage(); USB_DirCtrlEP(SetupPacket.bmRequestType.BM.Dir); EP0Data.Count = SetupPacket.wLength; /* Number of bytes to transfer */ switch (SetupPacket.bmRequestType.BM.Type) { case REQUEST_STANDARD: switch (SetupPacket.bRequest) { case USB_REQUEST_SET_ADDRESS: // <----- it is Set_Address request if (!USB_ReqSetAddress()) { // <----- pass it to Set_Address handler goto stall_i; } USB_StatusInStage(); break; ... ... case USB_EVT_IN : // <----- IN transaction just finishes if (SetupPacket.bmRequestType.BM.Dir == REQUEST_DEVICE_TO_HOST) { USB_DataInStage(); /* send data */ } else { // <----- this IN transaction is STATUS stage if (USB_DeviceAddress & 0x80) { // <----- process specific to STATUS stage of Set_Address USB_DeviceAddress &= 0x7F; // <----- recover original device address, masking flag bit USB_SetAddress(USB_DeviceAddress); // <----- set the address to the engine } } break; /* end case USB_EVT_IN */ ... ... /* * Set Address USB Request * Parameters: None (global SetupPacket) * Return Value: TRUE - Success, FALSE - Error */ __inline BOOL USB_ReqSetAddress (void) { switch (SetupPacket.bmRequestType.BM.Recipient) { case REQUEST_TO_DEVICE: USB_DeviceAddress = 0x80 | SetupPacket.wValue.WB.L; // <----- the address is kept on a variable with flag break; default: return (__FALSE); } return (__TRUE); } usbhw.c /* * USB Set Address Function * Parameters: adr: USB Address * Return Value: None */ void USB_SetAddress (U32 adr) { // <----- set the address to the engine, twice WrCmdDat(CMD_SET_ADDR, DAT_WR_BYTE(DEV_EN | adr)); /* Don't wait for next */ WrCmdDat(CMD_SET_ADDR, DAT_WR_BYTE(DEV_EN | adr)); /* Setup Status Phase */ }
2) The second one comes from LPCUSB by Bertrik LPCUSB registers the device address directly to the engine at the SETUP stage of Set_Address handling before STATUS stage comes, like other request handling. This way is allowed for NXP LPC family, thanking to the feature specific to LPC USB engine. In this case, SET ADDRESS command to the engine is just once.
Excerpt from LPCUSB lpcusb.svn.sourceforge.net/.../usbstdreq.c /** Local function to handle a standard device request @param [in] pSetup The setup packet @param [in,out] *piLen Pointer to data length @param [in,out] ppbData Data buffer. @return TRUE if the request was handled successfully */ static BOOL HandleStdDeviceReq(TSetupPacket *pSetup, int *piLen, U8 **ppbData) { U8 *pbData = *ppbData; switch (pSetup->bRequest) { case REQ_GET_STATUS: // bit 0: self-powered // bit 1: remote wakeup = not supported pbData[0] = 0; pbData[1] = 0; *piLen = 2; break; case REQ_SET_ADDRESS: // <------- Set_Address request handler USBHwSetAddress(pSetup->wValue); // <------- Set address to the engine before STATUS stage break; // <------- like handling of other requests case REQ_GET_DESCRIPTOR: DBG("D%x", pSetup->wValue); return USBGetDescriptor(pSetup->wValue, pSetup->wIndex, piLen, ppbData); lpcusb.svn.sourceforge.net/.../usbhw_lpc.c /** Sets the USB address. @param [in] bAddr Device address to set */ void USBHwSetAddress(U8 bAddr) { USBHwCmdWrite(CMD_DEV_SET_ADDRESS, DEV_EN | bAddr); // <------- set the address to the engine, once }
Both ways work. But which one is better for LPC family? I take LPCUSB way. The reason is two-fold, 1) It gives simpler code. 2) If we would take the traditional coding like KEIL, there is a chance that we miss the next request from host when the USB interrupt is disturbed by another higher-priority interrupts. The USB engine of NXP LPC family is designed to prevent this accident. There is no reason to stick to the traditional coding, wasting up NXP effort ;-)
Actually, this can be a silly question, but in Keil program, why they don't face problem of interrupt within interrupt? When initially I get Get_descriptor command, if i set TxPLen=18, then I don't get Reset generated by host after reception of 8th byte because when reset occurs I am in ISR only, busy writing 9th to 18th bytes. To avoid it when I get Get_descriptor command for 1st time, I set TxPLen=08 only. Can there be any better solution or is there any mistake from my side in understanding the program?
Actually, this can be a silly question, but in Keil program, why they don't face problem of interrupt within interrupt?
Because nested interrupts are disabled by default. This is not a silly question; it just shows that you need to read somewhat more. Try an ARM (7) architecture guide.
"Because nested interrupts are disabled by default."
They're not disabled; standard ARM(7) architecture doesn't have direct support per se. You have to insert code to provide the facility.