This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

How to enable and use DMA for USB bulk IN endpoints

Hi,

I'm using uVision 4 with the MCB2300 development board. I'm developing a software that periodically transmits data (status information) from the device to the host (as first step, the data are sent without being explicitly requested by the host as soon as a connection has been established). Thereto I've modified the USBCDC example included with uVision. The software transmits 20 bytes periodically, using USB_WriteEP (in usbhw.c, which uses the register interface). This works fine so far. Now I would like to use the DMA engine to avoid copying the bytes word by word into the register. I've enabled DMA in the USB configuration for logical IN endpoint 2 (physical endpoint 5 which is called CDC_DEP_IN in the example), and I have managed to set up DMA descriptors. But how do I trigger a DMA transfer?
I've tried several combinations of USB_DMA_Setup and USB_DMA_Enable. Manually setting the appropriate bit in DMA_REQ_SET to trigger the interrupt results in an error state. What am I doing wrong?
Could anyone please give me some hints concerning USB DMA? Thanks a lot.
I'll be back in January, after my Christmas vacation. I wish everybody a merry Christmas and a happy new year!

CC

Parents

  • Raise a flag in SendDataToHost().
    In USB_EndPoint2() callback, catch (event == USB_EVT_IN_DMA_NDR) and drop the flag

    That means you will have to disable the usb interrupt, setting up the flag together with the usb dma descriptor in the SendDataToHost method. And easily drop the flag in the endpoint USB ISR. IS it not possible to miss an important usb interrupt during the settin up a new dma descriptor?

    void SendDataToHost()
    {
       //set up dma descriptor
    
      //disable usb interrupts
    
      //if flag is set -> add Link = 1
    
      USB_DMA_Setup (CDC_DEP_IN, &DD);
    
      //enable usb interrupts again
    
     /* and these two lines must only be called if the flag was not set... right? */
      USB_DMA_Enable(CDC_DEP_IN);                  /* Enable DMA */
      LPC_USB->USBDMARSet = 1 << EPAdr(CDC_DEP_IN);
    
    }
    


    a) PC application sends a command (start / stop data streaming) over serial TX
    The device receives this command over the bulk OUT EP.

    Which command would that be - start transmitting/receiving?

    ::CreateFile() SetCommState ();
    Readfile() or Writefile() - is there a call to the usb device?

Reply

  • Raise a flag in SendDataToHost().
    In USB_EndPoint2() callback, catch (event == USB_EVT_IN_DMA_NDR) and drop the flag

    That means you will have to disable the usb interrupt, setting up the flag together with the usb dma descriptor in the SendDataToHost method. And easily drop the flag in the endpoint USB ISR. IS it not possible to miss an important usb interrupt during the settin up a new dma descriptor?

    void SendDataToHost()
    {
       //set up dma descriptor
    
      //disable usb interrupts
    
      //if flag is set -> add Link = 1
    
      USB_DMA_Setup (CDC_DEP_IN, &DD);
    
      //enable usb interrupts again
    
     /* and these two lines must only be called if the flag was not set... right? */
      USB_DMA_Enable(CDC_DEP_IN);                  /* Enable DMA */
      LPC_USB->USBDMARSet = 1 << EPAdr(CDC_DEP_IN);
    
    }
    


    a) PC application sends a command (start / stop data streaming) over serial TX
    The device receives this command over the bulk OUT EP.

    Which command would that be - start transmitting/receiving?

    ::CreateFile() SetCommState ();
    Readfile() or Writefile() - is there a call to the usb device?

Children
  • is it possible to get the number of bytes which were received by the last packet to determine if the packet was a normal data packet or a command packet? Is it also possible to use USB_ReadEP() because I will also use DMA for the BULK out endpoints?

    void EndpointISR()
    {
            /* use of dma */
            if(event & USB_EVT_OUT_DMA_EOT)
            {       /* End of Transfer */
            if (USB_DMA_BufAdr(CDC_DEP_OUT) != ((unsigned int)DataBuf0 + DataIn0))
                    {       /* Data Available */
    
                            /* determine if the rxd data is a cmd or a dmx data pkt */
    
    /* is it possible to get the size of the rxd packet to determine if the packet is a data packet or a command packet?
    */
                             if(command_pkt)
                            {
                                 USB_ReadEP(CDC_DEP_OUT, &CommandReq[0]);
                                 SetupCommand(&CommandReq[0]);
                                 return;
                            }
    
                    }
            }
            if (event & (USB_EVT_OUT_DMA_EOT) | (USB_EVT_OUT_DMA_NDR))
            {       /* End of Transfer or New Descriptor Request */
    
                    DD.BufAdr  = (unsigned int)DataBuf0 + DataIn0;
                       DD.BufLen  = DATA_TX_PKT_LEN;
                       DD.MaxSize = 64;              /* 64Byte for bulk-transfer */
                    DD.Cfg.Val = 0;              /* Initial DMA Configuration */
                      USB_DMA_Setup (CDC_DEP_OUT, &DD);             /* Setup DMA */
                      USB_DMA_Enable(CDC_DEP_OUT);
    
            }
    
    
              /* 2) */
            if(event & USB_EVT_IN_DMA_NDR)
                    dmaInProgress &= ~(1 << EPAdr(CDC_DEP_IN));
    
    }
    

  • >> Raise a flag in SendDataToHost().
    In USB_EndPoint2() callback, catch (event == USB_EVT_IN_DMA_NDR) and drop the flag

    > That means you will have to disable the usb interrupt, setting up the flag together with the usb dma descriptor in the SendDataToHost method. And easily drop the flag in the endpoint USB ISR. IS it not possible to miss an important usb interrupt during the settin up a new dma descriptor?

    Keil's USB_DMA_Setup() expects that the DD linked list is not touched by the firmware while DMA is going on the target EP. Therefore, while above flag raises, your firmware should not call SendDataToHost() for new transfer.

    If you want to run the linked list dynamically, you'll need more elaboration in the USB_DMA_Setup()
    - Firstly, reserve a new DD on USB_RAM, populate it by the function parameter (Next_DD_valid = false).
    - If there is no DD on the linked list, assign the new DD as the top of the linked list.
    - Else, find the tail DD on the linked list of the EP
    - - Fill Next_DD_pointer on the tail DD with the new DD
    - - Raise Next_DD_valid on the tail DD
    - checks EPxx_DMA_ENABLE bit for the target EP, by reading USBEpDMASt register.
    - If the bit drops, enable it by calling USB_DMA_Enable() routine. For IN EP, start DMA manually, like above post.

    Tsuneo

  • > is it possible to get the number of bytes which were received by the last packet to determine if the packet was a normal data packet or a command packet?

    The byte size of received transfer is written-back to the DD for OUT EP.

    > Is it also possible to use USB_ReadEP() because I will also use DMA for the BULK out endpoints?

    No. USB_ReadEP() is not available for the OUT EP, while DMA is enabled on the EP.

    Tsuneo

  • Hi,

    I'm back from Christmas vacation and first want to thank you all for your replies. And I have some good news: my example works now. My solution was almost correct, the only problem was that the data buffer the DMA descriptor points to was not located in USB RAM (obviously this is required - thanks for the hint). I've moved it to USB RAM (a higher region starting at 0x7fd01f00), and now my code works as expected.
    The next thing I'm going to do is to implement DMA for OUT endpoints...

    CC