cancel
Showing results for 
Search instead for 
Did you mean: 

Nucleo-f439zi, Device Descriptor Request Failed, USB_OTG_FS from scratch

Stratosstratos
Associate II

Hello

We are using the Nucleo-f439zi (STM32F439ZITbu microcontroller) https://www.st.com/resource/en/reference_manual/dm00031020-stm32f405-415-stm32f407-417-stm32f427-437-and-stm32f429-439-advanced-arm-based-32-bit-mcus-stmicroelectronics.pdf

We have set up the Nucleo in device mode for USB_OTG_FS, we are able to receive setup packets through the FIFO and read them but when we try to reply to the Device Descriptor request, even though it seems like everything is correct (size to send out packet is 0x12, pointer to the 0x50001000 FIFO address and simplest device descriptor matrix, even though XFRC bit in DIEPINT0 gets set to signal the transfer complete.

The order of events is :

  1. The device gets physically connected to the host
  2. Host sends setup packet
  3. The device reads the FIFO and stores the setup packet.
  4. when setup phase is done (DOEPINT_STUP interrupt triggered) the device reads the setup packet and checks to see if it is a Device Descriptor Request (DDR), which it is.
  5. If it is a DDR then it writes the Device Descriptor to the FIFO and sets the CNAK and EPENA bits for the IN endpoint 0 (Function USB_OTG_Send_IN_Packet ).
  6. The XFRC interrupt gets triggered for IN endpoint 0 signaling that the Transfer Completed.
  7. The host resends the setup packet for a total of 3 to 4 tries and after that the device appears as a Device Descriptor Request Failed in Device Manager.

The host (windows 11 PC) still presents the Device Descriptor Request Failed after about 30 seconds.

Why doesn't the Device Descriptor Request get to the host ? It would be expected to be received by the host and a set address failed to be appeared on Device Manager .
The above mentioned function:

 

void USB_OTG_Send_IN_Packet(uint8_t n/*endpoint*/, uint16_t length) {
//PAGE 1368 IN data transfers  Packet 
	USB_OTG_FS_DIEPTSIZ0 = ( 1 << USB_OTG_DIEPTSIZ_PKTCNT_Pos);
	USB_OTG_FS_DIEPTSIZ0 |= (/*length*/0x12 & USB_OTG_DIEPTSIZ_XFRSIZ);
// Wait until the IN endpoint is ready (TxFIFO not full)
while ((USB_OTG_FS->GINTSTS & USB_OTG_GINTSTS_PTXFE) != USB_OTG_GINTSTS_PTXFE){}
//1
while ( USB_OTG_DIEPINT_TXFE == 0x0){}
if(lock==0){ lock=1;
//-----------------TIFO--------------------------------------------
		volatile uint32_t* tifo;
		   tifo=&USB_OTG_FIFOTX ;
		   //----------------------------
		   for (uint8_t ii=0; ii<5;ii++){
			  // USB_OTG_FIFOTX=descdata[ii*4];
			  *tifo = descdata[ii]; //change to ii

				   tifo++;}
		   lock=0;
}else{lock=0;}
//2    // Enable the endpoint to transmit IN data
   USB_OTG_FS_DIEPCTL0 |= USB_OTG_DIEPCTL_CNAK ; // Clear NAK
   USB_OTG_FS_DIEPCTL0 |= USB_OTG_DIEPCTL_EPENA; // MODIFY BEFORE ENABLING
}

 

I am also attaching the whole project.

1 ACCEPTED SOLUTION

Accepted Solutions

> If I am not mistaken USB_WritePacket() is called there only for the Isochronous case.

Indeed.

As you've shown above, in the non-isochronous case, the TXFE interrupt is enabled, which in turn (if FIFO sizes are set up properly and there was no event stalling the FIFO previously) throws the TXFE interrupt, in which data are pumped into FIFO.

I don't use Cube so I can't point out the chain of calls, start with the OTG interrupt handler, find the TXFE interrupt handling in it, and proceed from there.

> 0x12 which is equal to 18 bytes

I stand corrected.

JW

View solution in original post

5 REPLIES 5

You cannot *assume* sequence of operations, but you have to meticulously follow what's given in the "Device programming model" in RM (although it's very badly written, I admit).

The flow for IN packets is:

- set transfer size and  packet count in OTG_FS_DIEPTSIZx

- transfers through the Control endpoint are non-periodic, so there's no point in checking PTXFE (not to mention that that's a host-only flag)

- set CNAK and EPENA in OTG_FS_DIEPCTLx (and do it in one write, not two)

- only after that, you start writing data to FIFO, up to the transfer size you've written and up to  OTG_FS_DTXFSTSx.INEPTFSAV words (note, that everything related to FIFO is in words, and everything related to transfers themselves is in bytes). If you would want to send more data than is space in FIFO, use the OTG_FS_DIEPINTx.TXFE-initiated interrupt.

That all assuming that you have properly set the FIFO sizes beforehand.

JW

Nothing is "assumed", all the checks in the code that RM mentions are there. This post is created after reading the RM0090 several times.

- OTG_FS_DIEPTSIZ0 is getting set up in line 3 and 4 ( for 1 packet of 0x12 size respectively).
-You are right about PTXFE, fixed it.

- Changed the code to enable CNAK and EPENA in one line. 

-Writing data to the FIFO after enabling CNAK and EPENA is wrong, since it is mentioned to activate them as the last step both in the RM0090 and also in USB_EPStartXfer from the STM32Cube FW low layer driver for USB https://github.com/STMicroelectronics/stm32f4xx-hal-driver/blob/8f20a7dfd0b5902e1a5796be78ed2ceddd342085/Src/stm32f4xx_ll_usb.cstm32f4xx-hal-driver/Src/stm32f4xx_ll_usb.c at 8f20a7dfd0b5902e1a5796be78ed2ceddd342085 · STMicroelectronics/stm32f4xx-hal-driver · GitHub

Even though it is wrong, we tried it and didn't work.

-The Device descriptor is 0x12 bits , so 5 bytes long, the host stops trying to communicate long before DTXFSTS0.INEPTFSAV gets full.
Question : on page 1356 the is OTG_FS_TX0FSIZ mentioned in step 3 , but there is no such register in the datasheet neither in debug mode of stm32IDE, there is only a OTG_HS one. Why ?

In USB_EPStartXfer (), DIEPCTL.CNAK and EPENA are set at line 839, and FIFO is written after that, in USB_WritePacket() called at line 860.

I'm not sure which portion in RM0090 says otherwise, but it may be simply consequence of the poorly written documentation by Synopsys. Thing is, that Synopsys wrote it from the point of view of the developer, and while they obviously understand the various internal sections of the module, their mutual interconnections as well as their historical relationships in the way we don't, and unfortunately they have very bad documentation writers (it's a hard work and a severely underrated skill). Here in particular, setting CNAK and EPENA is the last thing to do *before data gets pumped* - but that latter thing goes without saying if you are familiar with the internals of the module.

> The Device descriptor is 0x12 bits , so 5 bytes long

Certainly not, but I guess you meant by this: The Standard Device Descriptor, as described by USB2.0 in Table 9-8, is 17 *bytes* long, so that's 5 *words*.

> on page 1356 the is OTG_FS_TX0FSIZ mentioned in step 3 , but there is no such register in the datasheet

That's just poor documentation again: in both HS and FS in device/peripheral mode you set up EP Tx FIFO in register in the global area at offset 0x28, and in both cases they work in exactly the same way.

JW


@waclawek.jan wrote:

In USB_EPStartXfer (), DIEPCTL.CNAK and EPENA are set at line 839, and FIFO is written after that, in USB_WritePacket() called at line 860.


If i am not mistaken USB_WritePacket() is called there only for the Isochronous case.

      if (ep->type != EP_TYPE_ISOC)
      {
        /* Enable the Tx FIFO Empty Interrupt for this EP */
        if (ep->xfer_len > 0U)
        {
          USBx_DEVICE->DIEPEMPMSK |= 1UL << (ep->num & EP_ADDR_MSK);
        }
      }
      else
      {
        if ((USBx_DEVICE->DSTS & (1U << 8)) == 0U)
        {
          USBx_INEP(epnum)->DIEPCTL |= USB_OTG_DIEPCTL_SODDFRM;
        }
        else
        {
          USBx_INEP(epnum)->DIEPCTL |= USB_OTG_DIEPCTL_SD0PID_SEVNFRM;
        }

        (void)USB_WritePacket(USBx, ep->xfer_buff, ep->num, (uint16_t)ep->xfer_len, dma);
      }

 


@waclawek.jan wrote:

> The Device descriptor is 0x12 bits , so 5 bytes long

Certainly not, but I guess you meant by this: The Standard Device Descriptor, as described by USB2.0 in Table 9-8, is 17 *bytes* long, so that's 5 *words*.


I meant 0x12 which is equal to 18 bytes which needs 5 words in the FIFO. https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-xusbi/2903d6e8-928b-43e7-9cd4-c6b9e8ec57f8

 

> If I am not mistaken USB_WritePacket() is called there only for the Isochronous case.

Indeed.

As you've shown above, in the non-isochronous case, the TXFE interrupt is enabled, which in turn (if FIFO sizes are set up properly and there was no event stalling the FIFO previously) throws the TXFE interrupt, in which data are pumped into FIFO.

I don't use Cube so I can't point out the chain of calls, start with the OTG interrupt handler, find the TXFE interrupt handling in it, and proceed from there.

> 0x12 which is equal to 18 bytes

I stand corrected.

JW