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

nRF52 USB instability with USB CDC and USB MSC instantiated

Hello,

I'm working with nRF52840 and SDK v15.2.

I implemented an USB layer with CDC and MSC classes based on nRF examples. All seems to work fine. The USB Serial port is available and I can send/receive data from it. The MSC drive is also properly mounted and I can access it.

But after receiving / sending data over USB serial port for 3, 10 or 15 minutes (it's random), I can't receive data anymore.

Looking at the USB event, I don't receive APP_USBD_CDC_ACM_USER_EVT_RX_DONE event reported from cdc_acm_user_ev_handler. But I noticed I receive APP_USBD_EVT_DRV_RESET event from USBD User defined event processor handler. 

The issue doesn't appear if USB MSC class is not instantiated.

Do you have any idea how to solve this issue ?

Best regards,

Aurélie

  • Hi,

    Can you check if you are able to reproduce the same issue with SDK 16.0.0? There is a lot of fixes added to the USBD driver and libraries in the releases after SDK 15.2.0.

    If not, can you provide a minimal project that can be used to reproduce this behavior?

    Best regards,
    Jørgen

  • Hi,

    Thanks for the suggestion about upgrading the SDK but I would rather keep the 15.2 version and find a workaround.

    I found how to reproduce this USB instability with a simple example, based on usbd_msc example (attached main.c modified below). I added USB CDC support and send a frame every minute to the com port.

    After several minutes, the com port is no more available and neither the mass storage.

    I didn't monitor the USB events for this example.

    /**
     * Copyright (c) 2016 - 2018, Nordic Semiconductor ASA
     *
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without modification,
     * are permitted provided that the following conditions are met:
     *
     * 1. Redistributions of source code must retain the above copyright notice, this
     *    list of conditions and the following disclaimer.
     *
     * 2. Redistributions in binary form, except as embedded into a Nordic
     *    Semiconductor ASA integrated circuit in a product or a software update for
     *    such product, must reproduce the above copyright notice, this list of
     *    conditions and the following disclaimer in the documentation and/or other
     *    materials provided with the distribution.
     *
     * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
     *    contributors may be used to endorse or promote products derived from this
     *    software without specific prior written permission.
     *
     * 4. This software, with or without modification, must only be used with a
     *    Nordic Semiconductor ASA integrated circuit.
     *
     * 5. Any software provided in binary form under this license must not be reverse
     *    engineered, decompiled, modified and/or disassembled.
     *
     * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
     * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
     * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     *
     */
    #include <stdint.h>
    #include <stdbool.h>
    #include <stddef.h>
    #include <inttypes.h>
    #include <stdlib.h>
    
    #include "sdk_config.h"
    
    #include "nrf.h"
    #include "nrf_block_dev.h"
    #include "nrf_block_dev_ram.h"
    #include "nrf_block_dev_empty.h"
    #include "nrf_block_dev_qspi.h"
    #include "nrf_block_dev_sdc.h"
    #include "nrf_drv_usbd.h"
    #include "nrf_drv_clock.h"
    #include "nrf_gpio.h"
    #include "nrf_atomic.h"
    #include "nrf_drv_power.h"
    
    #include "ff.h"
    #include "diskio_blkdev.h"
    
    #include "app_usbd.h"
    #include "app_usbd_core.h"
    #include "app_usbd_string_desc.h"
    #include "app_usbd_msc.h"
    #include "app_error.h"
    #include "app_timer.h"
    
    // ### Add app_usbd_cdc_acm.h to the list of include files. 
    #include "app_usbd_cdc_acm.h"
    
    #include "bsp.h"
    
    
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    
    /**@file
     * @defgroup usbd_msc_example main.c
     * @{
     * @ingroup usbd_msc_example
     * @brief USBD MSC example
     *
     */
    
    /**
     * @brief Enable power USB detection
     *
     * Configure if example supports USB port connection
     */
    #ifndef USBD_POWER_DETECTION
    #define USBD_POWER_DETECTION true
    #endif
    
    /**
     * @brief Mass storage class user event handler
     */
    static void msc_user_ev_handler(app_usbd_class_inst_t const * p_inst,
                                    app_usbd_msc_user_event_t     event);
    
    
    #define SDC_SCK_PIN     (NRF_GPIO_PIN_MAP(0, 19)) /* SD_CARD_MCLK */  ///< SDC serial clock (SCK) pin.
    #define SDC_MOSI_PIN    (NRF_GPIO_PIN_MAP(0, 14)) /* SD_CARD_MOSI */  ///< SDC serial data in (DI) pin.
    #define SDC_MISO_PIN    (NRF_GPIO_PIN_MAP(0, 21)) /* SD_CARD_MISO */  ///< SDC serial data out (DO) pin.
    #define SDC_CS_PIN      (NRF_GPIO_PIN_MAP(1, 0)) /* SD_CARD_CSN */   ///< SDC chip select (CS) pin.
    
    /**
     * @brief  SDC block device definition
     */
    NRF_BLOCK_DEV_SDC_DEFINE(
        m_block_dev_sdc,
        NRF_BLOCK_DEV_SDC_CONFIG(
            SDC_SECTOR_SIZE,
            APP_SDCARD_CONFIG(SDC_MOSI_PIN, SDC_MISO_PIN, SDC_SCK_PIN, SDC_CS_PIN)
         ),
         NFR_BLOCK_DEV_INFO_CONFIG("Nordic", "SDC", "1.00")
    );
    
    
    /**
     * @brief Block devices list passed to @ref APP_USBD_MSC_GLOBAL_DEF
     */
    #define BLOCKDEV_LIST() (                                   \
        NRF_BLOCKDEV_BASE_ADDR(m_block_dev_sdc, block_dev)      \
    )
    
    /**
     * @brief Endpoint list passed to @ref APP_USBD_MSC_GLOBAL_DEF
     */
    // ### Change the MSC endpoints in APP_USBD_MSC_ENDPOINT_LIST from 1 to 3 to avoid conflict with the CDC endpoints
    #define ENDPOINT_LIST() APP_USBD_MSC_ENDPOINT_LIST(3, 3)
    
    /**
     * @brief Mass storage class work buffer size
     */
    #define MSC_WORKBUFFER_SIZE (1024)
    
    /**
     * @brief Mass storage class instance
     */
    APP_USBD_MSC_GLOBAL_DEF(m_app_msc,
                            0,
                            msc_user_ev_handler,
                            ENDPOINT_LIST(),
                            BLOCKDEV_LIST(),
                            MSC_WORKBUFFER_SIZE);
    
    /**
     * @brief Timer instance
     */
    APP_TIMER_DEF(ssm_timer_id);
    
    /**
     * @brief  USB connection status
     */
    static bool m_usb_connected = false;
    
    
    static FATFS m_filesystem;
    
    static bool fatfs_init(void)
    {
        FRESULT ff_result;
        DSTATUS disk_state = STA_NOINIT;
    
        memset(&m_filesystem, 0, sizeof(FATFS));
    
        // Initialize FATFS disk I/O interface by providing the block device.
        static diskio_blkdev_t drives[] =
        {
            DISKIO_BLOCKDEV_CONFIG(NRF_BLOCKDEV_BASE_ADDR(m_block_dev_sdc, block_dev), NULL)
        };
    
        diskio_blockdev_register(drives, ARRAY_SIZE(drives));
    
        NRF_LOG_INFO("Initializing disk 0 (SDC)...");
        disk_state = disk_initialize(0);
        if (disk_state)
        {
            NRF_LOG_ERROR("Disk initialization failed.");
            return false;
        }
    
        NRF_LOG_INFO("Mounting volume...");
        ff_result = f_mount(&m_filesystem, "", 1);
        if (ff_result != FR_OK)
        {
            if (ff_result == FR_NO_FILESYSTEM)
            {
                NRF_LOG_ERROR("Mount failed. Filesystem not found. Please format device.");
            }
            else
            {
                NRF_LOG_ERROR("Mount failed: %u", ff_result);
            }
            return false;
        }
    
        return true;
    }
    
    static void fatfs_ls(void)
    {
        DIR dir;
        FRESULT ff_result;
        FILINFO fno;
    
        if (m_usb_connected)
        {
            NRF_LOG_ERROR("Unable to operate on filesystem while USB is connected");
            return;
        }
    
        NRF_LOG_INFO("\r\nListing directory: /");
        ff_result = f_opendir(&dir, "/");
        if (ff_result != FR_OK)
        {
            NRF_LOG_ERROR("Directory listing failed: %u", ff_result);
            return;
        }
    
        uint32_t entries_count = 0;
        do
        {
            ff_result = f_readdir(&dir, &fno);
            if (ff_result != FR_OK)
            {
                NRF_LOG_ERROR("Directory read failed: %u", ff_result);
                return;
            }
    
            if (fno.fname[0])
            {
                if (fno.fattrib & AM_DIR)
                {
                    NRF_LOG_RAW_INFO("   <DIR>   %s\r\n",(uint32_t)fno.fname);
                }
                else
                {
                    NRF_LOG_RAW_INFO("%9lu  %s\r\n", fno.fsize, (uint32_t)fno.fname);
                }
            }
    
            ++entries_count;
            NRF_LOG_FLUSH();
        } while (fno.fname[0]);
    
    
        NRF_LOG_RAW_INFO("Entries count: %u\r\n", entries_count);
    }
    
    static void fatfs_file_create(void)
    {
        FRESULT ff_result;
        FIL file;
        char filename[16];
    
        if (m_usb_connected)
        {
            NRF_LOG_ERROR("Unable to operate on filesystem while USB is connected");
            return;
        }
    
        (void)snprintf(filename, sizeof(filename), "HELLO.txt");
    
        NRF_LOG_RAW_INFO("Creating random file: %s ...", (uint32_t)filename);
        NRF_LOG_FLUSH();
    
        ff_result = f_open(&file, filename, FA_CREATE_ALWAYS | FA_WRITE);
        if (ff_result != FR_OK)
        {
            NRF_LOG_ERROR("\r\nUnable to open or create file: %u", ff_result);
            NRF_LOG_FLUSH();
            return;
        }
    
        ff_result = f_close(&file);
        if (ff_result != FR_OK)
        {
            NRF_LOG_ERROR("\r\nUnable to close file: %u", ff_result);
            NRF_LOG_FLUSH();
            return;
        }
        NRF_LOG_RAW_INFO("done\r\n");
    }
    
    static void fatfs_uninit(void)
    {
        NRF_LOG_INFO("Un-initializing disk 0 (QSPI)...");
        UNUSED_RETURN_VALUE(disk_uninitialize(0));
    }
    
    
    static void cdc_acm_user_ev_handler(app_usbd_class_inst_t const * p_inst,
                                        app_usbd_cdc_acm_user_event_t event);
    
    #define CDC_ACM_COMM_INTERFACE  1
    #define CDC_ACM_COMM_EPIN       NRF_DRV_USBD_EPIN2
    
    #define CDC_ACM_DATA_INTERFACE  2
    #define CDC_ACM_DATA_EPIN       NRF_DRV_USBD_EPIN1
    #define CDC_ACM_DATA_EPOUT      NRF_DRV_USBD_EPOUT1
    
    /**
     * @brief Interfaces list passed to @ref APP_USBD_CDC_ACM_GLOBAL_DEF
     * */
    #define CDC_ACM_INTERFACES_CONFIG()                 \
        APP_USBD_CDC_ACM_CONFIG(CDC_ACM_COMM_INTERFACE, \
                                CDC_ACM_COMM_EPIN,      \
                                CDC_ACM_DATA_INTERFACE, \
                                CDC_ACM_DATA_EPIN,      \
                                CDC_ACM_DATA_EPOUT)
    
    /**
     * @brief CDC_ACM class instance
     * */
    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
    );
    
    static char m_rx_buffer[NRF_DRV_USBD_EPSIZE * 16];
    static char m_tx_buffer[NRF_DRV_USBD_EPSIZE];
    
    /**
     * @brief User event handler @ref app_usbd_cdc_acm_user_ev_handler_t (headphones)
     * */
    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(&m_app_cdc_acm,
                                                       m_rx_buffer,
                                                       sizeof(m_rx_buffer));
                APP_ERROR_CHECK(ret);
                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:
            {
                /*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\r\n", size, m_rx_buffer[0]);
    
    
                /*Setup next transfer*/
                ret_code_t ret = app_usbd_cdc_acm_read(&m_app_cdc_acm,
                                                       m_rx_buffer,
                                                       sizeof(m_rx_buffer));
    
                ASSERT(ret == NRF_SUCCESS); /*Should not happen*/
                break;
            }
            default:
                break;
        }
    }
    // ### ----------------------------------------------------
    
    /**
     * @brief Class specific event handler.
     *
     * @param p_inst    Class instance.
     * @param event     Class specific event.
     */
    static void msc_user_ev_handler(app_usbd_class_inst_t const * p_inst,
                                    app_usbd_msc_user_event_t     event)
    {
        UNUSED_PARAMETER(p_inst);
        UNUSED_PARAMETER(event);
    }
    
    /**
     * @brief USBD library specific event handler.
     *
     * @param event     USBD library event.
     */
    static void usbd_user_ev_handler(app_usbd_event_type_t event)
    {
        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:
                UNUSED_RETURN_VALUE(fatfs_init());
                app_usbd_disable();
                bsp_board_leds_off();
                break;
            case APP_USBD_EVT_POWER_DETECTED:
                NRF_LOG_INFO("USB power detected");
    
                if (!nrf_drv_usbd_is_enabled())
                {
                    fatfs_uninit();
                    app_usbd_enable();
                }
                break;
            case APP_USBD_EVT_POWER_REMOVED:
                NRF_LOG_INFO("USB power removed");
                app_usbd_stop();
                m_usb_connected = false;
                break;
            case APP_USBD_EVT_POWER_READY:
                NRF_LOG_INFO("USB ready");
                app_usbd_start();
                m_usb_connected = true;
                break;
            case APP_USBD_EVT_DRV_RESET:
                NRF_LOG_INFO("USB reset");
                break;
            default:
                break;
        }
    }
    
    static void ssm_app_timer_handler(void *ctx)
    {
        ret_code_t ret;
        static int  frame_counter;
    
        size_t size = sprintf(m_tx_buffer, "Hello USB demo: %u\r\n", frame_counter);
    
        ret = app_usbd_cdc_acm_write(&m_app_cdc_acm, m_tx_buffer, size);
        if (ret == NRF_SUCCESS)
        {
            ++frame_counter;
        }
    }
    
    int main(void)
    {
        ret_code_t ret;
        static const app_usbd_config_t usbd_config = {
            .ev_state_proc = usbd_user_ev_handler
        };
    
        ret = NRF_LOG_INIT(app_usbd_sof_timestamp_get);
        APP_ERROR_CHECK(ret);
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    
        ret = nrf_drv_clock_init();
        APP_ERROR_CHECK(ret);
    
        nrf_drv_clock_lfclk_request(NULL);
        ret = app_timer_init();
        APP_ERROR_CHECK(ret);
    
        if (fatfs_init())
        {
            fatfs_ls();
            fatfs_file_create();
        }
    
        ret = app_usbd_init(&usbd_config);
        APP_ERROR_CHECK(ret);
        
        // ### Copy and paste the CDC class init phase 
        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);
        APP_ERROR_CHECK(ret);    
        
        app_usbd_class_inst_t const * class_inst_msc = app_usbd_msc_class_inst_get(&m_app_msc);
        ret = app_usbd_class_append(class_inst_msc);
        APP_ERROR_CHECK(ret);
    
        NRF_LOG_INFO("USBD MSC example started.");
    
        if (USBD_POWER_DETECTION)
        {
            ret = app_usbd_power_events_enable();
            APP_ERROR_CHECK(ret);
        }
        else
        {
            NRF_LOG_INFO("No USB power detection enabled\r\nStarting USB now");
    
            app_usbd_enable();
            app_usbd_start();
            m_usb_connected = true;
        }
    
        app_timer_create(&ssm_timer_id, APP_TIMER_MODE_REPEATED, ssm_app_timer_handler);
        app_timer_start(ssm_timer_id, APP_TIMER_TICKS(60000), NULL);
        
        while (true)
        {
            while (app_usbd_event_queue_process())
            {
                /* Nothing to do */
            }
    
            UNUSED_RETURN_VALUE(NRF_LOG_PROCESS());
            /* Sleep CPU only if there was no interrupt since last loop processing */
            __WFE();
        }
    }
    
    /** @} */
    

    Do you see any issue on the USB CDC and MSC implementation that could explain the issue ?

  • Is it possible to reproduce without an SD card? If not, I will not be able to test/debug your example until I get hold of an SD Card board on Monday.

  • We are facing the similar issue. If we write to the SDCard there is no error. But if we plugin the USB connection and read the data via msc the data sometimes is corrupted meaning lines are deleted or repeated spuriosly.

    But if we plugin the SD card directly to the PC there is no corruption.

    Any reason for this?

    Current SDK version 15.3.

  • Can you provide more details on how this issue looks, and post log files, example of corrupted files, etc, and include details about your system environment.

Related