Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs

Add CDC class to the USB Device Example

I am developing a USB keyboard + mouse using a NRF52833. I have use the USB Device Example from nRF5 SDK v17.1.0. I chose this example because my project is in C++ and had problems compiling the USB HID Composite Example regarding to "designator order for field 'app_usbd_hid_inst_t::p_hid_methods' does not match declaration order in 'app_usbd_hid_inst_t'".

I have implemented the keyboard + mouse HID to my project since the original example only works as a mouse. Now I need to add the CDC class to have a serial com port, any suggestions or examples on how to do it?

Parents
  • I'm sorry, I think I didn't express myself well, I was referring to sending the USB HID report of the keyboard as I did with the legacy driver, anyway I was able to solve the problem.

    Now I am having problems with the CDC class, it does not receive the first Byte. I've seen this problem come up several times on the forum but I still can't fix it. This is my code, I am using the nRF5 SDK v17.1.0:

    void usb_cdc_ev_handler(app_usbd_class_inst_t const *p_inst, app_usbd_cdc_acm_user_event_t event)
    {
        app_usbd_cdc_acm_t const * p_cdc_acm = app_usbd_cdc_acm_class_get(p_inst);
    
        switch (event)
        {
            case APP_USBD_CDC_ACM_USER_EVT_PORT_OPEN:
                NRF_LOG_INFO("USB serial port open");
    
                uint32_t n = app_usbd_cdc_acm_bytes_stored(p_cdc_acm);
                NRF_LOG_INFO("Bytes waiting: %d", n);
                NRF_LOG_FLUSH();
    
                /*
                    Setup first transfer.
                    If it does not do this, no Bytes can be received later.
                */
                app_usbd_cdc_acm_read(&serial_cdc_class, serial_rx_buffer, 1);
                break;
    
            case APP_USBD_CDC_ACM_USER_EVT_PORT_CLOSE:
                NRF_LOG_INFO("USB serial port close");
                NRF_LOG_FLUSH();
                break;
    
            case APP_USBD_CDC_ACM_USER_EVT_TX_DONE:
                /*NRF_LOG_INFO("Tx done");
                NRF_LOG_FLUSH();*/
                break;
    
            case APP_USBD_CDC_ACM_USER_EVT_RX_DONE:
                {     
                    NRF_LOG_INFO(" ");                          
                    NRF_LOG_INFO("---- New Rx ----------------------");
                    
                    uint32_t n = app_usbd_cdc_acm_bytes_stored(p_cdc_acm);
                    NRF_LOG_INFO("Bytes waiting: %d", n);
                    NRF_LOG_INFO("----");
                    
                    ret_code_t ret;
                    num_bytes = 0;
                    do
                    {
                        n = app_usbd_cdc_acm_rx_size(p_cdc_acm);
                        NRF_LOG_INFO("app_usbd_cdc_acm_rx_size() = %d", n);
     
                        ret = app_usbd_cdc_acm_read(&serial_cdc_class, &serial_rx_buffer[num_bytes], 1);
                        /*
                            ret = 0   -> NRF_SUCCESS
                            ret = 146 -> NRF_ERROR_IO_PENDING
                        */
                        char _ret[30];
                        if (ret == NRF_SUCCESS)
                        {
                            strcpy(_ret, "NRF_SUCCESS");
                        }
                        else if (ret == NRF_ERROR_IO_PENDING)
                        {
                            strcpy(_ret, "NRF_ERROR_IO_PENDING");
                        }
                        else if (ret == NRF_ERROR_BUSY)
                        {
                            strcpy(_ret, "NRF_ERROR_BUSY");
                        }
                        else
                        {
                            strcpy(_ret, "OTHER");
                        }
                        NRF_LOG_INFO("ret = %d -> %s", ret, _ret);
                        NRF_LOG_INFO("serial_rx_buffer[%d] = (int)%d -> (char)%c", num_bytes, serial_rx_buffer[num_bytes], serial_rx_buffer[num_bytes]);
                        NRF_LOG_INFO("----");
                        NRF_LOG_FLUSH();
    
                        num_bytes++;
                    } while (ret == NRF_SUCCESS);  // Fetch data until internal buffer is empty.
                }
                break;
    
            default:
                break;
        }
    }

    And this is the log screen when I send "123" from another serial terminal:

    <info> app: USB serial port open
    
    <info> app: Bytes waiting: 0
    
    <info> app:  
    
    <info> app: ---- New Rx ----------------------
    
    <info> app: Bytes waiting: 2
    
    <info> app: ----
    
    <info> app: app_usbd_cdc_acm_rx_size() = 1
    
    <info> app: ret = 0 -> NRF_SUCCESS
    
    <info> app: serial_rx_buffer[0] = (int)50 -> (char)2
    
    <info> app: ----
    
    <info> app: app_usbd_cdc_acm_rx_size() = 1
    
    <info> app: ret = 0 -> NRF_SUCCESS
    
    <info> app: serial_rx_buffer[1] = (int)51 -> (char)3
    
    <info> app: ----
    
    <info> app: app_usbd_cdc_acm_rx_size() = 1
    
    <info> app: ret = 146 -> NRF_ERROR_IO_PENDING
    
    <info> app: serial_rx_buffer[2] = (int)0 -> (char)
    
    <info> app:  
    
    <info> app: ---- New Rx ----------------------
    
    <info> app: Bytes waiting: 2
    
    <info> app: ----
    
    <info> app: app_usbd_cdc_acm_rx_size() = 1
    
    <info> app: ret = 0 -> NRF_SUCCESS
    
    <info> app: serial_rx_buffer[0] = (int)50 -> (char)2
    
    <info> app: ----
    
    <info> app: app_usbd_cdc_acm_rx_size() = 1
    
    <info> app: ret = 0 -> NRF_SUCCESS
    
    <info> app: serial_rx_buffer[1] = (int)51 -> (char)3
    
    <info> app: ----
    
    <info> app: app_usbd_cdc_acm_rx_size() = 1
    
    <info> app: ret = 146 -> NRF_ERROR_IO_PENDING
    
    <info> app: serial_rx_buffer[2] = (int)49 -> (char)1
    
    <info> app: ----
    
    <info> app:  
    
    <info> app: ---- New Rx ----------------------
    
    <info> app: Bytes waiting: 2
    
    <info> app: ----
    
    <info> app: app_usbd_cdc_acm_rx_size() = 1
    
    <info> app: ret = 0 -> NRF_SUCCESS
    
    <info> app: serial_rx_buffer[0] = (int)50 -> (char)2
    
    <info> app: ----
    
    <info> app: app_usbd_cdc_acm_rx_size() = 1
    
    <info> app: ret = 0 -> NRF_SUCCESS
    
    <info> app: serial_rx_buffer[1] = (int)51 -> (char)3
    
    <info> app: ----
    
    <info> app: app_usbd_cdc_acm_rx_size() = 1
    
    <info> app: ret = 146 -> NRF_ERROR_IO_PENDING
    
    <info> app: serial_rx_buffer[2] = (int)49 -> (char)1
    
    <info> app: ----

    You can see that on the first try it only receives "23" and then the null character when ret == NRF_ERROR_IO_PENDING.
    On subsequent attempts it always receive "231".

    It's weird that the app_usbd_cdc_acm_bytes_stored() function always returns 2 Bytes when actually I'm sending 3 Bytes.

    I have also tried with app_usbd_cdc_acm_read_any() getting the same result.

  • Hi,

     

    Don't log directly in your event handlers.

    You can push log messages, but only if deferred logging is enabled. This means that if sdk_config.h::NRF_LOG_DEFERRED = 1, you can log in the event handler, but you cannot call NRF_LOG_FLUSH then.

    This will cause issues.

     

    In your sdk_config.h, can you try to set "#define APP_USBD_CDC_ACM_ZLP_ON_EPSIZE_WRITE 1" and see if this helps as well?

     

    Kind regards,

    Håkon

  • That flag is already enabled:

    Also I have deleted all the logs inside the event handler and print the Rx buffer in the main but it still does not work:

    void usb_cdc_ev_handler(app_usbd_class_inst_t const *p_inst, app_usbd_cdc_acm_user_event_t event)
    {
        app_usbd_cdc_acm_t const * p_cdc_acm = app_usbd_cdc_acm_class_get(p_inst);
    
        switch (event)
        {
            case APP_USBD_CDC_ACM_USER_EVT_PORT_OPEN:
                /*
                    Setup first transfer.
                    If it does not do this, no Bytes can be received later.
                */
                app_usbd_cdc_acm_read(&serial_cdc_class, serial_rx_buffer, 1);
                break;
    
            case APP_USBD_CDC_ACM_USER_EVT_PORT_CLOSE:
                break;
    
            case APP_USBD_CDC_ACM_USER_EVT_TX_DONE:
                break;
    
            case APP_USBD_CDC_ACM_USER_EVT_RX_DONE:
                {
                    ret_code_t ret;
                    num_bytes = 0;
                    do
                    {
                        ret = app_usbd_cdc_acm_read(&serial_cdc_class, &serial_rx_buffer[num_bytes++], 1);
                    } while (ret == NRF_SUCCESS);  // Fetch data until internal buffer is empty.
    
                    serial_rx_buffer[num_bytes] = '\0';
    
                    flag_rx_done = true;
                }
                break;
    
            default:
                break;
        }
    }

    This is the log:

    <info> app: Rx 3 Bytes = 23
    
    <info> app: Rx 3 Bytes = 231
    
    <info> app: Rx 3 Bytes = 231
    
    <info> app: Rx 3 Bytes = 231

    The same result, the first Byte is missing and the third character of the first transmission is the null character '\0'.

    If I enable the log for the USBD_CDC_ACM module I get these messages:

    <debug> cdc_acm: Received 3 bytes. Stored in internal buffer.
    
    <info> cdc_acm: EPOUT_DATA: 04 done
    
    <info> app: -> Rx 3 Bytes = 23
    
    <debug> cdc_acm: Received 3 bytes. Stored in internal buffer.
    
    <info> cdc_acm: EPOUT_DATA: 04 done
    
    <info> app: -> Rx 3 Bytes = 231
    
    <debug> cdc_acm: Received 3 bytes. Stored in internal buffer.
    
    <info> cdc_acm: EPOUT_DATA: 04 done
    
    <info> app: -> Rx 3 Bytes = 231
    
    <debug> cdc_acm: Received 3 bytes. Stored in internal buffer.
    
    <info> cdc_acm: EPOUT_DATA: 04 done
    
    <info> app: -> Rx 3 Bytes = 231

    Update:

    I have added this log in the cdc_acm_consumer() function from the app_usbd_cdc_acm.c file to print the rx buffer:

            char *cstr = (char *)p_next->p_data.rx;
            NRF_LOG_DEBUG("Rx: %s", cstr);

    /**
     * @brief CDC ACM consumer.
     *
     * @note See @ref nrf_drv_usbd_consumer_t
     */
    static bool cdc_acm_consumer(nrf_drv_usbd_ep_transfer_t * p_next,
                                 void *                       p_context,
                                 size_t                       ep_size,
                                 size_t                       data_size)
    {
        app_usbd_cdc_acm_ctx_t * p_cdc_acm_ctx = (app_usbd_cdc_acm_ctx_t *) p_context;
        p_next->size = data_size;
    
        if (data_size <= p_cdc_acm_ctx->rx_transfer[0].read_left)
        {
            p_next->p_data.rx = p_cdc_acm_ctx->rx_transfer[0].p_buf;
    
            p_cdc_acm_ctx->rx_transfer[0].p_buf     += data_size;
            p_cdc_acm_ctx->bytes_read               += data_size;
            p_cdc_acm_ctx->rx_transfer[0].read_left -= data_size;
            NRF_LOG_DEBUG("Received %d bytes. Space left in user buffer: %d bytes.",
                          data_size,
                          p_cdc_acm_ctx->rx_transfer[0].read_left);
            return (p_cdc_acm_ctx->rx_transfer[0].read_left) != 0;
        }
        else
        {
            p_next->p_data.rx = p_cdc_acm_ctx->internal_rx_buf;
            p_cdc_acm_ctx->cur_read = data_size;
            NRF_LOG_DEBUG("Received %d bytes. Stored in internal buffer.", data_size);
            
            char *cstr = (char *)p_next->p_data.rx;
            NRF_LOG_DEBUG("Rx: %s", cstr);
            
            return false;
        }
    }

    And up to this point the Bytes are really being received well. This is the log:

    <debug> cdc_acm: Received 3 bytes. Stored in internal buffer.
    
    <debug> cdc_acm: Rx: 123
    
    <info> cdc_acm: EPOUT_DATA: 04 done
    
    <info> app: -> Rx 3 Bytes = 23
    
    <debug> cdc_acm: Received 3 bytes. Stored in internal buffer.
    
    <debug> cdc_acm: Rx: 123
    
    <info> cdc_acm: EPOUT_DATA: 04 done
    
    <info> app: -> Rx 3 Bytes = 231

Reply
  • That flag is already enabled:

    Also I have deleted all the logs inside the event handler and print the Rx buffer in the main but it still does not work:

    void usb_cdc_ev_handler(app_usbd_class_inst_t const *p_inst, app_usbd_cdc_acm_user_event_t event)
    {
        app_usbd_cdc_acm_t const * p_cdc_acm = app_usbd_cdc_acm_class_get(p_inst);
    
        switch (event)
        {
            case APP_USBD_CDC_ACM_USER_EVT_PORT_OPEN:
                /*
                    Setup first transfer.
                    If it does not do this, no Bytes can be received later.
                */
                app_usbd_cdc_acm_read(&serial_cdc_class, serial_rx_buffer, 1);
                break;
    
            case APP_USBD_CDC_ACM_USER_EVT_PORT_CLOSE:
                break;
    
            case APP_USBD_CDC_ACM_USER_EVT_TX_DONE:
                break;
    
            case APP_USBD_CDC_ACM_USER_EVT_RX_DONE:
                {
                    ret_code_t ret;
                    num_bytes = 0;
                    do
                    {
                        ret = app_usbd_cdc_acm_read(&serial_cdc_class, &serial_rx_buffer[num_bytes++], 1);
                    } while (ret == NRF_SUCCESS);  // Fetch data until internal buffer is empty.
    
                    serial_rx_buffer[num_bytes] = '\0';
    
                    flag_rx_done = true;
                }
                break;
    
            default:
                break;
        }
    }

    This is the log:

    <info> app: Rx 3 Bytes = 23
    
    <info> app: Rx 3 Bytes = 231
    
    <info> app: Rx 3 Bytes = 231
    
    <info> app: Rx 3 Bytes = 231

    The same result, the first Byte is missing and the third character of the first transmission is the null character '\0'.

    If I enable the log for the USBD_CDC_ACM module I get these messages:

    <debug> cdc_acm: Received 3 bytes. Stored in internal buffer.
    
    <info> cdc_acm: EPOUT_DATA: 04 done
    
    <info> app: -> Rx 3 Bytes = 23
    
    <debug> cdc_acm: Received 3 bytes. Stored in internal buffer.
    
    <info> cdc_acm: EPOUT_DATA: 04 done
    
    <info> app: -> Rx 3 Bytes = 231
    
    <debug> cdc_acm: Received 3 bytes. Stored in internal buffer.
    
    <info> cdc_acm: EPOUT_DATA: 04 done
    
    <info> app: -> Rx 3 Bytes = 231
    
    <debug> cdc_acm: Received 3 bytes. Stored in internal buffer.
    
    <info> cdc_acm: EPOUT_DATA: 04 done
    
    <info> app: -> Rx 3 Bytes = 231

    Update:

    I have added this log in the cdc_acm_consumer() function from the app_usbd_cdc_acm.c file to print the rx buffer:

            char *cstr = (char *)p_next->p_data.rx;
            NRF_LOG_DEBUG("Rx: %s", cstr);

    /**
     * @brief CDC ACM consumer.
     *
     * @note See @ref nrf_drv_usbd_consumer_t
     */
    static bool cdc_acm_consumer(nrf_drv_usbd_ep_transfer_t * p_next,
                                 void *                       p_context,
                                 size_t                       ep_size,
                                 size_t                       data_size)
    {
        app_usbd_cdc_acm_ctx_t * p_cdc_acm_ctx = (app_usbd_cdc_acm_ctx_t *) p_context;
        p_next->size = data_size;
    
        if (data_size <= p_cdc_acm_ctx->rx_transfer[0].read_left)
        {
            p_next->p_data.rx = p_cdc_acm_ctx->rx_transfer[0].p_buf;
    
            p_cdc_acm_ctx->rx_transfer[0].p_buf     += data_size;
            p_cdc_acm_ctx->bytes_read               += data_size;
            p_cdc_acm_ctx->rx_transfer[0].read_left -= data_size;
            NRF_LOG_DEBUG("Received %d bytes. Space left in user buffer: %d bytes.",
                          data_size,
                          p_cdc_acm_ctx->rx_transfer[0].read_left);
            return (p_cdc_acm_ctx->rx_transfer[0].read_left) != 0;
        }
        else
        {
            p_next->p_data.rx = p_cdc_acm_ctx->internal_rx_buf;
            p_cdc_acm_ctx->cur_read = data_size;
            NRF_LOG_DEBUG("Received %d bytes. Stored in internal buffer.", data_size);
            
            char *cstr = (char *)p_next->p_data.rx;
            NRF_LOG_DEBUG("Rx: %s", cstr);
            
            return false;
        }
    }

    And up to this point the Bytes are really being received well. This is the log:

    <debug> cdc_acm: Received 3 bytes. Stored in internal buffer.
    
    <debug> cdc_acm: Rx: 123
    
    <info> cdc_acm: EPOUT_DATA: 04 done
    
    <info> app: -> Rx 3 Bytes = 23
    
    <debug> cdc_acm: Received 3 bytes. Stored in internal buffer.
    
    <debug> cdc_acm: Rx: 123
    
    <info> cdc_acm: EPOUT_DATA: 04 done
    
    <info> app: -> Rx 3 Bytes = 231

Children
Related