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

USB CDC ACM only starts with debugger (and only under specific conditions)

Hey all,

I have a USB CDC ACM application that works great when it starts, but I'm not able to get it running without the debugger.  This is SDK 17.0.2, there is a SoftDevice but all the BLE functionality is commented out right now, using a J-Link.  After downloading code, the rest of my application (blinking LEDs) runs fine, but the USB CDC ACM doesn't start (Windows 10).  Here are the only two conditions under which the USB CDC starts:

  1. Running the debugger from SES.
  2. Connect J-Link, erase the device, download the code to the device, then reconnect the J-Link and reset the device with the J-Link.

I added code to the BLE template example to make the application, and besides adding the app_usb_cdc_asm library (and pre-requisites for it) and a couple of peripherals (nrfx_twim with TWIM 0, nrfx_spim with SPIM 3, nrfx_timer with Timer 2), I don't think I added anything that would account for this.  I have logging turned off.  It's clearly my application because the usb_cdc_acm example works fine.  EDIT: I should add that the usb_cdc_acm example works on my DKs and my custom board but not my application - which pretty much confirms that it's something I'm doing along the way.

I'm under the assumption that somehow the app_usb_cdc_acm library is crashing without the debugger, but I'm not sure how to debug it without the debugger... I do have a red LED that I set to light if I get an error code a few times along the way, but it's not lighting...  If there anything obvious that I'm missing?

EDIT: Now that I've had a chance to hook it up to the scope, I can see that with the J-Link plugged in and issuing a reset, I get data on USB.  Without the J-Link, there is no activity on the USB data lines.

  • Hi,

    Would you be able to post your code/config, so that we could compare it to the app_usb_cdc_acm example?

    The only thing I could think of is some clock configs, or clocks that are not started, but I would have expected this to happen with the example on the custom board as well if that was an issue.

    Best regards,
    Jørgen

  • Yea I had the same thought, and I think in the end that this will have something to do with the clock for app_usb_cdc_acm and maybe the timer I'm using, but I'm just not seeing it.  Is there a way to share code privately?  The client is reluctant to allow sharing it publicly.

  • Actually, I got the okay to post some relevant code.  Here is my USB CDC init routine (you'll notice that I just wrap the app_usb_cdc_acm in a struct to ease consumption elsewhere - although none of that is implemented yet):

    /** @brief USB CDC ACM state structure */
    typedef struct
    {
        app_usbd_cdc_acm_t const *  cdc;          // Pointer to usb cdc acm class
        nrfx_drv_state_t            init_state;   // Init state of usb_cdc driver
        usb_cdc_status_t            state;        // Start/stop state of usb_cdc driver
    } usb_cdc_t;
    
    #define USB_CDC_DRIVER_INSTANCE                     \
    {                                                   \
        .cdc          = NULL,                           \
        .init_state   = NRFX_DRV_STATE_UNINITIALIZED,   \
        .state        = USB_CDC_STOPPED                 \
    }
    
    uint8_t data[64];
    
    APP_USBD_CDC_ACM_GLOBAL_DEF(m_app_cdc_acm,
                                cdc_acm_user_ev_handler,
                                CDC_ACM_COMM_INTERFACE,
                                CDC_ACM_DATA_INTERFACE,
                                CDC_ACM_COMM_EPIN,
                                CDC_ACM_DATA_EPIN,
                                CDC_ACM_DATA_EPOUT,
                                APP_USBD_CDC_COMM_PROTOCOL_AT_V250
    );
    
    nrfx_err_t usb_cdc_init(usb_cdc_t * cdc)
    {
        nrfx_err_t ret;
    
        ASSERT(cdc->init_state == NRFX_DRV_STATE_UNINITIALIZED);
    
        static const app_usbd_config_t usbd_config = {
            .ev_state_proc = usbd_user_ev_handler
        };
    
        ret = nrf_drv_clock_init();
        if (ret) nrf_gpio_pin_set(S_LED_R);
        APP_ERROR_CHECK(ret);
        
        nrf_drv_clock_lfclk_request(NULL);
    
        while(!nrf_drv_clock_lfclk_is_running())
        {
            /* Just waiting */
        }
    
        ret = app_timer_init();
        if (ret) nrf_gpio_pin_set(S_LED_R);
        APP_ERROR_CHECK(ret);
    
        app_usbd_serial_num_generate();
        usb_cdc_serial_num_generate();
    
        ret = app_usbd_init(&usbd_config);
        if (ret) nrf_gpio_pin_set(S_LED_R);
        APP_ERROR_CHECK(ret);
    
        app_usbd_class_inst_t const * class_cdc_acm = app_usbd_cdc_acm_class_inst_get(&m_app_cdc_acm);
        ret = app_usbd_class_append(class_cdc_acm);
        if (ret) nrf_gpio_pin_set(S_LED_R);
        APP_ERROR_CHECK(ret);
    
        cdc->init_state = NRFX_DRV_STATE_INITIALIZED;
        cdc->cdc = &m_app_cdc_acm;
    
        /* Start file handler driver */
        file_driver_init(&usb_cdc_file_driver, file_driver_event_handler);
        cdc->file_driver = usb_cdc_file_driver;
    
        return ret;
    }
    
    
    void usb_cdc_start(usb_cdc_t * cdc)
    {
        ASSERT(cdc->state == USB_CDC_STOPPED)
        app_usbd_enable();
        app_usbd_start();
    
        cdc->state = USB_CDC_STARTED;
    }
    
    void usb_cdc_process_queue(usb_cdc_t * cdc)
    {
        while (app_usbd_event_queue_process())
        {
            /* Nothing to do */
        }
    }
    
    static void cdc_acm_user_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*/
    				ret_code_t ret = app_usbd_cdc_acm_read(p_cdc_acm,
    													   data,
    													   READ_SIZE);
    				app_usbd_cdc_acm_write(p_cdc_acm, data, READ_SIZE);
    				UNUSED_VARIABLE(ret);
    			}
    			break;
            case APP_USBD_CDC_ACM_USER_EVT_PORT_CLOSE:
    			{
    				uint8_t clear_buffer[64];
    				size_t size = app_usbd_cdc_acm_rx_size(p_cdc_acm);
    				app_usbd_cdc_acm_read_any(p_cdc_acm, clear_buffer, size);
    			}
    			break;
            case APP_USBD_CDC_ACM_USER_EVT_TX_DONE:
                break;
            case APP_USBD_CDC_ACM_USER_EVT_RX_DONE:
    			{
    				ret_code_t ret;
    				do
    				{
    					if (++m_cdc_buffer_idx >= ARRAY_LIST_SIZE)
    						m_cdc_buffer_idx = 0;
    
    					/* Fetch data until internal buffer is empty */
    					ret = app_usbd_cdc_acm_read(p_cdc_acm,
    												data,
    												READ_SIZE);
    				} while (ret == NRF_SUCCESS);
    
    				/* Commands */
    				process_data(data);
    			}
    			break;
            default:
                break;
        }
    }
    
    static void usbd_user_ev_handler(app_usbd_event_type_t event)
    {
        /* Could probably switch to jump table for better readability/maintainability
         * but for now most of the events induce no further behavior
         */
        switch (event)
        {
            case APP_USBD_EVT_DRV_SUSPEND:
                {}
                break;
            case APP_USBD_EVT_DRV_RESUME:
                {}
                break;
            case APP_USBD_EVT_STARTED:
                {}
                break;
            case APP_USBD_EVT_STOPPED:
                {
                    app_usbd_disable();
                }
                break;
            case APP_USBD_EVT_POWER_DETECTED:
                {
                    if (!nrfx_usbd_is_enabled())
                        app_usbd_enable();
                }
                break;
            case APP_USBD_EVT_POWER_REMOVED:
                {
                    app_usbd_stop();
                }
                break;
            case APP_USBD_EVT_POWER_READY:
                {
                    app_usbd_start();
                }
                break;
            default:
                break;
        }
    }

    and in main, which is just main.c from the ble_app_template example with the following amendments:

    ...
    #include "cdc.h"
    ...
    static usb_cdc_t cdc = USB_CDC_DRIVER_INSTANCE;
    ...
    void main(void)
    {
    	nrfx_err_t err_code;
    
        // Initialize.
        //log_init();
        //timers_init();
        //buttons_leds_init(&erase_bonds);
        //power_management_init();
        //ble_stack_init();
        //gap_params_init();
        //gatt_init();
        //advertising_init();
        //services_init();
        //conn_params_init();
        //peer_manager_init();
    
        //// Start execution.
        //NRF_LOG_INFO("Template example started.");
        //application_timers_start();
    
        //advertising_start(erase_bonds);
    
        /* Setup blinkies to show signs of life */
        nrf_gpio_pin_set(S_LED_B);
        nrf_gpio_pin_clear(S_LED_R);
        nrf_gpio_cfg_output(S_LED_B);
        nrf_gpio_cfg_output(S_LED_R);
    
        /* Setup and start USB CDC */
        err_code = usb_cdc_init(&cdc);
        //APP_ERROR_CHECK(err_code);
        if (err_code)
        {
            nrf_gpio_pin_set(S_LED_R);
        }
        usb_cdc_start(&cdc);
    
        /* Blinky counter just to know we are alive */
        uint32_t i = 0;
    
        // Enter main loop.
        for (;;)
        {
            usb_cdc_process_queue(&cdc);
    
            if (i == 500000)
    		{
                nrf_gpio_pin_toggle(S_LED_B);
    			i = 0;
    		}
        }
    }

    My sdk_config is the ble_app_template sdk_config with logging turned off and some USB CDC stuff turned on.  So as you know, pretty long, but here it is:

    Actually, it won't let me post it - I'm guessing it's too long.  Let me know if there is somewhere else I can post it, or if there is something specific I can post from it.

  • Have you tried using LED toggling to see how far in the init process the code gets, or if it is completed successfully?

    Do you have the optional 32.768 kHz crystal on your custom board? If not, have you configured the clock driver correctly in your sdk_config.h file to use the internal RC? You should be able to upload long source files using Insert->"Image/Video/File", if not you can try to zip it first.

  • Unfortunately, I fixed this problem but cannot remember what I did... Sorry to anyone in the future with a similar problem that finds this.

Related