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

USB CDC UART - Reassign buffer to app_usbd_cdc_acm_read_any

Hi,

I modified the USB BLE example (nRF5_SDK_17.0.2_d674dde/examples/peripheral/usbd_ble_uart) to sends bytes using a timer timeout as "fixed bytes transactions" or "waits for a special characters" are not feasible.

I used app_usbd_cdc_acm_read_any to receive partial bytes and the buffer pointer increments in usb event handler (if the event triggers before timeout) as 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;
        }

And in the timeout event, I have to reset the buffer pointer to the initial position for the next transaction. This is not possible as there was a null pointer check inside the function, i.e. if a buffer assigned already, the function is not allowed to change it. I removed that option, and it works fine in my tests. However, I would like to know if there any other consequences, modified code is below:

ret_code_t app_usbd_cdc_acm_read_any(app_usbd_cdc_acm_t const * p_cdc_acm,
                                     void *                     p_buf,
                                     size_t                     length)
{
    ASSERT(p_buf != NULL);
    ret_code_t ret;
    app_usbd_cdc_acm_ctx_t * p_cdc_acm_ctx = cdc_acm_ctx_get(p_cdc_acm);

    if (0U == (p_cdc_acm_ctx->line_state & APP_USBD_CDC_ACM_LINE_STATE_DTR))
    {
        /*Port is not opened*/
        return NRF_ERROR_INVALID_STATE;
    }

#if (APP_USBD_CONFIG_EVENT_QUEUE_ENABLE == 0)
    CRITICAL_REGION_ENTER();
#endif // (APP_USBD_CONFIG_EVENT_QUEUE_ENABLE == 0)
    if (p_cdc_acm_ctx->bytes_left > 0)
    {
        size_t to_copy = MIN(length, p_cdc_acm_ctx->bytes_left);
        memcpy(p_buf, p_cdc_acm_ctx->p_copy_pos, to_copy);
        p_cdc_acm_ctx->bytes_left -= to_copy;
        p_cdc_acm_ctx->p_copy_pos += to_copy;
        p_cdc_acm_ctx->last_read   = to_copy;
        ret = NRF_SUCCESS;
    }
    else
    {
//        if (p_cdc_acm_ctx->rx_transfer[0].p_buf == NULL)
//        {
            p_cdc_acm_ctx->rx_transfer[0].p_buf     = p_buf;
            p_cdc_acm_ctx->rx_transfer[0].read_left = length;
            nrf_drv_usbd_ep_t ep = data_ep_out_addr_get(app_usbd_cdc_acm_class_inst_get(p_cdc_acm));
            nrf_drv_usbd_handler_desc_t const handler_desc = {
               .handler.consumer = cdc_acm_single_shoot_consumer,
               .p_context        = p_cdc_acm_ctx
            };

            ret = app_usbd_ep_handled_transfer(ep, &handler_desc);
            if (ret == NRF_SUCCESS)
            {
                ret = NRF_ERROR_IO_PENDING;
            }
//        }
        else
        {
            ret = NRF_ERROR_BUSY;
        }
    }

#if (APP_USBD_CONFIG_EVENT_QUEUE_ENABLE == 0)
    CRITICAL_REGION_EXIT();
#endif // (APP_USBD_CONFIG_EVENT_QUEUE_ENABLE == 0)

    return ret;
}

The line "if (p_cdc_acm_ctx->rx_transfer[0].p_buf == NULL)" has been removed.

Parents Reply Children
  • My application requires to send data of varying size, say 1 byte to 240 bytes length packet at > 30ms between packets. When I am sending a 200 bytes packet over USB, the USB breaks them into multiple packets depends on the endpoint configurations (probably), so the event "case APP_USBD_CDC_ACM_USER_EVT_RX_DONE" (and app_usbd_cdc_acm_read_any()) triggers multiple times. Since nRF application doesn't know the length of the packet before hand, "app_usbd_cdc_acm_read_any" function need to wait for the next usb packet with the buffer pointed to the next free space (of the rx_buffer). When a packet transmission completes, the timer timeout triggers, which transfers buffer over BLE and reset to the buffer pointer to the beginning. And to reset the buffer, I need this changes in code, otherwise it doesn't. Or what you do to send data of varying size and no special characters encoded to detect the end?

    I can't see any problems till now, but don't need to ship an application with a bug. I wish to know why the code this check (if (p_cdc_acm_ctx->rx_transfer[0].p_buf == NULL)).

  •         if (p_cdc_acm_ctx->rx_transfer[0].p_buf == NULL)
            {
                p_cdc_acm_ctx->rx_transfer[0].p_buf     = p_buf;
                ...
            }
            else
            {
                ret = NRF_ERROR_BUSY;
            }

    It is used to check that the USB is not used for anything else simultaneously. Did it return NRF_ERROR_BUSY before you removed it? If so, perhaps one of your calls to app_usbd_cdc_acm_read_any() is interrupted by another call to app_usbd_cdc_acm_read_any()?

    Based on the behavior in the SDK\examples\peripheral\usbd_ble_uart example the APP_USBD_CDC_ACM_USER_EVT_RX_DONE event will be generated whenever at least one byte is received over the USB. However, it will not send the data over BLE until either the buffer is full or it receives a linefeed.

    Is that not the behavior in your case?

Related