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

Where does USB CDC ACM data get read?

Hi All,

trying to understand the USB CDC ACM example for the Nordic 52840 using SDK17. I believe I've got it working, but there's one thing I'm not quite understanding. I'm using the example's event handler to notify me when data has been received, but when I'm in debug mode, I'm noticing that my data buffer is getting updated even before I make a call to app_usbd_cdc_acm_read() . Could anyone explain to me how this is happening? I was under the impression that I have to use the aforementioned function in order for my application to know where to put the received data. Thanks!

Parents
  • I was under the impression that I have to use the aforementioned function in order for my application to know where to put the received data.

    I agree with that, the second (buffer) and third (length) parameter in app_usbd_cdc_acm_read() control where to copy the data. How have you initialized the buffer (is is a global static)? Is there any other block of code that may be modifying the buffer?

  • The buffer is exactly as it is in the example, so yes, a global static in my main. 

    static char m_rx_buffer[READ_SIZE]

    The function app_usbd_cdc_acm_read() is only used in two locations! Once for the event APP_USBD_CDC_ACM_USER_EVT_PORT_OPEN and then in APP_USBD_CDC_ACM_USER_EVT_RX_DONE it is quite peculiar. There are no modifications to m_rx_buffer otherwise. 

    At first I thought maybe the APP_USBD_CDC_ACM_USER_EVT_PORT_OPEN  was getting triggered along with APP_USBD_CDC_ACM_USER_EVT_RX_DONE, but if I set breakpoints accordingly, when I send a character via terminal, only APP_USBD_CDC_ACM_USER_EVT_RX_DONE gets called. And then as I stated, even before invoking app_usbd_cdc_acm_read(), my buffer has already been updated. 

    I do notice that in the APP_USBD_CDC_ACM_USER_EVT_PORT_OPEN event, the comment before calling app_usbd_cdc_acm_read() says "Setup first transfer". I'm wondering if that first call to app_usbd_cdc_acm_read() creates some link to my buffer initially...? 

Reply
  • The buffer is exactly as it is in the example, so yes, a global static in my main. 

    static char m_rx_buffer[READ_SIZE]

    The function app_usbd_cdc_acm_read() is only used in two locations! Once for the event APP_USBD_CDC_ACM_USER_EVT_PORT_OPEN and then in APP_USBD_CDC_ACM_USER_EVT_RX_DONE it is quite peculiar. There are no modifications to m_rx_buffer otherwise. 

    At first I thought maybe the APP_USBD_CDC_ACM_USER_EVT_PORT_OPEN  was getting triggered along with APP_USBD_CDC_ACM_USER_EVT_RX_DONE, but if I set breakpoints accordingly, when I send a character via terminal, only APP_USBD_CDC_ACM_USER_EVT_RX_DONE gets called. And then as I stated, even before invoking app_usbd_cdc_acm_read(), my buffer has already been updated. 

    I do notice that in the APP_USBD_CDC_ACM_USER_EVT_PORT_OPEN event, the comment before calling app_usbd_cdc_acm_read() says "Setup first transfer". I'm wondering if that first call to app_usbd_cdc_acm_read() creates some link to my buffer initially...? 

Children
  • lorenzoFIL said:
    I do notice that in the APP_USBD_CDC_ACM_USER_EVT_PORT_OPEN event, the comment before calling app_usbd_cdc_acm_read() says "Setup first transfer". I'm wondering if that first call to app_usbd_cdc_acm_read() creates some link to my buffer initially...? 

    Looks like you are right, I can see from the declaration of app_usbd_cdc_acm_read():

    /**
     * @brief Reads data from CDC ACM serial port.
     *
     * This function uses internal buffer and double buffering for continuous transmission.
     *
     * If there is enough data in internal buffer to fill user buffer, NRF_SUCCESS is
     * returned and data is immediately available in the user buffer.
     *
     * If not, up to two user buffers can be scheduled, function returns NRF_ERROR_IO_PENDING
     * when first buffer is filled and @ref APP_USBD_CDC_ACM_USER_EVT_RX_DONE event is generated.
     *
     * @sa app_usbd_cdc_acm_read_any
     * @sa app_usbd_cdc_acm_rx_size
     *
     * @param[in]  p_cdc_acm CDC ACM class instance (defined by @ref APP_USBD_CDC_ACM_GLOBAL_DEF).
     * @param[out] p_buf     Output buffer.
     * @param[in]  length    Number of bytes to read.
     *
     * @retval NRF_SUCCESS          Data is stored into user buffer.
     * @retval NRF_ERROR_IO_PENDING Awaiting transmission, when data is stored into user buffer,
     *                              @ref APP_USBD_CDC_ACM_USER_EVT_RX_DONE event will be raised.
     * @retval NRF_ERROR_BUSY       There are already 2 buffers queued for transfers.
     * @retval other                Standard error code.
     */
    ret_code_t app_usbd_cdc_acm_read(app_usbd_cdc_acm_t const * p_cdc_acm,
                                     void *                     p_buf,
                                     size_t                     length);

    In specific:

    * @retval NRF_ERROR_IO_PENDING Awaiting transmission, when data is stored into user buffer,
    * @ref APP_USBD_CDC_ACM_USER_EVT_RX_DONE event will be raised.

    You may consider using app_usbd_cdc_acm_read_any() instead.

  • Alright, I think it's becoming more clear now. So the function app_usbd_cdc_acm_read() actually sets up the user buffer, and does not actually do the transfer of data from internal buffer to user at the time of invocation. 

    So if my understanding is correct, then on the first run of app_usbd_cdc_acm_read() , the return should be NRF_ERROR_IO_PENDING since (in my case) there has been no data received. After the USB receives as much data as specified in the first call to app_usbd_cdc_acm_read(), then APP_USBD_CDC_ACM_USER_EVT_RX_DONE event will occur. At which point I can then setup the buffer again for the next transfer by using app_usbd_cdc_acm_read(). If at this time there was a sufficient amount of data in my internal buffer, the function would return NRF_SUCCESS and then the buffer would also be updated accordingly? 

    Also, am I right when I say that the size of the internal buffer is 64 bytes? 

  • lorenzoFIL said:
    If at this time there was a sufficient amount of data in my internal buffer, the function would return NRF_SUCCESS and then the buffer would also be updated accordingly? 

     Yes. 

    lorenzoFIL said:
    Also, am I right when I say that the size of the internal buffer is 64 bytes? 

     The max endpoint size is 64bytes yes (NRFX_USBD_FEEDER_BUFFER_SIZE).

  • Alright, I think I've almost got it. Just curious though about the function app_usbd_cdc_arm_rx_size(). From my understanding this function should let me know how much data has been transferred from the internal buffer to the user buffer. The loop breaks when there is not enough data in the internal buffer to transfer into the user buffer. Why is it then that when I check app_usbd_cdc_arm_rx_size() after the loop, it doesn't show me a value of zero? 

     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));
                do
                {
                    /*Get amount of data transfered*/
                    size_t size = app_usbd_cdc_acm_rx_size(p_cdc_acm);
                    NRF_LOG_INFO("RX: size: %lu char: %c", size, m_rx_buffer[0]);
    
                    /* Fetch data until internal buffer is empty */
                    ret = app_usbd_cdc_acm_read(&m_app_cdc_acm,
                                                m_rx_buffer,
                                                READ_SIZE);
                } while (ret == NRF_SUCCESS);
                
                size = app_usbd_cdc_acm_rx_size(p_cdc_acm);
    
                bsp_board_led_invert(LED_CDC_ACM_RX);
                break;
            }

  • The app_usbd_cdc_acm_read() will only return success if the buffer is filled, while the app_usbd_cdc_acm_rx_size() will return number of bytes received even if it's not enough to fill the buffer. You may consider using app_usbd_cdc_acm_read_any().

    Best regards,
    Kenneth

Related