USB CDC ACM large file transfer to PC

Hello everyone, I am looking for advice!

I am trying to find a way to send a file stored in an SD card from my nRF52840 to the PC usinig the USB CDC protocol.

The file size is quite large, in the order of Mb, do you think this solution is possible?

My idea is to read the file by block of 2048 byte and then send it trough USB using the .app_usbd_cdc_acm_write funcyion in a for cicle.

First issue I came across is the new line character used in app_usbd_cdc_acm_write function which is "\r\n", is there a way to change it to be '\n'?

The other issue regard the use of the app_usbd_cdc_acm_write which is asynchronous, how can i be sure that the all buffer has been sent before calling again the function?

In other words I need to wait that the transfer complted before reading new data from sd card and send them.

Thank you in advance!

Parents
  • Hi Lorenzo

    The file size is quite large, in the order of Mb, do you think this solution is possible?

    You can send as much data as you like, only the throughput will be limited. 

    My idea is to read the file by block of 2048 byte and then send it trough USB using the .app_usbd_cdc_acm_write funcyion in a for cicle.

    Bulk and interrupt endpoints are limited to 64 bytes only, so you have to split your data in 64 byte blocks. 

    First issue I came across is the new line character used in app_usbd_cdc_acm_write function which is "\r\n", is there a way to change it to be '\n'?

    The USB CDC driver doesn't care if you use newline or not. You can use '\n' if you want, or remove it completely. The question is what you expect to receive on the other side. 

    \r\n is used because it will trigger a line break in most terminal programs, which is where CDC ACM data usually ends up. 

    The other issue regard the use of the app_usbd_cdc_acm_write which is asynchronous, how can i be sure that the all buffer has been sent before calling again the function?

    You have to wait for the APP_USBD_CDC_ACM_USER_EVT_TX_DONE event to occur in the cdc_acm_user_ev_handler(..) function, this is signal that the TX buffer is ready to receive more data. If you try to call app_usbd_cdc_acm_write(..) while a transaction is still ongoing you will get the NRFX_ERROR_BUSY error in return. 

    I included some pseudo code below illustrating the concept (untested, so use at your own risk):

    uint8_t big_buffer[]; // Put your data here
    uint32_t buffer_index = 0;
    #define EP_MAX_SIZE 64
    while (true)
    {
        while (app_usbd_event_queue_process())
        {
            /* Nothing to do */
        }
    
        if(!usbd_cdc_tx_busy)
        {
            usbd_cdc_tx_busy = true;
            ret = app_usbd_cdc_acm_write(&m_app_cdc_acm, &big_buffer[buffer_index], EP_MAX_SIZE);
            if (ret == NRF_SUCCESS)
            {
                // In case of success, get the next 64 byte buffer ready for the next call to app_usbd_cdc_acm_write()
                buffer_index += EP_MAX_SIZE;
                // Add some code to check the size of buffer_index, and stop the operation when the end of the buffer is reached
            }
            else 
            {
                // In case of an error set the flag to false again, since no transaction has started
                usbd_cdc_tx_busy = false;
            }
        }
        
        UNUSED_RETURN_VALUE(NRF_LOG_PROCESS());
        /* Sleep CPU only if there was no interrupt since last loop processing */
        __WFE();
    }

    Then you just have to clear the flag when the TX_DONE callback occurs:

        case APP_USBD_CDC_ACM_USER_EVT_TX_DONE:
            usb_cdc_tx_busy = false;
            break;

    In other words I need to wait that the transfer complted before reading new data from sd card and send them.

    This is the basic way to do it, yes. 

    If you want to be more efficient you should read the next piece of data from the SD card while the current packet is being sent over USB, then you will be able to maximize the throughput. 

    Best regards
    Torbjørn

  • Hi Torbjørn, I followed your suggestion and i got the program working. Thank you!
    I only noticed a strange behaviour: if I use a buffer size (to read sd card data and then sending thorugh USB by 64 byte chunks) of 1024, or 2048 the data don't show up in terminal until i send 4096 bytes in total; if I read sd card data using a 4096 bytes buffer data only the first 4096 are printed.

    If I use a buffer size not multiple of 1024 the data are correctly sent and printed out on terminal.
    Do you have any suggestion?

Reply
  • Hi Torbjørn, I followed your suggestion and i got the program working. Thank you!
    I only noticed a strange behaviour: if I use a buffer size (to read sd card data and then sending thorugh USB by 64 byte chunks) of 1024, or 2048 the data don't show up in terminal until i send 4096 bytes in total; if I read sd card data using a 4096 bytes buffer data only the first 4096 are printed.

    If I use a buffer size not multiple of 1024 the data are correctly sent and printed out on terminal.
    Do you have any suggestion?

Children
Related