USBD CDC seems too slow

Hi,

I am trying to make a USB to BLE (central) device with nRF52840. I have removed the end character check from the "usbd_ble_uart" example and used a 50ms timeout instead. However, when sending about 240 bytes of data, the timer reaches the timeout value. It is not expected as I thought the device supports at least USB 2.0, which has 480Mbps or 12Mbps data rate, later I found the device is only capable of much lower speed (https://devzone.nordicsemi.com/f/nordic-q-a/38822/usbd-with-cdc-acm-class-windows-10-maximum-data-rate  CDC ACM binary file transfer (Windows): 215 kB/s (1720 kbit/s). 

Is there any way to improve the transfer rate? As my setup shows much lower speed than given in forums, as it cannot transfer 240 bytes within a 50ms window. Is it possible to get 100kbps with the device working as BLE central as well?

The code is given below:

case APP_USBD_CDC_ACM_USER_EVT_RX_DONE:
        {
            ret_code_t ret;
            //NRF_LOG_INFO("Bytes waiting: %d", app_usbd_cdc_acm_bytes_stored(p_cdc_acm));
            
            nrfx_timer_pause(&m_usb_cdc_timer);
            nrfx_timer_clear(&m_usb_cdc_timer);
            do
            {
                /*Get amount of data transferred*/
                size_t size = app_usbd_cdc_acm_rx_size(p_cdc_acm);
                rx_buffer += size;
                //NRF_LOG_INFO("RX: size: %lu char: %c", size, *rx_buffer);
               
                /* Fetch data until internal buffer is empty */
                ret = app_usbd_cdc_acm_read_any(&m_app_cdc_acm,
                                            rx_buffer,
                                            READ_SIZE);
                
            } while (ret == NRF_SUCCESS);
            
            
            nrfx_timer_resume(&m_usb_cdc_timer);
            break;
        }

Other question is, how to re-assign buffer to "app_usbd_cdc_acm_read_any" function before APP_USBD_CDC_ACM_USER_EVT_RX_DONE event? Because the buffer pointer increments during the "APP_USBD_CDC_ACM_USER_EVT_RX_DONE" event (see the code) which should be set to the beginning of the buffer on timeout event.

Thank you.

Ras

  • Hi Ras

    It should definitely be possible to get higher data throughput than what you are experiencing. 

    To be clear, is the problem receiving data from the USB host, or sending it?
    The code you attach is showing the receiving part. 

    Also, why are you incrementing the rx_buffer before receiving the data?
    In the original example the rx_buffer pointer is pointing to an empty buffer, and there is no need to increment this buffer until after the data has been transferred into it. 

    Best regards
    Torbjørn

  • Hi,

    Thanks for the reply. Yes, I am trying to get higher data rate for receiving part. I didn't show whole code here, the idea is:

    1. The first transfer is setup on APP_USBD_CDC_ACM_USER_EVT_PORT_OPEN event

    case APP_USBD_CDC_ACM_USER_EVT_PORT_OPEN:
            {
                /*Set up the first transfer*/
                ret_code_t ret = app_usbd_cdc_acm_read_any(&m_app_cdc_acm,
                                                       rx_bytes,
                                                       READ_SIZE);
            }

    This is the reason for incrementing rx_buffer pointer as I am trying to setup "app_usbd_cdc_acm_read_any" for next transaction.

    2. For each "APP_USBD_CDC_ACM_USER_EVT_RX_DONE" event, set buffer for next transaction and clear timer. The code is posted in my first post.

    3. On timer timeout event, sends the received data through BLE, and set the buffer pointer for "app_usbd_cdc_acm_read_any" to the beginning.

    static void timer1_event_handler(nrf_timer_event_t event_type, void * p_context)
    {
        // Copy the data and initialize BLE data transmission
        uint16_t len = get_rx_data_len();
        reset_rx_buf_ptr();
        
        memcpy(ble_buffer, rx_buffer, len);
        ble_nus_c_send_data(ble_buffer, len);
        
        /* Set buffer pointer to the beginning */
        ret_code_t ret = app_usbd_cdc_acm_read_any(&m_app_cdc_acm,
                                                       rx_buffer,
                                                       READ_SIZE);
    }

    In my testing, the function call to reset the buffer pointer to the beginning is not working. The next data write to the location set by the step 2.

  • Hi

    I will try to do some testing on my end to see if I can reproduce the issue you are seeing. 240 bytes in more than 50ms sounds very slow indeed. 

    How are you sending the data from the host side?

    Are you using some standard terminal software, or a custom application?

    Best regards
    Torbjørn

  • I am using Realterm with Winows10 to send data from a file of 240 bytes long.

    Could you please answer the second question?

  • Hi 

    I don't know if this is really answering the question, but in order to figure out a good way to do buffering of the incoming CDC data I made a small example that uses the nrf_queue library to organize a 'fifo of buffers', each 64 bytes in size. 

    The trick is to pre-load two buffers for the CDC driver when the APP_USBD_CDC_ACM_USER_EVT_PORT_OPEN event occurs, and then provide one more buffer every time the APP_USBD_CDC_ACM_USER_EVT_RX_DONE event occurs. 

    Then you ensure that the CDC driver will always have a buffer in spare, in addition to the buffer currently being written to, so that you don't get any data slow downs caused by application processing. 

    You can find my code example here:
    https://github.com/too1/usbd-ble-uart-c/tree/throughput_testing

    It only writes the incoming data to the log, but this can be replaced with BLE transfers quite easily. 

    Best regards
    Torbjørn

Related