cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H750: unexpected behaviour when trying to send multi-packet messages over USB

Stonerose35
Associate

Hi,

During the development of a bare-metal driver for USB CDC (virtual com port) in device mode i noticed an unexpected behaviour when trying to configure the USB Core to do a transmission of a multi packet message.

I've got the following code which prepares the transmission over usb for a data array "data" whose size is "dlen" over endpoint number "epNr". "epInBuffers" contain the pointers to the data buffers for each endpoint, "epInBytesTransferred" keep track of how many bytes have already been transmitted and "epInDataCntrs" the total amount of bytes to transmit.

__RAMFUNC
void prepareUSBTransfer(uint8_t epNr,const uint8_t*data,uint16_t dlen)
{
    USB_OTG_INEndpointTypeDef * inEndpoint = ((USB_OTG_INEndpointTypeDef*)(USB2_OTG_FS_PERIPH_BASE + USB_OTG_IN_ENDPOINT_BASE + epNr*0x20));
    uint32_t epCtrl = inEndpoint->DIEPCTL;
    uint16_t npackets;

    if(dlen==0)
    {
        npackets = 1;
        USB2_OTG_FS_DEVICE->DIEPEMPMSK &= ~(1UL << epNr);
    }
    else
    {
        npackets = ((dlen + epInMaxPacketSizes[epNr] - 1) / epInMaxPacketSizes[epNr]);
        USB2_OTG_FS_DEVICE->DIEPEMPMSK |= (1UL << epNr);
    }
    
    epInBuffers[epNr] = (uint8_t*)data;
    epInBytesTransferred[epNr]=0;
    epInDataCntrs[epNr] = dlen;

    inEndpoint->DIEPTSIZ = (npackets << USB_OTG_DIEPTSIZ_PKTCNT_Pos) | dlen;

    epCtrl &= ~(1 << USB_OTG_DIEPCTL_STALL_Pos);
    epCtrl |= (1 << USB_OTG_DIEPCTL_EPENA_Pos) | (1 << USB_OTG_DIEPCTL_CNAK_Pos);
    inEndpoint->DIEPCTL = epCtrl;

}

 The function essentially computes of how many packages the message consist and stores this in inEndpoint->DIEPTSIZ. Before that the interrupt in empty transmit buffer is unmasked, at the end the endpoint is enabled.

Then the transmit fifo buffer empty is fired, the code block below executes. The driver make sure that a maximum the max packet size is fed into the fifo, although the fifo is configured to allow more, then the amount of word to put into the fifo is calculated, the the words are transferred into the fifo.

                    #ifdef USB_DBG
                    sendStringBlocking("USB TX Fifo empty\r\n");
                    #endif
                    upperLimit=0;
                    if ((epInDataCntrs[epNr] - epInBytesTransferred[epNr]) > (epInMaxPacketSizes[epNr]))
                    {
                        upperLimit = epInMaxPacketSizes[epNr];
                    }
                    else
                    {
                        upperLimit = epInDataCntrs[epNr] - epInBytesTransferred[epNr];
                    }
                    uint16_t wordCount = (upperLimit + 3)/4;
                    uint16_t c=0;
                    while (c< wordCount && ((inEndpoint->DTXFSTS& 0xFFFF)!=0))
                    {
                        *((volatile uint32_t *)(USB_OTG_FS_PERIPH_BASE + USB_OTG_FIFO_BASE + epNr * USB_OTG_FIFO_SIZE)) = (((const struct T_UINT32_READ *)(const void *)(epInBuffers[epNr] + epInBytesTransferred[epNr]))->v);
                        epInBytesTransferred[epNr]+=4;
                        c++;
                    }   
                    if (epInBytesTransferred[epNr] >= epInDataCntrs[epNr])
                    {
                        USB2_OTG_FS_DEVICE->DIEPEMPMSK &= ~(1 << epNr);
                    }
                    inEndpoint->DIEPINT = (1 << USB_OTG_DIEPINT_TXFE_Pos);

 The unexpected behavior happens at line 18. With a maximum packet size of 64 bytes and a message containing  140 bytes i expected the following to happen (according from what i understood from the reference manual):

Upon the copying 64 bytes (16 words)) into the transmit buffer the size in DIEPTSIZ decreases by 64 and the number of packets by one. What really happens is that the size in DIEPTSIZ is set to zero and the packet number is decreased by one. 

When the transmit fifo buffer interrupt is fired a second time no data in transmitted. Also the tranmission complete interrupt isn't called resulting in the device not recognizing the end of transfer. The host sees a single packet of maximum packet size, therefore expecting more (zero- or not zero length packages) to arrive.

When transmitting less than the maximum packet size the packet count is decreased by one and the size decreased to zero at the expected moment (when transmitting 20 bytes when c is 4). 

What did is miss here?

Of course the driver work fine when splitting the message into maximum packet size chunks beforehand.

0 REPLIES 0