This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

SPI/USB will drop large multiple of packets in a row. Can't keep up with 1 Mbps on nrf52840 dk?

Hi,

I'm using an nrf52840 dk to query another device over spi and then pipe the response back USB to a host PC. Ideally, we're piping data at 1 Mbps (which should be very much doable for USB) and for a little bit, it will work perfectly. Then randomly we'll miss 4 kB worth of packets. After plotting the data stream it's become apparent that it's quite bad. We consistently lose at least a 3rd of our packets. Furthermore, the lost data is lost in chunks. Here's an example of a 'noisy' sine wave that we sent through the serial link.

I don't believe it's an issue with the spi because I've tied an oscope to the spi pins and observed perfect spi packets being transmitted at a rate of 1 Mbps (but I could only observe maybe 20 packets at a time and didn't decode them so ...). Given that we're missing consecutive packets, and it seems to happen more or less regularly, I think this might have to do with a USB overhead issue.  

Is there something I'm missing about USB? Is there an ideal packet size that I should send over the USB at a given moment? I've played with the following settings but they've seem to make no difference:

  • APP_USBD_CONFIG_SOF_HANDLING_MODE (tried both normal and compress queue)
  • APP_USBD_CONFIG_EVENT_QUEUE_SIZE (Tried setting this to the max, 64, in case I was trying to send packets too frequently and they needed a larger queue)

I'm pretty new with USB communication so any help would be amazing! 

EDIT: I've started saving the data to a buffer on the dev kit and reading it all out at once (ideally this decouples the SPI and USB). While I'm missing significantly fewer packets, I am still missing a few. Now I'm getting 75%-85% of all my packets. So I guess this means there is some function that's interrupting the SPI's interrupt, possibly? I've tried bumping the SPI interrupts priority but it doesn't seem to have changed anything. I'm completely out of ideas so any pointers would be a huge help.

Thanks,

Ryan

  • Sorry I did mean 500us. That was a silly mistake on my part. 512 bits every 500 us.

    I'm using v16.0.0 of the SDK (which I believe is the latest). In this setup, it's purely SPIM and USBD. At least, those are the only things that I'm making calls to.

  • The SPI clock? I set it to 4MHz because I was getting set up errors at 8MHz. 

    I should note that I found a bug in my code. Now if I save all the SPI codes and send them over USB *after* the fact, I don't drop any packets. So is there a recommended way of transferring data from SPI to USB on a packet-per-packet basis? 

    Earlier I had a naive nested conditional:

    app_usbd_event_queue_process();
    if (spi_xfer_done)
    {
        spis_xfer_done = 0;
        if (usb_xfer_done)
        {
            size = sprintf(some data);
            app_usbd_cdc_acm_write(some data, size);
            usb_xfer_done = 0;
        }
    }

    Where spi_xfer_done is set to 1 by the spi_event_handler & usb_xfer_done is set to 1 by a handler looking out for the APP_USBD_CDC_ACM_USER_EVT_TX_DONE event.

    This assumes that the USB driver is much faster than the SPI driver and will finish before the next SPI transaction is fired off/complete. Unfortunately, I must be missing something because experimentally speaking, this doesn't work. 

  • You can send a packet containing prbs code.  Google 'prbs'.  On the receiving side check the code for error.  You only printout when there is an error.  Then you'll know whether your spirits transfer is good. I am assuming you are handling Master/Slave.

  • Did you call app_usbd_cdc_acm_write() directly from the SPI interrupt callback prior to this? By moving it to main, with a flag "usb_xfer_done", you are essentially forcing the usbd process to occur in main priority instead of interrupt priority, which is a good thing, as you want interrupts to finish processing as quick as possible.

    Do you clear the usb_xfer_done flag somewhere else in that loop? Right now, your code will run the inner if-sentence regardless of if the usb is finished or not.

     

    Kind regards,

    Håkon

Related