2024-12-19 01:51 AM
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 :
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.
Solved! Go to Solution.
2024-12-23 03:23 AM - edited 2024-12-23 03:25 AM
> 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
2024-12-19 02:20 PM
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
2024-12-20 12:21 AM
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 ?
2024-12-21 07:08 AM
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
2024-12-23 01:12 AM
@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
2024-12-23 03:23 AM - edited 2024-12-23 03:25 AM
> 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